E-Invoice - New state field when filling E-invoice info, validation of tin field and msic code field, etc...

This commit is contained in:
Dillon Ngo
2025-05-19 02:54:00 +08:00
parent d3de6f8997
commit 0643b28329
11 changed files with 230 additions and 44 deletions
@@ -0,0 +1,51 @@
<?php
namespace App\Classes\Modules\Addresses\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Addresses\Services\ListsStates;
use App\Classes\Modules\Addresses\Standards\Rules\CanListAddresses;
use App\Http\Resources\StateResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListStatesLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved States',
'message' => 'You have successfully retrieved a list of States'
];
}
/** @var ListsStates */
private $listsStates;
/**
* ListStatesLogic constructor.
* @param ListsStates $listsStates
*/
public function __construct(ListsStates $listsStates)
{
$this->listsStates = $listsStates;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$query = $this->listsStates->execute($this->listsStates->deserializeFilters($request->input('filters')));
return $this->collectionResponse(StateResource::collection($query));
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Addresses\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use App\Models\State;
use Illuminate\Database\Eloquent\Builder;
class ListsStates extends AbstractListRecord
{
/** @var State */
private $repository;
/**
* ListsStates constructor.
* @param State $repository
*/
public function __construct(State $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -93,7 +93,7 @@ class UpdateCompanyEInvoiceInfoLogic extends AbstractControllerLogic
}
$district = $this->fetchesDistrict->execute(['id' => $dto->districtId]);
$object = new AddressObject($dto->streetOne, $dto->streetTwo, $district->country_id, $district->state_id, $district->id, $dto->postCode);
$object = new AddressObject($dto->streetOne, $dto->streetTwo, $district->country_id, $dto->stateId, $district->id, $dto->postCode);
//Update Address
$this->canCreateAddress->passes($object);
@@ -6,9 +6,10 @@ use App\Classes\General\Interfaces\DataTransferObject;
class EInvoiceInfoDTO implements DataTransferObject
{
public int $tin;
public string $tin;
public int $msicCode;
public int $districtId;
public int $stateId;
public int $companyId;
public string $streetOne;
public string $streetTwo;
@@ -16,13 +17,14 @@ class EInvoiceInfoDTO implements DataTransferObject
public function __construct(array $data)
{
$this->tin = (int) ($data['tin'] ?? '');
$this->msicCode = (int) ($data['msic_code'] ?? '');
$this->districtId = (int) ($data['district_id'] ?? '');
$this->companyId = (int) ($data['company_id'] ?? '');
$this->tin = (string) ($data['tin'] ?? '');
$this->msicCode = (int) ($data['msic_code'] ?? 0);
$this->districtId = (int) ($data['district_id'] ?? 0);
$this->stateId = (int) ($data['state_id'] ?? 0);
$this->companyId = (int) ($data['company_id'] ?? 0);
$this->streetOne = (string) ($data['street_one'] ?? '');
$this->streetTwo = (string) ($data['street_two'] ?? '');
$this->postCode = (int) ($data['post_code'] ?? '');
$this->postCode = (int) ($data['post_code'] ?? 0);
}
public function toArray(): array
@@ -31,6 +33,7 @@ class EInvoiceInfoDTO implements DataTransferObject
'tin' => $this->tin,
'msic_code' => $this->msicCode,
'district_id' => $this->districtId,
'state_id' => $this->stateId,
'company_id' => $this->companyId,
'street_one' => $this->streetOne,
'street_two' => $this->streetTwo,
@@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Addresses;
use App\Classes\Modules\Addresses\ControllersLogic\ListStatesLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListStatesController
{
/**
* @param Request $request
* @param ListStatesLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListStatesLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class StateResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'state' => $this->name,
];
}
}
@@ -32,7 +32,7 @@
<div class="row m-b-15">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.street_one">
<label>Address Line 1</label>
<label>Billing Address Line 1</label>
<input class="form-control" name="street_one" v-model="parameters.street_one">
</validation-wrapper-component>
</div>
@@ -40,7 +40,7 @@
<div class="row m-b-15">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.street_two">
<label>Address Line 2</label>
<label>Billing Address Line 2</label>
<input class="form-control" name="street_two" v-model="parameters.street_two">
</validation-wrapper-component>
</div>
@@ -59,6 +59,14 @@
</validation-wrapper-component>
</div>
</div>
<div class="row m-b-15">
<div class="col-6 p-r-5">
<validation-wrapper-component selectable :validator="$v.parameters.state_id">
<label>State</label>
<selectable-component :endpoint="route('api.address.state.list')" section="stateListSection" valueColumn="id" :labelColumn="['state']" v-model="parameters.state_id"></selectable-component>
</validation-wrapper-component>
</div>
</div>
</div>
</div>
<div class="row m-b-15">
@@ -103,7 +111,7 @@
<div class="col">
<div class="padding-15 bg-master-lightest">
<p class="muted">Billing Address</p>
<p class="m-b-0">{{parameters.street_one}} {{parameters.street_two}}, {{districts[parseFloat(parameters.district_id) - 1].city}} {{parameters.post_code}} {{districts[parseFloat(parameters.district_id) - 1].state.name}}, {{districts[parseFloat(parameters.district_id) - 1].country.name}}</p>
<p class="m-b-0">{{parameters.street_one}} {{parameters.street_two}}, {{districts[parseFloat(parameters.district_id) - 1].city}} {{parameters.post_code}} {{states[parseFloat(parameters.state_id) - 1].state}}, {{districts[parseFloat(parameters.district_id) - 1].country.name}}</p>
</div>
</div>
</div>
@@ -128,68 +136,105 @@
</template>
<script>
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
import { required, helpers } from 'vuelidate/lib/validators'
import { required, minLength, maxLength, alphaNum, helpers } from 'vuelidate/lib/validators'
function mustContainLetterAndNumber(value) {
if (!value) return true;
const hasLetter = /[a-zA-Z]/.test(value);
const hasNumber = /\d/.test(value);
return hasLetter && hasNumber;
}
function notZero(value) {
return value !== 0 && value !== '0' && value !== null && value !== undefined
}
function fiveDigits(value) {
return /^\d{5}$/.test(value)
}
export default {
props: {
id: {
companyId: {
type: Number,
required: true
},
companyType: {
type: Number,
required: true
},
},
watch: {
'id': function() {
this.parameters.company_id = this.id;
this.parameters.company_id = this.companyId;
},
'eInvoiceData': function() {
this.resetForm();
this.parameters = {
company_id: this.id,
street_one: this.eInvoiceData.street_one,
street_two: this.eInvoiceData.street_two,
district_id: this.eInvoiceData.district.id,
post_code: this.eInvoiceData.post_code,
tin: this.eInvoiceData.tin,
msic_code: this.eInvoiceData.msic_code,
}
}
// 'eInvoiceData': function() {
// this.resetForm();
// this.parameters = {
// company_id: this.companyId,
// street_one: this.eInvoiceData.street_one,
// street_two: this.eInvoiceData.street_two,
// district_id: this.eInvoiceData.district.id,
// state_id: this.eInvoiceData.state.id,
// post_code: this.eInvoiceData.post_code,
// tin: this.eInvoiceData.tin,
// msic_code: this.eInvoiceData.msic_code,
// }
// }
},
computed: {
districts () {
districts () { //cief todo: 90
return this.$store.getters.getSelectableList('original_districtListSection');
},
states () {
return this.$store.getters.getSelectableList('original_stateListSection');
}
},
data() {
return {
step: 1,
parameters : {
company_id: this.id,
company_id: this.companyId,
street_one: '',
street_two: '',
district_id: '',
state_id: '',
post_code: 0,
tin: '',
msic_code: 0,
}
}
},
validations: {
parameters: {
street_one: { required },
street_two: {},
district_id: { required },
post_code: {
required,
notZero
},
tin: { required },
msic_code: {
required,
notZero
validations() {
const companyType = this.companyType
return {
parameters: {
street_one: { required },
street_two: {},
district_id: { required },
state_id: { required },
post_code: { required, notZero},
tin: {
...(companyType === 0
? {
required,
alphaNum,
minLength: minLength(11),
maxLength: maxLength(13)
}
: {}),
...(companyType === 1
? {
required,
alphaNum,
minLength: minLength(10),
maxLength: maxLength(12)
}
: {})
},
msic_code: {
notZero, fiveDigits
}
}
}
},
@@ -19,7 +19,7 @@
<div class="padding-15 bg-master-lightest">
<p class="muted">Billing Address</p>
<p class="mb-0">
<a class="ml-2" data-toggle="collapse" href="#billingDetails" role="button" aria-expanded="false" aria-controls="billingDetails">
<a data-toggle="collapse" href="#billingDetails" role="button" aria-expanded="false" aria-controls="billingDetails">
{{eInvoiceData.street_one}} {{eInvoiceData.street_two}}, {{eInvoiceData.district.name}}, {{eInvoiceData.post_code}} {{eInvoiceData.state.name}}, {{eInvoiceData.country.name}}
</a>
</p>
@@ -163,7 +163,8 @@
styleType="fill-in" type="requestEInvoiceB" size="large">
<e-invoice-info-form-component
:section="section"
:id="data.company.id"
:company-id="data.company.id"
:company-type="data.company.type"
v-on:eInvoiceInfoUpdated="updatedEInvoiceInfo($event)"
v-on:close="createAddress = !createAddress">
</e-invoice-info-form-component>
@@ -2,9 +2,14 @@
<div v-if="validator.$error" class="text-danger fs-10">
<small class="bold" v-for="(object, param) in validator.$params">
<span class="btn-block" v-if="object && object.type === 'required'">{{errorMessages[param]}}</span>
<span class="btn-block" v-if="object && object.type === 'maxLength'">{{errorMessages[param]}} {{object.max}} characters</span>
<span class="btn-block" v-if="object && object.type === 'minLength'">{{errorMessages[param]}} {{object.min}} characters</span>
<span class="btn-block" v-if="object && object.type === 'sameAs'">{{errorMessages[param]}} {{object.eq}} field</span>
<span class="btn-block" v-if="object && object.type === 'maxValue'">{{errorMessages[param]}} {{object.max}}</span>
<span class="btn-block" v-if="object && object.type === 'alphaNum'">{{errorMessages[param]}}</span>
<span class="btn-block" v-if="['fiveDigits'].includes(param)">
{{ errorMessages[param] }}
</span>
</small>
</div>
</template>
@@ -14,16 +19,20 @@
props: {
validator: {
required: true
}
},
},
data(){
return {
errorMessages:{
required: 'this field is required',
email: 'enter a valid email address',
maxLength: 'this field must have at most',
minLength: 'this field must have at least',
sameAs: 'this field must match the',
maxValue: 'this value must not exceeds'
maxValue: 'this value must not exceeds',
alphaNum: 'this value must be alphanumeric',
fiveDigits: 'this value must be exactly 5 digits', //custom
// notZero: 'this value cannot be zero',
},
}
},
+3
View File
@@ -1,5 +1,7 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Addresses\ListStatesController;
Route::group(['prefix' => 'address', 'as' => 'address.', 'namespace' => 'Addresses'], function () {
@@ -7,6 +9,7 @@ Route::group(['prefix' => 'address', 'as' => 'address.', 'namespace' => 'Address
Route::get('/list', 'ListAddressesController@list')->name('list');
Route::get('district/list', 'ListDistrictsController@list')->name('district.list');
Route::get('state/list', [ListStatesController::class, 'list'])->name('state.list');
Route::post('/create', 'CreateAddressController@create')->name('create');