From d8ce379560a64d6959bbd954a6b72d55ad59f39f Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 08:43:48 +0800 Subject: [PATCH 1/6] E-Invoice - Automapping Issues, Credit Note Report (Import) Partial Completion --- .../ControllersLogic/ImportExcelLogic.php | 65 ++++++++++++++++++- app/Classes/ValueObjects/Constants/KVPKey.php | 4 ++ .../Controllers/Imports/ImportController.php | 7 +- .../elements/DownloadUploadComponent.vue | 2 + routes/export.php | 1 + 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index 27ecc768..db810cb9 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -124,6 +124,15 @@ class ImportExcelLogic extends AbstractControllerLogic 'data' => $result ]; } + else if ($reportType === 'Credit Note Report') { + $result = $this->processCreditNoteReport($sheet); + $result = [ + 'message' => empty($result) + ? '' + : 'Some data are unprocessed: ', + 'data' => $result + ]; + } else{ throw new MalformedRequestException('Cannot process report type: ' . $reportType); } @@ -145,7 +154,7 @@ class ImportExcelLogic extends AbstractControllerLogic } private function processSalesInvoiceReport($sheet){ - $rows = $sheet->skip(1); + $rows = $sheet->skip(1); foreach ($rows as $index => $details) { $docNo = $details[0] ?? null; @@ -242,4 +251,58 @@ class ImportExcelLogic extends AbstractControllerLogic return $unprocessedKnockOffs; } + + private function processCreditNoteReport($sheet){ + $unprocessedDocNos = []; + $rows = $sheet->skip(1); + + foreach ($rows as $index => $details) { + $docNo = $details[0] ?? null; + $docDate = $details[1] ?? null; + $debtorCode = $details[2] ?? null; + $ref = $details[3] ?? null; + $description = $details[4] ?? null; + $reason = $details[5] ?? null; + $deptNo = $details[6] ?? null; + $qty = $details[7] ?? null; + $unitPrice = $details[8] ?? null; + $accNo = $details[9] ?? null; + $submitEinvoice = $details[10] ?? null; + $einvoiceIssueDateTime = $details[11] ?? null; + $consolidatedEinvoice = $details[12] ?? null; + $eInvoiceValidationLink = $details[13] ?? null; + + Log::info("Row {$index} processCreditNoteReport:", [ + 'DocNo' => $docNo, + 'DocDate' => $docDate, + 'DebtorCode' => $debtorCode, + 'Ref' => $ref, + 'Description' => $description, + 'Reason' => $reason, + 'DeptNo' => $deptNo, + 'Qty' => $qty, + 'UnitPrice' => $unitPrice, + 'AccNo' => $accNo, + 'SubmitEinvoice' => $submitEinvoice, + 'EInvoiceIssueDateTime' => $einvoiceIssueDateTime, + 'ConsolidatedEinvoice' => $consolidatedEinvoice, + 'EInvoiceValidationLink' => $eInvoiceValidationLink, + ]); + + $booking = Booking::where('marking', $ref)->first(); + if($booking){ + if($docNo != "" && $docNo != "<>"){ + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE, $docNo); + if($eInvoiceValidationLink){ + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE, $eInvoiceValidationLink); + } + } + } + else{ + $unprocessedDocNos[] = $docNo; + } + } + + return $unprocessedDocNos; + } } diff --git a/app/Classes/ValueObjects/Constants/KVPKey.php b/app/Classes/ValueObjects/Constants/KVPKey.php index 7a8b42ae..4e0484d7 100644 --- a/app/Classes/ValueObjects/Constants/KVPKey.php +++ b/app/Classes/ValueObjects/Constants/KVPKey.php @@ -10,8 +10,12 @@ class KVPKey public const AUTOCOUNT_DOCNO_OFFICIAL_RECEIPT = 'AUTOCOUNT_DOCNO_OR'; + public const AUTOCOUNT_DOCNO_CREDIT_NOTE = 'AUTOCOUNT_DOCNO_CN'; + public const AUTOCOUNT_EINVOICE_VALIDATION_LINK = 'AUTOCOUNT_EINVOICE_VALIDATION_LINK'; + public const AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE = 'AUTOCOUNT_EINVOICE_VALIDATION_LINK_CN'; + public const CREDIT_NOTE_APPROVAL_DATE = 'CREDIT_NOTE_APPROVAL_DATE'; public const TRANSACTION_MODEL_CLASS = 'App\Models\Transaction'; diff --git a/app/Http/Controllers/Imports/ImportController.php b/app/Http/Controllers/Imports/ImportController.php index 5e045b03..79bfc0f0 100644 --- a/app/Http/Controllers/Imports/ImportController.php +++ b/app/Http/Controllers/Imports/ImportController.php @@ -14,7 +14,12 @@ class ImportController extends Controller return $logic->execute($request); } - public function officialReceipt(Request $request, ImportExcelLogic $logic): JsonResponse + public function officialReceipt(Request $request, ImportExcelLogic $logic): JsonResponse + { + return $logic->execute($request); + } + + public function creditNote(Request $request, ImportExcelLogic $logic): JsonResponse { return $logic->execute($request); } diff --git a/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue b/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue index c43d73dc..cf142e56 100644 --- a/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue +++ b/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue @@ -106,6 +106,7 @@ export default { allowedReportTypes: [ 'Sales Invoice Report', '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]', + 'Credit Note Report' ] } }, @@ -129,6 +130,7 @@ export default { const importRoutesMap = { 'Sales Invoice Report': route('api.import.sales_invoices'), '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]': route('api.import.official_receipt'), + 'Credit Note Report': route('api.import.credit_note'), }; return importRoutesMap[reportType] || ''; diff --git a/routes/export.php b/routes/export.php index 4b1c2576..a0815a20 100644 --- a/routes/export.php +++ b/routes/export.php @@ -22,4 +22,5 @@ Route::group(['prefix' => 'export', 'as' => 'export.', 'namespace' => 'Exports'] Route::group(['prefix' => 'import', 'as' => 'import.', 'namespace' => 'Imports'], function () { Route::post('/import/sales-invoice', [ImportController::class, 'salesInvoices'])->name('sales_invoices'); Route::post('/import/offical-receipt', [ImportController::class, 'officialReceipt'])->name('official_receipt'); + Route::post('/import/credit-note', [ImportController::class, 'creditNote'])->name('credit_note'); }); From d63b46c0aba14717cd0ae1dca5fa9c82aa789d8c Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 10:09:18 +0800 Subject: [PATCH 2/6] E-Invoice - Update business logic for downloading E-CreditNote --- .../GenerateCreditNotePdfV2Logic.php | 22 +++++++++++++------ app/Http/Resources/TransactionResource.php | 12 +++++++++- .../elements/PaymentHistoryComponent.vue | 20 +++++++++-------- .../views/pages/pdfs/e_credit_note.blade.php | 7 +++--- resources/views/vendor/head.blade.php | 1 - 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php index 0d74ec3d..22366236 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php @@ -46,6 +46,8 @@ class GenerateCreditNotePdfV2Logic { $pdfTemplateName = 'pages.pdfs.credit_note_v2'; //default since e-invoice implementation $transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]); + $autoCountInvoiceId = ''; + $autoCountEInvoiceValidationLink = 'CIEF'; if($transaction->type === TransactionType::REFUND){ //Retrieve TransactionType::CREDIT_NOTE @@ -91,20 +93,18 @@ class GenerateCreditNotePdfV2Logic if($eInvoiceStarted) { $eInvoiceStarted = false; //reset to re-evaluate second time - $autoCountInvoiceId = ''; - $autoCountEInvoiceValidationLink = ''; - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first(); + $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE)->first(); if($metadata){ $autoCountInvoiceId = $metadata->value; } - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK)->first(); + $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE)->first(); if($metadata){ $autoCountEInvoiceValidationLink = $metadata->value; } - Log::info('autoCountInvoiceId: ' . $autoCountInvoiceId); - Log::info('autoCountEInvoiceValidationLink: ' . $autoCountEInvoiceValidationLink); + Log::info('AUTOCOUNT_DOCNO_CREDIT_NOTE: ' . $autoCountInvoiceId); + Log::info('AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE: ' . $autoCountEInvoiceValidationLink); if($autoCountInvoiceId && $autoCountEInvoiceValidationLink){ $eInvoiceStarted = true; @@ -127,7 +127,15 @@ class GenerateCreditNotePdfV2Logic Log::info('Based on booking created date, E-Credit Note not yet started. / Not Yet Ready.'); } - $pdf = LaravelMpdf::loadView($pdfTemplateName, ['transaction' => $transaction, 'booking' => $booking, 'supplier' => $supplier, 'date' => $date, 'brn' => $brn,]); + $pdf = LaravelMpdf::loadView($pdfTemplateName, [ + 'transaction' => $transaction, + 'booking' => $booking, + 'supplier' => $supplier, + 'date' => $date, + 'brn' => $brn, + 'autocountId' => $autoCountInvoiceId, + 'autocountEInvoiceValidationLink' => $autoCountEInvoiceValidationLink, + ]); $exportFileName = 'CreditNote.pdf'; $filesystemDriver = Storage::getDefaultDriver(); diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index 46c1f29b..d5655dec 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -4,6 +4,7 @@ namespace App\Http\Resources; use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\KVPKey; use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; use Carbon\Carbon; @@ -37,6 +38,14 @@ class TransactionResource extends JsonResource } //Check if Transaction of type PAYMENT has an override for recipient bank - ends + $eInvoice = false; + if($booking && $this->type === TransactionType::REFUND){ + $kvp = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE)->first(); + if($kvp){ + $eInvoice = true; + } + } + $days = $this->created_at->endOfDay()->addWeekdays($booking->service_id === 3 ? 3 : 1); return [ 'id' => $this->id, @@ -72,7 +81,8 @@ class TransactionResource extends JsonResource 'remarks' => RemarkResource::collection($this->remarks), 'redemption' => new VoucherRedemptionResource($this->voucherRedemption), 'bank' => ((int) $this->type === TransactionType::PAYMENT) ? new BankResource($bank) : null, //When a transaction (of type payment) has an override recipient bank details on booking, this is NOT null - 'bank_recipient_edited' => $isEditedBankRecipient + 'bank_recipient_edited' => $isEditedBankRecipient, + 'e_invoice' => $this->when($this->type === TransactionType::REFUND, $eInvoice), ]; } } diff --git a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue index c5cb9b96..dadbbdd0 100644 --- a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue +++ b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue @@ -403,7 +403,7 @@ -
+
Amount
{{refund.currency.short_code}} {{(Math.round((refund.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
@@ -412,7 +412,7 @@
{{refund.original_currency.short_code}} {{(Math.round((refund.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
-
+
Credit Note
@@ -425,7 +425,15 @@
- + + + +
+ +
+
+
@@ -500,7 +508,6 @@ bank_id: 1 }, section: 'bookingDetailSection', - eInvoiceStartDate: window.E_INVOICE_START_DATE || '' } }, computed: { @@ -545,11 +552,6 @@ showEditBookingAmount(){ return this.data.booking.company.employee.status === 2 && this.data.booking.company.status === 2 && (Math.round((this.data.booking.outstanding_amount + Number.EPSILON) * 100) / 100) > 0; }, - showDownloadCreditNote() { - const today = new Date(); - const einvoiceStartDate = new Date(this.eInvoiceStartDate); - return today > einvoiceStartDate; - } }, methods: { clickExpand(){ diff --git a/resources/views/pages/pdfs/e_credit_note.blade.php b/resources/views/pages/pdfs/e_credit_note.blade.php index aa4c67d8..0828ba1a 100644 --- a/resources/views/pages/pdfs/e_credit_note.blade.php +++ b/resources/views/pages/pdfs/e_credit_note.blade.php @@ -7,7 +7,7 @@ $credit_title = 'E-Credit'; $bill_no = $transaction->bill_no; @endphp -
{{ $bill_no }}
+
{{ $autocountId }}
@@ -36,6 +36,7 @@
Ref# {{ $booking->marking }}
+
EI# {{ $autocountId ?? 'NONE'}}
Date: {{ $date->toDateString() }}
 
@@ -123,12 +124,12 @@ diff --git a/resources/views/vendor/head.blade.php b/resources/views/vendor/head.blade.php index 6f621539..fe49fba7 100644 --- a/resources/views/vendor/head.blade.php +++ b/resources/views/vendor/head.blade.php @@ -6,7 +6,6 @@ })(window,document,'script','dataLayer','GTM-TQKCPCD'); From c59cec3f40b2bd97d7c39acc43d013eeb59fa95c Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 13:30:28 +0800 Subject: [PATCH 3/6] E-Invoice - Automapping Issues, Credit Note Report (Import) Partial Completion --- .../ControllersLogic/ImportExcelLogic.php | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index db810cb9..f8813f4c 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -95,6 +95,24 @@ class ImportExcelLogic extends AbstractControllerLogic 'knockoffdocno' ]; + $creditNoteReportHeader = [ + 'docno', + 'docdate', + 'debtorcode', + 'ref', + 'description', + 'reason', + 'deptno', + 'qty', + 'unitprice', + 'accno', + 'submiteinvoice', + 'einvoiceissuedatetime', + 'consolidatedeinvoice', + 'einvoicevalidationlink' + ]; + + if ($reportType === 'Sales Invoice Report') { $optionalColumn = 'einvoicevalidationlink'; @@ -105,12 +123,16 @@ class ImportExcelLogic extends AbstractControllerLogic throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); } - } elseif ($reportType === 'Customers Report' && $normalizedHeader !== $customersReportHeader) { + } + elseif ($reportType === 'Customers Report' && $normalizedHeader !== $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]' && $normalizedHeader !== $paymentReportHeader) { throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); } + elseif ($reportType === 'Credit Note Report' && $normalizedHeader !== $creditNoteReportHeader) { + throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); + } if ($reportType === 'Sales Invoice Report') { $this->processSalesInvoiceReport($sheet); From 25db0a42a2938fa3eed01a8bbf75e31ee6ecf2d9 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 15:24:34 +0800 Subject: [PATCH 4/6] E-Invoice - Automapping Issues, Credit Note Report (Import) Partial Completion --- .../ControllersLogic/ImportExcelLogic.php | 18 ++++++++++-- .../GenerateCreditNotePdfV2Logic.php | 28 +++++++++++-------- app/Http/Resources/TransactionResource.php | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index f8813f4c..f20e652f 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -314,9 +314,21 @@ class ImportExcelLogic extends AbstractControllerLogic $booking = Booking::where('marking', $ref)->first(); if($booking){ if($docNo != "" && $docNo != "<>"){ - $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE, $docNo); - if($eInvoiceValidationLink){ - $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE, $eInvoiceValidationLink); + $payments = $booking->transactions()->payments()->get(); + $processed = false; + foreach ($payments as $payment) { + $refundTransaction = $payment->transactions()->refunds()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->latest()->first(); + if($refundTransaction){ + $this->updateOrCreateKeyValuePair($refundTransaction, KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE, $docNo); + if($eInvoiceValidationLink){ + $this->updateOrCreateKeyValuePair($refundTransaction, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE, $eInvoiceValidationLink); + } + $processed = true; + break; + } + } + if(!$processed){ + $unprocessedDocNos[] = $docNo; } } } diff --git a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php index 22366236..b65985b0 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php @@ -94,20 +94,24 @@ class GenerateCreditNotePdfV2Logic if($eInvoiceStarted) { $eInvoiceStarted = false; //reset to re-evaluate second time - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE)->first(); - if($metadata){ - $autoCountInvoiceId = $metadata->value; - } - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE)->first(); - if($metadata){ - $autoCountEInvoiceValidationLink = $metadata->value; - } + $kvp = KeyValuePair::where('key', KVPKey::TRANSACTION_MODEL_CLASS)->where('value', $transaction->id)->first(); + $refundTransaction = $kvp ? $kvp->owner : null; + if($refundTransaction && $refundTransaction->type === TransactionType::REFUND){ + $metadata = $refundTransaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE)->first(); + if($metadata){ + $autoCountInvoiceId = $metadata->value; + } + $metadata = $refundTransaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE)->first(); + if($metadata){ + $autoCountEInvoiceValidationLink = $metadata->value; + } - Log::info('AUTOCOUNT_DOCNO_CREDIT_NOTE: ' . $autoCountInvoiceId); - Log::info('AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE: ' . $autoCountEInvoiceValidationLink); + Log::info('AUTOCOUNT_DOCNO_CREDIT_NOTE: ' . $autoCountInvoiceId); + Log::info('AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE: ' . $autoCountEInvoiceValidationLink); - if($autoCountInvoiceId && $autoCountEInvoiceValidationLink){ - $eInvoiceStarted = true; + if($autoCountInvoiceId && $autoCountEInvoiceValidationLink){ + $eInvoiceStarted = true; + } } } diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index d5655dec..b013f9db 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -40,7 +40,7 @@ class TransactionResource extends JsonResource $eInvoice = false; if($booking && $this->type === TransactionType::REFUND){ - $kvp = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE)->first(); + $kvp = $this->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE)->first(); if($kvp){ $eInvoice = true; } From 95d53360fd76d02d555af27d60c4c7ecc23f3a4e Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Wed, 27 Aug 2025 11:47:41 +0800 Subject: [PATCH 5/6] E-Invoice - Automapping Issues, Credit Note Report (Import) Business Logic update --- .../ProcessCreditNoteReportV2CommandJob.php | 108 ++++++++++++++++++ .../Imports/Services/AutoCountDataImport.php | 29 ++++- 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 app/Classes/Jobs/Commands/V2/ProcessCreditNoteReportV2CommandJob.php diff --git a/app/Classes/Jobs/Commands/V2/ProcessCreditNoteReportV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ProcessCreditNoteReportV2CommandJob.php new file mode 100644 index 00000000..91dc27bb --- /dev/null +++ b/app/Classes/Jobs/Commands/V2/ProcessCreditNoteReportV2CommandJob.php @@ -0,0 +1,108 @@ +details = $details; + } + + public function handle() + { + Log::info(Carbon::now() . ': Start job - Processing single record for E-Invoice from Credit Note Report Import.'); + $start = new Carbon(); + + $docNo = $this->details['docno'] ?? null; + $docDate = $this->details['docdate'] ?? null; + $debtorCode = $this->details['debtorcode'] ?? null; + $ref = $this->details['ref'] ?? null; + $description = $this->details['description'] ?? null; + $reason = $this->details['reason'] ?? null; + $deptNo = $this->details['deptno'] ?? null; + $qty = $this->details['qty'] ?? null; + $unitPrice = $this->details['unitprice'] ?? null; + $accNo = $this->details['accno'] ?? null; + $submitEinvoice = $this->details['submiteinvoice'] ?? null; + $einvoiceIssueDateTime = $this->details['einvoiceissuedatetime'] ?? null; + $consolidatedEinvoice = $this->details['consolidatedeinvoice'] ?? null; + $eInvoiceValidationLink = $this->details['einvoicevalidationlink'] ?? null; + + Log::info("Processing Credit Note Report:", [ + 'DocNo' => $docNo, + 'DocDate' => $docDate, + 'DebtorCode' => $debtorCode, + 'Ref' => $ref, + 'Description' => $description, + 'Reason' => $reason, + 'DeptNo' => $deptNo, + 'Qty' => $qty, + 'UnitPrice' => $unitPrice, + 'AccNo' => $accNo, + 'SubmitEinvoice' => $submitEinvoice, + 'EInvoiceIssueDateTime' => $einvoiceIssueDateTime, + 'ConsolidatedEinvoice' => $consolidatedEinvoice, + 'EInvoiceValidationLink' => $eInvoiceValidationLink, + ]); + + $booking = Booking::where('marking', $ref)->first(); + if($booking){ + if($docNo != "" && $docNo != "<>"){ + $payments = $booking->transactions()->payments()->get(); + foreach ($payments as $payment) { + $refundTransaction = $payment->transactions()->refunds()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->latest()->first(); + if($refundTransaction){ + $this->updateOrCreateKeyValuePair($refundTransaction, KVPKey::AUTOCOUNT_DOCNO_CREDIT_NOTE, $docNo); + if($eInvoiceValidationLink){ + $this->updateOrCreateKeyValuePair($refundTransaction, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK_CREDIT_NOTE, $eInvoiceValidationLink); + } + break; + } + } + } + } + + $end = new Carbon(); + $elapsedTime = $start->diff($end)->format('%H:%I:%S'); + Log::info(Carbon::now() . ': End job - Processing single record for E-Invoice from Credit Note 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/Services/AutoCountDataImport.php b/app/Classes/Modules/Imports/Services/AutoCountDataImport.php index 17016aeb..85d47a7b 100644 --- a/app/Classes/Modules/Imports/Services/AutoCountDataImport.php +++ b/app/Classes/Modules/Imports/Services/AutoCountDataImport.php @@ -9,6 +9,7 @@ use Maatwebsite\Excel\Concerns\WithChunkReading; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\Jobs\Commands\V2\ProcessPaymentReportV2CommandJob; use App\Classes\Jobs\Commands\V2\ProcessSalesInvoiceReportV2CommandJob; +use App\Classes\Jobs\Commands\V2\ProcessCreditNoteReportV2CommandJob; use Illuminate\Support\Facades\Log; class AutoCountDataImport implements ToCollection, WithHeadingRow, WithChunkReading @@ -40,10 +41,14 @@ class AutoCountDataImport implements ToCollection, WithHeadingRow, WithChunkRead if ($this->reportType === 'Sales Invoice Report') { ProcessSalesInvoiceReportV2CommandJob::dispatch($row->toArray()); - } elseif ($this->reportType === '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]') { + } + elseif ($this->reportType === '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]') { ProcessPaymentReportV2CommandJob::dispatch($row->toArray()); } - else{ + else if ($this->reportType === 'Credit Note Report') { + ProcessCreditNoteReportV2CommandJob::dispatch($row->toArray()); + } + else { throw new MalformedRequestException('Cannot process report type: ' . $this->reportType); } } @@ -77,6 +82,23 @@ class AutoCountDataImport implements ToCollection, WithHeadingRow, WithChunkRead 'knockoffdocno' ]; + $creditNoteReportHeader = [ + 'docno', + 'docdate', + 'debtorcode', + 'ref', + 'description', + 'reason', + 'deptno', + 'qty', + 'unitprice', + 'accno', + 'submiteinvoice', + 'einvoiceissuedatetime', + 'consolidatedeinvoice', + 'einvoicevalidationlink' + ]; + if ($reportType === 'Sales Invoice Report' && $header !== $salesInvoiceHeader && $header !== [...$salesInvoiceHeader, $optionalColumn]) { @@ -88,5 +110,8 @@ class AutoCountDataImport implements ToCollection, WithHeadingRow, WithChunkRead 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.'); } + elseif ($reportType === 'Credit Note Report' && $header !== $creditNoteReportHeader) { + throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.'); + } } } From 4ff703bd751eb58e56633bd6ec3855a933c983ca Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Fri, 5 Sep 2025 15:45:28 +0800 Subject: [PATCH 6/6] E-Invoice - Updated business logic for refund / return (credit note) without updating booking amount --- .../UpdateBookingAmountWithPOLogic.php | 7 +++++-- .../UpdateRefundTransactionStatusLogic.php | 6 +++--- app/Http/Resources/BookingResource.php | 5 ++++- .../BookingPaymentQuotationV2Component.vue | 20 +++++++++++++++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php index 0ad1680c..73265a74 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php @@ -75,10 +75,13 @@ class UpdateBookingAmountWithPOLogic extends AbstractControllerLogic $booking = $this->fetchesBooking->execute(['id' => $request->route('id') ?? $id]); $outstandingAmount = $this->calculatesBookingOutstanding->execute($booking); $bookingAttribute = $booking->attributesKVP()->where('key', "BOOKING_AMOUNT_UPDATE")->first(); - $paidAmount = floatval($this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id)) - floatval($this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id)); + $refundAmount = $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id); + $paidAmount = floatval($this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id)) - floatval($refundAmount); if($paidAmount > 0 && $outstandingAmount > 0 && !$bookingAttribute){ - throw new MalformedRequestException('Purchase Order at this point can only be edited after editing the booking amount'); + if($refundAmount != $outstandingAmount){ + throw new MalformedRequestException('Purchase Order at this point can only be edited after editing the booking amount'); + } } if($bookingAttribute){ diff --git a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php index 5ce9e915..4a8fe3ae 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/UpdateRefundTransactionStatusLogic.php @@ -125,9 +125,9 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic $this->updatesTransactionStatus->execute($po_transaction, (float) number_format($po_transaction->amount, 2, '.', '') === (float) number_format((float)$booking->fix_amount - $refundTransaction->original_amount, 2, '.', '') ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::PENDING_SUBMISSION); } - $request['fix_amount'] = $booking->fix_amount - $refundTransaction->original_amount; - $request->route()->setParameter('id', $booking->id); - $this->updateBookingAmountLogic->execute($request); + // $request['fix_amount'] = $booking->fix_amount - $refundTransaction->original_amount; + // $request->route()->setParameter('id', $booking->id); + // $this->updateBookingAmountLogic->execute($request); } if ($supplierRefundTransaction) { diff --git a/app/Http/Resources/BookingResource.php b/app/Http/Resources/BookingResource.php index dbb11d0c..d8bcb6ec 100644 --- a/app/Http/Resources/BookingResource.php +++ b/app/Http/Resources/BookingResource.php @@ -39,6 +39,8 @@ class BookingResource extends JsonResource $eInvoice = true; } + $outStandingAmountWithRefund = floatval((App()->make(CalculatesBookingOutstanding::class))->execute($this->resource)) - floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)); + return [ 'id' => $this->id, 'company' => new CompanyResource($this->company), @@ -48,7 +50,8 @@ class BookingResource extends JsonResource 'amount' => $this->fix_amount, 'floating_amount' => floatval((App()->make(CalculatesBookingFloatingAmount::class))->execute($this->resource, $this->fix_currency_id)), 'paid_amount' => floatval((App()->make(CalculatesBookingPayableAmount::class))->execute($this->resource, $this->fix_currency_id)) - floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)), - // 'outstanding_amount' => floatval((App()->make(CalculatesBookingOutstanding::class))->execute($this->resource)) + floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)), + 'outstanding_amount_with_refund' => $outStandingAmountWithRefund > 0 ? $outStandingAmountWithRefund : 0, + 'refunded_amount' => floatval((App()->make(CalculatesBookingRefundAmount::class))->execute($this->resource, $this->fix_currency_id)), 'outstanding_amount' => floatval((App()->make(CalculatesBookingOutstanding::class))->execute($this->resource)), 'fixed_currency' => new CurrencyResource($this->fixedCurrency), 'convertible_currency' => new CurrencyResource($this->convertibleCurrency), diff --git a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue index ae894419..ad07a927 100644 --- a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue +++ b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue @@ -100,7 +100,7 @@
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.paid_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
-
+
Floating Amount:
@@ -108,7 +108,15 @@
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.floating_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
-
+
+
+
OutStanding Total:
+
+
+
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.outstanding_amount_with_refund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
+
+
OutStanding Total:
@@ -116,6 +124,14 @@
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.outstanding_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
+
+
Refunded Total:
+
+
+
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
+
- +
-

http://e-invoice uuid link

+

{{ $autocountEInvoiceValidationLink }}