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/General/Eloquent/Filters/IsEInvoice.php b/app/Classes/General/Eloquent/Filters/IsEInvoice.php new file mode 100644 index 00000000..001b5f3e --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsEInvoice.php @@ -0,0 +1,19 @@ +where('e_invoice', $value); + } +} diff --git a/app/Classes/Modules/Addresses/ControllersLogic/ListAddressesLogic.php b/app/Classes/Modules/Addresses/ControllersLogic/ListAddressesLogic.php index 57e3a2f0..c75f0623 100644 --- a/app/Classes/Modules/Addresses/ControllersLogic/ListAddressesLogic.php +++ b/app/Classes/Modules/Addresses/ControllersLogic/ListAddressesLogic.php @@ -50,10 +50,10 @@ class ListAddressesLogic extends AbstractControllerLogic public function logic(Request $request) : JsonResponse { try { - $this->canListAddresses->passes(); - $query = $this->listsAddresses->execute($this->listsAddresses->deserializeFilters($request->input('filters'))); + $query = $this->listsAddresses->execute( + array_merge($this->listsAddresses->deserializeFilters($request->input('filters')), ['is_e_invoice' => false])); //exclude all addresses meant for e_invoice return $this->collectionResponse(AddressResource::collection($query)); @@ -63,4 +63,4 @@ class ListAddressesLogic extends AbstractControllerLogic } -} \ No newline at end of file +} 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 182d1a5e..75c2daf2 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\DataTransferObjects\CalculationObject; @@ -17,9 +17,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; @@ -78,6 +83,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var CalculatesBookingRefundAmount */ private $calculatesBookingRefundAmount; + /** @var RuleEvaluator */ + private $ruleEvaluator; + /** * @param FetchesBookingQuotation $fetchBookingQuotation * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit @@ -90,8 +98,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; @@ -104,10 +113,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/Bookings/Services/GeneratesBookingQuotation.php b/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php index 5c1ecdd9..dcc4b8d8 100644 --- a/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php +++ b/app/Classes/Modules/Bookings/Services/GeneratesBookingQuotation.php @@ -7,6 +7,7 @@ use App\Classes\Modules\Bookings\DataTransferObjects\CalculationObject; use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; use App\Http\Resources\BankResource; use App\Models\Bank; +use App\Models\Currency; use Carbon\Carbon; use Carbon\CarbonInterval; @@ -44,6 +45,8 @@ class GeneratesBookingQuotation ->format('4:00 \P\M, jS M, Y \G\M\T T'); } + $foreign_currency = Currency::find($calculationObject->getConversionObject()->getCurrencyId()); + $foreign_currency_short_code = $foreign_currency->short_code ?? 'CNY'; return [ 'bank' => new BankResource(Bank::find($calculationObject->getConfigurations()->getBankId())), @@ -61,6 +64,7 @@ class GeneratesBookingQuotation 'payment_attempt_limit' => CarbonInterval::days($days)->hours($hours)->minutes($minutes)->forHumans(), 'voucher_discount_amount' => $calculationObject->getVoucherDiscountAmount(), 'voucher_code' => $calculationObject->getVoucherCode(), + 'foreign_currency' => $foreign_currency_short_code, ]; } } 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 new file mode 100644 index 00000000..7edf2d72 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsEInvoiceDebtorSummary.php @@ -0,0 +1,106 @@ +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]); + })->orWhereHas('wallets', function($query){ + return $query->whereHas('transactions'); + }); + }); + } + + /** + * @param Company $company + * + * @return array + */ + public function map($company): array + { + return [ + '<>', // Code + $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 + $company->reference, // Desc2 + '', // DebtorType + 'PIA', // DisplayTerm + 'MYR', // CurrencyCode + '', // RegisterNo + '', // Address1 + '', // Address2 + '', // Address3 + '', // PostCode + '', // DeliverAddr1 + '', // DeliverAddr2 + '', // DeliverAddr3 + '', // DeliverPostCode + '', // EmailAddress + '', // Attention + '', // Phone1 + '', // Phone2 + '', // Fax1 + ]; + } +} diff --git a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php index 85df0be8..d890dfba 100644 --- a/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php +++ b/app/Classes/Modules/Exports/Services/ExportsInvoiceTransactions.php @@ -94,7 +94,8 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading return [ '<>', - $row->updated_at->format('m/d/Y H:m'), + // $row->updated_at->format('m/d/Y H:m'), + $row->updated_at->format('m/d/Y'), $company->debtor, $row->type === TransactionType::PAYMENT ? $booking->marking : $company->reference, '', @@ -129,7 +130,8 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading return [ '<>', - Carbon::parse($row['created_at'])->format('m/d/Y H:m'), + // Carbon::parse($row['created_at'])->format('m/d/Y H:m'), + Carbon::parse($row['created_at'])->format('m/d/Y'), $row['debtor_code'], $row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['marking'], '', @@ -149,7 +151,8 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading return [ 'Transaction Not Found', - $transaction->posting_date->format('m/d/Y H:m'), + // $transaction->posting_date->format('m/d/Y H:m'), + $transaction->posting_date->format('m/d/Y'), $transaction->transaction_description.' - '.$transaction->transaction_description_2, $statementTransactionOwner->system, '', diff --git a/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php b/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php index 53a71772..11e48130 100644 --- a/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php +++ b/app/Classes/Modules/Exports/Services/ExportsNullDebtors.php @@ -72,28 +72,28 @@ class ExportsNullDebtors implements FromQuery, WithHeadings, WithHeadingRow, Wit public function map($company): array { return [ - '<>', - '300-0000', - '300-0000', - $company->name.' (PURCHASE)', - $company->reference, - '', - 'PIA', - 'MYR', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '',//EmailAddress - '', - '', - '', - '' + '<>', // Code + '300-0000', // DebtorControlAcc + '300-0000', // ControlAccount + $company->name.' (PURCHASE)', // CompanyName + $company->reference, // Desc2 + '', // DebtorType + 'PIA', // DisplayTerm + 'MYR', // CurrencyCode + '', // RegisterNo + '', // Address1 + '', // Address2 + '', // Address3 + '', // PostCode + '', // DeliverAddr1 + '', // DeliverAddr2 + '', // DeliverAddr3 + '', // DeliverPostCode + '', // EmailAddress + '', // Attention + '', // Phone1 + '', // Phone2 + '', // Fax1 ]; } -} \ No newline at end of file +} 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/Exports/ExportCustomersToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php index b693f326..ddb1fb96 100644 --- a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php +++ b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php @@ -13,15 +13,16 @@ use App\Classes\Modules\Exports\Services\ExportsNullDebtors; use App\Classes\Modules\Exports\Services\ExportsPaymentTransactions; use App\Classes\Modules\Exports\Services\ExportsWalletTransactions; use App\Classes\Modules\Exports\Services\ExportsInvoiceTransactions; +use App\Classes\Modules\Exports\Services\ExportsReceiptTransactions; +use App\Classes\Modules\Exports\Services\ExportsImportedReceiptMappeds; +use App\Classes\Modules\Exports\Services\ExportsWhiteFormTransactions; +use App\Classes\Modules\Exports\Services\ExportsImportedInvoiceMappeds; +use App\Classes\Modules\Exports\Services\ExportsEInvoiceDebtorSummary; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Maatwebsite\Excel\Excel; -use App\Classes\Modules\Exports\Services\ExportsImportedInvoiceMappeds; use App\Models\TransactionMappingLog; -use App\Classes\Modules\Exports\Services\ExportsReceiptTransactions; -use App\Classes\Modules\Exports\Services\ExportsImportedReceiptMappeds; -use App\Classes\Modules\Exports\Services\ExportsWhiteFormTransactions; use Illuminate\Support\Facades\Storage; use App\Classes\General\AWSS3Helper; use Illuminate\Support\Carbon; @@ -241,5 +242,17 @@ class ExportCustomersToExcelController return $response; } } -} + public function eInvoiceDebtorSummary(ExportsEInvoiceDebtorSummary $exportsEInvoiceDebtorSummary, Request $request){ + $exportFileName = 'EINV_DEBTOR_SUMMARY.xls'; + $filesystemDriver = Storage::getDefaultDriver(); + if($filesystemDriver === 's3'){ + return response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $exportsEInvoiceDebtorSummary) ]); + } + else{ + $response = $exportsEInvoiceDebtorSummary->download($exportFileName, Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } + } +} 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 2492bbec..84d5ae48 100644 --- a/app/Http/Resources/CompanyResource.php +++ b/app/Http/Resources/CompanyResource.php @@ -38,11 +38,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() && 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..1e30b167 --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/EInvoiceInfoComponent.vue @@ -0,0 +1,68 @@ + + diff --git a/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue b/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue index 46ae77b5..36432a62 100644 --- a/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue @@ -96,7 +96,7 @@

The recipient can expect to receive the transfer within 3-5 working days. Explore our BANK TRANSFER (SAVER) option for a better rate!

Enjoy a better rate with this option! The recipient will receive the transfer after 5-7 working days.

You can request Pay-on-Behalf via Alipay for platforms like Taobao, 1688, Pinduoduo, or any Alipay-supported platform.
Please use Alipay account "2766384544@QQ.com" to apply for Daifu. This account may change, so always confirm the latest Alipay account before placing an order.

-

Please ensure is a PERSONAL bank account details. Company bank account details only allow to use as E2E service.

+

Please ensure is a PERSONAL bank account details. Company bank account details only allow to use as E2E service.

Cancellation of E2E service are strictly NO refund on the 2% transfer fee charge.

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/forms/CurrencyConverterComponent.vue b/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue index 59291ed1..8df81f86 100644 --- a/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue +++ b/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue @@ -99,7 +99,7 @@
-
1 MYR = {{(Math.round((this.calculation.rate + Number.EPSILON) * 100000) / 100000).toFixed(5)}} RMB
+
1 MYR = {{(Math.round((this.calculation.rate + Number.EPSILON) * 100000) / 100000).toFixed(5)}} {{ this.calculation.foreign_currency }}
diff --git a/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue b/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue index 62be372d..b8a106b3 100644 --- a/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue +++ b/resources/assets/vue/components/bookings/sections/BookingDetailsSectionComponent.vue @@ -180,6 +180,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 + +

+ + + +
@@ -313,7 +328,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 0b246f2a..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 => { @@ -41,7 +41,7 @@ export default { } successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null; - this.successHandler(response, section); + this.successHandler(response, section) }); diff --git a/resources/views/pages/downloads/index.blade.php b/resources/views/pages/downloads/index.blade.php index bb03689d..57d39696 100644 --- a/resources/views/pages/downloads/index.blade.php +++ b/resources/views/pages/downloads/index.blade.php @@ -66,9 +66,34 @@
+
+
+
+ nullDebtor.xls +

New Debtors Report from settings page

+
+ + Download + +
+
+
+
+
+
+
+
+
+ EINV_DEBTOR_SUMMARY.xls +

e-Invoice Debtor Summary Report

+
+ + Download + +
+
-
diff --git a/routes/api.php b/routes/api.php index 8c7d6f14..41505987 100644 --- a/routes/api.php +++ b/routes/api.php @@ -94,6 +94,7 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function require __DIR__ . '/questionnaires.php'; require __DIR__ . '/admin_workflow.php'; + require __DIR__ . '/rule.php'; // require __DIR__ . '/rate.php'; // require __DIR__ . '/receipt.php'; diff --git a/routes/company.php b/routes/company.php index 19f73e34..91a12b66 100644 --- a/routes/company.php +++ b/routes/company.php @@ -1,5 +1,8 @@ 'company', 'as' => 'company.', 'namespace' => 'Companies'], function () { @@ -37,4 +40,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 c90d445d..bec33441 100644 --- a/routes/web.php +++ b/routes/web.php @@ -330,6 +330,7 @@ Route::get('/export/analytic/booking', 'Exports\ExportAnalyticToExcelController@ Route::get('/export/analytic/bills', 'Exports\ExportAnalyticToExcelController@billingData')->name('billingData.export');; Route::get('/export/customers/leads', 'Exports\ExportCustomersToExcelController@leadsData')->name('leads.export'); route::get('/export/excel/{id}', 'Exports\ExportCustomersToExcelController@exportCurrencyVendorOrder')->name('group.excel'); +Route::get('/export/einv-debtor/f614e339d7058904a831aad742e24d55', 'Exports\ExportCustomersToExcelController@eInvoiceDebtorSummary')->name('eInvoiceDebtorSummary.export'); Route::get('/products', function (\App\Classes\Modules\Exports\Services\ExportsProducts $exportsProducts) { $bookings = Booking::where(function($query){ @@ -920,7 +921,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(){