feat(webstatement): tambah stmt_entry_detail migrasi, model, dan job processing

Menambahkan fitur pengelolaan data stmt_entry_detail untuk integrasi transaksi dengan detail yang lebih lengkap.

Perubahan yang dilakukan:

- Membuat migrasi create_stmt_entry_detail_table dengan struktur field sesuai kebutuhan bisnis
- Menambahkan index pada kolom penting untuk meningkatkan performa query

- Membuat model StmtEntryDetail dengan relasi ke:
  - Account
  - TempFundsTransfer
  - TempTransaction
  - Teller
  - DataCapture
  - TempArrangement
- Mengimplementasikan $fillable dan $casts sesuai struktur tabel
- Menambahkan relasi untuk memudahkan integrasi antar modul

- Membuat job ProcessStmtEntryDetailDataJob untuk memproses file CSV dengan batch processing
- Mengimplementasikan chunking untuk menangani file besar secara efisien
- Membersihkan trans_reference dari karakter tidak valid sebelum penyimpanan
- Menggunakan updateOrCreate untuk mencegah duplikasi primary key
- Menggunakan database transaction untuk menjaga konsistensi data
- Menambahkan logging komprehensif untuk monitoring dan debugging
- Mengimplementasikan error handling yang robust untuk menghindari job failure tanpa informasi
- Memastikan penggunaan resource memory tetap optimal saat memproses data besar

- Menambahkan case baru di MigrasiController untuk memproses stmt_entry_detail
- Konsisten dengan pattern migrasi data yang sudah ada di sistem

Tujuan perubahan:

- Menyediakan sistem import dan pengolahan data stmt_entry_detail dengan proses yang aman dan efisien
- Memudahkan integrasi transaksi dengan detail tambahan di modul Webstatement
- Menjamin integritas data dengan penggunaan transaction, logging, dan error handling yang komprehensif
This commit is contained in:
Daeng Deni Mardaeni
2025-07-21 11:10:49 +07:00
parent 5de1c19d09
commit ad9780ccd6
8 changed files with 721 additions and 181 deletions

View File

@@ -136,72 +136,6 @@ class LaporanClosingBalanceController extends Controller
return view('webstatement::laporan-closing-balance.show', compact('closingBalanceReport'));
}
/**
* Download laporan jika tersedia
*
* @param ClosingBalanceReportLog $closingBalanceReport
* @return \Illuminate\Http\Response
*/
public function download(ClosingBalanceReportLog $closingBalanceReport)
{
Log::info('Download laporan closing balance', [
'report_id' => $closingBalanceReport->id,
'user_id' => Auth::id()
]);
try {
// Check if report is available
if ($closingBalanceReport->status !== 'completed' || !$closingBalanceReport->file_path) {
Log::warning('Laporan tidak tersedia untuk download', [
'report_id' => $closingBalanceReport->id,
'status' => $closingBalanceReport->status
]);
return back()->with('error', 'Laporan tidak tersedia untuk download.');
}
DB::beginTransaction();
// Update download status
$closingBalanceReport->update([
'is_downloaded' => true,
'downloaded_at' => now(),
'updated_by' => Auth::id()
]);
DB::commit();
// Download the file
$filePath = $closingBalanceReport->file_path;
if (Storage::exists($filePath)) {
$fileName = "closing_balance_report_{$closingBalanceReport->account_number}_{$closingBalanceReport->period}.csv";
Log::info('File laporan berhasil didownload', [
'report_id' => $closingBalanceReport->id,
'file_path' => $filePath
]);
return Storage::download($filePath, $fileName);
}
Log::error('File laporan tidak ditemukan', [
'report_id' => $closingBalanceReport->id,
'file_path' => $filePath
]);
return back()->with('error', 'File laporan tidak ditemukan.');
} catch (Exception $e) {
DB::rollback();
Log::error('Error saat download laporan', [
'report_id' => $closingBalanceReport->id,
'error' => $e->getMessage()
]);
return back()->with('error', 'Terjadi kesalahan saat download laporan.');
}
}
/**
* Authorize permintaan laporan
*
@@ -273,15 +207,53 @@ class LaporanClosingBalanceController extends Controller
// Retrieve data from the database
$query = ClosingBalanceReportLog::query();
// Apply search filter if provided
// Apply search filter if provided (handle JSON search parameters)
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('account_number', 'LIKE', "%$search%")
->orWhere('period', 'LIKE', "%$search%")
->orWhere('status', 'LIKE', "%$search%")
->orWhere('authorization_status', 'LIKE', "%$search%");
});
// Check if search is JSON format
if (is_string($search) && json_decode($search, true) !== null) {
$searchParams = json_decode($search, true);
// Apply account number filter
if (!empty($searchParams['account_number'])) {
$query->where('account_number', 'LIKE', "%{$searchParams['account_number']}%");
}
// Apply date range filter
if (!empty($searchParams['start_date'])) {
$startPeriod = Carbon::createFromFormat('Y-m-d', $searchParams['start_date'])->format('Ymd');
$query->where('period', '>=', $startPeriod);
}
if (!empty($searchParams['end_date'])) {
$endPeriod = Carbon::createFromFormat('Y-m-d', $searchParams['end_date'])->format('Ymd');
$query->where('period', '<=', $endPeriod);
}
} else {
// Handle regular string search (fallback)
$query->where(function ($q) use ($search) {
$q->where('account_number', 'LIKE', "%$search%")
->orWhere('period', 'LIKE', "%$search%")
->orWhere('status', 'LIKE', "%$search%")
->orWhere('authorization_status', 'LIKE', "%$search%");
});
}
}
// Apply individual parameter filters (for backward compatibility)
if ($request->has('account_number') && !empty($request->get('account_number'))) {
$query->where('account_number', 'LIKE', "%{$request->get('account_number')}%");
}
if ($request->has('start_date') && !empty($request->get('start_date'))) {
$startPeriod = Carbon::createFromFormat('Y-m-d', $request->get('start_date'))->format('Ymd');
$query->where('period', '>=', $startPeriod);
}
if ($request->has('end_date') && !empty($request->get('end_date'))) {
$endPeriod = Carbon::createFromFormat('Y-m-d', $request->get('end_date'))->format('Ymd');
$query->where('period', '<=', $endPeriod);
}
// Apply column filters if provided
@@ -519,4 +491,81 @@ class LaporanClosingBalanceController extends Controller
return back()->with('error', 'Gagal mengulang generate laporan: ' . $e->getMessage());
}
}
/**
* Download laporan berdasarkan nomor rekening dan periode
*
* @param string $accountNumber
* @param string $period
* @return \Illuminate\Http\Response
*/
public function download($accountNumber, $period)
{
Log::info('Download laporan closing balance', [
'account_number' => $accountNumber,
'period' => $period,
'user_id' => Auth::id()
]);
try {
// Cari laporan berdasarkan account number dan period
$closingBalanceReport = ClosingBalanceReportLog::where('account_number', $accountNumber)
->where('period', $period)
->where('status', 'completed')
->whereNotNull('file_path')
->first();
if (!$closingBalanceReport) {
Log::warning('Laporan tidak ditemukan atau belum selesai', [
'account_number' => $accountNumber,
'period' => $period
]);
return back()->with('error', 'Laporan tidak ditemukan atau belum selesai diproses.');
}
DB::beginTransaction();
// Update download status
$closingBalanceReport->update([
'is_downloaded' => true,
'downloaded_at' => now(),
'updated_by' => Auth::id()
]);
DB::commit();
// Download the file
$filePath = $closingBalanceReport->file_path;
if (Storage::exists($filePath)) {
$fileName = "closing_balance_report_{$accountNumber}_{$period}.csv";
Log::info('File laporan berhasil didownload', [
'account_number' => $accountNumber,
'period' => $period,
'file_path' => $filePath
]);
return Storage::download($filePath, $fileName);
}
Log::error('File laporan tidak ditemukan di storage', [
'account_number' => $accountNumber,
'period' => $period,
'file_path' => $filePath
]);
return back()->with('error', 'File laporan tidak ditemukan.');
} catch (Exception $e) {
DB::rollback();
Log::error('Error saat download laporan', [
'account_number' => $accountNumber,
'period' => $period,
'error' => $e->getMessage()
]);
return back()->with('error', 'Terjadi kesalahan saat mengunduh laporan: ' . $e->getMessage());
}
}
}

View File

@@ -24,7 +24,8 @@
ProcessTellerDataJob,
ProcessTransactionDataJob,
ProcessSectorDataJob,
ProcessProvinceDataJob};
ProcessProvinceDataJob,
ProcessStmtEntryDetailDataJob};
class MigrasiController extends Controller
{
@@ -38,6 +39,7 @@
'customer' => ProcessCustomerDataJob::class,
'account' => ProcessAccountDataJob::class,
'stmtEntry' => ProcessStmtEntryDataJob::class,
'stmtEntryDetail' => ProcessStmtEntryDetailDataJob::class, // Tambahan baru
'dataCapture' => ProcessDataCaptureDataJob::class,
'fundsTransfer' => ProcessFundsTransferDataJob::class,
'teller' => ProcessTellerDataJob::class,
@@ -63,6 +65,7 @@
'customer',
'account',
'stmtEntry',
'stmtEntryDetail', // Tambahan baru
'dataCapture',
'fundsTransfer',
'teller',