From fc410a24aa03bed944d1dd8b9ae1bc384fafae0e Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Fri, 22 Aug 2025 21:15:16 +0800 Subject: [PATCH 1/3] E-Invoice - special handling of cases for those booking done before 1st July 2025 that need to generate E-Invoice --- .../BatchBookingsGenerateEInvoiceLogic.php | 10 ++++++++-- .../Processors/CreateInvoiceTransactionProcessor.php | 6 +++++- app/Classes/ValueObjects/Constants/KVPKey.php | 2 ++ app/Http/Resources/BookingResource.php | 10 +++++++--- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php index 703fe348..9d44e244 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php @@ -82,7 +82,13 @@ class BatchBookingsGenerateEInvoiceLogic extends AbstractControllerLogic $endDate = $endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now(); $bookings = Booking::where('status', ApprovalStatus::COMPLETED) - ->whereBetween('created_at', [$startDate, $endDate]) + // ->whereBetween('created_at', [$startDate, $endDate]) + ->whereHas('transactions', function ($query) use ($startDate, $endDate) { + $query->payments() + ->complete() + ->whereBetween('created_at', [$startDate, $endDate]) + ->latest('created_at'); + }) ->whereHas('attributesKVP', function (Builder $query) { $query->where('key', KVPKey::AUTOCOUNT_DOCNO); }) @@ -92,7 +98,7 @@ class BatchBookingsGenerateEInvoiceLogic extends AbstractControllerLogic ->get(); foreach ($bookings as $booking) { - // $autocountValue = optional($booking->attributesKVP->first())->value; + //$autocountValue = optional($booking->attributesKVP->first())->value; //Log::info('Booking ID: ' . $booking->marking . ' | AUTOCOUNT_DOCNO: ' . $autocountValue); // $this->regenerateInvoiceBookingProcessor->execute($booking); ProcessBookingForEInvoiceV2CommandJob::dispatch($booking); diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php index 44f0dbbf..799032ab 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php @@ -18,6 +18,7 @@ use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\SegmentConstants; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\DocumentType; +use App\Classes\ValueObjects\Constants\KVPKey; use App\Models\Booking; use App\Models\SegmentConstant; use Carbon\Carbon; @@ -136,7 +137,10 @@ class CreateInvoiceTransactionProcessor if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $supplier->e_invoice === 1) { $eInvoice = true; } - // $eInvoice = true; //cief todo: 90 - for testing + $kvp = $booking->attributesKVP()->where('key', KVPKey::BOOKING_EINVOICE_ELIGIBLE)->first(); + if($kvp){ + $eInvoice = true; + } if($isAllowNormalInvoice){ $invoiceNo = ""; //July 2025 workaround generate normal invoice instead of E-Invoice diff --git a/app/Classes/ValueObjects/Constants/KVPKey.php b/app/Classes/ValueObjects/Constants/KVPKey.php index 50d3f17e..b2943b4c 100644 --- a/app/Classes/ValueObjects/Constants/KVPKey.php +++ b/app/Classes/ValueObjects/Constants/KVPKey.php @@ -16,4 +16,6 @@ class KVPKey public const TRANSACTION_MODEL_CLASS = 'App\Models\Transaction'; + public const BOOKING_EINVOICE_ELIGIBLE = 'BOOKING_EINVOICE_ELIGIBLE'; + } diff --git a/app/Http/Resources/BookingResource.php b/app/Http/Resources/BookingResource.php index 449c6a59..dbb11d0c 100644 --- a/app/Http/Resources/BookingResource.php +++ b/app/Http/Resources/BookingResource.php @@ -10,6 +10,7 @@ use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\BookingAttributeNames; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\DocumentType; +use App\Classes\ValueObjects\Constants\KVPKey; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; @@ -32,9 +33,12 @@ class BookingResource extends JsonResource if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $this->company->e_invoice === 1) { //&& $bookingCreatedDate->diffInMinutes($eInvoiceRequestedDate) <= 480 cief todo: 90 $eInvoice = true; } - // if ($this->company->e_invoice === 1) { - // $eInvoice = true; - // } + + $kvp = $this->attributesKVP()->where('key', KVPKey::BOOKING_EINVOICE_ELIGIBLE)->first(); + if($kvp){ + $eInvoice = true; + } + return [ 'id' => $this->id, 'company' => new CompanyResource($this->company), From e159d0d76948e7404196c0ef4eeb10a3dd6ce86f Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Fri, 22 Aug 2025 21:33:20 +0800 Subject: [PATCH 2/3] E-Invoice - special handling of cases for those booking done before 1st July 2025 that need to generate E-Invoice --- .../ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php | 6 +++--- .../Modules/Exports/Services/ExportsARCreditNoteReport.php | 2 +- .../Services/ExportsReceivePaymentForBookingReport.php | 2 +- .../Modules/Imports/ControllersLogic/ImportExcelLogic.php | 6 +++--- .../ControllersLogic/GenerateCreditNotePdfV2Logic.php | 2 +- .../Processors/CreateInvoiceDocumentProcessor.php | 2 +- app/Classes/ValueObjects/Constants/KVPKey.php | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php index 9d44e244..84cae498 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/BatchBookingsGenerateEInvoiceLogic.php @@ -90,16 +90,16 @@ class BatchBookingsGenerateEInvoiceLogic extends AbstractControllerLogic ->latest('created_at'); }) ->whereHas('attributesKVP', function (Builder $query) { - $query->where('key', KVPKey::AUTOCOUNT_DOCNO); + $query->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE); }) ->with(['attributesKVP' => function ($query) { - $query->where('key', KVPKey::AUTOCOUNT_DOCNO); + $query->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE); }]) ->get(); foreach ($bookings as $booking) { //$autocountValue = optional($booking->attributesKVP->first())->value; - //Log::info('Booking ID: ' . $booking->marking . ' | AUTOCOUNT_DOCNO: ' . $autocountValue); + //Log::info('Booking ID: ' . $booking->marking . ' | AUTOCOUNT_DOCNO_INVOICE: ' . $autocountValue); // $this->regenerateInvoiceBookingProcessor->execute($booking); ProcessBookingForEInvoiceV2CommandJob::dispatch($booking); } diff --git a/app/Classes/Modules/Exports/Services/ExportsARCreditNoteReport.php b/app/Classes/Modules/Exports/Services/ExportsARCreditNoteReport.php index 9592cd93..fb140642 100644 --- a/app/Classes/Modules/Exports/Services/ExportsARCreditNoteReport.php +++ b/app/Classes/Modules/Exports/Services/ExportsARCreditNoteReport.php @@ -88,7 +88,7 @@ class ExportsARCreditNoteReport implements FromQuery, WithHeadings, WithHeadingR $refundRemark = $refundTransaction->remarks && $refundTransaction->remarks->first() ? $refundTransaction->remarks->first()->content : null; $booking = $refundTransaction->owner->booking; if($booking){ - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO)->first(); + $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first(); if($metadata){ $autoCountSalesInvoiceId = $metadata->value; } diff --git a/app/Classes/Modules/Exports/Services/ExportsReceivePaymentForBookingReport.php b/app/Classes/Modules/Exports/Services/ExportsReceivePaymentForBookingReport.php index 32d6e86d..cacbd43b 100644 --- a/app/Classes/Modules/Exports/Services/ExportsReceivePaymentForBookingReport.php +++ b/app/Classes/Modules/Exports/Services/ExportsReceivePaymentForBookingReport.php @@ -80,7 +80,7 @@ class ExportsReceivePaymentForBookingReport implements FromQuery, WithHeadings, } if($booking){ - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO)->first(); + $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first(); if($metadata){ $autoCountSalesInvoiceId = $metadata->value; } diff --git a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php index 22eb7eb4..27ecc768 100644 --- a/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php +++ b/app/Classes/Modules/Imports/ControllersLogic/ImportExcelLogic.php @@ -185,7 +185,7 @@ class ImportExcelLogic extends AbstractControllerLogic $booking = Booking::where('marking', $ref)->first(); if($booking){ if($docNo != "" && $docNo != "<>"){ - $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO, $docNo); + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_INVOICE, $docNo); } if($eInvoiceValidationLink){ $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); @@ -219,12 +219,12 @@ class ImportExcelLogic extends AbstractControllerLogic if($knockOffDocNo) { - $kvp = KeyValuePair::where('key', KVPKey::AUTOCOUNT_DOCNO)->where('value', $knockOffDocNo)->first(); + $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_OFFICIAL_RECEIPT_DOCNO, $docNo); + $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_DOCNO_OFFICIAL_RECEIPT, $docNo); } // if($eInvoiceValidationLink){ // $this->updateOrCreateKeyValuePair($booking, KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK, $eInvoiceValidationLink); diff --git a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php index 7168a175..0d74ec3d 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfV2Logic.php @@ -94,7 +94,7 @@ class GenerateCreditNotePdfV2Logic $autoCountInvoiceId = ''; $autoCountEInvoiceValidationLink = ''; - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO)->first(); + $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first(); if($metadata){ $autoCountInvoiceId = $metadata->value; } diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php index 8f36ea91..96e64155 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php @@ -69,7 +69,7 @@ class CreateInvoiceDocumentProcessor } if($document_type === DocumentType::EINVOICE){ - $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO)->first(); + $metadata = $booking->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first(); if($metadata){ $autoCountInvoiceId = $metadata->value; } diff --git a/app/Classes/ValueObjects/Constants/KVPKey.php b/app/Classes/ValueObjects/Constants/KVPKey.php index b2943b4c..7a8b42ae 100644 --- a/app/Classes/ValueObjects/Constants/KVPKey.php +++ b/app/Classes/ValueObjects/Constants/KVPKey.php @@ -6,9 +6,9 @@ namespace App\Classes\ValueObjects\Constants; class KVPKey { - public const AUTOCOUNT_DOCNO = 'AUTOCOUNT_DOCNO'; + public const AUTOCOUNT_DOCNO_INVOICE = 'AUTOCOUNT_DOCNO_I'; - public const AUTOCOUNT_OFFICIAL_RECEIPT_DOCNO = 'AUTOCOUNT_OR_DOCNO'; + public const AUTOCOUNT_DOCNO_OFFICIAL_RECEIPT = 'AUTOCOUNT_DOCNO_OR'; public const AUTOCOUNT_EINVOICE_VALIDATION_LINK = 'AUTOCOUNT_EINVOICE_VALIDATION_LINK'; From cc1037d6116b1a4e14d2fcd4c004af98ba68360c Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Tue, 26 Aug 2025 07:49:14 +0800 Subject: [PATCH 3/3] E-Invoice - Automapping Issues, Exchange Wallet Top Up - AR Deposit Entry Report (Export) --- .../ExportsWalletTopUpDepositEntryReport.php | 81 +++++++++++++++++++ .../Controllers/Exports/ExportController.php | 7 ++ .../elements/DownloadUploadComponent.vue | 2 + routes/export.php | 1 + 4 files changed, 91 insertions(+) create mode 100644 app/Classes/Modules/Exports/Services/ExportsWalletTopUpDepositEntryReport.php diff --git a/app/Classes/Modules/Exports/Services/ExportsWalletTopUpDepositEntryReport.php b/app/Classes/Modules/Exports/Services/ExportsWalletTopUpDepositEntryReport.php new file mode 100644 index 00000000..a244de73 --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsWalletTopUpDepositEntryReport.php @@ -0,0 +1,81 @@ +startDate = $startDate ? Carbon::parse($startDate)->startOfDay() : Carbon::now()->subMonths(1); + $this->endDate = $endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now(); + } + + public function headings(): array + { + return [ + 'DocNo', + 'DebtorCode', + 'DocDate', + 'Description', + 'DeptNo', + 'DepositPaymentMethod', + 'CurrencyCode', + 'PaymentMethod', + 'PaymentAmt', + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + $type = TransactionType::TOP_UP; + $query = Transaction::query(); + $query->where('owner_type', Wallet::class); + $query->where('type', $type); + $query->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + $query->whereBetween('created_at', [$this->startDate, $this->endDate]); + + return $query; + } + + /** + * @param Transaction $transaction + * @return array + */ + public function map($transaction): array + { + $formattedDocumentDate = Carbon::parse($transaction->created_at)->format('m/d/Y'); + $owner = $transaction->owner; + $company = $owner->owner; + return [ + '<>', //DocNo + $company ? $company->debtor : '', //DebtorCode + $formattedDocumentDate, //DocDate + 'Wallet Deposit', //Description + 'C', //DeptNo + 'WALLET DEPOSIT - EXC', //DepositPaymentMethod + 'MYR', //CurrencyCode + 'MBB', //PaymentMethod + number_format($transaction->amount, 2), //PaymentAmt + ]; + } +} diff --git a/app/Http/Controllers/Exports/ExportController.php b/app/Http/Controllers/Exports/ExportController.php index 3e482509..89d67322 100644 --- a/app/Http/Controllers/Exports/ExportController.php +++ b/app/Http/Controllers/Exports/ExportController.php @@ -12,6 +12,7 @@ use App\Classes\Modules\Exports\Services\ExportsARCreditNoteReport; use App\Classes\Modules\Exports\Services\ExportsCompanies; use App\Classes\Modules\Exports\Services\ExportsReceivePaymentDepositEntryReport; use App\Classes\Modules\Exports\Services\ExportsReceivePaymentForBookingReport; +use App\Classes\Modules\Exports\Services\ExportsWalletTopUpDepositEntryReport; use Carbon\Carbon; class ExportController @@ -46,6 +47,12 @@ class ExportController return $this->handleExport($exporter, '01R- RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT].xls'); } + public function walletTopUpDepositEntry(Request $request){ + [$startDate, $endDate] = $this->getValidatedDates($request); + $exporter = new ExportsWalletTopUpDepositEntryReport($startDate, $endDate); + return $this->handleExport($exporter, 'Exchange Wallet Top Up - AR Deposit Entry.xls'); + } + private function getValidatedDates(Request $request): array { $validated = $request->validate([ diff --git a/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue b/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue index c43d73dc..32e62f4d 100644 --- a/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue +++ b/resources/assets/vue/components/bookings/elements/DownloadUploadComponent.vue @@ -142,6 +142,7 @@ export default { '01D - RECEIVE PAYMENT (FULL PAYMENT) [AR DEPOSIT ENTRY]', '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]', 'Credit Note Report', + 'Wallet Top Up Report', ]; }, handleExportClick(){ @@ -156,6 +157,7 @@ export default { '01D - RECEIVE PAYMENT (FULL PAYMENT) [AR DEPOSIT ENTRY]': route('api.export.transactions.receive_payment_deposit_entry'), '01R - RECEIVE PAYMENT (FULL PAYMENT) [AR RECEIVE PAYMENT]': route('api.export.transactions.receive_payment_for_booking'), 'Credit Note Report': route('api.export.transactions.ar_credit_note'), + 'Wallet Top Up Report': route('api.export.transactions.wallet_top_up_deposit_entry'), }; let url = `${routesMap[reportType]}?startDate=${this.parameters.startDate}&endDate=${this.parameters.endDate}`; diff --git a/routes/export.php b/routes/export.php index 4b1c2576..c9068ccb 100644 --- a/routes/export.php +++ b/routes/export.php @@ -16,6 +16,7 @@ Route::group(['prefix' => 'export', 'as' => 'export.', 'namespace' => 'Exports'] Route::get('/ar-credit-note', [ExportController::class, 'arCreditNote'])->name('ar_credit_note'); Route::get('/receive-payment-deposit-entry', [ExportController::class, 'receivePaymentDepositEntry'])->name('receive_payment_deposit_entry'); Route::get('/receive-payment-for-booking', [ExportController::class, 'receivePaymentDepositForBooking'])->name('receive_payment_for_booking'); + Route::get('/wallet-top-up-deposit-entry', [ExportController::class, 'walletTopUpDepositEntry'])->name('wallet_top_up_deposit_entry'); }); });