♻️ refactor(GenerateClosingBalanceReportJob): Sederhanakan penanganan duplikasi sesuai pendekatan ExportStatementJob

- Menambahkan method `updateOrCreate` untuk penanganan duplikasi yang lebih efisien
- Menghapus method `deleteExistingProcessedDataWithVerification` dengan verifikasi kompleks
- Menghapus method `verifyNoDuplicatesAfterInsert` yang tidak diperlukan lagi
- Menghapus method `generateReportData` yang sudah tidak digunakan
- Menghapus method `buildReportDataRow` yang menjadi bagian dari method di atas
- Menghapus method `exportToCsv` yang sudah tidak relevan
- Menyederhanakan query penghapusan data dengan menghapus kondisi `group_name`
- Mengubah pendekatan dari insertOrIgnore menjadi updateOrCreate untuk penanganan duplikasi
- Menghapus kode yang tidak digunakan untuk mengurangi kompleksitas
This commit is contained in:
Daeng Deni Mardaeni
2025-08-04 11:34:00 +07:00
parent 150d52f8da
commit aae0c4ab15

View File

@@ -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;
}
}