From 710cbb523267bb459f25f6d95e5b5fd3c18ce6f5 Mon Sep 17 00:00:00 2001 From: Daeng Deni Mardaeni Date: Thu, 31 Jul 2025 11:06:11 +0700 Subject: [PATCH] feat(closing-balance): implementasi unique_hash dan insertOrIgnore untuk eliminasi duplikasi Perbaikan masalah duplikasi pada laporan penutupan saldo dengan pendekatan hash unik dan query insert yang toleran terhadap duplikasi. Perubahan: - Tambah kolom `unique_hash` pada tabel `processed_closing_balances` (via migrasi `2025_07_31_035159_add_unique_hash_field_to_processed_closing_balances_table.php`) - Tambah field `unique_hash` ke `$fillable` pada model `ProcessedClosingBalance` - Update logika generate unique key di `prepareProcessedClosingBalanceData()` menggunakan `md5(trans_reference + '_' + amount_lcy)` - Query pencarian duplikasi berdasarkan `unique_hash`, bukan `trans_reference` saja - Ganti `insert()` dengan `insertOrIgnore()` untuk mencegah error saat insert duplikat data Dampak: - Duplikasi data dihindari secara efektif lewat hash unik - Tidak ada error meski data duplicate ditemukan, karena query otomatis mengabaikannya - `trans_reference` yang sama tetap valid selama nilai `amount_lcy` berbeda - Data laporan lebih konsisten dan terhindar dari konflik constraint --- app/Jobs/GenerateClosingBalanceReportJob.php | 31 +++++-------------- app/Models/ProcessedClosingBalance.php | 3 +- ...ld_to_processed_closing_balances_table.php | 28 +++++++++++++++++ 3 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 database/migrations/2025_07_31_035159_add_unique_hash_field_to_processed_closing_balances_table.php diff --git a/app/Jobs/GenerateClosingBalanceReportJob.php b/app/Jobs/GenerateClosingBalanceReportJob.php index 6eb82bb..86da8ec 100644 --- a/app/Jobs/GenerateClosingBalanceReportJob.php +++ b/app/Jobs/GenerateClosingBalanceReportJob.php @@ -177,7 +177,8 @@ class GenerateClosingBalanceReportJob implements ShouldQueue $insertChunks = array_chunk($allProcessedData, 1000); foreach ($insertChunks as $chunk) { - DB::table('processed_closing_balances')->insert($chunk); + // Menggunakan insertOrIgnore untuk mengabaikan duplikasi dan mencegah error unique constraint + DB::table('processed_closing_balances')->insertOrIgnore($chunk); } Log::info('All processed data inserted successfully', [ @@ -527,35 +528,17 @@ class GenerateClosingBalanceReportJob implements ShouldQueue private function prepareProcessedClosingBalanceData($transactions, &$runningBalance, &$sequenceNo): array { $processedData = []; - $seenReferences = []; - - // Buat lookup array untuk validasi cepat - periksa kombinasi trans_reference + amount_lcy - $existingReferences = ProcessedClosingBalance::where('account_number', $this->accountNumber) - ->where('period', $this->period) - ->where('group_name', $this->groupName) - ->get(['trans_reference', 'amount_lcy']) - ->map(function ($item) { - return md5($item->trans_reference . '_' . $item->amount_lcy); - }) - ->toArray(); - - // Buat lookup array untuk validasi cepat - $existingReferences = array_flip($existingReferences); foreach ($transactions as $transaction) { // Validasi duplikasi berbasis trans_reference + amount_lcy + booking_date $uniqueKey = md5($transaction->trans_reference . '_' . $transaction->amount_lcy); - if (isset($seenReferences[$uniqueKey])) { - Log::warning('Duplicate transaction skipped', [ - 'trans_reference' => $transaction->trans_reference, - 'amount_lcy' => $transaction->amount_lcy - ]); - continue; - } + $existingReferences = ProcessedClosingBalance::select('unique_hash') + ->where('unique_hash', $uniqueKey) + ->get(); // Periksa kombinasi trans_reference + amount_lcy, bukan hanya trans_reference - if (isset($existingReferences[$uniqueKey])) { + if ($existingReferences->count() > 0) { Log::warning('Transaction already exists in database', [ 'trans_reference' => $transaction->trans_reference, 'amount_lcy' => $transaction->amount_lcy @@ -610,7 +593,7 @@ class GenerateClosingBalanceReportJob implements ShouldQueue 'created_at' => now(), 'updated_at' => now(), // Tambahkan hash unik untuk memastikan keunikan - 'unique_hash' => md5($this->accountNumber . $this->period . $this->groupName . $transaction->trans_reference . $transaction->amount_lcy . $transaction->booking_date) + 'unique_hash' => md5($transaction->trans_reference . '_' . $transaction->amount_lcy) ]; } diff --git a/app/Models/ProcessedClosingBalance.php b/app/Models/ProcessedClosingBalance.php index b82171a..b8b3e57 100644 --- a/app/Models/ProcessedClosingBalance.php +++ b/app/Models/ProcessedClosingBalance.php @@ -35,7 +35,8 @@ class ProcessedClosingBalance extends Model 'ref_no', 'merchant_id', 'term_id', - 'closing_balance' + 'closing_balance', + 'unique_hash', ]; protected $casts = [ diff --git a/database/migrations/2025_07_31_035159_add_unique_hash_field_to_processed_closing_balances_table.php b/database/migrations/2025_07_31_035159_add_unique_hash_field_to_processed_closing_balances_table.php new file mode 100644 index 0000000..75718fa --- /dev/null +++ b/database/migrations/2025_07_31_035159_add_unique_hash_field_to_processed_closing_balances_table.php @@ -0,0 +1,28 @@ +string('unique_hash')->after('id')->unique(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('processed_closing_balances', function (Blueprint $table) { + $table->dropColumn('unique_hash'); + }); + } +};