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
This commit is contained in:
@@ -177,7 +177,8 @@ class GenerateClosingBalanceReportJob implements ShouldQueue
|
|||||||
$insertChunks = array_chunk($allProcessedData, 1000);
|
$insertChunks = array_chunk($allProcessedData, 1000);
|
||||||
|
|
||||||
foreach ($insertChunks as $chunk) {
|
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', [
|
Log::info('All processed data inserted successfully', [
|
||||||
@@ -527,35 +528,17 @@ class GenerateClosingBalanceReportJob implements ShouldQueue
|
|||||||
private function prepareProcessedClosingBalanceData($transactions, &$runningBalance, &$sequenceNo): array
|
private function prepareProcessedClosingBalanceData($transactions, &$runningBalance, &$sequenceNo): array
|
||||||
{
|
{
|
||||||
$processedData = [];
|
$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) {
|
foreach ($transactions as $transaction) {
|
||||||
// Validasi duplikasi berbasis trans_reference + amount_lcy + booking_date
|
// Validasi duplikasi berbasis trans_reference + amount_lcy + booking_date
|
||||||
$uniqueKey = md5($transaction->trans_reference . '_' . $transaction->amount_lcy);
|
$uniqueKey = md5($transaction->trans_reference . '_' . $transaction->amount_lcy);
|
||||||
|
|
||||||
if (isset($seenReferences[$uniqueKey])) {
|
$existingReferences = ProcessedClosingBalance::select('unique_hash')
|
||||||
Log::warning('Duplicate transaction skipped', [
|
->where('unique_hash', $uniqueKey)
|
||||||
'trans_reference' => $transaction->trans_reference,
|
->get();
|
||||||
'amount_lcy' => $transaction->amount_lcy
|
|
||||||
]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Periksa kombinasi trans_reference + amount_lcy, bukan hanya trans_reference
|
// 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', [
|
Log::warning('Transaction already exists in database', [
|
||||||
'trans_reference' => $transaction->trans_reference,
|
'trans_reference' => $transaction->trans_reference,
|
||||||
'amount_lcy' => $transaction->amount_lcy
|
'amount_lcy' => $transaction->amount_lcy
|
||||||
@@ -610,7 +593,7 @@ class GenerateClosingBalanceReportJob implements ShouldQueue
|
|||||||
'created_at' => now(),
|
'created_at' => now(),
|
||||||
'updated_at' => now(),
|
'updated_at' => now(),
|
||||||
// Tambahkan hash unik untuk memastikan keunikan
|
// 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)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ class ProcessedClosingBalance extends Model
|
|||||||
'ref_no',
|
'ref_no',
|
||||||
'merchant_id',
|
'merchant_id',
|
||||||
'term_id',
|
'term_id',
|
||||||
'closing_balance'
|
'closing_balance',
|
||||||
|
'unique_hash',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('processed_closing_balances', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user