diff --git a/app/Jobs/GenerateClosingBalanceReportJob.php b/app/Jobs/GenerateClosingBalanceReportJob.php index cb560ec..77ed5a3 100644 --- a/app/Jobs/GenerateClosingBalanceReportJob.php +++ b/app/Jobs/GenerateClosingBalanceReportJob.php @@ -153,7 +153,17 @@ $processedData = $this->prepareProcessedClosingBalanceData($transactions, $runningBalance, $sequenceNo); if (!empty($processedData)) { - DB::table('processed_closing_balances')->insert($processedData); + foreach ($processedData as $data) { + ProcessedClosingBalance::updateOrCreate( + [ + 'account_number' => $data['account_number'], + 'period' => $data['period'], + 'trans_reference' => $data['trans_reference'], + 'amount_lcy' => $data['amount_lcy'], + ], + $data + ); + } } }); @@ -178,46 +188,6 @@ } } - - /** - * Delete existing processed data dengan verifikasi lengkap - * Menghapus data processed yang sudah ada dengan verifikasi untuk memastikan tidak ada sisa - */ - private function deleteExistingProcessedDataWithVerification(array $criteria) - : void - { - Log::info('Deleting existing processed data with verification', $criteria); - - // Count before deletion - $beforeCount = ProcessedClosingBalance::where('account_number', $criteria['account_number']) - ->where('period', $criteria['period']) - ->where('group_name', $criteria['group_name']) - ->count(); - - // Delete with force - $deletedCount = ProcessedClosingBalance::where('account_number', $criteria['account_number']) - ->where('period', $criteria['period']) - ->where('group_name', $criteria['group_name']) - ->delete(); - - // Verify deletion - $afterCount = ProcessedClosingBalance::where('account_number', $criteria['account_number']) - ->where('period', $criteria['period']) - ->where('group_name', $criteria['group_name']) - ->count(); - - if ($afterCount > 0) { - throw new Exception("Failed to delete all existing data. Remaining records: {$afterCount}"); - } - - Log::info('Existing processed data deleted and verified', [ - 'before_count' => $beforeCount, - 'deleted_count' => $deletedCount, - 'after_count' => $afterCount, - 'criteria' => $criteria - ]); - } - /** * Get opening balance from account balance table * Mengambil saldo awal dari tabel account balance @@ -462,58 +432,6 @@ return $datetime; } } - - /** - * Verify no duplicates after insert - simplified version - * Memverifikasi tidak ada duplikasi setelah insert data dengan pengecekan yang disederhanakan - */ - private function verifyNoDuplicatesAfterInsert(array $criteria) - : void - { - Log::info('Verifying no duplicates after insert with simplified check', $criteria); - - // PERBAIKAN: Check for duplicate trans_reference + amount_lcy combinations saja - // Karena trans_reference sudah unique secara global, tidak perlu filter by account/period - $duplicates = DB::table('processed_closing_balances') - ->select('trans_reference', 'amount_lcy', DB::raw('COUNT(*) as count')) - ->where('account_number', $criteria['account_number']) - ->where('period', $criteria['period']) - ->where('group_name', $criteria['group_name']) - ->groupBy('trans_reference', 'amount_lcy') - ->having('count', '>', 1) - ->get(); - - if ($duplicates->count() > 0) { - Log::error('Duplicates found after insert', [ - 'duplicate_count' => $duplicates->count(), - 'duplicates' => $duplicates->toArray() - ]); - - throw new Exception("Duplicates detected after insert: {$duplicates->count()} duplicate combinations found"); - } - - // Check for duplicate sequence numbers dalam scope yang sama - $sequenceDuplicates = DB::table('processed_closing_balances') - ->select('sequence_no', DB::raw('COUNT(*) as count')) - ->where('account_number', $criteria['account_number']) - ->where('period', $criteria['period']) - ->where('group_name', $criteria['group_name']) - ->groupBy('sequence_no') - ->having('count', '>', 1) - ->get(); - - if ($sequenceDuplicates->count() > 0) { - Log::error('Sequence number duplicates found after insert', [ - 'sequence_duplicate_count' => $sequenceDuplicates->count(), - 'sequence_duplicates' => $sequenceDuplicates->toArray() - ]); - - throw new Exception("Sequence number duplicates detected: {$sequenceDuplicates->count()} duplicate sequences found"); - } - - Log::info('No duplicates found after insert - verification passed', $criteria); - } - /** * Export from database to CSV (very fast) */ @@ -676,7 +594,6 @@ $deletedCount = ProcessedClosingBalance::where('account_number', $criteria['account_number']) ->where('period', $criteria['period']) - ->where('group_name', $criteria['group_name']) ->delete(); Log::info('Existing processed data deleted', [ @@ -684,237 +601,4 @@ 'criteria' => $criteria ]); } - - /** - * Updated generateReportData method using pure ORM - * Method generateReportData yang diperbarui menggunakan ORM murni - */ - private function generateReportData() - : array - { - Log::info('Starting report data generation using pure ORM', [ - 'account_number' => $this->accountNumber, - 'period' => $this->period, - 'group_name' => $this->groupName, - 'chunk_size' => $this->chunkSize - ]); - - $reportData = []; - $runningBalance = $this->getOpeningBalance(); - $sequenceNo = 1; - - try { - DB::beginTransaction(); - - // Build query menggunakan pure ORM - $query = $this->buildTransactionQuery(); - - // Process data dalam chunks untuk efisiensi memory - $query->chunk($this->chunkSize, function ($transactions) use (&$reportData, &$runningBalance, &$sequenceNo) { - Log::info('Processing transaction chunk', [ - 'chunk_size' => $transactions->count(), - 'current_sequence' => $sequenceNo, - 'current_balance' => $runningBalance - ]); - - foreach ($transactions as $transaction) { - // Process transaction data - $processedData = $this->processTransactionData($transaction); - - // Update running balance - $amount = (float) $transaction->amount_lcy; - $runningBalance += $amount; - - // Format transaction date - $transactionDate = $this->formatDateTime($processedData['date_time']); - - // Build report data row - $reportData[] = $this->buildReportDataRow( - (object) $processedData, - $sequenceNo, - $transactionDate, - $runningBalance - ); - - $sequenceNo++; - } - - Log::info('Chunk processed successfully', [ - 'processed_count' => $transactions->count(), - 'total_records_so_far' => count($reportData), - 'current_balance' => $runningBalance - ]); - }); - - DB::commit(); - - Log::info('Report data generation completed using pure ORM', [ - 'total_records' => count($reportData), - 'final_balance' => $runningBalance, - 'final_sequence' => $sequenceNo - 1 - ]); - - } catch (Exception $e) { - DB::rollback(); - - Log::error('Error generating report data using pure ORM', [ - 'error' => $e->getMessage(), - 'trace' => $e->getTraceAsString(), - 'account_number' => $this->accountNumber, - 'period' => $this->period - ]); - - throw $e; - } - - return $reportData; - } - - /** - * Build report data row from transaction - * Membangun baris data laporan dari transaksi - */ - private function buildReportDataRow($transaction, int $sequenceNo, string $transactionDate, float $runningBalance) - : array - { - return [ - 'sequence_no' => $sequenceNo, - 'trans_reference' => $transaction->trans_reference, - 'booking_date' => $transaction->booking_date, - 'transaction_date' => $transactionDate, - 'amount_lcy' => $transaction->amount_lcy, - 'debit_acct_no' => $transaction->debit_acct_no, - 'debit_value_date' => $transaction->debit_value_date, - 'debit_amount' => $transaction->debit_amount, - 'credit_acct_no' => $transaction->credit_acct_no, - 'bif_rcv_acct' => $transaction->bif_rcv_acct, - 'bif_rcv_name' => $transaction->bif_rcv_name, - 'credit_value_date' => $transaction->credit_value_date, - 'credit_amount' => $transaction->credit_amount, - 'at_unique_id' => $transaction->at_unique_id, - 'bif_ref_no' => $transaction->bif_ref_no, - 'atm_order_id' => $transaction->atm_order_id, - 'recipt_no' => $transaction->recipt_no, - 'api_iss_acct' => $transaction->api_iss_acct, - 'api_benff_acct' => $transaction->api_benff_acct, - 'authoriser' => $transaction->authoriser, - 'remarks' => $transaction->remarks, - 'payment_details' => $transaction->payment_details, - 'ref_no' => $transaction->ref_no, - 'merchant_id' => $transaction->merchant_id, - 'term_id' => $transaction->term_id, - 'closing_balance' => $runningBalance - ]; - } - - /** - * Export report data to CSV file - * Export data laporan ke file CSV - */ - private function exportToCsv(array $reportData) - : string - { - Log::info('Starting CSV export for closing balance report', [ - 'account_number' => $this->accountNumber, - 'period' => $this->period, - 'record_count' => count($reportData) - ]); - - // Create directory structure - $basePath = "closing_balance_reports"; - $accountPath = "{$basePath}/{$this->accountNumber}"; - - Storage::disk($this->disk)->makeDirectory($basePath); - Storage::disk($this->disk)->makeDirectory($accountPath); - - // Generate filename - $fileName = "closing_balance_{$this->accountNumber}_{$this->period}.csv"; - $filePath = "{$accountPath}/{$fileName}"; - - // Delete existing file if exists - if (Storage::disk($this->disk)->exists($filePath)) { - Storage::disk($this->disk)->delete($filePath); - } - - // Create CSV header - $csvHeader = [ - 'NO', - 'TRANS_REFERENCE', - 'BOOKING_DATE', - 'TRANSACTION_DATE', - 'AMOUNT_LCY', - 'DEBIT_ACCT_NO', - 'DEBIT_VALUE_DATE', - 'DEBIT_AMOUNT', - 'CREDIT_ACCT_NO', - 'BIF_RCV_ACCT', - 'BIF_RCV_NAME', - 'CREDIT_VALUE_DATE', - 'CREDIT_AMOUNT', - 'AT_UNIQUE_ID', - 'BIF_REF_NO', - 'ATM_ORDER_ID', - 'RECIPT_NO', - 'API_ISS_ACCT', - 'API_BENFF_ACCT', - 'AUTHORISER', - 'REMARKS', - 'PAYMENT_DETAILS', - 'REF_NO', - 'MERCHANT_ID', - 'TERM_ID', - 'CLOSING_BALANCE' - ]; - - $csvContent = implode('|', $csvHeader) . "\n"; - - // Add data rows - foreach ($reportData as $row) { - $csvRow = [ - $row['sequence_no'], - $row['trans_reference'] ?? '', - $row['booking_date'] ?? '', - $row['transaction_date'] ?? '', - $row['amount_lcy'] ?? '', - $row['debit_acct_no'] ?? '', - $row['debit_value_date'] ?? '', - $row['debit_amount'] ?? '', - $row['credit_acct_no'] ?? '', - $row['bif_rcv_acct'] ?? '', - $row['bif_rcv_name'] ?? '', - $row['credit_value_date'] ?? '', - $row['credit_amount'] ?? '', - $row['at_unique_id'] ?? '', - $row['bif_ref_no'] ?? '', - $row['atm_order_id'] ?? '', - $row['recipt_no'] ?? '', - $row['api_iss_acct'] ?? '', - $row['api_benff_acct'] ?? '', - $row['authoriser'] ?? '', - $row['remarks'] ?? '', - $row['payment_details'] ?? '', - $row['ref_no'] ?? '', - $row['merchant_id'] ?? '', - $row['term_id'] ?? '', - $row['closing_balance'] ?? '' - ]; - - $csvContent .= implode('|', $csvRow) . "\n"; - } - - // Save file - Storage::disk($this->disk)->put($filePath, $csvContent); - - // Verify file creation - if (!Storage::disk($this->disk)->exists($filePath)) { - throw new Exception("Failed to create CSV file: {$filePath}"); - } - - Log::info('CSV export completed successfully', [ - 'file_path' => $filePath, - 'file_size' => Storage::disk($this->disk)->size($filePath) - ]); - - return $filePath; - } }