diff --git a/app/Jobs/GenerateMultiAccountPdfJob.php b/app/Jobs/GenerateMultiAccountPdfJob.php index 35f0b56..8b7d354 100644 --- a/app/Jobs/GenerateMultiAccountPdfJob.php +++ b/app/Jobs/GenerateMultiAccountPdfJob.php @@ -54,7 +54,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue $this->accounts = $accounts; $this->period = $period; $this->clientName = $clientName; - + // Calculate period dates using same logic as ExportStatementPeriodJob $this->calculatePeriodDates(); } @@ -78,7 +78,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue // 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'), @@ -92,7 +92,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue public function handle(): void { try { - + Log::info('Starting multi account PDF generation', [ 'statement_id' => $this->statement->id, 'total_accounts' => $this->accounts->count(), @@ -112,13 +112,13 @@ class GenerateMultiAccountPdfJob implements ShouldQueue 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) { @@ -127,7 +127,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue 'account_number' => $account->account_number, 'error' => $e->getMessage() ]; - + Log::error('Failed to generate PDF for account', [ 'account_number' => $account->account_number, 'error' => $e->getMessage() @@ -153,7 +153,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue 'error_message' => !empty($errors) ? json_encode($errors) : null ]); - + Log::info('Multi account PDF generation completed', [ 'statement_id' => $this->statement->id, 'success_count' => $successCount, @@ -162,7 +162,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue ]); } catch (Exception $e) { - + Log::error('Multi account PDF generation failed', [ 'statement_id' => $this->statement->id, 'error' => $e->getMessage(), @@ -195,23 +195,23 @@ class GenerateMultiAccountPdfJob implements ShouldQueue '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(); @@ -219,7 +219,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue $headerTableBg = file_exists($headerImagePath) ? base64_encode(file_get_contents($headerImagePath)) : null; - + // Render HTML $html = view('webstatement::statements.stmt', [ 'stmtEntries' => $stmtEntries, @@ -231,18 +231,18 @@ class GenerateMultiAccountPdfJob implements ShouldQueue '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() @@ -258,18 +258,18 @@ class GenerateMultiAccountPdfJob implements ShouldQueue 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; } } @@ -311,7 +311,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue 'account_number' => $criteria['account_number'], 'period' => $criteria['period'] ]); - + ProcessedStatement::where('account_number', $criteria['account_number']) ->where('period', $criteria['period']) ->delete(); @@ -469,7 +469,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue */ protected function getFormatNarrative($narr, $item) { - + $narrParam = TempStmtNarrParam::where('_id', $narr)->first(); if (!$narrParam) { @@ -582,7 +582,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue 'account_number' => $accountNumber, 'period' => $this->period ]); - + return ProcessedStatement::where('account_number', $accountNumber) ->where('period', $this->period) ->orderBy('sequence_no', 'ASC') @@ -604,19 +604,19 @@ class GenerateMultiAccountPdfJob implements ShouldQueue ->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]; } @@ -632,7 +632,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue if ($period === '202505') { return '20250510'; } - + // For periods after 202505, get last day of previous month if ($period > '202505') { $year = substr($period, 0, 4); @@ -640,7 +640,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue $firstDay = Carbon::createFromFormat('Ym', $period)->startOfMonth(); return $firstDay->copy()->subDay()->format('Ymd'); } - + return $period . '01'; } @@ -652,7 +652,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue protected function prepareImagesForPdf() { $images = []; - + $imagePaths = [ 'headerTableBg' => 'assets/media/images/bg-header-table.png', 'watermark' => 'assets/media/images/watermark.png', @@ -660,7 +660,7 @@ class GenerateMultiAccountPdfJob implements ShouldQueue '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)) { @@ -670,12 +670,12 @@ class GenerateMultiAccountPdfJob implements ShouldQueue Log::warning('Image file not found', ['path' => $fullPath]); } } - + return $images; } /** - * Create ZIP file dari multiple PDF files + * Create ZIP file dari multiple PDF files dengan password protection * * @param array $pdfFiles * @return string|null Path to ZIP file @@ -686,53 +686,71 @@ class GenerateMultiAccountPdfJob implements ShouldQueue $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}"); - + + // Get password from statement or use default + $password = $this->statement->password ?? config('webstatement.zip_password', 'statement123'); + $zip = new ZipArchive(); if ($zip->open($zipPath, ZipArchive::CREATE) !== TRUE) { throw new Exception('Cannot create ZIP file'); } - + + // Set password for the ZIP file + if (!empty($password)) { + $zip->setPassword($password); + Log::info('ZIP password protection enabled', [ + 'statement_id' => $this->statement->id, + 'zip_path' => $zipPath + ]); + } + foreach ($pdfFiles as $pdfFile) { if (file_exists($pdfFile)) { $filename = basename($pdfFile); $zip->addFile($pdfFile, $filename); + + // Set encryption for each file in ZIP + if (!empty($password)) { + $zip->setEncryptionName($filename, ZipArchive::EM_AES_256); + } } } - + $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', [ + + Log::info('ZIP file created successfully with password protection', [ 'zip_path' => $zipPath, 'pdf_count' => count($pdfFiles), - 'statement_id' => $this->statement->id + 'statement_id' => $this->statement->id, + 'password_protected' => !empty($password) ]); - + // 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; } } -} \ No newline at end of file +} diff --git a/config/config.php b/config/config.php index 83c0e2e..40f5a20 100644 --- a/config/config.php +++ b/config/config.php @@ -2,4 +2,7 @@ return [ 'name' => 'Webstatement', + + // ZIP file password configuration + 'zip_password' => env('WEBSTATEMENT_ZIP_PASSWORD', 'statement123'), ];