From 8559332d343083ea2683550b3e0ee12f978a7563 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Sat, 10 May 2025 00:48:20 +0800 Subject: [PATCH] E-Invoice - Initial commit main feature integration --- .../CriteriaNotFulfilledException.php | 14 + .../General/Abstracts/AbstractRule.php | 5 +- .../Services/UpdatesAddressMetadata.php | 25 + .../Addresses/Services/UpsertsAddress.php | 36 + .../CreateBookingPaymentLogic.php | 26 +- .../FetchCompanyEInvoiceInfoLogic.php | 68 ++ .../UpdateCompanyDetailsLogic.php | 132 +++ .../UpdateCompanyEInvoiceInfoLogic.php | 112 +++ .../UpdateCompanyEInvoiceRequestLogic.php | 59 ++ .../UpdateCompanyNameAndDebtorLogic.php | 6 +- .../UpdateCompanyDetailsDTO.php | 61 ++ .../Services/UpdatesCompanyEInvoiceInfo.php | 25 + .../UpdatesCompanyEInvoiceRequest.php | 27 + .../Services/ExportsEInvoiceDebtorSummary.php | 9 +- .../CheckEInvoiceRuleLogic.php | 62 ++ .../CheckPurchaseOrderRuleLogic.php | 62 ++ .../CheckEInvoiceRuleDTO.php | 22 + .../CheckPurchaseOrderRuleDTO.php | 25 + .../DataTransferObjects/ConfirmBookingDTO.php | 25 + .../DataTransferObjects/EInvoiceInfoDTO.php | 40 + .../EInvoiceRequestDTO.php | 25 + .../Modules/Rules/Services/RuleEvaluator.php | 44 + .../Rules/CanProceedEInvoicePromptedRule.php | 56 ++ .../Rules/CanProceedPurchaseOrderRule.php | 96 ++ .../Standards/Rules/CanProceedTINRule.php | 55 ++ .../Constants/SegmentNameConstants.php | 7 + .../Constants/ServiceTypeNameConstants.php | 9 + .../Response/RuleEvaluationResult.php | 30 + .../FetchCompanyEInvoiceInfoController.php | 20 + .../UpdateCompanyDetailsController.php | 21 + .../UpdateCompanyEInvoiceInfoController.php | 29 + .../Controllers/Rules/CheckRuleController.php | 29 + app/Http/Resources/CompanyResource.php | 4 + app/Http/Resources/EInvoiceInfoResource.php | 31 + app/Http/Resources/RuleResource.php | 22 + ...150726_add_einvoice_to_addresses_table.php | 32 + ...5_04_150735_add_tin_to_companies_table.php | 38 + .../forms/EInvoiceInfoFormComponent.vue | 213 +++++ .../elements/EInvoiceInfoComponent.vue | 68 ++ .../BookingPaymentQuotationV2Component.vue | 833 ++++++++++++++++++ .../forms/ConfirmQuotationFormComponent.vue | 12 +- .../BookingDetailsSectionComponent.vue | 18 +- .../elements/CustomerProfileComponent.vue | 3 +- ...ngeCustomerCompanyDetailsFormComponent.vue | 147 ++++ .../forms/EInvoiceRequestFormComponent.vue | 65 ++ .../assets/vue/general/mixins/request.js | 8 +- routes/api.php | 2 + routes/company.php | 7 + routes/rule.php | 11 + routes/web.php | 2 +- 50 files changed, 2757 insertions(+), 21 deletions(-) create mode 100644 app/Classes/Exceptions/CriteriaNotFulfilledException.php create mode 100644 app/Classes/Modules/Addresses/Services/UpdatesAddressMetadata.php create mode 100644 app/Classes/Modules/Addresses/Services/UpsertsAddress.php create mode 100644 app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php create mode 100644 app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyDetailsLogic.php create mode 100644 app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceInfoLogic.php create mode 100644 app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceRequestLogic.php create mode 100644 app/Classes/Modules/Companies/DataTransferObjects/UpdateCompanyDetailsDTO.php create mode 100644 app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceInfo.php create mode 100644 app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceRequest.php create mode 100644 app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceRuleLogic.php create mode 100644 app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php create mode 100644 app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceRuleDTO.php create mode 100644 app/Classes/Modules/Rules/DataTransferObjects/CheckPurchaseOrderRuleDTO.php create mode 100644 app/Classes/Modules/Rules/DataTransferObjects/ConfirmBookingDTO.php create mode 100644 app/Classes/Modules/Rules/DataTransferObjects/EInvoiceInfoDTO.php create mode 100644 app/Classes/Modules/Rules/DataTransferObjects/EInvoiceRequestDTO.php create mode 100644 app/Classes/Modules/Rules/Services/RuleEvaluator.php create mode 100644 app/Classes/Modules/Rules/Standards/Rules/CanProceedEInvoicePromptedRule.php create mode 100644 app/Classes/Modules/Rules/Standards/Rules/CanProceedPurchaseOrderRule.php create mode 100644 app/Classes/Modules/Rules/Standards/Rules/CanProceedTINRule.php create mode 100644 app/Classes/ValueObjects/Constants/SegmentNameConstants.php create mode 100644 app/Classes/ValueObjects/Constants/ServiceTypeNameConstants.php create mode 100644 app/Classes/ValueObjects/Response/RuleEvaluationResult.php create mode 100644 app/Http/Controllers/Companies/FetchCompanyEInvoiceInfoController.php create mode 100644 app/Http/Controllers/Companies/UpdateCompanyDetailsController.php create mode 100644 app/Http/Controllers/Companies/UpdateCompanyEInvoiceInfoController.php create mode 100644 app/Http/Controllers/Rules/CheckRuleController.php create mode 100644 app/Http/Resources/EInvoiceInfoResource.php create mode 100644 app/Http/Resources/RuleResource.php create mode 100644 database/migrations/2025_05_04_150726_add_einvoice_to_addresses_table.php create mode 100644 database/migrations/2025_05_04_150735_add_tin_to_companies_table.php create mode 100644 resources/assets/vue/components/address/forms/EInvoiceInfoFormComponent.vue create mode 100644 resources/assets/vue/components/bookings/elements/EInvoiceInfoComponent.vue create mode 100644 resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue create mode 100644 resources/assets/vue/components/companies/forms/ChangeCustomerCompanyDetailsFormComponent.vue create mode 100644 resources/assets/vue/components/companies/forms/EInvoiceRequestFormComponent.vue create mode 100644 routes/rule.php diff --git a/app/Classes/Exceptions/CriteriaNotFulfilledException.php b/app/Classes/Exceptions/CriteriaNotFulfilledException.php new file mode 100644 index 00000000..da105690 --- /dev/null +++ b/app/Classes/Exceptions/CriteriaNotFulfilledException.php @@ -0,0 +1,14 @@ +getMessage()); } catch(\Exception $exception){ throw new RequestValidationException($exception->getMessage()); } } - } diff --git a/app/Classes/Modules/Addresses/Services/UpdatesAddressMetadata.php b/app/Classes/Modules/Addresses/Services/UpdatesAddressMetadata.php new file mode 100644 index 00000000..cfecb739 --- /dev/null +++ b/app/Classes/Modules/Addresses/Services/UpdatesAddressMetadata.php @@ -0,0 +1,25 @@ +billing = $billing; + $model->e_invoice = $eInvoice; + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Addresses/Services/UpsertsAddress.php b/app/Classes/Modules/Addresses/Services/UpsertsAddress.php new file mode 100644 index 00000000..ec130189 --- /dev/null +++ b/app/Classes/Modules/Addresses/Services/UpsertsAddress.php @@ -0,0 +1,36 @@ +addresses()->where('id', $id)->first() ?? new Address(); + + $model->street_one = $object->getStreetOne(); + $model->street_two = $object->getStreetTwo(); + $model->country_id = $object->getCountryId(); + $model->state_id = $object->getStateId(); + $model->district_id = $object->getDistrictId(); + $model->postcode = $object->getPostCode(); + + return $this->handler($company->addresses(), $model); + } +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index 478c71a4..6b75dd49 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -2,7 +2,7 @@ namespace App\Classes\Modules\Bookings\ControllersLogic; - +use App\Classes\Exceptions\CriteriaNotFulfilledException; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; @@ -16,9 +16,14 @@ use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance; use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; +use App\Classes\Modules\Rules\DataTransferObjects\ConfirmBookingDTO; +use App\Classes\Modules\Rules\Standards\Rules\CanProceedEInvoicePromptedRule; +use App\Classes\Modules\Rules\Standards\Rules\CanProceedPurchaseOrderRule; +use App\Classes\Modules\Rules\Standards\Rules\CanProceedTINRule; use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor; use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; +use App\Classes\Modules\Rules\Services\RuleEvaluator; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\TransactionType; @@ -80,6 +85,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var CalculatesBookingRefundAmount */ private $calculatesBookingRefundAmount; + /** @var RuleEvaluator */ + private $ruleEvaluator; + /** * CreateBookingPaymentLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -94,8 +102,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic * @param RecalculatesWalletBalance $recalculatesWalletBalance * @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount + * @param RuleEvaluator $ruleEvaluator */ - public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingOutstanding $calculatesBookingOutstanding, CreatesBillplzBill $createsBillplzBill, UpdatesWalletBalance $updatesWalletBalance, UpdatesTransactionStatus $updatesTransactionStatus, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor, CalculatesBookingRefundAmount $calculatesBookingRefundAmount) + public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingOutstanding $calculatesBookingOutstanding, CreatesBillplzBill $createsBillplzBill, UpdatesWalletBalance $updatesWalletBalance, UpdatesTransactionStatus $updatesTransactionStatus, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, RuleEvaluator $ruleEvaluator) { $this->fetchBookingQuotation = $fetchBookingQuotation; $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; @@ -109,15 +118,28 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $this->recalculatesWalletBalance = $recalculatesWalletBalance; $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; + $this->ruleEvaluator = $ruleEvaluator; } /** * @param Request $request * @return JsonResponse * @throws MalformedRequestException + * @throws CriteriaNotFulfilledException */ public function logic(Request $request) : JsonResponse { + $dto = new ConfirmBookingDTO($request->all()); + $result = $this->ruleEvaluator->evaluate([ + App()->make(CanProceedEInvoicePromptedRule::class), + App()->make(CanProceedTINRule::class), + App()->make(CanProceedPurchaseOrderRule::class), + ], $dto); + + if ($result->failed()) { + throw new CriteriaNotFulfilledException("- " . implode("
- ", $result->messages())); + } + $voucherCode = $request->input('voucher_code'); $booking = Booking::find($request->route('id')); diff --git a/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php b/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php new file mode 100644 index 00000000..ebb2d275 --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php @@ -0,0 +1,68 @@ + 'Retrieved Company E-Invoice Info', + 'message' => 'You have successfully retrieved a Company E-Invoice Info' + ]; + } + + /** @var CanFetchCompany */ + private $canFetchCompany; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** + * FetchCompanyEInvoiceInfoLogic constructor. + * @param CanFetchCompany $canFetchCompany + * @param FetchesCompany $fetchesCompany + */ + public function __construct(CanFetchCompany $canFetchCompany, FetchesCompany $fetchesCompany) + { + $this->canFetchCompany = $canFetchCompany; + $this->fetchesCompany = $fetchesCompany; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + $this->canFetchCompany->passes(); + + $company = $this->fetchesCompany->execute(['id' => $request->route('id')]); + $eInvoiceInfo = $company->addresses()->where('billing', '=', false)->where('e_invoice', '=', true)->latest()->first(); + + if($eInvoiceInfo){ + $eInvoiceInfo->tin = $company->tin; + $eInvoiceInfo->msic_code = $company->msic_code; + $eInvoiceInfo->e_invoice = $company->e_invoice; + } + else{ + return $this->response([]); + } + + return $this->resourceResponse(new EInvoiceInfoResource($eInvoiceInfo)); + } +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyDetailsLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyDetailsLogic.php new file mode 100644 index 00000000..7cb9c3c8 --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyDetailsLogic.php @@ -0,0 +1,132 @@ + 'Update Company Details', + 'message' => 'You have successfully updated the Company Details' + ]; + } + /** @var CanUpdateCompany */ + private $canUpdateCompany; + + /** @var UpdatesCompany */ + private $updatesCompany; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var UpdatesCompanyDebtor */ + private $updatesCompanyDebtor; + + /** @var CanCreateAddress */ + private $canCreateAddress; + + /** @var FetchesDistrict */ + private $fetchesDistrict; + + /** @var UpsertsAddress */ + private $upsertsAddress; + + /** @var UpdatesCompanyEInvoiceInfo */ + private $updatesCompanyEInvoiceInfo; + + /** @var UpdatesAddressMetadata */ + private $updatesAddressMetadata; + + + /** + * UpdateCompanyDetailsLogic constructor. + * @param CanUpdateCompany $canUpdateCompany + * @param UpdatesCompany $updatesCompany + * @param FetchesCompany $fetchesCompany + * @param UpdatesCompanyDebtor $updatesCompanyDebtor + * @param CanCreateAddress $canCreateAddress + * @param FetchesDistrict $fetchesDistrict + * @param FetchesCompany $fetchesCompany + * @param UpsertsAddress $upsertsAddress + * @param UpdatesCompanyEInvoiceInfo $updatesCompanyEInvoiceInfo; + * @param UpdatesAddressMetadata $updatesAddressMetadata + */ + public function __construct( + CanUpdateCompany $canUpdateCompany, + UpdatesCompany $updatesCompany, + FetchesCompany $fetchesCompany, + UpdatesCompanyDebtor $updatesCompanyDebtor, + CanCreateAddress $canCreateAddress, + FetchesDistrict $fetchesDistrict, + UpsertsAddress $upsertsAddress, + UpdatesCompanyEInvoiceInfo $updatesCompanyEInvoiceInfo, + UpdatesAddressMetadata $updatesAddressMetadata + ) + { + $this->canUpdateCompany = $canUpdateCompany; + $this->updatesCompany = $updatesCompany; + $this->fetchesCompany = $fetchesCompany; + $this->updatesCompanyDebtor = $updatesCompanyDebtor; + $this->canCreateAddress = $canCreateAddress; + $this->fetchesDistrict = $fetchesDistrict; + $this->fetchesCompany = $fetchesCompany; + $this->upsertsAddress = $upsertsAddress; + $this->updatesCompanyEInvoiceInfo = $updatesCompanyEInvoiceInfo; + $this->updatesAddressMetadata = $updatesAddressMetadata; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $dto = new UpdateCompanyDetailsDTO($request->all()); + + $company = $this->fetchesCompany->execute(['id' => $request->route('id')]); + $object = new CompanyObject($dto->name, $dto->reference, $company->business_type, $dto->type); + + $this->canUpdateCompany->passes($object); + + //Update Name and Debtor + $company = $this->updatesCompany->execute($company, $object); + if ($dto->debtor|| $company->first()->debtor !== null) { + $this->updatesCompanyDebtor->execute($company, $dto->debtor); + } + + //Update EInvoice Related Info + if($company->e_invoice){ + $district = $this->fetchesDistrict->execute(['id' => $dto->districtId]); + $addObj = new AddressObject($dto->streetOne, $dto->streetTwo, $district->country_id, $district->state_id, $district->id, $dto->postCode); + $this->canCreateAddress->passes($addObj); + + $address = $this->upsertsAddress->execute($company, $addObj, $dto->addressId); + $query = $this->updatesAddressMetadata->execute($address, false, true); + $this->updatesCompanyEInvoiceInfo->execute($company, $dto->tin, $dto->msicCode); + } + + return $this->resourceResponse(new CompanyResource($company)); + } +} + diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceInfoLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceInfoLogic.php new file mode 100644 index 00000000..9978006f --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceInfoLogic.php @@ -0,0 +1,112 @@ + 'EInvoice Info Update', + 'message' => 'You have successfully updated company E-Invoice information' + ]; + } + + /** @var CanCreateAddress */ + private $canCreateAddress; + + /** @var FetchesDistrict */ + private $fetchesDistrict; + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var CreatesAddress */ + private $createsAddress; + + /** @var RuleEvaluator */ + private $ruleEvaluator; + + /** @var UpdatesCompanyEInvoiceInfo */ + private $updatesCompanyEInvoiceInfo; + + /** @var UpdatesAddressMetadata */ + private $updatesAddressMetadata; + + /** + * UpdateCompanyEInvoiceInfoLogic constructor. + * @param CanCreateAddress $canCreateAddress + * @param FetchesDistrict $fetchesDistrict + * @param FetchesCompany $fetchesCompany + * @param CreatesAddress $createsAddress + * @param RuleEvaluator $ruleEvaluator; + * @param UpdatesCompanyEInvoiceInfo $updatesCompanyEInvoiceInfo; + * @param UpdatesAddressMetadata $updatesAddressMetadata + */ + public function __construct(CanCreateAddress $canCreateAddress, FetchesDistrict $fetchesDistrict, FetchesCompany $fetchesCompany, CreatesAddress $createsAddress, RuleEvaluator $ruleEvaluator, UpdatesCompanyEInvoiceInfo $updatesCompanyEInvoiceInfo, UpdatesAddressMetadata $updatesAddressMetadata) + { + $this->canCreateAddress = $canCreateAddress; + $this->fetchesDistrict = $fetchesDistrict; + $this->fetchesCompany = $fetchesCompany; + $this->createsAddress = $createsAddress; + $this->ruleEvaluator = $ruleEvaluator; + $this->updatesCompanyEInvoiceInfo = $updatesCompanyEInvoiceInfo; + $this->updatesAddressMetadata = $updatesAddressMetadata; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + * @throws \App\Classes\Exceptions\CriteriaNotFulfilledException + */ + public function logic(Request $request) : JsonResponse + { + $dto = new EInvoiceInfoDTO($request->all()); + $result = $this->ruleEvaluator->evaluate([ + App()->make(CanProceedEInvoicePromptedRule::class), + ], $dto); + + if ($result->failed()) { + throw new CriteriaNotFulfilledException("- " . implode("
- ", $result->messages())); + } + + $district = $this->fetchesDistrict->execute(['id' => $dto->districtId]); + $object = new AddressObject($dto->streetOne, $dto->streetTwo, $district->country_id, $district->state_id, $district->id, $dto->postCode); + + //Update Address + $this->canCreateAddress->passes($object); + $company = $this->fetchesCompany->execute(['id' => $dto->companyId]); + $address = $this->createsAddress->execute($company, $object); + $query = $this->updatesAddressMetadata->execute($address, false, true); + + //Update tin, msic code + $this->updatesCompanyEInvoiceInfo->execute($company, $dto->tin, $dto->msicCode); + + $query->tin = $dto->tin; + $query->msic_code = $dto->msicCode; + + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceRequestLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceRequestLogic.php new file mode 100644 index 00000000..86051447 --- /dev/null +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyEInvoiceRequestLogic.php @@ -0,0 +1,59 @@ + 'Updated EInvoice Request', + 'message' => 'You have successfully updated company E-Invoice request' + ]; + } + + /** @var FetchesCompany */ + private $fetchesCompany; + + /** @var UpdatesCompanyEInvoiceRequest */ + private $updatesCompanyEInvoiceRequest; + + + /** + * UpdateCompanyEInvoiceRequestLogic constructor. + * @param FetchesCompany $fetchesCompany + * @param UpdatesCompanyEInvoiceRequest $updatesCompanyEInvoiceRequest + */ + public function __construct(FetchesCompany $fetchesCompany, UpdatesCompanyEInvoiceRequest $updatesCompanyEInvoiceRequest) + { + $this->fetchesCompany = $fetchesCompany; + $this->updatesCompanyEInvoiceRequest = $updatesCompanyEInvoiceRequest; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + $dto = new EInvoiceRequestDTO($request->all()); + $company = $this->fetchesCompany->execute(['id' => $dto->companyId]); + $this->updatesCompanyEInvoiceRequest->execute($company, $dto->eInvoiceRequest); + + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php index 05fb0746..5a110d85 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/UpdateCompanyNameAndDebtorLogic.php @@ -20,8 +20,8 @@ class UpdateCompanyNameAndDebtorLogic extends AbstractControllerLogic */ protected function notification():array { return [ - 'title' => 'Update Company Account Status', - 'message' => 'You have successfully updated the Company Account Status' + 'title' => 'Update Company Details', + 'message' => 'You have successfully updated the Company Details' ]; } /** @var CanUpdateCompany */ @@ -77,6 +77,4 @@ class UpdateCompanyNameAndDebtorLogic extends AbstractControllerLogic return $this->resourceResponse(new CompanyResource($query)); } - } - diff --git a/app/Classes/Modules/Companies/DataTransferObjects/UpdateCompanyDetailsDTO.php b/app/Classes/Modules/Companies/DataTransferObjects/UpdateCompanyDetailsDTO.php new file mode 100644 index 00000000..5a16f12c --- /dev/null +++ b/app/Classes/Modules/Companies/DataTransferObjects/UpdateCompanyDetailsDTO.php @@ -0,0 +1,61 @@ +id = (int) ($data['id'] ?? 0); + $this->name = (string) ($data['name'] ?? ''); + $this->debtor = (string) ($data['debtor'] ?? ''); + $this->reference = (string) ($data['reference'] ?? ''); + $this->type = (int) ($data['type'] ?? 0); + + $this->tin = (int) ($data['tin'] ?? 0); + $this->msicCode = (int) ($data['msic_code'] ?? 0); + $this->addressId = (int) ($data['address_id'] ?? 0); + $this->districtId = (int) ($data['district_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'] ?? 0); + } + + public function toArray(): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'debtor' => $this->debtor, + 'reference' => $this->reference, + 'type' => $this->type, + + 'tin' => $this->tin, + 'msic_code' => $this->msicCode, + 'address_id' => $this->addressId, + 'district_id' => $this->districtId, + 'company_id' => $this->companyId, + 'street_one' => $this->streetOne, + 'street_two' => $this->streetTwo, + 'post_code' => $this->postCode, + ]; + } +} diff --git a/app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceInfo.php b/app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceInfo.php new file mode 100644 index 00000000..0a2c5cc5 --- /dev/null +++ b/app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceInfo.php @@ -0,0 +1,25 @@ +tin = $tin; + $model->msic_code = $msicCode; + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceRequest.php b/app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceRequest.php new file mode 100644 index 00000000..780f8150 --- /dev/null +++ b/app/Classes/Modules/Companies/Services/UpdatesCompanyEInvoiceRequest.php @@ -0,0 +1,27 @@ +e_invoice) && is_null($model->e_invoice_requested_at)) { + $model->e_invoice_requested_at = now(); + } + $model->e_invoice = $eInvoice; + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsEInvoiceDebtorSummary.php b/app/Classes/Modules/Exports/Services/ExportsEInvoiceDebtorSummary.php index 69e237fb..7edf2d72 100644 --- a/app/Classes/Modules/Exports/Services/ExportsEInvoiceDebtorSummary.php +++ b/app/Classes/Modules/Exports/Services/ExportsEInvoiceDebtorSummary.php @@ -57,7 +57,8 @@ class ExportsEInvoiceDebtorSummary implements FromQuery, WithHeadings, WithHeadi public function query() { return Company::where(function($query){ - $query->whereNull('debtor')->orWhere('debtor', ''); + // $query->whereNull('debtor')->orWhere('debtor', ''); + $query->whereNotNull('e_invoice'); })->whereNotIn('id', [2207, 2248, 2029])->where('business_type', BusinessType::IMPORTER)->where('status', ApprovalStatus::APPROVED)->where(function($query){ $query->whereHas('transactions', function($query){ return $query->whereIn('type', [TransactionType::PAYMENT, TransactionType::TOP_UP])->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED, ApprovalStatus::PENDING_VERIFICATION]); @@ -76,9 +77,9 @@ class ExportsEInvoiceDebtorSummary implements FromQuery, WithHeadings, WithHeadi { return [ '<>', // Code - 'TODO', // Need Tax INV? - 'TODO', // Request Date - 'TODO', // TIN NO. + $company->e_invoice, // Need Tax INV? + $company->e_invoice_requested_at, // Request Date + $company->tin, // TIN NO. '300-0000', // DebtorControlAcc '300-0000', // ControlAccount $company->name.' (PURCHASE)', // CompanyName diff --git a/app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceRuleLogic.php b/app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceRuleLogic.php new file mode 100644 index 00000000..dd19bec1 --- /dev/null +++ b/app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceRuleLogic.php @@ -0,0 +1,62 @@ + 'Rule Check E-Invoice', + 'message' => 'You have successfully passed all rules evaluated' + ]; + } + + /** @var RuleEvaluator */ + private $ruleEvaluator; + + /** + * CheckEInvoiceRuleLogic constructor. + */ + public function __construct(RuleEvaluator $ruleEvaluator) + { + $this->ruleEvaluator = $ruleEvaluator; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + * @throws \App\Classes\Exceptions\CriteriaNotFulfilledException + */ + public function logic(Request $request) : JsonResponse + { + $dto = new CheckEInvoiceRuleDTO($request->all()); + + $result = $this->ruleEvaluator->evaluate([ + App()->make(CanProceedEInvoicePromptedRule::class), + App()->make(CanProceedTINRule::class), + ], $dto); + + if ($result->failed()) { + throw new CriteriaNotFulfilledException("- " . implode("
- ", $result->messages())); + } + + return $this->resourceResponse(new RuleResource((object)$result)); + } +} diff --git a/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php b/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php new file mode 100644 index 00000000..577e4618 --- /dev/null +++ b/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php @@ -0,0 +1,62 @@ + 'Rule Check Purchase Order', + 'message' => 'You have successfully passed all rules evaluated' + ]; + } + + /** @var RuleEvaluator */ + private $ruleEvaluator; + + /** + * CheckPurchaseOrderRuleLogic constructor. + */ + public function __construct(RuleEvaluator $ruleEvaluator) + { + $this->ruleEvaluator = $ruleEvaluator; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + * @throws \App\Classes\Exceptions\CriteriaNotFulfilledException + */ + public function logic(Request $request) : JsonResponse + { + $dto = new CheckPurchaseOrderRuleDTO($request->all()); + + $result = $this->ruleEvaluator->evaluate([ + App()->make(CanProceedEInvoicePromptedRule::class), + App()->make(CanProceedPurchaseOrderRule::class), + ], $dto); + + if ($result->failed()) { + throw new CriteriaNotFulfilledException("- " . implode("
- ", $result->messages())); + } + + return $this->resourceResponse(new RuleResource((object)$result)); + } +} diff --git a/app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceRuleDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceRuleDTO.php new file mode 100644 index 00000000..75726e47 --- /dev/null +++ b/app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceRuleDTO.php @@ -0,0 +1,22 @@ +companyId = $data['company_id']; + } + + public function toArray(): array + { + return [ + 'company_id' => $this->companyId, + ]; + } +} diff --git a/app/Classes/Modules/Rules/DataTransferObjects/CheckPurchaseOrderRuleDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/CheckPurchaseOrderRuleDTO.php new file mode 100644 index 00000000..2c5c2ab5 --- /dev/null +++ b/app/Classes/Modules/Rules/DataTransferObjects/CheckPurchaseOrderRuleDTO.php @@ -0,0 +1,25 @@ +bookingId = $data['booking_id']; + $this->companyId = $data['company_id']; + } + + public function toArray(): array + { + return [ + 'booking_id' => $this->bookingId, + 'company_id' => $this->companyId, + ]; + } +} diff --git a/app/Classes/Modules/Rules/DataTransferObjects/ConfirmBookingDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/ConfirmBookingDTO.php new file mode 100644 index 00000000..2c154833 --- /dev/null +++ b/app/Classes/Modules/Rules/DataTransferObjects/ConfirmBookingDTO.php @@ -0,0 +1,25 @@ +bookingId = $data['booking_id']; + $this->companyId = $data['company_id']; + } + + public function toArray(): array + { + return [ + 'booking_id' => $this->bookingId, + 'company_id' => $this->companyId, + ]; + } +} diff --git a/app/Classes/Modules/Rules/DataTransferObjects/EInvoiceInfoDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/EInvoiceInfoDTO.php new file mode 100644 index 00000000..f768b2c1 --- /dev/null +++ b/app/Classes/Modules/Rules/DataTransferObjects/EInvoiceInfoDTO.php @@ -0,0 +1,40 @@ +tin = (int) ($data['tin'] ?? ''); + $this->msicCode = (int) ($data['msic_code'] ?? ''); + $this->districtId = (int) ($data['district_id'] ?? ''); + $this->companyId = (int) ($data['company_id'] ?? ''); + $this->streetOne = (string) ($data['street_one'] ?? ''); + $this->streetTwo = (string) ($data['street_two'] ?? ''); + $this->postCode = (int) ($data['post_code'] ?? ''); + } + + public function toArray(): array + { + return [ + 'tin' => $this->tin, + 'msic_code' => $this->msicCode, + 'district_id' => $this->districtId, + 'company_id' => $this->companyId, + 'street_one' => $this->streetOne, + 'street_two' => $this->streetTwo, + 'post_code' => $this->postCode, + ]; + } +} diff --git a/app/Classes/Modules/Rules/DataTransferObjects/EInvoiceRequestDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/EInvoiceRequestDTO.php new file mode 100644 index 00000000..cbc2b0ea --- /dev/null +++ b/app/Classes/Modules/Rules/DataTransferObjects/EInvoiceRequestDTO.php @@ -0,0 +1,25 @@ +eInvoiceRequest = $data['e_invoice_request']; + $this->companyId = $data['company_id']; + } + + public function toArray(): array + { + return [ + 'e_invoice_request' => $this->eInvoiceRequest, + 'company_id' => $this->companyId, + ]; + } +} diff --git a/app/Classes/Modules/Rules/Services/RuleEvaluator.php b/app/Classes/Modules/Rules/Services/RuleEvaluator.php new file mode 100644 index 00000000..ac248a25 --- /dev/null +++ b/app/Classes/Modules/Rules/Services/RuleEvaluator.php @@ -0,0 +1,44 @@ +passes($object)) { + $success = false; + $messages[] = get_class($rule) . ' failed without exception'; + } + } catch (AccessForbiddenException | RequestValidationException | CriteriaNotFulfilledException $e) { + $success = false; + $messages[] = $e->getMessage(); + } catch (\Exception $e) { + $success = false; + $messages[] = 'Unexpected error in ' . get_class($rule) . ': ' . $e->getMessage(); + } + } + + return new RuleEvaluationResult($success, $messages); + } + +} diff --git a/app/Classes/Modules/Rules/Standards/Rules/CanProceedEInvoicePromptedRule.php b/app/Classes/Modules/Rules/Standards/Rules/CanProceedEInvoicePromptedRule.php new file mode 100644 index 00000000..dcddbcdd --- /dev/null +++ b/app/Classes/Modules/Rules/Standards/Rules/CanProceedEInvoicePromptedRule.php @@ -0,0 +1,56 @@ +fetchesCompany = $fetchesCompany; + } + + /** + * @return bool + */ + protected function authorized($object): bool + { + return true; + + } + + /** + * @return bool + */ + protected function validators($object): bool + { + return true; + + } + + + /** + * @return bool + */ + protected function criteria($object): bool + { + //Check if account requires E-Invoice + $company = $this->fetchesCompany->execute(['id' => $object->companyId]); + if($company->e_invoice === null){ + throw new CriteriaNotFulfilledException("Please refresh page to answer question related to E-Invoice."); + } + return true; + } + +} diff --git a/app/Classes/Modules/Rules/Standards/Rules/CanProceedPurchaseOrderRule.php b/app/Classes/Modules/Rules/Standards/Rules/CanProceedPurchaseOrderRule.php new file mode 100644 index 00000000..f9483835 --- /dev/null +++ b/app/Classes/Modules/Rules/Standards/Rules/CanProceedPurchaseOrderRule.php @@ -0,0 +1,96 @@ +fetchesBooking = $fetchesBooking; + $this->fetchesCompany = $fetchesCompany; + } + + /** + * @return bool + */ + protected function authorized($object): bool + { + return true; + + } + + /** + * @return bool + */ + protected function validators($object): bool + { + return true; + + } + + + /** + * @return bool + */ + protected function criteria($object): bool + { + //Check if the transfer/booking already has purchase order filled + $booking = $this->fetchesBooking->execute(['id' => $object->bookingId]); + $purchaseOrder = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first(); + + $isPOEmptyException = false; + if (!$purchaseOrder || $purchaseOrder->status === ApprovalStatus::PENDING_SUBMISSION) { + $isPOEmptyException = true; + } + + if($isPOEmptyException){ //If PO is indeed empty, there are some scenarios where PO actually can be left empty + $ids = ServiceType::whereIn('name', [ + ServiceTypeNameConstants::PAYMENT_1688, + ServiceTypeNameConstants::VIP_1688, + ])->get()->pluck('id'); + + //It can be left empty when booking is of type 1688: specifcally: 1688 Payment and 1688 VIP + if (in_array($booking->service_id, $ids->all())) { + $isPOEmptyException = false; + } + + //However when the customer falls under the segment '1688 Manual PO Periodic', even if their booking is of type 1688 (1688 Payment and 1688 VIP), + //they still must fill up the PO. Confusing?? IKR + $company = $this->fetchesCompany->execute(['id' => $object->companyId]); + $filteredSegments = $company->segments()->where('name', SegmentNameConstants::MANUAL_PO_PERIODIC_1688)->get(); + if(!$isPOEmptyException){ + if (!$filteredSegments->isEmpty()) { + $isPOEmptyException = true; + } + } + } + + if($isPOEmptyException){ + throw new CriteriaNotFulfilledException("Please complete the purchase order form."); + } + return true; + } +} diff --git a/app/Classes/Modules/Rules/Standards/Rules/CanProceedTINRule.php b/app/Classes/Modules/Rules/Standards/Rules/CanProceedTINRule.php new file mode 100644 index 00000000..a4adc674 --- /dev/null +++ b/app/Classes/Modules/Rules/Standards/Rules/CanProceedTINRule.php @@ -0,0 +1,55 @@ +fetchesCompany = $fetchesCompany; + } + + /** + * @return bool + */ + protected function authorized($object): bool + { + return true; + } + + /** + * @return bool + */ + protected function validators($object): bool + { + return true; + } + + + /** + * @return bool + */ + protected function criteria($object): bool + { + //Check if TIN already provided if account requires E-Invoice + $company = $this->fetchesCompany->execute(['id' => $object->companyId]); + if($company->e_invoice === 1 && !$company->tin){ + throw new CriteriaNotFulfilledException("Please provide all requested E-Invoice Info."); + } + return true; + } + +} diff --git a/app/Classes/ValueObjects/Constants/SegmentNameConstants.php b/app/Classes/ValueObjects/Constants/SegmentNameConstants.php new file mode 100644 index 00000000..a8e881d7 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/SegmentNameConstants.php @@ -0,0 +1,7 @@ +success = $success; + $this->messages = $messages; + } + + public function failed(): bool + { + return ! $this->success; + } + + public function passed(): bool + { + return $this->success; + } + + public function messages(): array + { + return $this->messages; + } +} diff --git a/app/Http/Controllers/Companies/FetchCompanyEInvoiceInfoController.php b/app/Http/Controllers/Companies/FetchCompanyEInvoiceInfoController.php new file mode 100644 index 00000000..d506e24e --- /dev/null +++ b/app/Http/Controllers/Companies/FetchCompanyEInvoiceInfoController.php @@ -0,0 +1,20 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Companies/UpdateCompanyDetailsController.php b/app/Http/Controllers/Companies/UpdateCompanyDetailsController.php new file mode 100644 index 00000000..e8cbfd1c --- /dev/null +++ b/app/Http/Controllers/Companies/UpdateCompanyDetailsController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Companies/UpdateCompanyEInvoiceInfoController.php b/app/Http/Controllers/Companies/UpdateCompanyEInvoiceInfoController.php new file mode 100644 index 00000000..9157e9de --- /dev/null +++ b/app/Http/Controllers/Companies/UpdateCompanyEInvoiceInfoController.php @@ -0,0 +1,29 @@ +execute($request); + } + + /** + * @param Request $request + * @param UpdateCompanyEInvoiceRequestLogic $logic + * @return JsonResponse + */ + public function updateRequest(Request $request, UpdateCompanyEInvoiceRequestLogic $logic): JsonResponse { + return $logic->execute($request); + } +} diff --git a/app/Http/Controllers/Rules/CheckRuleController.php b/app/Http/Controllers/Rules/CheckRuleController.php new file mode 100644 index 00000000..881c6abe --- /dev/null +++ b/app/Http/Controllers/Rules/CheckRuleController.php @@ -0,0 +1,29 @@ +execute($request); + } + + /** + * @param Request $request + * @param CheckPurchaseOrderRuleLogic $logic + * @return JsonResponse + */ + public function checkPurchaseOrderRule(Request $request, CheckPurchaseOrderRuleLogic $logic): JsonResponse { + return $logic->execute($request); + } +} diff --git a/app/Http/Resources/CompanyResource.php b/app/Http/Resources/CompanyResource.php index 82b4cf3e..4e3ac2f0 100644 --- a/app/Http/Resources/CompanyResource.php +++ b/app/Http/Resources/CompanyResource.php @@ -37,11 +37,15 @@ class CompanyResource extends JsonResource 'name' => $this->name, 'reference' => $this->reference, 'debtor' => $this->debtor, + 'e_invoice' => $this->e_invoice, + 'tin' => $this->tin, + 'msic_code' => $this->msic_code, 'type' => (int) $this->type, 'business_type' => (int) $this->business_type, 'status' => (int) $this->status, 'contact' => new ContactResource ($this->when($this->has('contacts'), $this->contacts->first())), 'address' => new AddressResource($this->when($this->has('addresses'), $this->addresses->where('billing', true)->first())), + 'address_einvoice' => $this->e_invoice ? new AddressResource($this->when($this->has('addresses'), $this->addresses->where('billing', false)->where('e_invoice', true)->sortByDesc('created_at')->first())) : null, 'employee' => new UserResource(Auth::user()->type === RoleTypes::USER ? $this->employees()->where('email', '=', Auth::user()->email)->first() : $this->employees()->orderBy('id', 'DESC')->first()), 'identification' => new DocumentResource($this->documents->whereIn('document_type', DocumentType::IDENTIFICATION_DOCUMENTS)->first()), 'bookings' => $this->whenLoaded('bookings', $this->bookings()->orderBy('id', 'DESC')->get(), []), diff --git a/app/Http/Resources/EInvoiceInfoResource.php b/app/Http/Resources/EInvoiceInfoResource.php new file mode 100644 index 00000000..9bf8c8c1 --- /dev/null +++ b/app/Http/Resources/EInvoiceInfoResource.php @@ -0,0 +1,31 @@ + $this->id, + 'street_one' => $this->street_one, + 'street_two' => $this->street_two, + 'district' => $this->district, + 'state' => $this->state, + 'post_code' => $this->postcode, + 'country' => $this->country, + 'billing' => (int) $this->billing, + 'msic_code' => (int) $this->msic_code, + 'tin' => (int) $this->tin, + 'e_invoice' => (int) $this->e_invoice, + ]; + } +} diff --git a/app/Http/Resources/RuleResource.php b/app/Http/Resources/RuleResource.php new file mode 100644 index 00000000..1fb42181 --- /dev/null +++ b/app/Http/Resources/RuleResource.php @@ -0,0 +1,22 @@ + $this->success, + 'messages' => $this->messages, + ]; + } +} diff --git a/database/migrations/2025_05_04_150726_add_einvoice_to_addresses_table.php b/database/migrations/2025_05_04_150726_add_einvoice_to_addresses_table.php new file mode 100644 index 00000000..990a9e1c --- /dev/null +++ b/database/migrations/2025_05_04_150726_add_einvoice_to_addresses_table.php @@ -0,0 +1,32 @@ +boolean('e_invoice')->default(false)->after('billing'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('addresses', function (Blueprint $table) { + $table->dropColumn('e_invoice'); + }); + } +} diff --git a/database/migrations/2025_05_04_150735_add_tin_to_companies_table.php b/database/migrations/2025_05_04_150735_add_tin_to_companies_table.php new file mode 100644 index 00000000..ed5f2c58 --- /dev/null +++ b/database/migrations/2025_05_04_150735_add_tin_to_companies_table.php @@ -0,0 +1,38 @@ +string('tin')->nullable()->after('debtor'); + $table->string('msic_code')->nullable()->after('debtor')->comment("5-digit code representing business activity"); + $table->timestamp('e_invoice_requested_at')->nullable()->after('debtor'); + $table->boolean('e_invoice')->nullable()->default(null)->after('debtor'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('companies', function (Blueprint $table) { + $table->dropColumn('tin'); + $table->dropColumn('msic_code'); + $table->dropColumn('e_invoice_requested_at'); + $table->dropColumn('e_invoice'); + }); + } +} diff --git a/resources/assets/vue/components/address/forms/EInvoiceInfoFormComponent.vue b/resources/assets/vue/components/address/forms/EInvoiceInfoFormComponent.vue new file mode 100644 index 00000000..5478b2f3 --- /dev/null +++ b/resources/assets/vue/components/address/forms/EInvoiceInfoFormComponent.vue @@ -0,0 +1,213 @@ + + diff --git a/resources/assets/vue/components/bookings/elements/EInvoiceInfoComponent.vue b/resources/assets/vue/components/bookings/elements/EInvoiceInfoComponent.vue new file mode 100644 index 00000000..0f803545 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/EInvoiceInfoComponent.vue @@ -0,0 +1,68 @@ + + diff --git a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue new file mode 100644 index 00000000..b5858ee3 --- /dev/null +++ b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue @@ -0,0 +1,833 @@ + + + diff --git a/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue b/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue index 975c4592..3b440ab2 100644 --- a/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue @@ -111,7 +111,11 @@ section: { type: String, required: true - } + }, + companyId: { + type: Number, + required: true + }, }, data(){ return { @@ -121,14 +125,16 @@ amount: this.amount, bank_code: this.bank_code, voucher_code: this.calculation.voucher_code, - voucher_discount_amount: this.calculation.voucher_discount_amount + voucher_discount_amount: this.calculation.voucher_discount_amount, + booking_id: this.id, + company_id: this.companyId, } } }, methods: { submitForm(){ this.isLoading = true; - this.submit(route('api.booking.payment.create', this.id), 'post', this.section, false, false); + this.submit(route('api.booking.payment.create', this.id), 'post', this.section, false, true); }, successHandler(response){ if (response.payload.data.payment_method === 5) { diff --git a/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue b/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue index e12074b0..78d7df35 100644 --- a/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue +++ b/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue @@ -175,6 +175,21 @@ {{booking.company.contact ? booking.company.contact.phone: ''}}

+
+
TIN
+

+ + {{ booking.company.e_invoice + ? (booking.company.tin ? booking.company.tin : 'INCOMPLETE E-INVOICE INFO') + : 'NOT PROVIDED' }} +
+ View E-Invoice Info + +

+ + + +
@@ -308,7 +323,8 @@
- + +
diff --git a/resources/assets/vue/components/companies/elements/CustomerProfileComponent.vue b/resources/assets/vue/components/companies/elements/CustomerProfileComponent.vue index 1435c448..d406e1ac 100644 --- a/resources/assets/vue/components/companies/elements/CustomerProfileComponent.vue +++ b/resources/assets/vue/components/companies/elements/CustomerProfileComponent.vue @@ -14,7 +14,8 @@
- + +
{{item.name}} diff --git a/resources/assets/vue/components/companies/forms/ChangeCustomerCompanyDetailsFormComponent.vue b/resources/assets/vue/components/companies/forms/ChangeCustomerCompanyDetailsFormComponent.vue new file mode 100644 index 00000000..1dc9ed4a --- /dev/null +++ b/resources/assets/vue/components/companies/forms/ChangeCustomerCompanyDetailsFormComponent.vue @@ -0,0 +1,147 @@ + + diff --git a/resources/assets/vue/components/companies/forms/EInvoiceRequestFormComponent.vue b/resources/assets/vue/components/companies/forms/EInvoiceRequestFormComponent.vue new file mode 100644 index 00000000..30828ea5 --- /dev/null +++ b/resources/assets/vue/components/companies/forms/EInvoiceRequestFormComponent.vue @@ -0,0 +1,65 @@ + + + diff --git a/resources/assets/vue/general/mixins/request.js b/resources/assets/vue/general/mixins/request.js index 4048415a..f8348b8a 100644 --- a/resources/assets/vue/general/mixins/request.js +++ b/resources/assets/vue/general/mixins/request.js @@ -20,7 +20,7 @@ export default { if (!success) { this.openModal(); errorNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'error' }) : null; - this.errorHandler(response, statusCode); return; + this.errorHandler(response, statusCode, section); return; } successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null; @@ -29,7 +29,7 @@ export default { link.href = window.URL.createObjectURL(response); link.download = fileName.trim(); link.click(); - this.successHandler(response) + this.successHandler(response, section) }); } else { response.json().then(response => { @@ -37,11 +37,11 @@ export default { if (!success) { this.openModal(); errorNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'error' }) : null; - this.errorHandler(response, statusCode); return; + this.errorHandler(response, statusCode, section); return; } successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null; - this.successHandler(response) + this.successHandler(response, section) }); diff --git a/routes/api.php b/routes/api.php index ef7025a5..2e28d9c5 100644 --- a/routes/api.php +++ b/routes/api.php @@ -71,6 +71,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function require __DIR__ . '/job.php'; + require __DIR__ . '/rule.php'; + // require __DIR__ . '/rate.php'; // require __DIR__ . '/receipt.php'; diff --git a/routes/company.php b/routes/company.php index d3e9d4ba..d88bd398 100644 --- a/routes/company.php +++ b/routes/company.php @@ -1,5 +1,8 @@ 'company', 'as' => 'company.', 'namespace' => 'Companies'], function () { @@ -36,4 +39,8 @@ Route::group(['prefix' => 'company', 'as' => 'company.', 'namespace' => 'Compani Route::put('/{document_id}/approval/{status}', 'ApproveIdentificationDocumentController@approve')->where('status', 'approve|reject')->name('approval'); }); + Route::put('/details/update/{id}', [UpdateCompanyDetailsController::class, 'update'])->name('update.details'); + Route::get('/e-invoice/info/{id}', [FetchCompanyEInvoiceInfoController::class, 'fetch'])->name('einvoice.info'); + Route::post('/e-invoice/info/update', [UpdateCompanyEInvoiceInfoController::class, 'updateInfo'])->name('einvoice.info.update'); + Route::post('/e-invoice/request/update', [UpdateCompanyEInvoiceInfoController::class, 'updateRequest'])->name('einvoice.request.update'); }); diff --git a/routes/rule.php b/routes/rule.php new file mode 100644 index 00000000..b2ea057b --- /dev/null +++ b/routes/rule.php @@ -0,0 +1,11 @@ +as('rule.') + ->group(function () { + Route::post('/check/eInvoice', [CheckRuleController::class, 'checkEInvoiceRule'])->name('check.einvoice'); + Route::post('/check/purchase-order', [CheckRuleController::class, 'checkPurchaseOrderRule'])->name('check.purchase.order'); + }); diff --git a/routes/web.php b/routes/web.php index 7fd4d49c..8ff26d13 100644 --- a/routes/web.php +++ b/routes/web.php @@ -917,7 +917,7 @@ Route::get('/statements/{account}/transactions', function ($account) { })->name('statements.account.transactions'); Route::get('/statements/{statement}', [BankStatementController::class, 'show'])->name('statements.show'); Route::get('/statements/mapping/rerun', [BankStatementController::class, 'rerun'])->name('statements.rerun'); -Route::get('/statements/{statement}/download', 'StatementController@download')->name('statements.download'); +// Route::get('/statements/{statement}/download', 'StatementController@download')->name('statements.download'); Route::get('/bank-record', 'Imports\ImportBankRecordController@import'); Route::get('/po/outsource/check', function(){