diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php index 0355c554..33d9df75 100644 --- a/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php +++ b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php @@ -98,7 +98,7 @@ class ImportBankStatementLogic extends AbstractControllerLogic $tellerId = $row[19]; $branchChannel = $row[20]; $transactionCode = $row[21]; - $endBalance = $row[22]; + $endBalance = ((float) str_replace(',', '', $row[22])); $description2 = $row[25]; $description3 = $row[26]; $description4 = $row[27]; @@ -123,11 +123,10 @@ class ImportBankStatementLogic extends AbstractControllerLogic $existingTransaction = StatementTransaction::where('transaction_ref', $transactionRef) ->where('posting_date', $postingDate) ->where('amount', $amount) - ->where('transaction_description', $transactionDescription) ->where('teller_id', $tellerId) ->where('branch_channel', $branchChannel) ->where('transaction_code', $transactionCode) - ->where('end_balance', $endBalance) + ->whereRaw("CAST(REPLACE(end_balance,',','') AS DECIMAL(15,2)) = ?",[$endBalance]) ->first(); if (!$existingTransaction) { diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessorWithInvoiceNo.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessorWithInvoiceNo.php new file mode 100644 index 00000000..27dfbe59 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessorWithInvoiceNo.php @@ -0,0 +1,210 @@ +createsTransaction = $createsTransaction; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->calculatesBookingPaidAmount = $calculatesBookingPaidAmount; + $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount; + $this->calculatesBookingTransferredAmount = $calculatesBookingTransferredAmount; + $this->calculatesBookingCurrencyAverageRate = $calculatesBookingCurrencyAverageRate; + $this->fetchesCompany = $fetchesCompany; + $this->updatesBookingStatus = $updatesBookingStatus; + $this->invoiceDocumentProcessor = $invoiceDocumentProcessor; + } + + + + /** + * @param Booking $booking + * @return void + * @throws MalformedRequestException + */ + public function execute(Booking $booking, String $invoiceNo) + { + + if ($booking->status === ApprovalStatus::COMPLETED) { + return; + } + + $payable_amount = $this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id); + $booking_amount = $booking->fix_amount; + + // confirm that booking amount has been fully paid + if ((float) $booking_amount > (float) $payable_amount) { + return; + } + // confirm that all payments has been transferred + if ($this->calculatesBookingTransferredAmount->execute($booking) !== $this->calculatesBookingPaidAmount->execute($booking)) { + return; + } + + $purchaseOrder = $booking->transactions() + ->where('type', TransactionType::PURCHASE_ORDER) + ->complete() + ->first(); + + $constants = SegmentConstant::where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->id', $booking->service->id)->first(); + + if ($constants->detail->is_billable && !$purchaseOrder) { + return; + } + + // $transaction = $booking->transactions() + // ->where('type', TransactionType::PAYMENT) + // ->first(); + + $transaction = $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->latest()->get()[0]; + + // $billNumber = $this->generatesTransactionBillNumber->execute('INV-'); + $billNumber = $invoiceNo; + + $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::PAYMENT); + + $total_service_charge = $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->sum('service_charge'); + + $total_tax = $booking->transactions() + ->where('type', TransactionType::PAYMENT) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->sum('tax'); + + $transaction_object = new TransactionObject( + $billNumber, + TransactionType::INVOICE, + $transaction->issuer, + $transaction->receiver, + $transaction->recipient_bank_account_id, + $transaction->payment_method, + $payable_amount, + $booking_amount, + $transaction->currency_id, + $transaction->original_currency_id, + $booking_currency_average_rate, + $total_tax, + $total_service_charge, + null, + ApprovalStatus::APPROVED + ); + $invoice_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); + + $voucherRedemption = $transaction->voucherRedemption; + + $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); + + // purchase order + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption); + + // deliver order + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER, $voucherRedemption); + + // invoice + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE, $voucherRedemption); + + $billNumber = $this->generatesTransactionBillNumber->execute('SPDO-'); + + $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(); + + $transaction_object = new TransactionObject( + $billNumber, + TransactionType::SUPPLIER_DELIVER, + $transaction->issuer, + $transaction->receiver, + $transaction->recipient_bank_account_id, + $transaction->payment_method, + $payable_amount, + $booking_amount, + $transaction->currency_id, + $transaction->original_currency_id, + $booking_currency_average_rate, + $total_tax, + $total_service_charge, + null, + ApprovalStatus::APPROVED + ); + $supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); + + // supply deliver order + $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null); + + $this->updatesBookingStatus->execute($booking, ApprovalStatus::COMPLETED); + + // update perfex crm + // if(config('perfexcrm.is_enabled') == 'true'){ + // CreatePerfexCRMInvoice::dispatch($invoice_transaction, $purchaseOrder, $supplier); + // } + } +} diff --git a/app/Console/Commands/RegenerateInvoice.php b/app/Console/Commands/RegenerateInvoice.php new file mode 100644 index 00000000..cf4a1751 --- /dev/null +++ b/app/Console/Commands/RegenerateInvoice.php @@ -0,0 +1,129 @@ +createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor; + $this->createInvoiceTransactionProcessorWithInvoiceNo = $createInvoiceTransactionProcessorWithInvoiceNo; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + // Allocate sufficient memory as needed + // ini_set('memory_limit', '256M'); + + $processed_invoice = 1; + + Booking::where('status', ApprovalStatus::COMPLETED) + ->whereDate('updated_at', '>=', Carbon::parse('2023-01-01')) + ->orderBy('id') + ->chunk(100, function ($bookings) use (&$processed_invoice) { + foreach ($bookings as $booking) { + $logMessage = 'Counter ' . $processed_invoice; + $this->printAndLog($logMessage); + $processed_invoice++; + + if ( + !$booking->transactions()->where('transactions.type', TransactionType::INVOICE)->exists() + || $booking->transactions()->where('transactions.type', TransactionType::INVOICE)->first()->created_at->greaterThanOrEqualTo(Carbon::parse('2023-09-06')) + ) { + continue; + } + + // Delete transactions and documents in one query + $booking->transactions()->whereIn('transactions.type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->delete(); + $booking->documents()->whereIn('document_type', [DocumentType::INVOICE, DocumentType::PURCHASE_ORDER, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->delete(); + + $booking->status = ApprovalStatus::APPROVED; + $booking->save(); + + $deletedInvoice = $booking->transactions() + ->whereIn('type', [TransactionType::INVOICE]) + ->onlyTrashed() + ->orderBy('created_at', 'asc') + ->first(); + + if ($deletedInvoice) { + $bill_no = $deletedInvoice->bill_no; + if (str_ends_with($bill_no, '-deleted')) { + $bill_no = str_replace('-deleted', '', $bill_no); + } + + $deletedInvoice->bill_no = $bill_no . '-deleted'; + $deletedInvoice->save(); + + $existing_invoice_bill_no = Transaction::where('bill_no', $bill_no)->first(); + + if ($existing_invoice_bill_no) { + $this->printAndLog('Delete existing bill_no'); + $this->printAndLog(json_encode($existing_invoice_bill_no)); + $existing_invoice_bill_no->forceDelete(); + } + + $invoiceProcessor = App()->make(CreateInvoiceTransactionProcessorWithInvoiceNo::class); + $invoiceProcessor->execute($booking, $bill_no); + $logMessage = 'Regenerated invoice. Booking Marking - ' . $booking->marking . '. Bill_no - ' . $bill_no . '. Old bill_no - ' . $deletedInvoice->bill_no; + $this->printAndLog($logMessage); + } else { + $invoiceProcessor = App()->make(CreateInvoiceTransactionProcessor::class); + $invoiceProcessor->execute($booking); + $logMessage = 'Regenerated new invoice. Booking Marking - ' . $booking->marking; + $this->printAndLog($logMessage); + } + } + }); + } + + public function printAndLog($string) + { + // $this->info($string); + Log::channel('regenerateInvoice')->info($string); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index e549a23a..cb9cf060 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -38,6 +38,11 @@ class Kernel extends ConsoleKernel ->dailyAt('01:00') ->appendOutputTo(storage_path().'/logs/soft-delete-seasonal-segmant-company.log') ->withoutOverlapping(); + + $schedule->command('regenerateInvoice') + ->everyMinute() + ->appendOutputTo(storage_path().'/logs/regenerateInvoice.log') + ->withoutOverlapping(); } /** diff --git a/config/logging.php b/config/logging.php index 088c204e..fb872693 100644 --- a/config/logging.php +++ b/config/logging.php @@ -99,6 +99,11 @@ return [ 'emergency' => [ 'path' => storage_path('logs/laravel.log'), ], + 'regenerateInvoice' => [ + 'driver' => 'single', + 'path' => storage_path('logs/regenerateInvoice.log'), + 'level' => 'info', + ], ], ]; diff --git a/resources/views/pages/pdfs/invoice.blade.php b/resources/views/pages/pdfs/invoice.blade.php index 40706ce0..db6c42c5 100644 --- a/resources/views/pages/pdfs/invoice.blade.php +++ b/resources/views/pages/pdfs/invoice.blade.php @@ -73,22 +73,17 @@ @php $subtotal = "0"; - $voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 5) : "0"; - $displayedSubtotal = "0"; - $exactTotal = "0"; + $voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 2) : "0"; + $displayedSubtotal = 0; @endphp @foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail) @php - $exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 5); + $exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 7); $itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5); - - // Round half to even for displayed item total - $displayedItemTotal = round(bcmul($exactUnitPrice, $transaction_detail->quantity, 2), 2, PHP_ROUND_HALF_EVEN); - + $displayedItemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 2); $displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2); $subtotal = bcadd($subtotal, $itemTotal, 5); - $exactTotal = bcadd($exactTotal, $displayedItemTotal, 5); @endphp {{ $key + 1 }} @@ -135,17 +130,10 @@ @endif @php - // Calculate the totals with 5 decimal places + $displayedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5); $expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5); - - // Calculate the displayed totals with 2 decimal places - $displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 2), $transaction->tax, 2), $voucherDiscount, 2); - - // Calculate the discrepancy - $discrepancy = bcsub($expectedTotal, $displayedTotal, 5); - - // Calculate the final total - $total = bcadd($expectedTotal, $discrepancy, 5); + $discrepancy = bcsub($displayedTotal, $expectedTotal, 5); + $total = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5); @endphp diff --git a/routes/web.php b/routes/web.php index 7980b80a..a9e51a50 100644 --- a/routes/web.php +++ b/routes/web.php @@ -26,7 +26,9 @@ use Webklex\PDFMerger\Facades\PDFMergerFacade as PDFMerger; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; use App\Classes\Modules\Bookings\Processors\CreatePurchaseOrderFor1688OrderProcessor; use App\Classes\Modules\Documents\Services\DeletesDocument; +use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessorWithInvoiceNo; use App\Classes\Modules\Transactions\Services\DeletesTransaction; +use Illuminate\Support\Facades\Log; /* |-------------------------------------------------------------------------- @@ -482,21 +484,69 @@ Route::get('/payments/manual', function(){ echo ''; }); - - Route::get('/invoice/fix', function(){ set_time_limit(14400); - $bookings = Booking::where('status', ApprovalStatus::COMPLETED)->whereDate('updated_at', '>=', Carbon::parse('01-01-2023'))->get(); + $processed_invoice = 1; - foreach($bookings as $booking){ - $booking->transactions()->whereIn('transactions.type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->delete(); - $booking->documents()->whereIn('document_type', [DocumentType::INVOICE, DocumentType::PURCHASE_ORDER, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->delete(); - $booking->status = ApprovalStatus::APPROVED; - $booking->save(); + Booking::where('status', ApprovalStatus::COMPLETED) + ->whereDate('updated_at', '>=', Carbon::parse('01-01-2023')) + ->orderBy('id') + ->chunk(100, function ($bookings) use (&$processed_invoice) { + foreach ($bookings as $booking) { - (App()->make(CreateInvoiceTransactionProcessor::class))->execute($booking); - } + Log::channel('regenerateInvoice')->info('Counter ' . $processed_invoice); + dump('Counter ' . $processed_invoice); + $processed_invoice += 1; + + if ($booking->transactions()->where('transactions.type', TransactionType::INVOICE) + ->first() + ->created_at + ->greaterThanOrEqualTo(Carbon::parse('2023-09-07'))) { + continue; + } + + $booking->transactions()->whereIn('transactions.type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->delete(); + $booking->documents()->whereIn('document_type', [DocumentType::INVOICE, DocumentType::PURCHASE_ORDER, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->delete(); + $booking->status = ApprovalStatus::APPROVED; + $booking->save(); + + $deletedInvoice = $booking->transactions() + ->whereIn('type', [TransactionType::INVOICE]) + ->onlyTrashed() + ->orderBy('created_at', 'asc') + ->first(); + + if ($deletedInvoice) { + $bill_no = $deletedInvoice->bill_no; + + // check if bill_no ends with '-deleted' + if (str_ends_with($bill_no, '-deleted')) { + $bill_no = str_replace('-deleted', '', $bill_no); + } + + $deletedInvoice->bill_no = $bill_no . '-deleted'; + $deletedInvoice->save(); + + $existing_invoice_bill_no = Transaction::withTrashed()->where('bill_no', $bill_no)->first(); + + if ($existing_invoice_bill_no) { + Log::channel('regenerateInvoice')->info('Delete existing bill_no'); + Log::channel('regenerateInvoice')->info(json_encode($existing_invoice_bill_no)); + $existing_invoice_bill_no->forceDelete(); + } + + (App()->make(CreateInvoiceTransactionProcessorWithInvoiceNo::class))->execute($booking, $bill_no); + dump('regenerated invoice. Booking Marking - ' . $booking->marking . '. Bill_no - ' . $bill_no . '. Old bill_no - ' . $deletedInvoice->bill_no); + Log::channel('regenerateInvoice')->info('regenerated invoice. Booking Marking - ' . $booking->marking . '. Bill_no - ' . $bill_no . '. Old bill_no - ' . $deletedInvoice->bill_no); + } else { + (App()->make(CreateInvoiceTransactionProcessor::class))->execute($booking); + dump('regenerated new invoice. Booking Marking - ' . $booking->marking); + Log::channel('regenerateInvoice')->info('regenerated new invoice. Booking Marking - ' . $booking->marking); + } + } + } + ); })->name('invoice.fix'); Route::get('/1688/fix/{reference}', function($reference){