From 5e22f0e405520dff32693fd568a6b507e709e685 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 16:31:56 +0800 Subject: [PATCH 1/9] E-Invoice - Fix a timeout problem when processing import for E-Invoice --- .../ProcessSalesInvoiceReportV2CommandJob.php | 102 ++++++++++++++++++ .../ControllersLogic/ImportExcelLogic.php | 20 ++-- 2 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php diff --git a/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php new file mode 100644 index 00000000..8c0ab335 --- /dev/null +++ b/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php @@ -0,0 +1,102 @@ +details = $details; + } + + public function handle() + { + Log::info(Carbon::now() . ': Start job - Processing single record for E-Invoice from Sales Invoice Report Import.'); + $start = new Carbon(); + + $docNo = $this->details[0] ?? null; + $docDate = $this->details[1] ?? null; + $debtorCode = $this->details[2] ?? null; + $ref = $this->details[3] ?? null; + $shipInfo = $this->details[4] ?? null; + $accNo = $this->details[5] ?? null; + $detailDescription = $this->details[6] ?? null; + $furtherDescription = $this->details[7] ?? null; + $classification = $this->details[8] ?? null; + $deptNo = $this->details[9] ?? null; + $qty = $this->details[10] ?? null; + $unitPrice = $this->details[11] ?? null; + $submitEinvoice = $this->details[12] ?? null; + $consolidatedEinvoice = $this->details[13] ?? null; + $eInvoiceValidationLink = $this->details[14] ?? null; // Safe access for the new column + + Log::info("ProcessSalesInvoiceReportV2CommandJob Details:", [ + 'DocNo' => $docNo, + 'DocDate' => $docDate, + 'DebtorCode' => $debtorCode, + 'Ref' => $ref, + 'ShipInfo' => $shipInfo, + 'AccNo' => $accNo, + 'DetailDescription' => $detailDescription, + 'FurtherDescription' => $furtherDescription, + 'Classification' => $classification, + 'DeptNo' => $deptNo, + 'Qty' => $qty, + 'UnitPrice' => $unitPrice, + 'SubmitEinvoice' => $submitEinvoice, + 'ConsolidatedEinvoice' => $consolidatedEinvoice, + 'EInvoiceValidationLink' => $eInvoiceValidationLink, + ]); + + $booking = Booking::where('marking', $ref)->first(); + if($booking){ + if($docNo != "" && $docNo != "<>"){ + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_INVOICE, $docNo); + } + if($eInvoiceValidationLink){ + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); + } + } + + $end = new Carbon(); + $elapsedTime = $start->diff($end)->format('%H:%I:%S'); + Log::info(Carbon::now() . ': End job - Processing single record for E-Invoice from Sales Invoice Report Import. ElapsedTime: ' . $elapsedTime . '.'); + } + + + private function updateOrCreateKeyValuePair($booking, $key, $value) + { + $keyValuePairObject = new KeyValuePairObject($key, $value); + $metadata = $booking->attributesKVP()->where('key', $key)->first(); + + if ($metadata) { + (App()->make(UpdatesKeyValuePair::class))->execute($metadata, $keyValuePairObject); + } else { + (App()->make(CreatesKeyValuePair::class))->execute($booking, $keyValuePairObject); + } + } +} diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index 27ecc768..8793474e 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -5,6 +5,7 @@ namespace App\Classes\Modules\Imports\ControllersLogic; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; +use App\Classes\Jobs\Commands\V2\ProcessSalesInvoiceReportV2CommandJob; use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair; use App\Classes\Modules\Accounts\Services\UpdatesKeyValuePair; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; @@ -182,15 +183,16 @@ class ImportExcelLogic extends AbstractControllerLogic 'EInvoiceValidationLink' => $eInvoiceValidationLink, ]); - $booking = Booking::where('marking', $ref)->first(); - if($booking){ - if($docNo != "" && $docNo != "<>"){ - $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_INVOICE, $docNo); - } - if($eInvoiceValidationLink){ - $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); - } - } + // $booking = Booking::where('marking', $ref)->first(); + // if($booking){ + // if($docNo != "" && $docNo != "<>"){ + // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_INVOICE, $docNo); + // } + // if($eInvoiceValidationLink){ + // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); + // } + // } + ProcessSalesInvoiceReportV2CommandJob::dispatch($details); } } From dc6031ef1a06437580e02b0360d1554d4efe480c Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 17:03:10 +0800 Subject: [PATCH 2/9] E-Invoice - Fix a timeout problem when processing import for E-Invoice --- .../V2/ProcessPaymentReportV2CommandJob.php | 92 +++++++++++++++++++ .../ControllersLogic/ImportExcelLogic.php | 38 ++++---- 2 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php diff --git a/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php new file mode 100644 index 00000000..b390a0a6 --- /dev/null +++ b/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php @@ -0,0 +1,92 @@ +details = $details; + } + + public function handle() + { + Log::info(Carbon::now() . ': Start job - Processing single record from 01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT] Import.'); + $start = new Carbon(); + + $docNo = $this->details[0] ?? null; + $docDate = $this->details[1] ?? null; + $debtorCode = $this->details[2] ?? null; + $description = $this->details[3] ?? null; + $paymentMethod = $this->details[4] ?? null; + $paymentAmt = $this->details[5] ?? null; + $knockOffDocNo = $this->details[6] ?? null; + + Log::info("Row Payment Details:", [ + 'DocNo' => $docNo, + 'DocDate' => $docDate, + 'DebtorCode' => $debtorCode, + 'Description' => $description, + 'PaymentMethod' => $paymentMethod, + 'PaymentAmt' => $paymentAmt, + 'KnockOffDocNo' => $knockOffDocNo, + ]); + + if($knockOffDocNo){ + $kvp = KeyValuePair::where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->where('value', $knockOffDocNo)->first(); + if($kvp){ + $booking = $kvp->owner; + if($booking){ + if($docNo != "" && $docNo != "<>"){ + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_OFFICIAL_RECEIPT, $docNo); + } + // if($eInvoiceValidationLink){ + // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); + // } + } + } + } + + $end = new Carbon(); + $elapsedTime = $start->diff($end)->format('%H:%I:%S'); + Log::info(Carbon::now() . ': End job - Processing single record from 01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT] Import. ElapsedTime: ' . $elapsedTime . '.'); + } + + + private function updateOrCreateKeyValuePair($booking, $key, $value) + { + $keyValuePairObject = new KeyValuePairObject($key, $value); + $metadata = $booking->attributesKVP()->where('key', $key)->first(); + + if ($metadata) { + (App()->make(UpdatesKeyValuePair::class))->execute($metadata, $keyValuePairObject); + } else { + (App()->make(CreatesKeyValuePair::class))->execute($booking, $keyValuePairObject); + } + } +} diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index 8793474e..1a57aec9 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -5,6 +5,7 @@ namespace App\Classes\Modules\Imports\ControllersLogic; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; +use App\Classes\Jobs\Commands\V2\ProcessPaymentReportV2CommandJob; use App\Classes\Jobs\Commands\V2\ProcessSalesInvoiceReportV2CommandJob; use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair; use App\Classes\Modules\Accounts\Services\UpdatesKeyValuePair; @@ -221,24 +222,25 @@ class ImportExcelLogic extends AbstractControllerLogic if($knockOffDocNo) { - $kvp = KeyValuePair::where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->where('value', $knockOffDocNo)->first(); - if($kvp){ - $booking = $kvp->owner; - if($booking){ - if($docNo != "" && $docNo != "<>"){ - $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_OFFICIAL_RECEIPT, $docNo); - } - // if($eInvoiceValidationLink){ - // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); - // } - } - else{ - $unprocessedKnockOffs[] = $knockOffDocNo; - } - } - else{ - $unprocessedKnockOffs[] = $knockOffDocNo; - } + ProcessPaymentReportV2CommandJob::dispatch($details); + // $kvp = KeyValuePair::where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->where('value', $knockOffDocNo)->first(); + // if($kvp){ + // $booking = $kvp->owner; + // if($booking){ + // if($docNo != "" && $docNo != "<>"){ + // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_OFFICIAL_RECEIPT, $docNo); + // } + // // if($eInvoiceValidationLink){ + // // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); + // // } + // } + // else{ + // $unprocessedKnockOffs[] = $knockOffDocNo; + // } + // } + // else{ + // $unprocessedKnockOffs[] = $knockOffDocNo; + // } } } From 3b295df9fcc56f3483894da7f2b7f6aa13ee4c1c Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Wed, 27 Aug 2025 10:51:43 +0800 Subject: [PATCH 3/9] E-Invoice - Fix a timeout problem when processing import for E-Invoice --- .../V2/ProcessPaymentReportV2CommandJob.php | 22 ++--- .../ProcessSalesInvoiceReportV2CommandJob.php | 39 ++++---- .../ControllersLogic/ImportExcelLogic.php | 12 ++- .../Imports/Services/AutoCountDataImport.php | 92 +++++++++++++++++++ 4 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 app/Classes/Modules/Imports/Services/AutoCountDataImport.php diff --git a/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php index b390a0a6..ed54580e 100644 --- a/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php +++ b/app/Classes/Jobs/Commands/V2/ProcessPaymentReportV2CommandJob.php @@ -22,14 +22,14 @@ class ProcessPaymentReportV2CommandJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** @var object */ + /** @var array */ private $details; /** * ProcessPaymentReportV2CommandJob constructor. - * @param object $details + * @param array $details */ - public function __construct(object $details) + public function __construct(array $details) { $this->details = $details; } @@ -39,15 +39,15 @@ class ProcessPaymentReportV2CommandJob implements ShouldQueue Log::info(Carbon::now() . ': Start job - Processing single record from 01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT] Import.'); $start = new Carbon(); - $docNo = $this->details[0] ?? null; - $docDate = $this->details[1] ?? null; - $debtorCode = $this->details[2] ?? null; - $description = $this->details[3] ?? null; - $paymentMethod = $this->details[4] ?? null; - $paymentAmt = $this->details[5] ?? null; - $knockOffDocNo = $this->details[6] ?? null; + $docNo = $this->details['docno'] ?? null; + $docDate = $this->details['docdate'] ?? null; + $debtorCode = $this->details['debtorcode'] ?? null; + $description = $this->details['description'] ?? null; + $paymentMethod = $this->details['paymentmethod'] ?? null; + $paymentAmt = $this->details['paymentamt'] ?? null; + $knockOffDocNo = $this->details['knockoffdocno'] ?? null; - Log::info("Row Payment Details:", [ + Log::info("Processing Payment Report:", [ 'DocNo' => $docNo, 'DocDate' => $docDate, 'DebtorCode' => $debtorCode, diff --git a/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php index 8c0ab335..d96fa761 100644 --- a/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php +++ b/app/Classes/Jobs/Commands/V2/ProcessSalesInvoiceReportV2CommandJob.php @@ -21,14 +21,14 @@ class ProcessSalesInvoiceReportV2CommandJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** @var object */ + /** @var array */ private $details; /** * ProcessSalesInvoiceReportV2CommandJob constructor. - * @param object $details + * @param array $details */ - public function __construct(object $details) + public function __construct(array $details) { $this->details = $details; } @@ -38,23 +38,24 @@ class ProcessSalesInvoiceReportV2CommandJob implements ShouldQueue Log::info(Carbon::now() . ': Start job - Processing single record for E-Invoice from Sales Invoice Report Import.'); $start = new Carbon(); - $docNo = $this->details[0] ?? null; - $docDate = $this->details[1] ?? null; - $debtorCode = $this->details[2] ?? null; - $ref = $this->details[3] ?? null; - $shipInfo = $this->details[4] ?? null; - $accNo = $this->details[5] ?? null; - $detailDescription = $this->details[6] ?? null; - $furtherDescription = $this->details[7] ?? null; - $classification = $this->details[8] ?? null; - $deptNo = $this->details[9] ?? null; - $qty = $this->details[10] ?? null; - $unitPrice = $this->details[11] ?? null; - $submitEinvoice = $this->details[12] ?? null; - $consolidatedEinvoice = $this->details[13] ?? null; - $eInvoiceValidationLink = $this->details[14] ?? null; // Safe access for the new column + $docNo = $this->details['docno'] ?? null; + $docDate = $this->details['docdate'] ?? null; + $debtorCode = $this->details['debtorcode'] ?? null; + $ref = $this->details['ref'] ?? null; + $shipInfo = $this->details['shipinfo'] ?? null; + $accNo = $this->details['accno'] ?? null; + $detailDescription = $this->details['detaildescription'] ?? null; + $furtherDescription = $this->details['furtherdescription'] ?? null; + $classification = $this->details['classification'] ?? null; + $deptNo = $this->details['deptno'] ?? null; + $qty = $this->details['qty'] ?? null; + $unitPrice = $this->details['unitprice'] ?? null; + $submitEinvoice = $this->details['submiteinvoice'] ?? null; + $consolidatedEinvoice = $this->details['consolidatedeinvoice'] ?? null; + $eInvoiceValidationLink = $this->details['einvoicevalidationlink'] ?? null; - Log::info("ProcessSalesInvoiceReportV2CommandJob Details:", [ + // Log for debugging + Log::info("Processing Sales Invoice Report:", [ 'DocNo' => $docNo, 'DocDate' => $docDate, 'DebtorCode' => $debtorCode, diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index 1a57aec9..d06f12ce 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -11,6 +11,7 @@ use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair; use App\Classes\Modules\Accounts\Services\UpdatesKeyValuePair; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject; +use App\Classes\Modules\Imports\Services\AutoCountDataImport; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\KVPKey; use App\Models\Booking; @@ -46,7 +47,7 @@ class ImportExcelLogic extends AbstractControllerLogic protected function notification():array { return [ 'title' => 'Import Excel', - 'message' => 'You have successfully imported and updated booking details' + 'message' => 'You have successfully imported data from excel file' ]; } @@ -70,6 +71,13 @@ class ImportExcelLogic extends AbstractControllerLogic throw new MalformedRequestException('Import function can only process one file at a time.'); } + foreach ($files as $file) { + $filePath = json_decode($file)->file_info->original->file; + $import = new AutoCountDataImport($reportType); + Excel::import($import, $filePath); + } + + /* foreach ($files as $file) { $collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true); @@ -130,7 +138,7 @@ class ImportExcelLogic extends AbstractControllerLogic throw new MalformedRequestException('Cannot process report type: ' . $reportType); } } - + */ return $this->response($result); } diff --git a/app/Classes/Modules/Imports/Services/AutoCountDataImport.php b/app/Classes/Modules/Imports/Services/AutoCountDataImport.php new file mode 100644 index 00000000..17016aeb --- /dev/null +++ b/app/Classes/Modules/Imports/Services/AutoCountDataImport.php @@ -0,0 +1,92 @@ +reportType = $reportType; + } + + public function headingRow(): int + { + return 1; + } + + /** + * @param Collection $collection + */ + public function collection(Collection $collection) + { + static $headerProcessed = false; + foreach ($collection as $row) { + if (!$headerProcessed) { + $header = $row->keys()->map(fn($h) => strtolower(trim($h)))->toArray(); + $this->validateHeader($header, $this->reportType); + $headerProcessed = true; + } + + if ($this->reportType === 'Sales Invoice Report') { + ProcessSalesInvoiceReportV2CommandJob::dispatch($row->toArray()); + } elseif ($this->reportType === '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]') { + ProcessPaymentReportV2CommandJob::dispatch($row->toArray()); + } + else{ + throw new MalformedRequestException('Cannot process report type: ' . $this->reportType); + } + } + } + + public function chunkSize(): int + { + return 1000; + } + + private function validateHeader(array $header, String $reportType) + { + $optionalColumn = 'einvoicevalidationlink'; + $salesInvoiceHeader = [ + 'docno', 'docdate', 'debtorcode', 'ref', 'shipinfo', 'accno', + 'detaildescription', 'furtherdescription', 'classification', + 'deptno', 'qty', 'unitprice', 'submiteinvoice', 'consolidatedeinvoice' + ]; + $customersReportHeader = [ + 'tin', 'identityno', 'name', 'identitytype', 'taxclassification', 'msiccode', + 'businessactivitydesc', 'debtorcode', 'tradename', 'address', 'postcode', + 'phone', 'emailaddress', 'city', 'countrycode', 'statecode' + ]; + $paymentReportHeader = [ + 'docno', + 'docdate', + 'debtorcode', + 'description', + 'paymentmethod', + 'paymentamt', + 'knockoffdocno' + ]; + + if ($reportType === 'Sales Invoice Report' && + $header !== $salesInvoiceHeader && + $header !== [...$salesInvoiceHeader, $optionalColumn]) { + throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); + } + elseif ($reportType === 'Customers Report' && $header !== $customersReportHeader) { + throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); + } + elseif ($reportType === '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]' && $header !== $paymentReportHeader) { + throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); + } + } +} From 42493625d0827f623e5f7b47bc6f6b6a24e1fc02 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Sat, 30 Aug 2025 12:22:00 +0800 Subject: [PATCH 4/9] Fix cannot approve po probem for 1 booking reported by Sin Yee --- .../CreateInvoiceTransactionProcessor.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php index 799032ab..afacb35f 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php @@ -22,6 +22,7 @@ use App\Classes\ValueObjects\Constants\KVPKey; use App\Models\Booking; use App\Models\SegmentConstant; use Carbon\Carbon; +use Exception; use Illuminate\Support\Facades\Log; class CreateInvoiceTransactionProcessor @@ -214,8 +215,23 @@ class CreateInvoiceTransactionProcessor $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::BILL); - $transaction = $booking->transactions()->payments()->where('status', ApprovalStatus::COMPLETED)->first() - ->transactions()->where('type', TransactionType::BILL)->first(); + $paymentTransaction = $booking->transactions()->payments()->where('status', ApprovalStatus::COMPLETED)->first(); + + $transaction = null; + if($paymentTransaction){ + $transaction = $paymentTransaction->transactions()->where('type', TransactionType::BILL)->first(); + } + else{ // Special handling for refund cases (When a refund is deleted via DeleteRefundTransactionLogic, a booking payment transaction is set to ApprovalStatus::APPROVED) + $temp = $booking->transactions()->payments()->where('status', ApprovalStatus::APPROVED)->first(); + // Lets check if there is a refund case + $refund = $temp->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->first(); + if($refund){ + $transaction = $temp; + } + else{ + throw new Exception("No payment found for booking '$booking->id'."); + } + } $transaction_object = new TransactionObject( $billNumber, From 825b4e7bc38b3817a735815100aefd3ff9952de8 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Sat, 30 Aug 2025 12:59:45 +0800 Subject: [PATCH 5/9] E-Invoice - Minor code refactor --- .../CreateBookingRefundLogic.php | 13 +++-- .../Rules/CanCreateBookingRefund.php | 46 ++++++++++++++++++ .../UpdateRefundTransactionStatusLogic.php | 14 ++++-- .../CanUpdateRefundTransactionStatus.php | 48 +++++++++++++++++++ 4 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 app/Classes/Modules/Bookings/Standards/Rules/CanCreateBookingRefund.php create mode 100644 app/Classes/Modules/Transactions/Standards/Rules/CanUpdateRefundTransactionStatus.php diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php index 16d047a8..b7e7123d 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php @@ -14,6 +14,7 @@ use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Transactions\Services\CreatesTransaction; use App\Classes\Modules\Transactions\Services\FetchesTransaction; use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; +use App\Classes\Modules\Bookings\Standards\Rules\CanCreateBookingRefund; use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject; use App\Classes\Modules\Transactions\ControllersLogic\UpdateRefundTransactionStatusLogic; @@ -58,6 +59,9 @@ class CreateBookingRefundLogic extends AbstractControllerLogic /** @var CreateRemarkProcessor */ private $createRemarkProcessor; + /** @var CanCreateBookingRefund */ + private $canCreateBookingRefund; + /** * CreateBookingPaymentLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -67,8 +71,9 @@ class CreateBookingRefundLogic extends AbstractControllerLogic * @param CreatesTransaction $createsTransaction * @param UpdateRefundTransactionStatusLogic $updateRefundTransactionStatusLogic * @param CreateRemarkProcessor $createRemarkProcessor + * @param CanCreateBookingRefund $canCreateBookingRefund */ - public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, UpdateRefundTransactionStatusLogic $updateRefundTransactionStatusLogic, CreateRemarkProcessor $createRemarkProcessor) + public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, UpdateRefundTransactionStatusLogic $updateRefundTransactionStatusLogic, CreateRemarkProcessor $createRemarkProcessor, CanCreateBookingRefund $canCreateBookingRefund) { $this->fetchBookingQuotation = $fetchBookingQuotation; $this->fetchesTransaction = $fetchesTransaction; @@ -77,6 +82,7 @@ class CreateBookingRefundLogic extends AbstractControllerLogic $this->createsTransaction = $createsTransaction; $this->updateRefundTransactionStatusLogic = $updateRefundTransactionStatusLogic; $this->createRemarkProcessor = $createRemarkProcessor; + $this->canCreateBookingRefund = $canCreateBookingRefund; } /** @@ -93,10 +99,7 @@ class CreateBookingRefundLogic extends AbstractControllerLogic $invoice = $booking->transactions()->where('type', TransactionType::INVOICE)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first(); - //cief todo: 90 - move this into rules - // if(auth()->user()->type === 3) { - // throw new MalformedRequestException('You do not have the permission to refund the order.'); - // } + $this->canCreateBookingRefund->passes(); $billNumber = $this->generatesTransactionBillNumber->execute('RFD-'); diff --git a/app/Classes/Modules/Bookings/Standards/Rules/CanCreateBookingRefund.php b/app/Classes/Modules/Bookings/Standards/Rules/CanCreateBookingRefund.php new file mode 100644 index 00000000..bd982018 --- /dev/null +++ b/app/Classes/Modules/Bookings/Standards/Rules/CanCreateBookingRefund.php @@ -0,0 +1,46 @@ +type, RoleTypes::ADMIN_ROLES)) { + return true; + } + return false; + + } + + /** + * @param BookingObject $object + * @return bool + */ + protected function validators($object): bool + { + return true; + + } + + + /** + * @param $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php index a8e6ca69..f35ec0e8 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php @@ -14,8 +14,10 @@ use Illuminate\Http\Request; use App\Classes\Modules\Wallets\Processors\CreditWalletProcessor; use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount; use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; +use App\Classes\Modules\Transactions\Standards\Rules\CanUpdateRefundTransactionStatus; use App\Classes\ValueObjects\Constants\RemarkRefundReason; use App\Classes\ValueObjects\Constants\TransactionType; +use Illuminate\Support\Facades\Log; class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic { @@ -54,6 +56,9 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic /** @var UpdateBookingAmountLogic */ private $updateBookingAmountLogic; + /** @var CanUpdateRefundTransactionStatus */ + private $canUpdateRefundTransactionStatus; + /** * CreatePaymentVerificationDocumentLogic constructor. * @param FetchesCompany $fetchesCompany @@ -64,8 +69,9 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic * @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount * @param UpdateBookingAmountLogic $updateBookingAmountLogic + * @param CanUpdateRefundTransactionStatus $canUpdateRefundTransactionStatus */ - public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, UpdateBookingAmountLogic $updateBookingAmountLogic) + public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, UpdateBookingAmountLogic $updateBookingAmountLogic, CanUpdateRefundTransactionStatus $canUpdateRefundTransactionStatus) { $this->fetchesCompany = $fetchesCompany; $this->fetchesTransaction = $fetchesTransaction; @@ -75,6 +81,7 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount; $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; $this->updateBookingAmountLogic = $updateBookingAmountLogic; + $this->canUpdateRefundTransactionStatus = $canUpdateRefundTransactionStatus; } /** @@ -84,10 +91,7 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - //cief todo: 90 - move this into rules - // if(auth()->user()->type === 3) { - // throw new MalformedRequestException('You do not have the permission to refund the order.'); - // } + $this->canUpdateRefundTransactionStatus->passes(); $refundTransaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]); diff --git a/app/Classes/Modules/Transactions/Standards/Rules/CanUpdateRefundTransactionStatus.php b/app/Classes/Modules/Transactions/Standards/Rules/CanUpdateRefundTransactionStatus.php new file mode 100644 index 00000000..41e58004 --- /dev/null +++ b/app/Classes/Modules/Transactions/Standards/Rules/CanUpdateRefundTransactionStatus.php @@ -0,0 +1,48 @@ +type, RoleTypes::ADMIN_ROLES)) { + return true; + } + return false; + + } + + /** + * @param BookingObject $object + * @return bool + */ + protected function validators($object): bool + { + return true; + + } + + + /** + * @param $object + * @return bool + */ + protected function criteria($object): bool + { + return true; + } + +} From eec405e5b2bfe6bf2e179775474376aefa8fbcb2 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 2 Sep 2025 04:40:32 +0800 Subject: [PATCH 6/9] E-Invoice - added footer note to invoice and einvoice pdf generate --- resources/views/pages/pdfs/e_invoice.blade.php | 9 ++++++++- resources/views/pages/pdfs/invoice.blade.php | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/resources/views/pages/pdfs/e_invoice.blade.php b/resources/views/pages/pdfs/e_invoice.blade.php index c78d93ec..e5ddcb49 100644 --- a/resources/views/pages/pdfs/e_invoice.blade.php +++ b/resources/views/pages/pdfs/e_invoice.blade.php @@ -111,5 +111,12 @@ - + + + + + + +
This is generated by computer. No signature required.Page {PAGENO} of {nbpg}
+
@endsection diff --git a/resources/views/pages/pdfs/invoice.blade.php b/resources/views/pages/pdfs/invoice.blade.php index 9e560b44..46738785 100644 --- a/resources/views/pages/pdfs/invoice.blade.php +++ b/resources/views/pages/pdfs/invoice.blade.php @@ -71,4 +71,12 @@ Account Name: CIEF Worldwide Sdn Bhd
Account No: 568603010762
+ + + + + + +
This is generated by computer. No signature required.Page {PAGENO} of {nbpg}
+
@endsection From eb3eac3d6d375df33c8c67165338bc50425d2a8f Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 2 Sep 2025 05:40:37 +0800 Subject: [PATCH 7/9] E-Invoice - BatchBookingsGenerateEInvoiceLogic will not process those booking with invoice already generated --- .../V2/ProcessBookingForEInvoiceV2CommandJob.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/Classes/Jobs/Commands/V2/ProcessBookingForEInvoiceV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ProcessBookingForEInvoiceV2CommandJob.php index e5e0a552..1410776a 100644 --- a/app/Classes/Jobs/Commands/V2/ProcessBookingForEInvoiceV2CommandJob.php +++ b/app/Classes/Jobs/Commands/V2/ProcessBookingForEInvoiceV2CommandJob.php @@ -3,6 +3,7 @@ namespace App\Classes\Jobs\Commands\V2; use App\Classes\Modules\Bookings\Processors\RegenerateInvoiceBookingProcessor; +use App\Classes\ValueObjects\Constants\DocumentType; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -34,7 +35,15 @@ class ProcessBookingForEInvoiceV2CommandJob implements ShouldQueue Log::info(Carbon::now() . ': Start job - Processing single booking for E-Invoice.'); $start = new Carbon(); - (App()->make(RegenerateInvoiceBookingProcessor::class))->execute($this->booking); + $documents = $this->booking->documents()->whereIn('document_type', [DocumentType::PURCHASE_ORDER, DocumentType::INVOICE, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->get(); + + if ($documents->isEmpty()) { + Log::info("Processing for E-Invoice, booking id : " . $this->booking->marking); + (App()->make(RegenerateInvoiceBookingProcessor::class))->execute($this->booking); + } + else{ + Log::info("NO Processing for E-Invoice, booking id : " . $this->booking->marking); + } $end = new Carbon(); $elapsedTime = $start->diff($end)->format('%H:%I:%S'); From 539c1fec532909c427e91dedae7dfe164626ea5f Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 2 Sep 2025 12:51:58 +0800 Subject: [PATCH 8/9] E-Invoice - Minor code refactor --- .../Bookings/ControllersLogic/CreateBookingRefundLogic.php | 2 +- .../ControllersLogic/UpdateRefundTransactionStatusLogic.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php index b7e7123d..f8a1e8cf 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingRefundLogic.php @@ -99,7 +99,7 @@ class CreateBookingRefundLogic extends AbstractControllerLogic $invoice = $booking->transactions()->where('type', TransactionType::INVOICE)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first(); - $this->canCreateBookingRefund->passes(); + // $this->canCreateBookingRefund->passes(); $billNumber = $this->generatesTransactionBillNumber->execute('RFD-'); diff --git a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php index f35ec0e8..5ce9e915 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php @@ -91,7 +91,7 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic */ public function logic(Request $request) : JsonResponse { - $this->canUpdateRefundTransactionStatus->passes(); + // $this->canUpdateRefundTransactionStatus->passes(); $refundTransaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]); From d23550daf54ef26fefc3a2d3d0f90916bb20639d Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Wed, 3 Sep 2025 14:43:01 +0800 Subject: [PATCH 9/9] E-Invoice - Minor update on Delivery Order PDF --- resources/views/pages/pdfs/deliver_order.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/pages/pdfs/deliver_order.blade.php b/resources/views/pages/pdfs/deliver_order.blade.php index d16a68f5..f06e4174 100644 --- a/resources/views/pages/pdfs/deliver_order.blade.php +++ b/resources/views/pages/pdfs/deliver_order.blade.php @@ -30,7 +30,7 @@ -
EDO: {{ $transaction->bill_no }}
+
EDO: {{ str_replace(['EINV-', 'INV-'], 'EDO-', $transaction->bill_no) }}
REF: {{ $transaction->booking->marking }}
Date: {{