Perubahan yang dilakukan: - Menambahkan PDFPasswordProtect::encrypt di dalam ExportStatementPeriodJob. - Mengikuti pola implementasi yang telah digunakan pada CombinePdfJob. - PDF statement kini otomatis diproteksi menggunakan password. - Password diambil dari konfigurasi: webstatement.pdf_password. - Menambahkan logging untuk memantau proses proteksi PDF. - Menjamin pengelolaan file sementara berjalan aman dan rapi. - Menjaga kompatibilitas ke belakang (backward compatible) dengan sistem PDF yang sudah ada. Tujuan perubahan: - Meningkatkan keamanan file PDF dengan proteksi password standar perusahaan. - Memastikan proses enkripsi berjalan otomatis tanpa mengubah alur penggunaan yang ada. - Memberikan visibilitas terhadap proses proteksi melalui log sistem.
738 lines
25 KiB
PHP
738 lines
25 KiB
PHP
<?php
|
|
|
|
namespace Modules\Webstatement\Jobs;
|
|
|
|
use Exception;
|
|
use ZipArchive;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Support\Facades\{
|
|
DB,
|
|
Log,
|
|
Storage
|
|
};
|
|
use Spatie\Browsershot\Browsershot;
|
|
use Modules\Basicdata\Models\Branch;
|
|
use Illuminate\Queue\{
|
|
SerializesModels,
|
|
InteractsWithQueue
|
|
};
|
|
use Modules\Webstatement\Models\{
|
|
StmtEntry,
|
|
AccountBalance,
|
|
PrintStatementLog,
|
|
ProcessedStatement,
|
|
TempStmtNarrParam,
|
|
TempStmtNarrFormat
|
|
};
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
|
|
class GenerateMultiAccountPdfJob implements ShouldQueue
|
|
{
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
protected $statement;
|
|
protected $accounts;
|
|
protected $period;
|
|
protected $clientName;
|
|
protected $chunkSize = 10; // Process 10 accounts at a time
|
|
protected $startDate;
|
|
protected $endDate;
|
|
|
|
/**
|
|
* Create a new job instance.
|
|
*
|
|
* @param PrintStatementLog $statement
|
|
* @param \Illuminate\Database\Eloquent\Collection $accounts
|
|
* @param string $period
|
|
* @param string $clientName
|
|
*/
|
|
public function __construct($statement, $accounts, $period, $clientName)
|
|
{
|
|
$this->statement = $statement;
|
|
$this->accounts = $accounts;
|
|
$this->period = $period;
|
|
$this->clientName = $clientName;
|
|
|
|
// Calculate period dates using same logic as ExportStatementPeriodJob
|
|
$this->calculatePeriodDates();
|
|
}
|
|
|
|
/**
|
|
* Calculate start and end dates for the given period
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*/
|
|
private function calculatePeriodDates(): void
|
|
{
|
|
$year = substr($this->period, 0, 4);
|
|
$month = substr($this->period, 4, 2);
|
|
|
|
// Special case for May 2025 - start from 12th
|
|
if ($this->period === '202505') {
|
|
$this->startDate = Carbon::createFromDate($year, $month, 12)->startOfDay();
|
|
} else {
|
|
// For all other periods, start from 1st of the month
|
|
$this->startDate = Carbon::createFromDate($year, $month, 1)->startOfDay();
|
|
}
|
|
|
|
// End date is always the last day of the month
|
|
$this->endDate = Carbon::createFromDate($year, $month, 1)->endOfMonth()->endOfDay();
|
|
|
|
Log::info('Period dates calculated for PDF generation', [
|
|
'period' => $this->period,
|
|
'start_date' => $this->startDate->format('Y-m-d'),
|
|
'end_date' => $this->endDate->format('Y-m-d')
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Execute the job.
|
|
*/
|
|
public function handle(): void
|
|
{
|
|
try {
|
|
|
|
Log::info('Starting multi account PDF generation', [
|
|
'statement_id' => $this->statement->id,
|
|
'total_accounts' => $this->accounts->count(),
|
|
'period' => $this->period,
|
|
'date_range' => $this->startDate->format('Y-m-d') . ' to ' . $this->endDate->format('Y-m-d')
|
|
]);
|
|
|
|
$pdfFiles = [];
|
|
$successCount = 0;
|
|
$failedCount = 0;
|
|
$errors = [];
|
|
|
|
// Process each account
|
|
foreach ($this->accounts as $account) {
|
|
try {
|
|
$pdfPath = $this->generateAccountPdf($account);
|
|
if ($pdfPath) {
|
|
$pdfFiles[] = $pdfPath;
|
|
$successCount++;
|
|
|
|
Log::info('PDF generated successfully for account', [
|
|
'account_number' => $account->account_number,
|
|
'pdf_path' => $pdfPath
|
|
]);
|
|
}
|
|
|
|
// Memory cleanup after each account
|
|
gc_collect_cycles();
|
|
} catch (Exception $e) {
|
|
$failedCount++;
|
|
$errors[] = [
|
|
'account_number' => $account->account_number,
|
|
'error' => $e->getMessage()
|
|
];
|
|
|
|
Log::error('Failed to generate PDF for account', [
|
|
'account_number' => $account->account_number,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
// Create ZIP file if there are PDFs
|
|
$zipPath = null;
|
|
if (!empty($pdfFiles)) {
|
|
$zipPath = $this->createZipFile($pdfFiles);
|
|
}
|
|
|
|
// Update statement log
|
|
$this->statement->update([
|
|
'processed_accounts' => $this->accounts->count(),
|
|
'success_count' => $successCount,
|
|
'failed_count' => $failedCount,
|
|
'status' => $failedCount > 0 ? 'completed_with_errors' : 'completed',
|
|
'completed_at' => now(),
|
|
'is_available' => $zipPath ? true : false,
|
|
'is_generated' => $zipPath ? true : false,
|
|
'error_message' => !empty($errors) ? json_encode($errors) : null
|
|
]);
|
|
|
|
|
|
Log::info('Multi account PDF generation completed', [
|
|
'statement_id' => $this->statement->id,
|
|
'success_count' => $successCount,
|
|
'failed_count' => $failedCount,
|
|
'zip_path' => $zipPath
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
|
|
Log::error('Multi account PDF generation failed', [
|
|
'statement_id' => $this->statement->id,
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
|
|
// Update statement with error status
|
|
$this->statement->update([
|
|
'status' => 'failed',
|
|
'completed_at' => now(),
|
|
'error_message' => $e->getMessage()
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate PDF untuk satu account
|
|
* Menggunakan data dari ProcessedStatement yang sudah diproses oleh ExportStatementPeriodJob
|
|
*
|
|
* @param Account $account
|
|
* @return string|null Path to generated PDF
|
|
*/
|
|
protected function generateAccountPdf($account)
|
|
{
|
|
try {
|
|
// Prepare account query untuk processing
|
|
$accountQuery = [
|
|
'account_number' => $account->account_number,
|
|
'period' => $this->period
|
|
];
|
|
|
|
// Get total entry count
|
|
$totalCount = $this->getTotalEntryCount($account->account_number);
|
|
|
|
// Delete existing processed data dan process ulang
|
|
$this->deleteExistingProcessedData($accountQuery);
|
|
$this->processAndSaveStatementEntries($account, $totalCount);
|
|
|
|
// Get statement entries from ProcessedStatement (data yang sudah diproses)
|
|
$stmtEntries = $this->getProcessedStatementEntries($account->account_number);
|
|
|
|
// Get saldo awal bulan menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
$saldoAwalBulan = $this->getSaldoAwalBulan($account->account_number);
|
|
|
|
// Get branch info
|
|
$branch = Branch::where('code', $account->branch_code)->first();
|
|
|
|
// Prepare images for PDF
|
|
$images = $this->prepareImagesForPdf();
|
|
|
|
$headerImagePath = public_path('assets/media/images/bg-header-table.png');
|
|
$headerTableBg = file_exists($headerImagePath)
|
|
? base64_encode(file_get_contents($headerImagePath))
|
|
: null;
|
|
|
|
// Render HTML
|
|
$html = view('webstatement::statements.stmt', [
|
|
'stmtEntries' => $stmtEntries,
|
|
'account' => $account,
|
|
'customer' => $account->customer,
|
|
'images' => $images,
|
|
'branch' => $branch,
|
|
'period' => $this->period,
|
|
'saldoAwalBulan' => $saldoAwalBulan,
|
|
'headerTableBg' => $headerTableBg,
|
|
])->render();
|
|
|
|
// Generate PDF filename
|
|
$filename = "statement_{$account->account_number}_{$this->period}_" . now()->format('YmdHis') . '.pdf';
|
|
$storagePath = "statements/{$this->period}/multi_account/{$this->statement->id}";
|
|
$fullStoragePath = "{$storagePath}/{$filename}";
|
|
|
|
// Ensure directory exists
|
|
Storage::disk('local')->makeDirectory($storagePath);
|
|
|
|
// Generate PDF path
|
|
$pdfPath = storage_path("app/{$fullStoragePath}");
|
|
|
|
// Generate PDF using Browsershot
|
|
Browsershot::html($html)
|
|
->showBackground()
|
|
->setOption('addStyleTag', json_encode(['content' => '@page { margin: 0; }']))
|
|
->setOption('protocolTimeout', 2147483) // 2 menit timeout
|
|
->format('A4')
|
|
->margins(0, 0, 0, 0)
|
|
->waitUntil('load')
|
|
->timeout(2147483)
|
|
->save($pdfPath);
|
|
|
|
// Verify file was created
|
|
if (!file_exists($pdfPath)) {
|
|
throw new Exception('PDF file was not created');
|
|
}
|
|
|
|
// Clear variables to free memory
|
|
unset($html, $stmtEntries, $images);
|
|
|
|
return $pdfPath;
|
|
|
|
} catch (Exception $e) {
|
|
Log::error('Failed to generate PDF for account', [
|
|
'account_number' => $account->account_number,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get total entry count untuk account
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param string $accountNumber
|
|
* @return int
|
|
*/
|
|
protected function getTotalEntryCount($accountNumber): int
|
|
{
|
|
$query = StmtEntry::where('account_number', $accountNumber)
|
|
->whereBetween('booking_date', [
|
|
$this->startDate->format('Ymd'),
|
|
$this->endDate->format('Ymd')
|
|
]);
|
|
|
|
Log::info("Getting total entry count for PDF generation", [
|
|
'account' => $accountNumber,
|
|
'start_date' => $this->startDate->format('Ymd'),
|
|
'end_date' => $this->endDate->format('Ymd')
|
|
]);
|
|
|
|
return $query->count();
|
|
}
|
|
|
|
/**
|
|
* Delete existing processed data untuk account
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param array $criteria
|
|
* @return void
|
|
*/
|
|
protected function deleteExistingProcessedData(array $criteria): void
|
|
{
|
|
Log::info('Deleting existing processed data for PDF generation', [
|
|
'account_number' => $criteria['account_number'],
|
|
'period' => $criteria['period']
|
|
]);
|
|
|
|
ProcessedStatement::where('account_number', $criteria['account_number'])
|
|
->where('period', $criteria['period'])
|
|
->delete();
|
|
}
|
|
|
|
/**
|
|
* Process dan save statement entries untuk account
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param Account $account
|
|
* @param int $totalCount
|
|
* @return void
|
|
*/
|
|
protected function processAndSaveStatementEntries($account, int $totalCount): void
|
|
{
|
|
// Get saldo awal dari AccountBalance
|
|
$saldoAwalBulan = $this->getSaldoAwalBulan($account->account_number);
|
|
$runningBalance = (float) $saldoAwalBulan->actual_balance;
|
|
$globalSequence = 0;
|
|
|
|
Log::info("Processing {$totalCount} statement entries for PDF generation", [
|
|
'account_number' => $account->account_number,
|
|
'starting_balance' => $runningBalance
|
|
]);
|
|
|
|
StmtEntry::with(['ft', 'transaction'])
|
|
->where('account_number', $account->account_number)
|
|
->whereBetween('booking_date', [
|
|
$this->startDate->format('Ymd'),
|
|
$this->endDate->format('Ymd')
|
|
])
|
|
->orderBy('date_time', 'ASC')
|
|
->orderBy('trans_reference', 'ASC')
|
|
->chunk(1000, function ($entries) use (&$runningBalance, &$globalSequence, $account) {
|
|
$processedData = $this->prepareProcessedData($entries, $runningBalance, $globalSequence, $account->account_number);
|
|
|
|
if (!empty($processedData)) {
|
|
DB::table('processed_statements')->insert($processedData);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Prepare processed data untuk batch insert
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param $entries
|
|
* @param float $runningBalance
|
|
* @param int $globalSequence
|
|
* @param string $accountNumber
|
|
* @return array
|
|
*/
|
|
protected function prepareProcessedData($entries, &$runningBalance, &$globalSequence, $accountNumber): array
|
|
{
|
|
$processedData = [];
|
|
|
|
foreach ($entries as $item) {
|
|
$globalSequence++;
|
|
$runningBalance += (float) $item->amount_lcy;
|
|
|
|
$actualDate = $this->formatActualDate($item);
|
|
|
|
$processedData[] = [
|
|
'account_number' => $accountNumber,
|
|
'period' => $this->period,
|
|
'sequence_no' => $globalSequence,
|
|
'transaction_date' => $item->booking_date,
|
|
'reference_number' => $item->trans_reference,
|
|
'transaction_amount' => $item->amount_lcy,
|
|
'transaction_type' => $item->amount_lcy < 0 ? 'D' : 'C',
|
|
'description' => $this->generateNarrative($item),
|
|
'end_balance' => $runningBalance,
|
|
'actual_date' => $actualDate,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
];
|
|
}
|
|
|
|
return $processedData;
|
|
}
|
|
|
|
/**
|
|
* Format actual date dari item
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param $item
|
|
* @return string
|
|
*/
|
|
protected function formatActualDate($item): string
|
|
{
|
|
try {
|
|
$prefix = substr($item->trans_reference ?? '', 0, 2);
|
|
$relationMap = [
|
|
'FT' => 'ft',
|
|
'TT' => 'tt',
|
|
'DC' => 'dc',
|
|
'AA' => 'aa'
|
|
];
|
|
|
|
$datetime = $item->date_time;
|
|
if (isset($relationMap[$prefix])) {
|
|
$relation = $relationMap[$prefix];
|
|
$datetime = $item->$relation?->date_time ?? $datetime;
|
|
}
|
|
|
|
return Carbon::createFromFormat(
|
|
'ymdHi',
|
|
$datetime
|
|
)->format('d/m/Y H:i');
|
|
} catch (Exception $e) {
|
|
Log::warning("Error formatting actual date: " . $e->getMessage());
|
|
return Carbon::now()->format('d/m/Y H:i');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate narrative untuk statement entry
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param $item
|
|
* @return string
|
|
*/
|
|
protected function generateNarrative($item)
|
|
{
|
|
$narr = [];
|
|
|
|
if ($item->transaction) {
|
|
if ($item->transaction->stmt_narr) {
|
|
$narr[] = $item->transaction->stmt_narr;
|
|
}
|
|
if ($item->narrative) {
|
|
$narr[] = $item->narrative;
|
|
}
|
|
if ($item->transaction->narr_type) {
|
|
$narr[] = $this->getFormatNarrative($item->transaction->narr_type, $item);
|
|
}
|
|
} else if ($item->narrative) {
|
|
$narr[] = $item->narrative;
|
|
}
|
|
|
|
if ($item->ft?->recipt_no) {
|
|
$narr[] = 'Receipt No: ' . $item->ft->recipt_no;
|
|
}
|
|
|
|
return implode(' ', array_filter($narr));
|
|
}
|
|
|
|
/**
|
|
* Get formatted narrative berdasarkan narrative type
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param $narr
|
|
* @param $item
|
|
* @return string
|
|
*/
|
|
protected function getFormatNarrative($narr, $item)
|
|
{
|
|
|
|
$narrParam = TempStmtNarrParam::where('_id', $narr)->first();
|
|
|
|
if (!$narrParam) {
|
|
return '';
|
|
}
|
|
|
|
$fmt = '';
|
|
if ($narrParam->_id == 'FTIN') {
|
|
$fmt = 'FT.IN';
|
|
} else if ($narrParam->_id == 'FTOUT') {
|
|
$fmt = 'FT.OUT';
|
|
} else if ($narrParam->_id == 'TTTRFOUT') {
|
|
$fmt = 'TT.O.TRF';
|
|
} else if ($narrParam->_id == 'TTTRFIN') {
|
|
$fmt = 'TT.I.TRF';
|
|
} else if ($narrParam->_id == 'APITRX'){
|
|
$fmt = 'API.TSEL';
|
|
} else if ($narrParam->_id == 'ONUSCR'){
|
|
$fmt = 'ONUS.CR';
|
|
} else if ($narrParam->_id == 'ONUSDR'){
|
|
$fmt = 'ONUS.DR';
|
|
}else {
|
|
$fmt = $narrParam->_id;
|
|
}
|
|
|
|
$narrFormat = TempStmtNarrFormat::where('_id', $fmt)->first();
|
|
|
|
if (!$narrFormat) {
|
|
return '';
|
|
}
|
|
|
|
// Get the format string from the database
|
|
$formatString = $narrFormat->text_data ?? '';
|
|
|
|
// Parse the format string
|
|
// Split by the separator ']'
|
|
$parts = explode(']', $formatString);
|
|
|
|
$result = '';
|
|
|
|
foreach ($parts as $index => $part) {
|
|
if (empty($part)) {
|
|
continue;
|
|
}
|
|
|
|
if ($index === 0) {
|
|
// For the first part, take only what's before the '!'
|
|
$splitPart = explode('!', $part);
|
|
if (count($splitPart) > 0) {
|
|
// Remove quotes, backslashes, and other escape characters
|
|
$cleanPart = trim($splitPart[0]).' ';
|
|
// Remove quotes at the beginning and end
|
|
$cleanPart = preg_replace('/^["\'\\\\]+|["\'\\\\]+$/', '', $cleanPart);
|
|
// Remove any remaining backslashes
|
|
$cleanPart = str_replace('\\', '', $cleanPart);
|
|
// Remove any remaining quotes
|
|
$cleanPart = str_replace('"', '', $cleanPart);
|
|
$result .= $cleanPart;
|
|
}
|
|
} else {
|
|
// For other parts, these are field placeholders
|
|
$fieldName = strtolower(str_replace('.', '_', $part));
|
|
|
|
// Get the corresponding parameter value from narrParam
|
|
$paramValue = null;
|
|
|
|
// Check if the field exists as a property in narrParam
|
|
if (property_exists($narrParam, $fieldName)) {
|
|
$paramValue = $narrParam->$fieldName;
|
|
} else if (isset($narrParam->$fieldName)) {
|
|
$paramValue = $narrParam->$fieldName;
|
|
}
|
|
|
|
// If we found a value, add it to the result
|
|
if ($paramValue !== null) {
|
|
$result .= $paramValue;
|
|
} else {
|
|
// If no value found, try to use the original field name as a fallback
|
|
if ($fieldName !== 'recipt_no') {
|
|
$prefix = substr($item->trans_reference ?? '', 0, 2);
|
|
$relationMap = [
|
|
'FT' => 'ft',
|
|
'TT' => 'tt',
|
|
'DC' => 'dc',
|
|
'AA' => 'aa'
|
|
];
|
|
|
|
if (isset($relationMap[$prefix])) {
|
|
$relation = $relationMap[$prefix];
|
|
$result .= ($item->$relation?->$fieldName ?? '') . ' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return str_replace('<NL>', ' ', $result);
|
|
}
|
|
|
|
/**
|
|
* Get processed statement entries untuk account
|
|
* Menggunakan data dari tabel ProcessedStatement yang sudah diproses
|
|
*
|
|
* @param string $accountNumber
|
|
* @return \Illuminate\Database\Eloquent\Collection
|
|
*/
|
|
protected function getProcessedStatementEntries($accountNumber)
|
|
{
|
|
Log::info('Getting processed statement entries', [
|
|
'account_number' => $accountNumber,
|
|
'period' => $this->period
|
|
]);
|
|
|
|
return ProcessedStatement::where('account_number', $accountNumber)
|
|
->where('period', $this->period)
|
|
->orderBy('sequence_no', 'ASC')
|
|
->get();
|
|
}
|
|
|
|
/**
|
|
* Get saldo awal bulan untuk account
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param string $accountNumber
|
|
* @return object
|
|
*/
|
|
protected function getSaldoAwalBulan($accountNumber)
|
|
{
|
|
// Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
// Ambil saldo dari ProcessedStatement entry pertama dikurangi transaction_amount
|
|
$firstEntry = ProcessedStatement::where('account_number', $accountNumber)
|
|
->where('period', $this->period)
|
|
->orderBy('sequence_no', 'ASC')
|
|
->first();
|
|
|
|
if ($firstEntry) {
|
|
$saldoAwal = $firstEntry->end_balance - $firstEntry->transaction_amount;
|
|
return (object) ['actual_balance' => $saldoAwal];
|
|
}
|
|
|
|
// Fallback ke AccountBalance jika tidak ada ProcessedStatement
|
|
$saldoPeriod = $this->calculateSaldoPeriod($this->period);
|
|
|
|
$saldo = AccountBalance::where('account_number', $accountNumber)
|
|
->where('period', $saldoPeriod)
|
|
->first();
|
|
|
|
return $saldo ?: (object) ['actual_balance' => 0];
|
|
}
|
|
|
|
/**
|
|
* Calculate saldo period berdasarkan aturan bisnis
|
|
* Menggunakan logika yang sama dengan ExportStatementPeriodJob
|
|
*
|
|
* @param string $period
|
|
* @return string
|
|
*/
|
|
protected function calculateSaldoPeriod($period)
|
|
{
|
|
if ($period === '202505') {
|
|
return '20250510';
|
|
}
|
|
|
|
// For periods after 202505, get last day of previous month
|
|
if ($period > '202505') {
|
|
$year = substr($period, 0, 4);
|
|
$month = substr($period, 4, 2);
|
|
$firstDay = Carbon::createFromFormat('Ym', $period)->startOfMonth();
|
|
return $firstDay->copy()->subDay()->format('Ymd');
|
|
}
|
|
|
|
return $period . '01';
|
|
}
|
|
|
|
/**
|
|
* Prepare images as base64 for PDF
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function prepareImagesForPdf()
|
|
{
|
|
$images = [];
|
|
|
|
$imagePaths = [
|
|
'headerTableBg' => 'assets/media/images/bg-header-table.png',
|
|
'watermark' => 'assets/media/images/watermark.png',
|
|
'logoArthagraha' => 'assets/media/images/logo-arthagraha.png',
|
|
'logoAgi' => 'assets/media/images/logo-agi.png',
|
|
'bannerFooter' => 'assets/media/images/banner-footer.png'
|
|
];
|
|
|
|
foreach ($imagePaths as $key => $path) {
|
|
$fullPath = public_path($path);
|
|
if (file_exists($fullPath)) {
|
|
$images[$key] = base64_encode(file_get_contents($fullPath));
|
|
} else {
|
|
$images[$key] = null;
|
|
Log::warning('Image file not found', ['path' => $fullPath]);
|
|
}
|
|
}
|
|
|
|
return $images;
|
|
}
|
|
|
|
/**
|
|
* Create ZIP file dari multiple PDF files
|
|
*
|
|
* @param array $pdfFiles
|
|
* @return string|null Path to ZIP file
|
|
*/
|
|
protected function createZipFile($pdfFiles)
|
|
{
|
|
try {
|
|
$zipFilename = "statements_{$this->period}_multi_account_{$this->statement->id}_" . now()->format('YmdHis') . '.zip';
|
|
$zipStoragePath = "statements/{$this->period}/multi_account/{$this->statement->id}";
|
|
$fullZipPath = "{$zipStoragePath}/{$zipFilename}";
|
|
|
|
// Ensure directory exists
|
|
Storage::disk('local')->makeDirectory($zipStoragePath);
|
|
|
|
$zipPath = storage_path("app/{$fullZipPath}");
|
|
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($zipPath, ZipArchive::CREATE) !== TRUE) {
|
|
throw new Exception('Cannot create ZIP file');
|
|
}
|
|
|
|
foreach ($pdfFiles as $pdfFile) {
|
|
if (file_exists($pdfFile)) {
|
|
$filename = basename($pdfFile);
|
|
$zip->addFile($pdfFile, $filename);
|
|
}
|
|
}
|
|
|
|
$zip->close();
|
|
|
|
// Verify ZIP file was created
|
|
if (!file_exists($zipPath)) {
|
|
throw new Exception('ZIP file was not created');
|
|
}
|
|
|
|
Log::info('ZIP file created successfully', [
|
|
'zip_path' => $zipPath,
|
|
'pdf_count' => count($pdfFiles),
|
|
'statement_id' => $this->statement->id
|
|
]);
|
|
|
|
// Clean up individual PDF files after creating ZIP
|
|
foreach ($pdfFiles as $pdfFile) {
|
|
if (file_exists($pdfFile)) {
|
|
unlink($pdfFile);
|
|
}
|
|
}
|
|
|
|
return $zipPath;
|
|
|
|
} catch (Exception $e) {
|
|
Log::error('Failed to create ZIP file', [
|
|
'error' => $e->getMessage(),
|
|
'statement_id' => $this->statement->id
|
|
]);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
} |