Compare commits
4 Commits
dbdeceb4c0
...
2c8f49af20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c8f49af20 | ||
|
|
4bfd937490 | ||
|
|
7b32cb8d39 | ||
|
|
4b889da5a5 |
110
app/Console/UpdateAllAtmCardsCommand.php
Normal file
110
app/Console/UpdateAllAtmCardsCommand.php
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Webstatement\Console;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Modules\Webstatement\Jobs\UpdateAllAtmCardsBatchJob;
|
||||||
|
|
||||||
|
class UpdateAllAtmCardsCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'atmcard:update-all
|
||||||
|
{--sync-log-id= : ID sync log yang akan digunakan}
|
||||||
|
{--batch-size=100 : Ukuran batch untuk processing}
|
||||||
|
{--queue=atmcard-update : Nama queue untuk job}
|
||||||
|
{--filters= : Filter JSON untuk kondisi kartu}
|
||||||
|
{--dry-run : Preview tanpa eksekusi aktual}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Jalankan job untuk update seluruh kartu ATM secara batch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
Log::info('Memulai command update seluruh kartu ATM');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$syncLogId = $this->option('sync-log-id');
|
||||||
|
$batchSize = (int) $this->option('batch-size');
|
||||||
|
$queueName = $this->option('queue');
|
||||||
|
$filtersJson = $this->option('filters');
|
||||||
|
$isDryRun = $this->option('dry-run');
|
||||||
|
|
||||||
|
// Parse filters jika ada
|
||||||
|
$filters = [];
|
||||||
|
if ($filtersJson) {
|
||||||
|
$filters = json_decode($filtersJson, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
$this->error('Format JSON filters tidak valid');
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validasi input
|
||||||
|
if ($batchSize <= 0) {
|
||||||
|
$this->error('Batch size harus lebih besar dari 0');
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info('Konfigurasi job:');
|
||||||
|
$this->info("- Sync Log ID: " . ($syncLogId ?: 'Akan dibuat baru'));
|
||||||
|
$this->info("- Batch Size: {$batchSize}");
|
||||||
|
$this->info("- Queue: {$queueName}");
|
||||||
|
$this->info("- Filters: " . ($filtersJson ?: 'Tidak ada'));
|
||||||
|
$this->info("- Dry Run: " . ($isDryRun ? 'Ya' : 'Tidak'));
|
||||||
|
|
||||||
|
if ($isDryRun) {
|
||||||
|
$this->warn('Mode DRY RUN - Job tidak akan dijalankan');
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Konfirmasi sebelum menjalankan
|
||||||
|
if (!$this->confirm('Apakah Anda yakin ingin menjalankan job update seluruh kartu ATM?')) {
|
||||||
|
$this->info('Operasi dibatalkan');
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch job
|
||||||
|
$job = new UpdateAllAtmCardsBatchJob($syncLogId, $batchSize, $filters);
|
||||||
|
$job->onQueue($queueName);
|
||||||
|
dispatch($job);
|
||||||
|
|
||||||
|
$this->info('Job berhasil dijadwalkan!');
|
||||||
|
$this->info("Queue: {$queueName}");
|
||||||
|
$this->info('Gunakan command berikut untuk memonitor:');
|
||||||
|
$this->info('php artisan queue:work --queue=' . $queueName);
|
||||||
|
|
||||||
|
Log::info('Command update seluruh kartu ATM selesai', [
|
||||||
|
'sync_log_id' => $syncLogId,
|
||||||
|
'batch_size' => $batchSize,
|
||||||
|
'queue' => $queueName,
|
||||||
|
'filters' => $filters
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->error('Terjadi error: ' . $e->getMessage());
|
||||||
|
Log::error('Error dalam command update seluruh kartu ATM: ' . $e->getMessage(), [
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,7 +63,9 @@
|
|||||||
$this->updateCsvLogStart();
|
$this->updateCsvLogStart();
|
||||||
|
|
||||||
// Generate CSV file
|
// Generate CSV file
|
||||||
$result = $this->generateAtmCardCsv();
|
// $result = $this->generateAtmCardCsv();
|
||||||
|
|
||||||
|
$result = $this->generateSingleAtmCardCsv();
|
||||||
|
|
||||||
// Update status CSV generation berhasil
|
// Update status CSV generation berhasil
|
||||||
$this->updateCsvLogSuccess($result);
|
$this->updateCsvLogSuccess($result);
|
||||||
@@ -175,6 +177,8 @@
|
|||||||
->whereNotNull('currency')
|
->whereNotNull('currency')
|
||||||
->where('currency', '!=', '')
|
->where('currency', '!=', '')
|
||||||
->whereIn('ctdesc', $cardTypes)
|
->whereIn('ctdesc', $cardTypes)
|
||||||
|
->whereNotIn('product_code',['6002','6004','6042','6031'])
|
||||||
|
->where('branch','!=','ID0019999')
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,4 +417,155 @@
|
|||||||
|
|
||||||
Log::error('Pembuatan file CSV gagal: ' . $errorMessage);
|
Log::error('Pembuatan file CSV gagal: ' . $errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate single CSV file with all ATM card data without branch separation
|
||||||
|
*
|
||||||
|
* @return array Information about the generated file and upload status
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
private function generateSingleAtmCardCsv(): array
|
||||||
|
{
|
||||||
|
Log::info('Memulai pembuatan file CSV tunggal untuk semua kartu ATM');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ambil semua kartu yang memenuhi syarat
|
||||||
|
$cards = $this->getEligibleAtmCards();
|
||||||
|
|
||||||
|
if ($cards->isEmpty()) {
|
||||||
|
Log::warning('Tidak ada kartu ATM yang memenuhi syarat untuk periode ini');
|
||||||
|
throw new RuntimeException('Tidak ada kartu ATM yang memenuhi syarat untuk diproses');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buat nama file dengan timestamp
|
||||||
|
$dateTime = now()->format('Ymd_Hi');
|
||||||
|
$singleFilename = pathinfo($this->csvFilename, PATHINFO_FILENAME)
|
||||||
|
. '_ALL_BRANCHES_'
|
||||||
|
. $dateTime . '.'
|
||||||
|
. pathinfo($this->csvFilename, PATHINFO_EXTENSION);
|
||||||
|
|
||||||
|
$filename = storage_path('app/' . $singleFilename);
|
||||||
|
|
||||||
|
Log::info('Membuat file CSV: ' . $filename);
|
||||||
|
|
||||||
|
// Buka file untuk menulis
|
||||||
|
$handle = fopen($filename, 'w+');
|
||||||
|
if (!$handle) {
|
||||||
|
throw new RuntimeException("Tidak dapat membuat file CSV: $filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
$recordCount = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Tulis semua kartu ke dalam satu file
|
||||||
|
foreach ($cards as $card) {
|
||||||
|
$fee = $this->determineCardFee($card);
|
||||||
|
$csvRow = $this->createCsvRow($card, $fee);
|
||||||
|
|
||||||
|
if (fputcsv($handle, $csvRow, '|') === false) {
|
||||||
|
throw new RuntimeException("Gagal menulis data kartu ke file CSV: {$card->crdno}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$recordCount++;
|
||||||
|
|
||||||
|
// Log progress setiap 1000 record
|
||||||
|
if ($recordCount % 1000 === 0) {
|
||||||
|
Log::info("Progress: {$recordCount} kartu telah diproses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info("Selesai menulis {$recordCount} kartu ke file CSV");
|
||||||
|
|
||||||
|
// Bersihkan file CSV (hapus double quotes)
|
||||||
|
$this->cleanupCsvFile($filename);
|
||||||
|
|
||||||
|
Log::info('File CSV berhasil dibersihkan dari double quotes');
|
||||||
|
|
||||||
|
// Upload file ke SFTP (tanpa branch specific directory)
|
||||||
|
$uploadSuccess = true; // $this->uploadSingleFileToSftp($filename);
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'localFilePath' => $filename,
|
||||||
|
'recordCount' => $recordCount,
|
||||||
|
'uploadToSftp' => $uploadSuccess,
|
||||||
|
'timestamp' => now()->format('Y-m-d H:i:s'),
|
||||||
|
'fileName' => $singleFilename
|
||||||
|
];
|
||||||
|
|
||||||
|
Log::info('Pembuatan file CSV tunggal selesai', $result);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error('Error dalam generateSingleAtmCardCsv: ' . $e->getMessage(), [
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload single CSV file to SFTP server without branch directory
|
||||||
|
*
|
||||||
|
* @param string $localFilePath Path to the local CSV file
|
||||||
|
* @return bool True if upload successful, false otherwise
|
||||||
|
*/
|
||||||
|
private function uploadSingleFileToSftp(string $localFilePath): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Log::info('Memulai upload file tunggal ke SFTP: ' . $localFilePath);
|
||||||
|
|
||||||
|
// Update status SFTP upload dimulai
|
||||||
|
$this->updateSftpLogStart();
|
||||||
|
|
||||||
|
// Ambil nama file dari path
|
||||||
|
$filename = basename($localFilePath);
|
||||||
|
|
||||||
|
// Ambil konten file
|
||||||
|
$fileContent = file_get_contents($localFilePath);
|
||||||
|
if ($fileContent === false) {
|
||||||
|
Log::error("Tidak dapat membaca file untuk upload: {$localFilePath}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dapatkan disk SFTP
|
||||||
|
$disk = Storage::disk('sftpKartu');
|
||||||
|
|
||||||
|
// Tentukan path tujuan di server SFTP (root directory)
|
||||||
|
$remotePath = env('BIAYA_KARTU_REMOTE_PATH', '/');
|
||||||
|
$remoteFilePath = rtrim($remotePath, '/') . '/' . $filename;
|
||||||
|
|
||||||
|
Log::info('Mengunggah ke path remote: ' . $remoteFilePath);
|
||||||
|
|
||||||
|
// Upload file ke server SFTP
|
||||||
|
$result = $disk->put($remoteFilePath, $fileContent);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
$this->updateSftpLogSuccess();
|
||||||
|
Log::info("File CSV tunggal berhasil diunggah ke SFTP: {$remoteFilePath}");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$errorMsg = "Gagal mengunggah file CSV tunggal ke SFTP: {$remoteFilePath}";
|
||||||
|
$this->updateSftpLogFailed($errorMsg);
|
||||||
|
Log::error($errorMsg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errorMsg = "Error saat mengunggah file tunggal ke SFTP: " . $e->getMessage();
|
||||||
|
$this->updateSftpLogFailed($errorMsg);
|
||||||
|
|
||||||
|
Log::error($errorMsg, [
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'periode' => $this->periode
|
||||||
|
]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
namespace Modules\Webstatement\Jobs;
|
namespace Modules\Webstatement\Jobs;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
@@ -184,33 +186,53 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save batched records to the database
|
* Simpan batch data ke database menggunakan updateOrCreate
|
||||||
|
* untuk menghindari error unique constraint
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function saveBatch()
|
private function saveBatch(): void
|
||||||
: void
|
|
||||||
{
|
{
|
||||||
|
Log::info('Memulai proses saveBatch dengan updateOrCreate');
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!empty($this->entryBatch)) {
|
if (!empty($this->entryBatch)) {
|
||||||
|
$totalProcessed = 0;
|
||||||
|
|
||||||
// Process in smaller chunks for better memory management
|
// Process in smaller chunks for better memory management
|
||||||
foreach ($this->entryBatch as $entry) {
|
foreach ($this->entryBatch as $entryChunk) {
|
||||||
// Extract all stmt_entry_ids from the current chunk
|
foreach ($entryChunk as $entryData) {
|
||||||
$entryIds = array_column($entry, 'stmt_entry_id');
|
// Gunakan updateOrCreate untuk menghindari duplicate key error
|
||||||
|
StmtEntry::updateOrCreate(
|
||||||
|
[
|
||||||
|
'stmt_entry_id' => $entryData['stmt_entry_id']
|
||||||
|
],
|
||||||
|
$entryData
|
||||||
|
);
|
||||||
|
|
||||||
// Delete existing records with these IDs to avoid conflicts
|
$totalProcessed++;
|
||||||
StmtEntry::whereIn('stmt_entry_id', $entryIds)->delete();
|
}
|
||||||
|
|
||||||
// Insert all records in the chunk at once
|
|
||||||
StmtEntry::insert($entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset entry batch after processing
|
DB::commit();
|
||||||
|
|
||||||
|
Log::info("Berhasil memproses {$totalProcessed} record dengan updateOrCreate");
|
||||||
|
|
||||||
|
// Reset entry batch after successful processing
|
||||||
$this->entryBatch = [];
|
$this->entryBatch = [];
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
DB::rollback();
|
||||||
|
|
||||||
Log::error("Error in saveBatch: " . $e->getMessage() . "\n" . $e->getTraceAsString());
|
Log::error("Error in saveBatch: " . $e->getMessage() . "\n" . $e->getTraceAsString());
|
||||||
$this->errorCount += count($this->entryBatch);
|
$this->errorCount += count($this->entryBatch);
|
||||||
|
|
||||||
// Reset batch even if there's an error to prevent reprocessing the same failed records
|
// Reset batch even if there's an error to prevent reprocessing the same failed records
|
||||||
$this->entryBatch = [];
|
$this->entryBatch = [];
|
||||||
|
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
379
app/Jobs/UpdateAllAtmCardsBatchJob.php
Normal file
379
app/Jobs/UpdateAllAtmCardsBatchJob.php
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Modules\Webstatement\Jobs;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Modules\Webstatement\Models\Atmcard;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Modules\Webstatement\Models\KartuSyncLog;
|
||||||
|
|
||||||
|
class UpdateAllAtmCardsBatchJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konstanta untuk konfigurasi batch processing
|
||||||
|
*/
|
||||||
|
private const BATCH_SIZE = 100;
|
||||||
|
private const MAX_EXECUTION_TIME = 7200; // 2 jam dalam detik
|
||||||
|
private const DELAY_BETWEEN_JOBS = 2; // 2 detik delay antar job
|
||||||
|
private const MAX_DELAY_SPREAD = 300; // Spread maksimal 5 menit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID log sinkronisasi
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $syncLogId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model log sinkronisasi
|
||||||
|
*
|
||||||
|
* @var KartuSyncLog
|
||||||
|
*/
|
||||||
|
protected $syncLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch size untuk processing
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $batchSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter kondisi kartu yang akan diupdate
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param int|null $syncLogId ID log sinkronisasi
|
||||||
|
* @param int $batchSize Ukuran batch untuk processing
|
||||||
|
* @param array $filters Filter kondisi kartu
|
||||||
|
*/
|
||||||
|
public function __construct(?int $syncLogId = null, int $batchSize = self::BATCH_SIZE, array $filters = [])
|
||||||
|
{
|
||||||
|
$this->syncLogId = $syncLogId;
|
||||||
|
$this->batchSize = $batchSize > 0 ? $batchSize : self::BATCH_SIZE;
|
||||||
|
$this->filters = $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job untuk update seluruh kartu ATM
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
set_time_limit(self::MAX_EXECUTION_TIME);
|
||||||
|
|
||||||
|
Log::info('Memulai job update seluruh kartu ATM', [
|
||||||
|
'sync_log_id' => $this->syncLogId,
|
||||||
|
'batch_size' => $this->batchSize,
|
||||||
|
'filters' => $this->filters
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
// Load atau buat log sinkronisasi
|
||||||
|
$this->loadOrCreateSyncLog();
|
||||||
|
|
||||||
|
// Update status job dimulai
|
||||||
|
$this->updateJobStartStatus();
|
||||||
|
|
||||||
|
// Ambil total kartu yang akan diproses
|
||||||
|
$totalCards = $this->getTotalCardsCount();
|
||||||
|
|
||||||
|
if ($totalCards === 0) {
|
||||||
|
Log::info('Tidak ada kartu ATM yang perlu diupdate');
|
||||||
|
$this->updateJobCompletedStatus(0, 0);
|
||||||
|
DB::commit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info("Ditemukan {$totalCards} kartu ATM yang akan diproses");
|
||||||
|
|
||||||
|
// Proses kartu dalam batch
|
||||||
|
$processedCount = $this->processCardsInBatches($totalCards);
|
||||||
|
|
||||||
|
// Update status job selesai
|
||||||
|
$this->updateJobCompletedStatus($totalCards, $processedCount);
|
||||||
|
|
||||||
|
Log::info('Job update seluruh kartu ATM selesai', [
|
||||||
|
'total_cards' => $totalCards,
|
||||||
|
'processed_count' => $processedCount,
|
||||||
|
'sync_log_id' => $this->syncLog->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
$this->updateJobFailedStatus($e->getMessage());
|
||||||
|
|
||||||
|
Log::error('Gagal menjalankan job update seluruh kartu ATM: ' . $e->getMessage(), [
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'sync_log_id' => $this->syncLogId,
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load atau buat log sinkronisasi baru
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function loadOrCreateSyncLog(): void
|
||||||
|
{
|
||||||
|
Log::info('Loading atau membuat sync log', ['sync_log_id' => $this->syncLogId]);
|
||||||
|
|
||||||
|
if ($this->syncLogId) {
|
||||||
|
$this->syncLog = KartuSyncLog::find($this->syncLogId);
|
||||||
|
if (!$this->syncLog) {
|
||||||
|
throw new Exception("Sync log dengan ID {$this->syncLogId} tidak ditemukan");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Buat log sinkronisasi baru
|
||||||
|
$this->syncLog = KartuSyncLog::create([
|
||||||
|
'periode' => now()->format('Y-m'),
|
||||||
|
'sync_notes' => 'Batch update seluruh kartu ATM dimulai',
|
||||||
|
'is_sync' => false,
|
||||||
|
'sync_at' => null,
|
||||||
|
'is_csv' => false,
|
||||||
|
'csv_at' => null,
|
||||||
|
'is_ftp' => false,
|
||||||
|
'ftp_at' => null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info('Sync log berhasil dimuat/dibuat', ['sync_log_id' => $this->syncLog->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update status saat job dimulai
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateJobStartStatus(): void
|
||||||
|
{
|
||||||
|
Log::info('Memperbarui status job dimulai');
|
||||||
|
|
||||||
|
$this->syncLog->update([
|
||||||
|
'sync_notes' => $this->syncLog->sync_notes . "\nBatch update seluruh kartu ATM dimulai pada " . now()->format('Y-m-d H:i:s'),
|
||||||
|
'is_sync' => false,
|
||||||
|
'sync_at' => null
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ambil total jumlah kartu yang akan diproses
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function getTotalCardsCount(): int
|
||||||
|
{
|
||||||
|
Log::info('Menghitung total kartu yang akan diproses', ['filters' => $this->filters]);
|
||||||
|
|
||||||
|
$query = $this->buildCardQuery();
|
||||||
|
$count = $query->count();
|
||||||
|
|
||||||
|
Log::info("Total kartu ditemukan: {$count}");
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build query untuk mengambil kartu berdasarkan filter
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
private function buildCardQuery()
|
||||||
|
{
|
||||||
|
$query = Atmcard::where('crsts', 1) // Kartu aktif
|
||||||
|
->whereNotNull('accflag')
|
||||||
|
->where('accflag', '!=', '');
|
||||||
|
|
||||||
|
// Terapkan filter default untuk kartu yang perlu update branch/currency
|
||||||
|
if (empty($this->filters) || !isset($this->filters['skip_branch_currency_filter'])) {
|
||||||
|
$query->where(function ($q) {
|
||||||
|
$q->whereNull('branch')
|
||||||
|
->orWhere('branch', '')
|
||||||
|
->orWhereNull('currency')
|
||||||
|
->orWhere('currency', '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terapkan filter tambahan jika ada
|
||||||
|
if (!empty($this->filters)) {
|
||||||
|
foreach ($this->filters as $field => $value) {
|
||||||
|
if ($field === 'skip_branch_currency_filter') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
$query->whereIn($field, $value);
|
||||||
|
} else {
|
||||||
|
$query->where($field, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proses kartu dalam batch
|
||||||
|
*
|
||||||
|
* @param int $totalCards
|
||||||
|
* @return int Jumlah kartu yang berhasil diproses
|
||||||
|
*/
|
||||||
|
private function processCardsInBatches(int $totalCards): int
|
||||||
|
{
|
||||||
|
Log::info('Memulai pemrosesan kartu dalam batch', [
|
||||||
|
'total_cards' => $totalCards,
|
||||||
|
'batch_size' => $this->batchSize
|
||||||
|
]);
|
||||||
|
|
||||||
|
$processedCount = 0;
|
||||||
|
$batchNumber = 1;
|
||||||
|
$totalBatches = ceil($totalCards / $this->batchSize);
|
||||||
|
|
||||||
|
// Proses kartu dalam chunk/batch
|
||||||
|
$this->buildCardQuery()->chunk($this->batchSize, function ($cards) use (&$processedCount, &$batchNumber, $totalBatches, $totalCards) {
|
||||||
|
Log::info("Memproses batch {$batchNumber}/{$totalBatches}", [
|
||||||
|
'cards_in_batch' => $cards->count(),
|
||||||
|
'processed_so_far' => $processedCount
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Dispatch job untuk setiap kartu dalam batch dengan delay
|
||||||
|
foreach ($cards as $index => $card) {
|
||||||
|
// Hitung delay berdasarkan nomor batch dan index untuk menyebar eksekusi job
|
||||||
|
$delay = (($batchNumber - 1) * $this->batchSize + $index) % self::MAX_DELAY_SPREAD;
|
||||||
|
$delay += self::DELAY_BETWEEN_JOBS; // Tambah delay minimum
|
||||||
|
|
||||||
|
// Dispatch job UpdateAtmCardBranchCurrencyJob
|
||||||
|
UpdateAtmCardBranchCurrencyJob::dispatch($card, $this->syncLog->id)
|
||||||
|
->delay(now()->addSeconds($delay))
|
||||||
|
->onQueue('default');
|
||||||
|
|
||||||
|
$processedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress di log setiap 10 batch
|
||||||
|
if ($batchNumber % 10 === 0) {
|
||||||
|
$this->updateProgressStatus($processedCount, $totalCards, $batchNumber, $totalBatches);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info("Batch {$batchNumber} berhasil dijadwalkan", [
|
||||||
|
'cards_scheduled' => $cards->count(),
|
||||||
|
'total_processed' => $processedCount
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error("Error saat memproses batch {$batchNumber}: " . $e->getMessage(), [
|
||||||
|
'batch_number' => $batchNumber,
|
||||||
|
'cards_count' => $cards->count(),
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$batchNumber++;
|
||||||
|
});
|
||||||
|
|
||||||
|
Log::info('Selesai memproses semua batch', [
|
||||||
|
'total_processed' => $processedCount,
|
||||||
|
'total_batches' => $batchNumber - 1
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $processedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update status progress pemrosesan
|
||||||
|
*
|
||||||
|
* @param int $processedCount
|
||||||
|
* @param int $totalCards
|
||||||
|
* @param int $batchNumber
|
||||||
|
* @param int $totalBatches
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateProgressStatus(int $processedCount, int $totalCards, int $batchNumber, int $totalBatches): void
|
||||||
|
{
|
||||||
|
Log::info('Memperbarui status progress', [
|
||||||
|
'processed' => $processedCount,
|
||||||
|
'total' => $totalCards,
|
||||||
|
'batch' => $batchNumber,
|
||||||
|
'total_batches' => $totalBatches
|
||||||
|
]);
|
||||||
|
|
||||||
|
$percentage = round(($processedCount / $totalCards) * 100, 2);
|
||||||
|
$progressNote = "\nProgress: {$processedCount}/{$totalCards} kartu dijadwalkan ({$percentage}%) - Batch {$batchNumber}/{$totalBatches}";
|
||||||
|
|
||||||
|
$this->syncLog->update([
|
||||||
|
'sync_notes' => $this->syncLog->sync_notes . $progressNote
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update status saat job selesai
|
||||||
|
*
|
||||||
|
* @param int $totalCards
|
||||||
|
* @param int $processedCount
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateJobCompletedStatus(int $totalCards, int $processedCount): void
|
||||||
|
{
|
||||||
|
Log::info('Memperbarui status job selesai', [
|
||||||
|
'total_cards' => $totalCards,
|
||||||
|
'processed_count' => $processedCount
|
||||||
|
]);
|
||||||
|
|
||||||
|
$completionNote = "\nBatch update selesai pada " . now()->format('Y-m-d H:i:s') .
|
||||||
|
" - Total {$processedCount} kartu dari {$totalCards} berhasil dijadwalkan untuk update";
|
||||||
|
|
||||||
|
$this->syncLog->update([
|
||||||
|
'is_sync' => true,
|
||||||
|
'sync_at' => now(),
|
||||||
|
'sync_notes' => $this->syncLog->sync_notes . $completionNote
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update status saat job gagal
|
||||||
|
*
|
||||||
|
* @param string $errorMessage
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function updateJobFailedStatus(string $errorMessage): void
|
||||||
|
{
|
||||||
|
Log::error('Memperbarui status job gagal', ['error' => $errorMessage]);
|
||||||
|
|
||||||
|
if ($this->syncLog) {
|
||||||
|
$failureNote = "\nBatch update gagal pada " . now()->format('Y-m-d H:i:s') .
|
||||||
|
" - Error: {$errorMessage}";
|
||||||
|
|
||||||
|
$this->syncLog->update([
|
||||||
|
'is_sync' => false,
|
||||||
|
'sync_notes' => $this->syncLog->sync_notes . $failureNote
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,14 @@ namespace Modules\Webstatement\Jobs;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Modules\Webstatement\Models\Account;
|
||||||
|
use Modules\Webstatement\Models\Atmcard;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Modules\Webstatement\Models\Atmcard;
|
|
||||||
|
|
||||||
class UpdateAtmCardBranchCurrencyJob implements ShouldQueue
|
class UpdateAtmCardBranchCurrencyJob implements ShouldQueue
|
||||||
{
|
{
|
||||||
@@ -86,7 +87,7 @@ class UpdateAtmCardBranchCurrencyJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Coba dapatkan data dari model Account terlebih dahulu
|
// Coba dapatkan data dari model Account terlebih dahulu
|
||||||
$account = \Modules\Webstatement\Models\Account::where('account_number', $accountNumber)->first();
|
$account = Account::where('account_number', $accountNumber)->first();
|
||||||
|
|
||||||
if ($account) {
|
if ($account) {
|
||||||
// Jika account ditemukan, format data sesuai dengan format response dari API
|
// Jika account ditemukan, format data sesuai dengan format response dari API
|
||||||
@@ -94,7 +95,7 @@ class UpdateAtmCardBranchCurrencyJob implements ShouldQueue
|
|||||||
'responseCode' => '00',
|
'responseCode' => '00',
|
||||||
'acctCompany' => $account->branch_code,
|
'acctCompany' => $account->branch_code,
|
||||||
'acctCurrency' => $account->currency,
|
'acctCurrency' => $account->currency,
|
||||||
'openCategory' => $account->open_category
|
'acctType' => $account->open_category
|
||||||
// Tambahkan field lain yang mungkin diperlukan
|
// Tambahkan field lain yang mungkin diperlukan
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -103,7 +104,8 @@ class UpdateAtmCardBranchCurrencyJob implements ShouldQueue
|
|||||||
$url = env('FIORANO_URL') . self::API_BASE_PATH;
|
$url = env('FIORANO_URL') . self::API_BASE_PATH;
|
||||||
$path = self::API_INQUIRY_PATH;
|
$path = self::API_INQUIRY_PATH;
|
||||||
$data = [
|
$data = [
|
||||||
'accountNo' => $accountNumber
|
'accountNo' => $accountNumber,
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = Http::post($url . $path, $data);
|
$response = Http::post($url . $path, $data);
|
||||||
@@ -125,6 +127,7 @@ class UpdateAtmCardBranchCurrencyJob implements ShouldQueue
|
|||||||
$cardData = [
|
$cardData = [
|
||||||
'branch' => !empty($accountInfo['acctCompany']) ? $accountInfo['acctCompany'] : null,
|
'branch' => !empty($accountInfo['acctCompany']) ? $accountInfo['acctCompany'] : null,
|
||||||
'currency' => !empty($accountInfo['acctCurrency']) ? $accountInfo['acctCurrency'] : null,
|
'currency' => !empty($accountInfo['acctCurrency']) ? $accountInfo['acctCurrency'] : null,
|
||||||
|
'product_code' => !empty($accountInfo['acctType']) ? $accountInfo['acctType'] : null,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->card->update($cardData);
|
$this->card->update($cardData);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace Modules\Webstatement\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
// use Modules\Webstatement\Database\Factories\AtmcardFactory;
|
// use Modules\Webstatement\Database\Factories\AtmcardFactory;
|
||||||
|
|
||||||
class Atmcard extends Model
|
class Atmcard extends Model
|
||||||
@@ -15,7 +16,64 @@ class Atmcard extends Model
|
|||||||
*/
|
*/
|
||||||
protected $guarded = ['id'];
|
protected $guarded = ['id'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relasi ke tabel JenisKartu untuk mendapatkan informasi biaya kartu
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
public function biaya(){
|
public function biaya(){
|
||||||
|
Log::info('Mengakses relasi biaya untuk ATM card', ['card_id' => $this->id]);
|
||||||
return $this->belongsTo(JenisKartu::class,'ctdesc','code');
|
return $this->belongsTo(JenisKartu::class,'ctdesc','code');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope untuk mendapatkan kartu ATM yang aktif
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
public function scopeActive($query)
|
||||||
|
{
|
||||||
|
Log::info('Menggunakan scope active untuk filter kartu ATM aktif');
|
||||||
|
return $query->where('crsts', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope untuk mendapatkan kartu berdasarkan product_code
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||||
|
* @param string $productCode
|
||||||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||||||
|
*/
|
||||||
|
public function scopeByProductCode($query, $productCode)
|
||||||
|
{
|
||||||
|
Log::info('Menggunakan scope byProductCode', ['product_code' => $productCode]);
|
||||||
|
return $query->where('product_code', $productCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessor untuk mendapatkan product_code dengan format yang konsisten
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getProductCodeAttribute($value)
|
||||||
|
{
|
||||||
|
return $value ? strtoupper(trim($value)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutator untuk menyimpan product_code dengan format yang konsisten
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setProductCodeAttribute($value)
|
||||||
|
{
|
||||||
|
$this->attributes['product_code'] = $value ? strtoupper(trim($value)) : null;
|
||||||
|
Log::info('Product code diset untuk ATM card', [
|
||||||
|
'card_id' => $this->id ?? 'new',
|
||||||
|
'product_code' => $this->attributes['product_code']
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,19 @@ use Illuminate\Support\Facades\Blade;
|
|||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Nwidart\Modules\Traits\PathNamespace;
|
use Nwidart\Modules\Traits\PathNamespace;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Modules\Webstatement\Console\CheckEmailProgressCommand;
|
|
||||||
use Modules\Webstatement\Console\UnlockPdf;
|
use Modules\Webstatement\Console\UnlockPdf;
|
||||||
use Modules\Webstatement\Console\CombinePdf;
|
use Modules\Webstatement\Console\CombinePdf;
|
||||||
use Modules\Webstatement\Console\ConvertHtmlToPdf;
|
use Modules\Webstatement\Console\ConvertHtmlToPdf;
|
||||||
use Modules\Webstatement\Console\ExportDailyStatements;
|
use Modules\Webstatement\Console\ExportDailyStatements;
|
||||||
use Modules\Webstatement\Console\ProcessDailyMigration;
|
use Modules\Webstatement\Console\ProcessDailyMigration;
|
||||||
use Modules\Webstatement\Console\ExportPeriodStatements;
|
use Modules\Webstatement\Console\ExportPeriodStatements;
|
||||||
|
use Modules\Webstatement\Console\UpdateAllAtmCardsCommand;
|
||||||
|
use Modules\Webstatement\Console\CheckEmailProgressCommand;
|
||||||
use Modules\Webstatement\Console\GenerateBiayakartuCommand;
|
use Modules\Webstatement\Console\GenerateBiayakartuCommand;
|
||||||
|
use Modules\Webstatement\Console\SendStatementEmailCommand;
|
||||||
use Modules\Webstatement\Jobs\UpdateAtmCardBranchCurrencyJob;
|
use Modules\Webstatement\Jobs\UpdateAtmCardBranchCurrencyJob;
|
||||||
use Modules\Webstatement\Console\GenerateAtmTransactionReport;
|
use Modules\Webstatement\Console\GenerateAtmTransactionReport;
|
||||||
use Modules\Webstatement\Console\GenerateBiayaKartuCsvCommand;
|
use Modules\Webstatement\Console\GenerateBiayaKartuCsvCommand;
|
||||||
use Modules\Webstatement\Console\SendStatementEmailCommand;
|
|
||||||
|
|
||||||
class WebstatementServiceProvider extends ServiceProvider
|
class WebstatementServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@@ -70,7 +71,8 @@ class WebstatementServiceProvider extends ServiceProvider
|
|||||||
ExportPeriodStatements::class,
|
ExportPeriodStatements::class,
|
||||||
GenerateAtmTransactionReport::class,
|
GenerateAtmTransactionReport::class,
|
||||||
SendStatementEmailCommand::class,
|
SendStatementEmailCommand::class,
|
||||||
CheckEmailProgressCommand::class
|
CheckEmailProgressCommand::class,
|
||||||
|
UpdateAllAtmCardsCommand::class
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Menjalankan migration untuk menambahkan field product_code pada tabel atmcards
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Log::info('Memulai migration: menambahkan field product_code ke tabel atmcards');
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Schema::table('atmcards', function (Blueprint $table) {
|
||||||
|
// Menambahkan field product_code setelah field ctdesc
|
||||||
|
$table->string('product_code')->nullable()->after('ctdesc')->comment('Kode produk kartu ATM');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
Log::info('Migration berhasil: field product_code telah ditambahkan ke tabel atmcards');
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
DB::rollback();
|
||||||
|
Log::error('Migration gagal: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Membalikkan migration dengan menghapus field product_code dari tabel atmcards
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Log::info('Memulai rollback migration: menghapus field product_code dari tabel atmcards');
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Schema::table('atmcards', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('product_code');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
Log::info('Rollback migration berhasil: field product_code telah dihapus dari tabel atmcards');
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
DB::rollback();
|
||||||
|
Log::error('Rollback migration gagal: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user