- **Perbaikan dan Penambahan Komando:**
- Memberikan komando baru `webstatement:check-progress` untuk memantau progres pengiriman email statement.
- Menampilkan informasi seperti `Log ID`, `Batch ID`, `Request Type`, status, hingga persentase progress.
- Menangani secara detail jumlah akun yang diproses, sukses, gagal, dan kalkulasi tingkat keberhasilan.
- Menyediakan penanganan error jika log tidak ditemukan atau terjadi kegagalan lainnya.
- Memperluas komando `webstatement:send-email`:
- Mendukung pengiriman berdasarkan `single account`, `branch`, atau `all branches`.
- Menambahkan validasi parameter `type` (`single`, `branch`, `all`) dan input spesifik seperti `--account` atau `--branch` untuk mode tertentu.
- Melakukan pencatatan log awal dengan metadata lengkap seperti `request_type`, `batch_id`, dan status.
- **Peningkatan Logika Proses Backend:**
- Menambahkan fungsi `createLogEntry` untuk mencatat log pengiriman email statement secara dinamis berdasarkan tipe request.
- Menyediakan reusable method seperti `validateParameters` dan `determineRequestTypeAndTarget` untuk mempermudah pengelolaan parameter pengiriman.
- Memberikan feedback dan panduan kepada pengguna mengenai ID log dan komando monitoring (`webstatement:check-progress`).
- **Penambahan Controller dan Fitur UI:**
- Menambahkan controller baru `EmailStatementLogController`:
- Mendukung pengelolaan log seperti list, detail, dan retry untuk pengiriman ulang email statement.
- Menyediakan fitur pencarian, filter, dan halaman data log yang responsif menggunakan datatable.
- Menambahkan kemampuan resend email untuk log dengan status `completed` atau `failed`.
- Mengimplementasikan UI untuk log pengiriman:
- Halaman daftar monitoring dengan filter berdasarkan branch, account number, request type, status, dan tanggal.
- Menampilkan kemajuan, tingkat keberhasilan, serta tombol aksi seperti detail dan pengiriman ulang.
- **Peningkatan Model dan Validasi:**
- Menyesuaikan model `PrintStatementLog` untuk mendukung lebih banyak atribut seperti `processed_accounts`, `success_count`, `failed_count`, `request_type`, serta metode utilitas seperti `getProgressPercentage()` dan `getSuccessRate()`.
- Memvalidasi parameter input lebih mendalam agar kesalahan dapat diminimalisasi di awal proses.
- **Peningkatan pada View dan Feedback Pengguna:**
- Menambah daftar command berguna untuk user di interface log:
- Status antrian dengan `php artisan queue:work`.
- Monitoring menggunakan komando custom yang baru ditambahkan.
- **Perbaikan Logging dan Error Handling:**
- Menambahkan logging komprehensif pada semua proses, termasuk batch pengiriman ulang.
- Memastikan rollback pada database jika terjadi error melalui transaksi pada critical path.
Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
249 lines
9.6 KiB
PHP
249 lines
9.6 KiB
PHP
<?php
|
|
|
|
namespace Modules\Webstatement\Console;
|
|
|
|
use Exception;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Modules\Webstatement\Jobs\SendStatementEmailJob;
|
|
use Modules\Webstatement\Models\Account;
|
|
use Modules\Webstatement\Models\PrintStatementLog;
|
|
use Modules\Basicdata\Models\Branch;
|
|
|
|
/**
|
|
* Command untuk mengirim email statement PDF ke nasabah
|
|
* Mendukung pengiriman per rekening, per cabang, atau seluruh cabang
|
|
*/
|
|
class SendStatementEmailCommand extends Command
|
|
{
|
|
protected $signature = 'webstatement:send-email
|
|
{period : Format periode YYYYMM (contoh: 202401)}
|
|
{--type=single : Tipe pengiriman: single, branch, all}
|
|
{--account= : Nomor rekening (untuk type=single)}
|
|
{--branch= : Kode cabang (untuk type=branch)}
|
|
{--batch-id= : ID batch untuk tracking (opsional)}
|
|
{--queue=emails : Nama queue untuk job (default: emails)}
|
|
{--delay=0 : Delay dalam menit sebelum job dijalankan}';
|
|
|
|
protected $description = 'Mengirim email statement PDF ke nasabah (per rekening, per cabang, atau seluruh cabang)';
|
|
|
|
public function handle()
|
|
{
|
|
$this->info('🚀 Memulai proses pengiriman email statement...');
|
|
|
|
try {
|
|
$period = $this->argument('period');
|
|
$type = $this->option('type');
|
|
$accountNumber = $this->option('account');
|
|
$branchCode = $this->option('branch');
|
|
$batchId = $this->option('batch-id');
|
|
$queueName = $this->option('queue');
|
|
$delay = (int) $this->option('delay');
|
|
|
|
// Validasi parameter
|
|
if (!$this->validateParameters($period, $type, $accountNumber, $branchCode)) {
|
|
return Command::FAILURE;
|
|
}
|
|
|
|
// Tentukan request type dan target value
|
|
[$requestType, $targetValue] = $this->determineRequestTypeAndTarget($type, $accountNumber, $branchCode);
|
|
|
|
// Buat log entry
|
|
$log = $this->createLogEntry($period, $requestType, $targetValue, $batchId);
|
|
|
|
// Dispatch job
|
|
$job = SendStatementEmailJob::dispatch($period, $requestType, $targetValue, $batchId, $log->id)
|
|
->onQueue($queueName);
|
|
|
|
if ($delay > 0) {
|
|
$job->delay(now()->addMinutes($delay));
|
|
$this->info("⏰ Job dijadwalkan untuk dijalankan dalam {$delay} menit");
|
|
}
|
|
|
|
$this->displayJobInfo($period, $requestType, $targetValue, $queueName, $log);
|
|
$this->info('✅ Job pengiriman email statement berhasil didispatch!');
|
|
$this->info('📊 Gunakan command berikut untuk monitoring:');
|
|
$this->line(" php artisan queue:work {$queueName}");
|
|
$this->line(' php artisan webstatement:check-progress ' . $log->id);
|
|
|
|
return Command::SUCCESS;
|
|
|
|
} catch (Exception $e) {
|
|
$this->error('❌ Error saat mendispatch job: ' . $e->getMessage());
|
|
Log::error('SendStatementEmailCommand failed', [
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
return Command::FAILURE;
|
|
}
|
|
}
|
|
|
|
private function validateParameters($period, $type, $accountNumber, $branchCode)
|
|
{
|
|
// Validasi format periode
|
|
if (!preg_match('/^\d{6}$/', $period)) {
|
|
$this->error('❌ Format periode tidak valid. Gunakan format YYYYMM (contoh: 202401)');
|
|
return false;
|
|
}
|
|
|
|
// Validasi type
|
|
if (!in_array($type, ['single', 'branch', 'all'])) {
|
|
$this->error('❌ Type tidak valid. Gunakan: single, branch, atau all');
|
|
return false;
|
|
}
|
|
|
|
// Validasi parameter berdasarkan type
|
|
switch ($type) {
|
|
case 'single':
|
|
if (!$accountNumber) {
|
|
$this->error('❌ Parameter --account diperlukan untuk type=single');
|
|
return false;
|
|
}
|
|
|
|
$account = Account::with('customer')
|
|
->where('account_number', $accountNumber)
|
|
->first();
|
|
|
|
if (!$account) {
|
|
$this->error("❌ Account {$accountNumber} tidak ditemukan");
|
|
return false;
|
|
}
|
|
|
|
$hasEmail = !empty($account->stmt_email) ||
|
|
($account->customer && !empty($account->customer->email));
|
|
|
|
if (!$hasEmail) {
|
|
$this->error("❌ Account {$accountNumber} tidak memiliki email");
|
|
return false;
|
|
}
|
|
|
|
$this->info("✅ Account {$accountNumber} ditemukan dengan email");
|
|
break;
|
|
|
|
case 'branch':
|
|
if (!$branchCode) {
|
|
$this->error('❌ Parameter --branch diperlukan untuk type=branch');
|
|
return false;
|
|
}
|
|
|
|
$branch = Branch::where('code', $branchCode)->first();
|
|
if (!$branch) {
|
|
$this->error("❌ Branch {$branchCode} tidak ditemukan");
|
|
return false;
|
|
}
|
|
|
|
$accountCount = Account::with('customer')
|
|
->where('branch_code', $branchCode)
|
|
->where('stmt_sent_type', 'BY.EMAIL')
|
|
->get()
|
|
->filter(function ($account) {
|
|
return !empty($account->stmt_email) ||
|
|
($account->customer && !empty($account->customer->email));
|
|
})
|
|
->count();
|
|
|
|
if ($accountCount === 0) {
|
|
$this->error("❌ Tidak ada account dengan email di branch {$branchCode}");
|
|
return false;
|
|
}
|
|
|
|
$this->info("✅ Ditemukan {$accountCount} account dengan email di branch {$branch->name}");
|
|
break;
|
|
|
|
case 'all':
|
|
$accountCount = Account::with('customer')
|
|
->where('stmt_sent_type', 'BY.EMAIL')
|
|
->get()
|
|
->filter(function ($account) {
|
|
return !empty($account->stmt_email) ||
|
|
($account->customer && !empty($account->customer->email));
|
|
})
|
|
->count();
|
|
|
|
if ($accountCount === 0) {
|
|
$this->error('❌ Tidak ada account dengan email ditemukan');
|
|
return false;
|
|
}
|
|
|
|
$this->info("✅ Ditemukan {$accountCount} account dengan email di seluruh cabang");
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function determineRequestTypeAndTarget($type, $accountNumber, $branchCode)
|
|
{
|
|
switch ($type) {
|
|
case 'single':
|
|
return ['single_account', $accountNumber];
|
|
case 'branch':
|
|
return ['branch', $branchCode];
|
|
case 'all':
|
|
return ['all_branches', null];
|
|
default:
|
|
throw new \InvalidArgumentException("Invalid type: {$type}");
|
|
}
|
|
}
|
|
|
|
private function createLogEntry($period, $requestType, $targetValue, $batchId)
|
|
{
|
|
$logData = [
|
|
'user_id' => null, // Command line execution
|
|
'period_from' => $period,
|
|
'period_to' => $period,
|
|
'is_period_range' => false,
|
|
'request_type' => $requestType,
|
|
'batch_id' => $batchId ?? uniqid('cmd_'),
|
|
'status' => 'pending',
|
|
'authorization_status' => 'approved', // Auto-approved untuk command line
|
|
'created_by' => null,
|
|
'ip_address' => '127.0.0.1',
|
|
'user_agent' => 'Command Line'
|
|
];
|
|
|
|
// Set branch_code dan account_number berdasarkan request type
|
|
switch ($requestType) {
|
|
case 'single_account':
|
|
$account = Account::where('account_number', $targetValue)->first();
|
|
$logData['branch_code'] = $account->branch_code;
|
|
$logData['account_number'] = $targetValue;
|
|
break;
|
|
case 'branch':
|
|
$logData['branch_code'] = $targetValue;
|
|
$logData['account_number'] = null;
|
|
break;
|
|
case 'all_branches':
|
|
$logData['branch_code'] = 'ALL';
|
|
$logData['account_number'] = null;
|
|
break;
|
|
}
|
|
|
|
return PrintStatementLog::create($logData);
|
|
}
|
|
|
|
private function displayJobInfo($period, $requestType, $targetValue, $queueName, $log)
|
|
{
|
|
$this->info('📋 Detail Job:');
|
|
$this->line(" Log ID: {$log->id}");
|
|
$this->line(" Periode: {$period}");
|
|
$this->line(" Request Type: {$requestType}");
|
|
|
|
switch ($requestType) {
|
|
case 'single_account':
|
|
$this->line(" Account: {$targetValue}");
|
|
break;
|
|
case 'branch':
|
|
$branch = Branch::where('code', $targetValue)->first();
|
|
$this->line(" Branch: {$targetValue} ({$branch->name})");
|
|
break;
|
|
case 'all_branches':
|
|
$this->line(" Target: Seluruh cabang");
|
|
break;
|
|
}
|
|
|
|
$this->line(" Batch ID: {$log->batch_id}");
|
|
$this->line(" Queue: {$queueName}");
|
|
}
|
|
}
|