Compare commits
4 Commits
f37707b2f6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5752427297 | ||
|
|
eb89916b1c | ||
|
|
80c866f646 | ||
|
|
e5c33bf631 |
@@ -64,16 +64,6 @@ class GenerateClosingBalanceReportBulkCommand extends Command
|
||||
'IDR1354400010001',
|
||||
'IDR1728500010001',
|
||||
'IDR1728600010001',
|
||||
'IDR1354500010001',
|
||||
'IDR1354500020001',
|
||||
'IDR1354500030001',
|
||||
'IDR1354500040001',
|
||||
'IDR1354500050001',
|
||||
'IDR1354500060001',
|
||||
'IDR1354500070001',
|
||||
'IDR1354500080001',
|
||||
'IDR1354500090001',
|
||||
'IDR1354500100001',
|
||||
'IDR1720500010001',
|
||||
'1078333878',
|
||||
'1081647484',
|
||||
@@ -89,6 +79,19 @@ class GenerateClosingBalanceReportBulkCommand extends Command
|
||||
'IDR1354200010001'
|
||||
];
|
||||
|
||||
private $qrisAccount = [
|
||||
'IDR1354500010001',
|
||||
'IDR1354500020001',
|
||||
'IDR1354500030001',
|
||||
'IDR1354500040001',
|
||||
'IDR1354500050001',
|
||||
'IDR1354500060001',
|
||||
'IDR1354500070001',
|
||||
'IDR1354500080001',
|
||||
'IDR1354500090001',
|
||||
'IDR1354500100001',
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* Menjalankan proses generate laporan closing balance untuk banyak rekening dengan periode range
|
||||
@@ -281,7 +284,7 @@ class GenerateClosingBalanceReportBulkCommand extends Command
|
||||
}
|
||||
|
||||
// Jika tidak ada parameter accounts, gunakan default list
|
||||
$accountList = ['DEFAULT' => $this->defaultAccounts];
|
||||
$accountList = ['DEFAULT' => $this->defaultAccounts, 'QRIS' => $this->qrisAccount];
|
||||
|
||||
// Filter by client jika ada (untuk backward compatibility)
|
||||
if ($clientFilter) {
|
||||
@@ -378,7 +381,7 @@ class GenerateClosingBalanceReportBulkCommand extends Command
|
||||
}
|
||||
|
||||
// Dispatch the job
|
||||
GenerateClosingBalanceReportJob::dispatch($accountNumber, $period, $reportLog->id);
|
||||
GenerateClosingBalanceReportJob::dispatch($accountNumber, $period, $reportLog->id, $groupName);
|
||||
|
||||
DB::commit();
|
||||
|
||||
|
||||
@@ -1,51 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Webstatement\Console;
|
||||
namespace Modules\Webstatement\Console;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Modules\Webstatement\Http\Controllers\MigrasiController;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Modules\Webstatement\Http\Controllers\MigrasiController;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ProcessDailyMigration extends Command
|
||||
class ProcessDailyMigration extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'webstatement:process-daily-migration
|
||||
{--process_parameter= : To process migration parameter true/false}
|
||||
{--period= : Period to process (default: -1 day, format: Ymd or relative date)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Process data migration for the specified period (default: previous day)';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'webstatement:process-daily-migration
|
||||
{--process_parameter= : To process migration parameter true/false}';
|
||||
$processParameter = $this->option('process_parameter');
|
||||
$period = $this->option('period');
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Process data migration for the previous day\'s period';
|
||||
// Log start of process
|
||||
Log::info('Starting daily data migration process', [
|
||||
'process_parameter' => $processParameter ?? 'false',
|
||||
'period' => $period ?? '-1 day'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$processParameter = $this->option('process_parameter');
|
||||
$this->info('Starting daily data migration process...');
|
||||
$this->info('Process Parameter: ' . ($processParameter ?? 'False'));
|
||||
$this->info('Period: ' . ($period ?? '-1 day (default)'));
|
||||
|
||||
$this->info('Starting daily data migration process...');
|
||||
$this->info('Process Parameter: ' . ($processParameter ?? 'False'));
|
||||
try {
|
||||
$controller = app(MigrasiController::class);
|
||||
$response = $controller->index($processParameter, $period);
|
||||
|
||||
try {
|
||||
$controller = app(MigrasiController::class);
|
||||
$response = $controller->index($processParameter);
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
$message = $responseData['message'] ?? 'Process completed';
|
||||
|
||||
$responseData = json_decode($response->getContent(), true);
|
||||
$this->info($responseData['message'] ?? 'Process completed');
|
||||
$this->info($message);
|
||||
Log::info('Daily migration process completed successfully', ['message' => $message]);
|
||||
|
||||
return Command::SUCCESS;
|
||||
} catch (Exception $e) {
|
||||
$this->error('Error processing daily migration: ' . $e->getMessage());
|
||||
return Command::FAILURE;
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = 'Error processing daily migration: ' . $e->getMessage();
|
||||
$this->error($errorMessage);
|
||||
Log::error($errorMessage, ['exception' => $e->getTraceAsString()]);
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,30 +101,99 @@
|
||||
return response()->json(['error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
public function index($processParameter = false)
|
||||
/**
|
||||
* Proses migrasi data dengan parameter dan periode yang dapat dikustomisasi
|
||||
*
|
||||
* @param bool|string $processParameter Flag untuk memproses parameter
|
||||
* @param string|null $period Periode yang akan diproses (default: -1 day)
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index($processParameter = false, $period = null)
|
||||
{
|
||||
$disk = Storage::disk('sftpStatement');
|
||||
try {
|
||||
Log::info('Starting migration process', [
|
||||
'process_parameter' => $processParameter,
|
||||
'period' => $period
|
||||
]);
|
||||
|
||||
if ($processParameter) {
|
||||
foreach (self::PARAMETER_PROCESSES as $process) {
|
||||
$this->processData($process, '_parameter');
|
||||
$disk = Storage::disk('sftpStatement');
|
||||
|
||||
if ($processParameter) {
|
||||
Log::info('Processing parameter data');
|
||||
|
||||
foreach (self::PARAMETER_PROCESSES as $process) {
|
||||
$this->processData($process, '_parameter');
|
||||
}
|
||||
|
||||
Log::info('Parameter processes completed successfully');
|
||||
return response()->json(['message' => 'Parameter processes completed successfully']);
|
||||
}
|
||||
return response()->json(['message' => 'Parameter processes completed successfully']);
|
||||
}
|
||||
|
||||
$period = date('Ymd', strtotime('-1 day'));
|
||||
if (!$disk->exists($period)) {
|
||||
// Tentukan periode yang akan diproses
|
||||
$targetPeriod = $this->determinePeriod($period);
|
||||
|
||||
Log::info('Processing data for period', ['period' => $targetPeriod]);
|
||||
|
||||
if (!$disk->exists($targetPeriod)) {
|
||||
$errorMessage = "Period {$targetPeriod} folder not found in SFTP storage";
|
||||
Log::warning($errorMessage);
|
||||
|
||||
return response()->json([
|
||||
"message" => $errorMessage
|
||||
], 404);
|
||||
}
|
||||
|
||||
foreach (self::DATA_PROCESSES as $process) {
|
||||
$this->processData($process, $targetPeriod);
|
||||
}
|
||||
|
||||
$successMessage = "Data processing for period {$targetPeriod} has been queued successfully";
|
||||
Log::info($successMessage);
|
||||
|
||||
return response()->json([
|
||||
"message" => "Period {$period} folder not found in SFTP storage"
|
||||
], 404);
|
||||
'message' => $successMessage
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
Log::error('Error in migration index method: ' . $e->getMessage());
|
||||
return response()->json(['error' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tentukan periode berdasarkan input atau gunakan default
|
||||
*
|
||||
* @param string|null $period Input periode
|
||||
* @return string Periode dalam format Ymd
|
||||
*/
|
||||
private function determinePeriod($period = null): string
|
||||
{
|
||||
if ($period === null) {
|
||||
// Default: -1 day
|
||||
$calculatedPeriod = date('Ymd', strtotime('-1 day'));
|
||||
Log::info('Using default period', ['period' => $calculatedPeriod]);
|
||||
return $calculatedPeriod;
|
||||
}
|
||||
|
||||
foreach (self::DATA_PROCESSES as $process) {
|
||||
$this->processData($process, $period);
|
||||
// Jika periode sudah dalam format Ymd (8 digit)
|
||||
if (preg_match('/^\d{8}$/', $period)) {
|
||||
Log::info('Using provided period in Ymd format', ['period' => $period]);
|
||||
return $period;
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => "Data processing for period {$period} has been queued successfully"
|
||||
]);
|
||||
// Jika periode dalam format relative date (contoh: -2 days, -1 week, etc.)
|
||||
try {
|
||||
$calculatedPeriod = date('Ymd', strtotime($period));
|
||||
Log::info('Calculated period from relative date', [
|
||||
'input' => $period,
|
||||
'calculated' => $calculatedPeriod
|
||||
]);
|
||||
return $calculatedPeriod;
|
||||
} catch (Exception $e) {
|
||||
Log::warning('Invalid period format, using default', [
|
||||
'input' => $period,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return date('Ymd', strtotime('-1 day'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,12 @@
|
||||
],
|
||||
'SWADAYA_PANDU' => [
|
||||
'0081272689',
|
||||
],
|
||||
"AWAN_LINTANG_SOLUSI"=> [
|
||||
"1084269430"
|
||||
],
|
||||
"MONETA"=> [
|
||||
"1085667890"
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace Modules\Webstatement\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
@@ -12,9 +10,14 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Modules\Webstatement\Models\AccountBalance;
|
||||
use Modules\Webstatement\Models\ClosingBalanceReportLog;
|
||||
use Modules\Webstatement\Models\StmtEntry;
|
||||
use Modules\Webstatement\Models\StmtEntryDetail;
|
||||
use Modules\Webstatement\Models\TempFundsTransfer;
|
||||
use Modules\Webstatement\Models\DataCapture;
|
||||
|
||||
/**
|
||||
* Job untuk generate laporan closing balance
|
||||
@@ -27,6 +30,7 @@ class GenerateClosingBalanceReportJob implements ShouldQueue
|
||||
protected $accountNumber;
|
||||
protected $period;
|
||||
protected $reportLogId;
|
||||
protected $groupName;
|
||||
protected $chunkSize = 1000;
|
||||
protected $disk = 'local';
|
||||
|
||||
@@ -37,11 +41,12 @@ class GenerateClosingBalanceReportJob implements ShouldQueue
|
||||
* @param string $period
|
||||
* @param int $reportLogId
|
||||
*/
|
||||
public function __construct(string $accountNumber, string $period, int $reportLogId)
|
||||
public function __construct(string $accountNumber, string $period, int $reportLogId, string $groupName='DEFAULT')
|
||||
{
|
||||
$this->accountNumber = $accountNumber;
|
||||
$this->period = $period;
|
||||
$this->reportLogId = $reportLogId;
|
||||
$this->groupName = $groupName ?? 'DEFAULT';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,106 +163,316 @@ class GenerateClosingBalanceReportJob implements ShouldQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate report data based on the provided SQL query
|
||||
* Menggenerate data laporan berdasarkan query yang diberikan
|
||||
* Build transaction query using pure Eloquent relationships
|
||||
* Membangun query transaksi menggunakan relasi Eloquent murni
|
||||
*/
|
||||
private function generateReportData(float $openingBalance): array
|
||||
private function buildTransactionQuery()
|
||||
{
|
||||
Log::info('Generating closing balance report data', [
|
||||
Log::info('Building transaction query using pure Eloquent relationships', [
|
||||
'group_name' => $this->groupName,
|
||||
'account_number' => $this->accountNumber,
|
||||
'period' => $this->period
|
||||
]);
|
||||
|
||||
// Tentukan model berdasarkan group name
|
||||
$modelClass = $this->getModelByGroup();
|
||||
|
||||
// Build query menggunakan pure Eloquent dengan eager loading
|
||||
$query = $modelClass::with([
|
||||
'ft' => function($query) {
|
||||
$query->select([
|
||||
'_id',
|
||||
'ref_no',
|
||||
'debit_acct_no',
|
||||
'debit_value_date',
|
||||
'credit_acct_no',
|
||||
'bif_rcv_acct',
|
||||
'bif_rcv_name',
|
||||
'credit_value_date',
|
||||
'at_unique_id',
|
||||
'bif_ref_no',
|
||||
'atm_order_id',
|
||||
'recipt_no',
|
||||
'api_iss_acct',
|
||||
'api_benff_acct',
|
||||
'authoriser',
|
||||
'remarks',
|
||||
'payment_details',
|
||||
'merchant_id',
|
||||
'term_id',
|
||||
'date_time'
|
||||
]);
|
||||
},
|
||||
'dc' => function($query) {
|
||||
$query->select([
|
||||
'id',
|
||||
'date_time'
|
||||
]);
|
||||
}
|
||||
])
|
||||
->select([
|
||||
'id',
|
||||
'trans_reference',
|
||||
'booking_date',
|
||||
'amount_lcy',
|
||||
'date_time'
|
||||
])
|
||||
->where('account_number', $this->accountNumber)
|
||||
->where('booking_date', $this->period)
|
||||
->orderBy('booking_date')
|
||||
->orderBy('date_time');
|
||||
|
||||
Log::info('Transaction query built successfully using pure Eloquent', [
|
||||
'model_class' => $modelClass,
|
||||
'account_number' => $this->accountNumber,
|
||||
'period' => $this->period
|
||||
]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model class based on group name
|
||||
* Mendapatkan class model berdasarkan group name
|
||||
*/
|
||||
private function getModelByGroup()
|
||||
{
|
||||
Log::info('Determining model by group', [
|
||||
'group_name' => $this->groupName
|
||||
]);
|
||||
|
||||
$model = $this->groupName === 'QRIS' ? StmtEntryDetail::class : StmtEntry::class;
|
||||
|
||||
Log::info('Model determined', [
|
||||
'group_name' => $this->groupName,
|
||||
'model_class' => $model
|
||||
]);
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process transaction data from ORM result
|
||||
* Memproses data transaksi dari hasil ORM
|
||||
*/
|
||||
private function processTransactionData($transaction): array
|
||||
{
|
||||
Log::info('Processing transaction data', [
|
||||
'trans_reference' => $transaction->trans_reference,
|
||||
'has_ft_relation' => !is_null($transaction->ft),
|
||||
'has_dc_relation' => !is_null($transaction->dc)
|
||||
]);
|
||||
|
||||
// Hitung debit dan credit amount
|
||||
$debitAmount = $transaction->amount_lcy < 0 ? abs($transaction->amount_lcy) : null;
|
||||
$creditAmount = $transaction->amount_lcy > 0 ? $transaction->amount_lcy : null;
|
||||
|
||||
// Ambil date_time dari prioritas: ft -> dc -> stmt
|
||||
$dateTime = $transaction->ft?->date_time ??
|
||||
$transaction->dc?->date_time ??
|
||||
$transaction->date_time;
|
||||
|
||||
$processedData = [
|
||||
'trans_reference' => $transaction->trans_reference,
|
||||
'booking_date' => $transaction->booking_date,
|
||||
'amount_lcy' => $transaction->amount_lcy,
|
||||
'debit_amount' => $debitAmount,
|
||||
'credit_amount' => $creditAmount,
|
||||
'date_time' => $dateTime,
|
||||
// Data dari TempFundsTransfer melalui relasi
|
||||
'debit_acct_no' => $transaction->ft?->debit_acct_no,
|
||||
'debit_value_date' => $transaction->ft?->debit_value_date,
|
||||
'credit_acct_no' => $transaction->ft?->credit_acct_no,
|
||||
'bif_rcv_acct' => $transaction->ft?->bif_rcv_acct,
|
||||
'bif_rcv_name' => $transaction->ft?->bif_rcv_name,
|
||||
'credit_value_date' => $transaction->ft?->credit_value_date,
|
||||
'at_unique_id' => $transaction->ft?->at_unique_id,
|
||||
'bif_ref_no' => $transaction->ft?->bif_ref_no,
|
||||
'atm_order_id' => $transaction->ft?->atm_order_id,
|
||||
'recipt_no' => $transaction->ft?->recipt_no,
|
||||
'api_iss_acct' => $transaction->ft?->api_iss_acct,
|
||||
'api_benff_acct' => $transaction->ft?->api_benff_acct,
|
||||
'authoriser' => $transaction->ft?->authoriser,
|
||||
'remarks' => $transaction->ft?->remarks,
|
||||
'payment_details' => $transaction->ft?->payment_details,
|
||||
'ref_no' => $transaction->ft?->ref_no,
|
||||
'merchant_id' => $transaction->ft?->merchant_id,
|
||||
'term_id' => $transaction->ft?->term_id,
|
||||
];
|
||||
|
||||
Log::info('Transaction data processed successfully', [
|
||||
'trans_reference' => $transaction->trans_reference,
|
||||
'final_date_time' => $dateTime,
|
||||
'debit_amount' => $debitAmount,
|
||||
'credit_amount' => $creditAmount
|
||||
]);
|
||||
|
||||
return $processedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
'opening_balance' => $openingBalance
|
||||
'group_name' => $this->groupName,
|
||||
'chunk_size' => $this->chunkSize
|
||||
]);
|
||||
|
||||
$reportData = [];
|
||||
$runningBalance = $openingBalance;
|
||||
$sequenceNo = 0;
|
||||
$runningBalance = $this->getOpeningBalance();
|
||||
$sequenceNo = 1;
|
||||
|
||||
// Query berdasarkan SQL yang diberikan user
|
||||
$query = DB::table('stmt_entry as s')
|
||||
->leftJoin('temp_funds_transfer as ft', 'ft._id', '=', 's.trans_reference')
|
||||
->leftJoin('data_captures as dc', 'dc.id', '=', 's.trans_reference')
|
||||
->select([
|
||||
's.trans_reference',
|
||||
's.booking_date',
|
||||
's.amount_lcy',
|
||||
'ft.debit_acct_no',
|
||||
'ft.debit_value_date',
|
||||
DB::raw('CASE WHEN s.amount_lcy::numeric < 0 THEN s.amount_lcy::numeric ELSE NULL END AS debit_amount'),
|
||||
'ft.credit_acct_no',
|
||||
'ft.bif_rcv_acct',
|
||||
'ft.bif_rcv_name',
|
||||
'ft.credit_value_date',
|
||||
DB::raw('CASE WHEN s.amount_lcy::numeric > 0 THEN s.amount_lcy::numeric ELSE NULL END AS credit_amount'),
|
||||
'ft.at_unique_id',
|
||||
'ft.bif_ref_no',
|
||||
'ft.atm_order_id',
|
||||
'ft.recipt_no',
|
||||
'ft.api_iss_acct',
|
||||
'ft.api_benff_acct',
|
||||
DB::raw('COALESCE(ft.date_time, dc.date_time, s.date_time) AS date_time'),
|
||||
'ft.authoriser',
|
||||
'ft.remarks',
|
||||
'ft.payment_details',
|
||||
'ft.ref_no',
|
||||
'ft.merchant_id',
|
||||
'ft.term_id'
|
||||
])
|
||||
->where('s.account_number', $this->accountNumber)
|
||||
->where('s.booking_date', $this->period)
|
||||
->orderBy('s.booking_date')
|
||||
->orderBy('date_time');
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
// Process data in chunks
|
||||
$query->chunk($this->chunkSize, function ($transactions) use (&$reportData, &$runningBalance, &$sequenceNo) {
|
||||
foreach ($transactions as $transaction) {
|
||||
$sequenceNo++;
|
||||
// Build query menggunakan pure ORM
|
||||
$query = $this->buildTransactionQuery();
|
||||
|
||||
// Calculate running balance
|
||||
$runningBalance += (float) $transaction->amount_lcy;
|
||||
// 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
|
||||
]);
|
||||
|
||||
// Format transaction date
|
||||
$transactionDate = $this->formatDateTime($transaction->date_time);
|
||||
foreach ($transactions as $transaction) {
|
||||
// Process transaction data
|
||||
$processedData = $this->processTransactionData($transaction);
|
||||
|
||||
$reportData[] = [
|
||||
'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
|
||||
];
|
||||
}
|
||||
});
|
||||
// Update running balance
|
||||
$amount = (float) $transaction->amount_lcy;
|
||||
$runningBalance += $amount;
|
||||
|
||||
Log::info('Report data generated', [
|
||||
'total_records' => count($reportData),
|
||||
'final_balance' => $runningBalance
|
||||
]);
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table name based on group name
|
||||
* Mendapatkan nama tabel berdasarkan group name
|
||||
*/
|
||||
private function getTableNameByGroup(): string
|
||||
{
|
||||
return $this->groupName === 'QRIS' ? 'stmt_entry' : 'stmt_entry_details';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get select fields for the query
|
||||
* Mendapatkan field select untuk query
|
||||
*/
|
||||
private function getSelectFields(): array
|
||||
{
|
||||
return [
|
||||
's.trans_reference',
|
||||
's.booking_date',
|
||||
's.amount_lcy',
|
||||
'ft.debit_acct_no',
|
||||
'ft.debit_value_date',
|
||||
DB::raw('CASE WHEN s.amount_lcy::numeric < 0 THEN s.amount_lcy::numeric ELSE NULL END AS debit_amount'),
|
||||
'ft.credit_acct_no',
|
||||
'ft.bif_rcv_acct',
|
||||
'ft.bif_rcv_name',
|
||||
'ft.credit_value_date',
|
||||
DB::raw('CASE WHEN s.amount_lcy::numeric > 0 THEN s.amount_lcy::numeric ELSE NULL END AS credit_amount'),
|
||||
'ft.at_unique_id',
|
||||
'ft.bif_ref_no',
|
||||
'ft.atm_order_id',
|
||||
'ft.recipt_no',
|
||||
'ft.api_iss_acct',
|
||||
'ft.api_benff_acct',
|
||||
DB::raw('COALESCE(ft.date_time, dc.date_time, s.date_time) AS date_time'),
|
||||
'ft.authoriser',
|
||||
'ft.remarks',
|
||||
'ft.payment_details',
|
||||
'ft.ref_no',
|
||||
'ft.merchant_id',
|
||||
'ft.term_id'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Format datetime string
|
||||
* Memformat string datetime
|
||||
|
||||
@@ -162,17 +162,20 @@ class ProcessStmtEntryDetailDataJob implements ShouldQueue
|
||||
}
|
||||
|
||||
$headers = (new StmtEntryDetail())->getFillable();
|
||||
// Tambahkan field 'id' ke headers untuk menangani kolom tambahan di akhir CSV
|
||||
$expectedHeaders = array_merge($headers, ['id']);
|
||||
$rowCount = 0;
|
||||
$chunkCount = 0;
|
||||
|
||||
Log::info('Memulai proses file', [
|
||||
'file_path' => $filePath,
|
||||
'headers_count' => count($headers)
|
||||
'headers_count' => count($headers),
|
||||
'expected_headers_count' => count($expectedHeaders)
|
||||
]);
|
||||
|
||||
while (($row = fgetcsv($handle, 0, self::CSV_DELIMITER)) !== false) {
|
||||
$rowCount++;
|
||||
$this->processRow($row, $headers, $rowCount, $filePath);
|
||||
$this->processRow($row, $expectedHeaders, $rowCount, $filePath);
|
||||
|
||||
// Process in chunks to avoid memory issues
|
||||
if (count($this->entryBatch) >= self::CHUNK_SIZE) {
|
||||
@@ -192,39 +195,67 @@ class ProcessStmtEntryDetailDataJob implements ShouldQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* Proses setiap baris data
|
||||
* Proses setiap baris data dengan penanganan field id tambahan
|
||||
*
|
||||
* @param array $row Data baris
|
||||
* @param array $headers Header kolom
|
||||
* @param array $expectedHeaders Header kolom yang diharapkan (termasuk id)
|
||||
* @param int $rowCount Nomor baris
|
||||
* @param string $filePath Path file
|
||||
* @return void
|
||||
*/
|
||||
private function processRow(array $row, array $headers, int $rowCount, string $filePath): void
|
||||
private function processRow(array $row, array $expectedHeaders, int $rowCount, string $filePath): void
|
||||
{
|
||||
if (count($headers) !== count($row)) {
|
||||
// Validasi jumlah kolom - sekarang menggunakan expectedHeaders yang sudah include field 'id'
|
||||
if (count($expectedHeaders) !== count($row)) {
|
||||
Log::warning("Row $rowCount in $filePath has incorrect column count. Expected: " .
|
||||
count($headers) . ", Got: " . count($row));
|
||||
count($expectedHeaders) . ", Got: " . count($row));
|
||||
$this->errorCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
$data = array_combine($headers, $row);
|
||||
// Kombinasikan data dengan headers
|
||||
$data = array_combine($expectedHeaders, $row);
|
||||
|
||||
// Log untuk debugging struktur data
|
||||
Log::debug('Processing row data', [
|
||||
'row_count' => $rowCount,
|
||||
'stmt_entry_id' => $data['stmt_entry_id'] ?? 'not_set',
|
||||
'id' => $data['id'] ?? 'not_set'
|
||||
]);
|
||||
|
||||
// Logika untuk menggunakan field 'id' sebagai fallback jika stmt_entry_id kosong
|
||||
$this->handleStmtEntryIdFallback($data);
|
||||
|
||||
// Hapus field 'id' dari data sebelum disimpan karena tidak ada di fillable model
|
||||
unset($data['id']);
|
||||
|
||||
$this->cleanTransReference($data);
|
||||
$this->addToBatch($data, $rowCount, $filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bersihkan trans_reference dari karakter tidak diinginkan
|
||||
* Menangani logika fallback untuk stmt_entry_id menggunakan field id
|
||||
*
|
||||
* @param array $data Data yang akan dibersihkan
|
||||
* @param array $data Data yang akan diproses
|
||||
* @return void
|
||||
*/
|
||||
private function cleanTransReference(array &$data): void
|
||||
private function handleStmtEntryIdFallback(array &$data): void
|
||||
{
|
||||
if (isset($data['trans_reference'])) {
|
||||
// Clean trans_reference from \\BNK if present
|
||||
$data['trans_reference'] = preg_replace('/\\\\.*$/', '', $data['trans_reference']);
|
||||
Log::debug('Trans reference cleaned', ['original' => $data['trans_reference']]);
|
||||
// Jika stmt_entry_id kosong atau null, gunakan value dari field 'id'
|
||||
if (empty($data['stmt_entry_id']) || $data['stmt_entry_id'] === '' || $data['stmt_entry_id'] === null) {
|
||||
if (isset($data['id']) && !empty($data['id'])) {
|
||||
$data['stmt_entry_id'] = $data['id'];
|
||||
|
||||
Log::info('Using id as stmt_entry_id fallback', [
|
||||
'original_stmt_entry_id' => $data['stmt_entry_id'] ?? 'empty',
|
||||
'fallback_id' => $data['id']
|
||||
]);
|
||||
} else {
|
||||
Log::warning('Both stmt_entry_id and id are empty', [
|
||||
'stmt_entry_id' => $data['stmt_entry_id'] ?? 'not_set',
|
||||
'id' => $data['id'] ?? 'not_set'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +270,11 @@ class ProcessStmtEntryDetailDataJob implements ShouldQueue
|
||||
private function addToBatch(array $data, int $rowCount, string $filePath): void
|
||||
{
|
||||
try {
|
||||
if (isset($data['stmt_entry_id']) && $data['stmt_entry_id'] !== 'stmt_entry_id') {
|
||||
// Validasi bahwa stmt_entry_id tidak kosong dan bukan header
|
||||
if (isset($data['stmt_entry_id']) &&
|
||||
$data['stmt_entry_id'] !== 'stmt_entry_id' &&
|
||||
!empty($data['stmt_entry_id'])) {
|
||||
|
||||
// Add timestamp fields
|
||||
$now = now();
|
||||
$data['created_at'] = $now;
|
||||
@@ -249,7 +284,16 @@ class ProcessStmtEntryDetailDataJob implements ShouldQueue
|
||||
$this->entryBatch[] = $data;
|
||||
$this->processedCount++;
|
||||
|
||||
Log::debug('Record added to batch', ['row' => $rowCount, 'stmt_entry_id' => $data['stmt_entry_id']]);
|
||||
Log::debug('Record added to batch', [
|
||||
'row' => $rowCount,
|
||||
'stmt_entry_id' => $data['stmt_entry_id']
|
||||
]);
|
||||
} else {
|
||||
Log::warning('Skipping row due to invalid stmt_entry_id', [
|
||||
'row' => $rowCount,
|
||||
'stmt_entry_id' => $data['stmt_entry_id'] ?? 'not_set'
|
||||
]);
|
||||
$this->errorCount++;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->errorCount++;
|
||||
@@ -257,6 +301,21 @@ class ProcessStmtEntryDetailDataJob implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bersihkan trans_reference dari karakter tidak diinginkan
|
||||
*
|
||||
* @param array $data Data yang akan dibersihkan
|
||||
* @return void
|
||||
*/
|
||||
private function cleanTransReference(array &$data): void
|
||||
{
|
||||
if (isset($data['trans_reference'])) {
|
||||
// Clean trans_reference from \\BNK if present
|
||||
$data['trans_reference'] = preg_replace('/\\\\.*$/', '', $data['trans_reference']);
|
||||
Log::debug('Trans reference cleaned', ['original' => $data['trans_reference']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simpan batch data ke database menggunakan updateOrCreate
|
||||
* untuk menghindari error unique constraint
|
||||
|
||||
@@ -130,6 +130,11 @@
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
font-size: 10px;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
overflow-wrap: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
table th {
|
||||
@@ -278,7 +283,7 @@
|
||||
$totalDebit = 0;
|
||||
$totalKredit = 0;
|
||||
$line = 1;
|
||||
$linePerPage = 26;
|
||||
$linePerPage = 23;
|
||||
@endphp
|
||||
@php
|
||||
// Hitung tanggal periode berdasarkan $period
|
||||
|
||||
Reference in New Issue
Block a user