# Conflicts: # app/Http/Controllers/PrintStatementController.php # app/Jobs/GenerateBiayaKartuCsvJob.php # app/Jobs/SendStatementEmailJob.php # app/Mail/StatementEmail.php # resources/views/statements/email.blade.php # resources/views/statements/index.blade.php
600 lines
21 KiB
PHP
600 lines
21 KiB
PHP
<?php
|
|
|
|
namespace Modules\Webstatement\Jobs;
|
|
|
|
use Carbon\Carbon;
|
|
use Exception;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Modules\Webstatement\Models\Atmcard;
|
|
use Modules\Webstatement\Models\JenisKartu;
|
|
use Modules\Webstatement\Models\KartuSyncLog;
|
|
use RuntimeException;
|
|
|
|
class GenerateBiayaKartuCsvJob implements ShouldQueue
|
|
{
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
// Changed from const to property
|
|
private const MAX_EXECUTION_TIME = 3600; // 1 jam dalam detik
|
|
/**
|
|
* Periode yang akan diproses (YYYY-MM)
|
|
*/
|
|
protected $periode;
|
|
/**
|
|
* ID log sinkronisasi
|
|
*/
|
|
protected $syncLogId;
|
|
/**
|
|
* Model log sinkronisasi
|
|
*/
|
|
protected $syncLog;
|
|
private $csvFilename;
|
|
|
|
/**
|
|
* Create a new job instance.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->csvFilename = env('BIAYA_KARTU_CSV_FILENAME', 'biaya_kartu_atm.csv');
|
|
$this->periode = $periode ?? Carbon::now()->format('Y-m');
|
|
$this->syncLogId = KartuSyncLog::where('periode', $this->periode)->first();
|
|
}
|
|
|
|
/**
|
|
* Execute the job.
|
|
*/
|
|
public function handle()
|
|
: void
|
|
{
|
|
set_time_limit(self::MAX_EXECUTION_TIME);
|
|
|
|
// Load log sinkronisasi
|
|
$this->syncLog = KartuSyncLog::findOrFail($this->syncLogId->id);
|
|
|
|
|
|
try {
|
|
// Update status CSV generation dimulai
|
|
$this->updateCsvLogStart();
|
|
|
|
// Generate CSV file
|
|
// $result = $this->generateAtmCardCsv();
|
|
|
|
$result = $this->generateSingleAtmCardCsv();
|
|
|
|
// Update status CSV generation berhasil
|
|
$this->updateCsvLogSuccess($result);
|
|
|
|
Log::info('Pembuatan dan upload file CSV biaya kartu ATM selesai', [
|
|
'file' => $result['localFilePath'],
|
|
'jumlah_kartu' => $result['recordCount'],
|
|
'upload_sftp' => $result['uploadToSftp'] ? 'Berhasil' : 'Gagal',
|
|
'waktu' => $result['timestamp']
|
|
]);
|
|
|
|
if (!$result['uploadToSftp']) {
|
|
Log::warning('File CSV biaya kartu ATM tidak berhasil diunggah ke SFTP, tetapi tersedia secara lokal di: ' . $result['localFilePath']);
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->updateCsvLogFailed($e->getMessage());
|
|
|
|
Log::error('Gagal membuat atau mengunggah file CSV biaya kartu ATM: ' . $e->getMessage(), [
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine(),
|
|
'periode' => $this->periode
|
|
]);
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update log saat pembuatan CSV dimulai
|
|
*/
|
|
private function updateCsvLogStart()
|
|
: void
|
|
{
|
|
$this->syncLog->update([
|
|
'csv_notes' => 'Pembuatan file CSV untuk periode ' . $this->periode . ' dimulai',
|
|
'is_csv' => false,
|
|
'csv_at' => null,
|
|
]);
|
|
|
|
Log::info('Memulai pembuatan file CSV untuk periode ' . $this->periode);
|
|
}
|
|
|
|
/**
|
|
* Generate CSV file with ATM card data and upload to SFTP
|
|
*
|
|
* @return array Information about the generated file and upload status
|
|
*/
|
|
private function generateAtmCardCsv(): array
|
|
{
|
|
$cards = $this->getEligibleAtmCards();
|
|
$cardsByBranch = $cards->groupBy('branch');
|
|
$results = [];
|
|
|
|
foreach ($cardsByBranch as $branch => $branchCards) {
|
|
$dateTime = now()->format('Ymd_Hi');
|
|
$branchFilename = pathinfo($this->csvFilename, PATHINFO_FILENAME)
|
|
. '_' . $branch
|
|
. '_' . $dateTime . '.'
|
|
. pathinfo($this->csvFilename, PATHINFO_EXTENSION);
|
|
|
|
$filename = storage_path('app/' . $branchFilename);
|
|
|
|
$handle = fopen($filename, 'w+');
|
|
if (!$handle) {
|
|
throw new RuntimeException("Tidak dapat membuat file CSV: $filename");
|
|
}
|
|
|
|
try {
|
|
foreach ($branchCards as $card) {
|
|
$fee = $this->determineCardFee($card);
|
|
$csvRow = $this->createCsvRow($card, $fee);
|
|
fputcsv($handle, $csvRow, '|');
|
|
}
|
|
} finally {
|
|
fclose($handle);
|
|
}
|
|
|
|
$this->cleanupCsvFile($filename);
|
|
|
|
// Upload file ke SFTP
|
|
$uploadSuccess = $this->uploadToSftpKartu($filename, $card->branch);
|
|
|
|
$results[] = [
|
|
'branch' => $branch,
|
|
'localFilePath' => $filename,
|
|
'recordCount' => count($branchCards),
|
|
'uploadToSftp' => $uploadSuccess,
|
|
'timestamp' => now()->format('Y-m-d H:i:s')
|
|
];
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get eligible ATM cards from database
|
|
* Mengambil data kartu ATM yang memenuhi syarat untuk dikenakan biaya admin
|
|
* dengan filter khusus untuk mengecualikan product_code 6021 yang ctdesc nya gold
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Collection
|
|
*/
|
|
private function getEligibleAtmCards()
|
|
{
|
|
// Log: Memulai proses pengambilan data kartu ATM yang eligible
|
|
Log::info('Starting to fetch eligible ATM cards', [
|
|
'periode' => $this->periode
|
|
]);
|
|
|
|
$cardTypes = array_keys($this->getDefaultFees());
|
|
|
|
$query = Atmcard::where('crsts', 1)
|
|
->whereNotNull('accflag')
|
|
->where('accflag', '!=', '')
|
|
->where('flag','')
|
|
->whereNotNull('branch')
|
|
->where('branch', '!=', '')
|
|
->whereNotNull('currency')
|
|
->where('currency', '!=', '')
|
|
->whereIn('ctdesc', $cardTypes)
|
|
->whereNotIn('product_code',['6002','6004','6042','6031']) // Hapus 6021 dari sini
|
|
->where('branch','!=','ID0019999')
|
|
// Filter khusus: Kecualikan product_code 6021 yang ctdesc nya gold
|
|
->where(function($subQuery) {
|
|
$subQuery->where('product_code', '!=', '6021')
|
|
->orWhere(function($nestedQuery) {
|
|
$nestedQuery->where('product_code', '6021')
|
|
->where('ctdesc', '!=', 'gold');
|
|
});
|
|
});
|
|
|
|
|
|
|
|
$cards = $query->get();
|
|
|
|
// Log: Hasil pengambilan data kartu ATM
|
|
Log::info('Eligible ATM cards fetched successfully', [
|
|
'total_cards' => $cards->count(),
|
|
'periode' => $this->periode,
|
|
'excluded_product_codes' => ['6002','6004','6042','6031'],
|
|
'special_filter' => 'product_code 6021 dengan ctdesc gold dikecualikan'
|
|
]);
|
|
|
|
return $cards;
|
|
}
|
|
|
|
/**
|
|
* Get default fees from JenisKartu table
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getDefaultFees()
|
|
: array
|
|
{
|
|
return JenisKartu::getDefaultFees();
|
|
}
|
|
|
|
/**
|
|
* Determine fee for a card based on its type
|
|
*
|
|
* @param Atmcard $card
|
|
*
|
|
* @return int
|
|
*/
|
|
private function determineCardFee(Atmcard $card)
|
|
: int
|
|
{
|
|
if ($card->fee) {
|
|
return $card->fee;
|
|
}
|
|
|
|
$defaultFees = $this->getDefaultFees();
|
|
return $defaultFees[$card->ctdesc] ?? 0;
|
|
}
|
|
|
|
/**
|
|
* Create CSV row data for a card
|
|
*
|
|
* @param Atmcard $card
|
|
* @param int $fee
|
|
*
|
|
* @return array
|
|
*/
|
|
private function createCsvRow(Atmcard $card, int $fee)
|
|
: array
|
|
{
|
|
$today = date('Ymd');
|
|
|
|
return [
|
|
'',
|
|
$card->accflag,
|
|
$card->currency ?? 'IDR',
|
|
$fee,
|
|
'PL65129',
|
|
'',
|
|
'',
|
|
$card->branch,
|
|
$today,
|
|
$today,
|
|
'',
|
|
'',
|
|
'ADMIN FEE ATM::' . $card->crdno . '::' . $today,
|
|
'',
|
|
'',
|
|
'',
|
|
'',
|
|
'',
|
|
'',
|
|
'ACAT'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Remove double quotes from CSV file
|
|
*
|
|
* @param string $filename
|
|
*
|
|
* @return void
|
|
*/
|
|
private function cleanupCsvFile(string $filename)
|
|
: void
|
|
{
|
|
$fileContent = file_get_contents($filename);
|
|
if ($fileContent === false) {
|
|
throw new RuntimeException("Tidak dapat membaca file CSV: $filename");
|
|
}
|
|
|
|
$fileContent = str_replace('"', '', $fileContent);
|
|
|
|
if (file_put_contents($filename, $fileContent) === false) {
|
|
throw new RuntimeException("Tidak dapat menulis ke file CSV: $filename");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Upload the generated CSV file to SFTP server
|
|
*
|
|
* @param string $localFilePath Path to the local CSV file
|
|
*
|
|
* @return bool True if upload successful, false otherwise
|
|
*/
|
|
private function uploadToSftpKartu(string $localFilePath, string $branch = ''): bool
|
|
{
|
|
try {
|
|
// 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
|
|
$remotePath = env('BIAYA_KARTU_REMOTE_PATH', '/');
|
|
|
|
// Add branch directory if provided
|
|
if (!empty($branch)) {
|
|
$remotePath = rtrim($remotePath, '/') . '/' . $branch;
|
|
|
|
// Create branch directory if it doesn't exist
|
|
if (!$disk->exists($remotePath)) {
|
|
$disk->makeDirectory($remotePath);
|
|
}
|
|
}
|
|
|
|
$remoteFilePath = rtrim($remotePath, '/') . '/' . $filename;
|
|
|
|
// Upload file ke server SFTP
|
|
$result = $disk->put($remoteFilePath, $fileContent);
|
|
|
|
if ($result) {
|
|
$this->updateSftpLogSuccess($branch);
|
|
Log::info("File CSV biaya kartu ATM untuk cabang {$branch} berhasil diunggah ke SFTP: {$remoteFilePath}");
|
|
return true;
|
|
} else {
|
|
$this->updateSftpLogFailed("Gagal mengunggah file CSV biaya kartu ATM ke SFTP: {$remoteFilePath}");
|
|
|
|
Log::error("Gagal mengunggah file CSV biaya kartu ATM untuk cabang {$branch} ke SFTP: {$remoteFilePath}");
|
|
return false;
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->updateSftpLogFailed($e->getMessage());
|
|
|
|
Log::error("Error saat mengunggah file ke SFTP untuk cabang {$branch}: " . $e->getMessage(), [
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine(),
|
|
'periode' => $this->periode
|
|
]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function updateSftpLogSuccess(string $branch = ''): void
|
|
{
|
|
$message = 'File berhasil diupload ke SFTP';
|
|
if (!empty($branch)) {
|
|
$message .= " untuk cabang {$branch}";
|
|
}
|
|
|
|
$this->syncLog->update([
|
|
'is_ftp' => true,
|
|
'ftp_at' => Carbon::now(),
|
|
'ftp_notes' => $message,
|
|
'ftp_destination' => env('SFTP_KARTU_HOST', '/'),
|
|
]);
|
|
|
|
Log::info($message . ' untuk periode ' . $this->periode);
|
|
}
|
|
|
|
/**
|
|
* Update log saat upload SFTP dimulai
|
|
*/
|
|
private function updateSftpLogStart()
|
|
: void
|
|
{
|
|
$this->syncLog->update([
|
|
'ftp_notes' => 'Upload ke SFTP untuk file periode ' . $this->periode . ' dimulai',
|
|
'is_ftp' => false,
|
|
'ftp_at' => null,
|
|
]);
|
|
|
|
Log::info('Memulai upload ke SFTP untuk periode ' . $this->periode);
|
|
}
|
|
|
|
/**
|
|
* Update log saat upload SFTP gagal
|
|
*/
|
|
private function updateSftpLogFailed($errorMessage)
|
|
: void
|
|
{
|
|
$this->syncLog->update([
|
|
'is_ftp' => false,
|
|
'ftp_notes' => 'Upload ke SFTP gagal: ' . $errorMessage
|
|
]);
|
|
|
|
Log::error('Upload ke SFTP gagal: ' . $errorMessage);
|
|
}
|
|
|
|
/**
|
|
* Update log saat pembuatan CSV berhasil
|
|
*/
|
|
private function updateCsvLogSuccess($results): void
|
|
{
|
|
foreach ($results as $result) {
|
|
// Get file info
|
|
$fileInfo = pathinfo($result['localFilePath']);
|
|
$fileName = $fileInfo['basename'];
|
|
|
|
$this->syncLog->update([
|
|
'is_csv' => true,
|
|
'csv_at' => Carbon::now(),
|
|
'csv_notes' => 'File CSV untuk cabang ' . $result['branch'] . ' berhasil dibuat: ' . $fileName,
|
|
'file_path' => $result['localFilePath'],
|
|
'file_name' => $fileName,
|
|
]);
|
|
|
|
Log::info('File CSV untuk cabang ' . $result['branch'] . ' berhasil dibuat: ' . $result['localFilePath']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update log saat pembuatan CSV gagal
|
|
*/
|
|
private function updateCsvLogFailed($errorMessage)
|
|
: void
|
|
{
|
|
$this->syncLog->update([
|
|
'is_csv' => false,
|
|
'csv_notes' => '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;
|
|
}
|
|
}
|
|
}
|