diff --git a/app/Http/Controllers/PrintStatementController.php b/app/Http/Controllers/PrintStatementController.php index 9dc09d3..724bbbb 100644 --- a/app/Http/Controllers/PrintStatementController.php +++ b/app/Http/Controllers/PrintStatementController.php @@ -16,7 +16,7 @@ use Modules\Webstatement\Models\{Account, AccountBalance, PrintStatementLog, Pro use Spatie\Browsershot\Browsershot; use ZipArchive; ini_set('memory_limit', '2G'); // Atau '1G' untuk data yang sangat besar -ini_set('max_execution_time', 300000); +ini_set('max_execution_time', 300000); class PrintStatementController extends Controller { @@ -49,7 +49,7 @@ ini_set('max_execution_time', 300000); if($request->input('branch_code') && !empty($request->input('stmt_sent_type'))){ $request_type = 'multi_account'; // Default untuk request manual } - + if($request_type=='single_account'){ $account = Account::where('account_number', $accountNumber)->first(); if ($account) { @@ -101,7 +101,7 @@ ini_set('max_execution_time', 300000); $validated['created_by'] = Auth::id(); $validated['ip_address'] = $request->ip(); $validated['user_agent'] = $request->userAgent(); - + $validated['status'] = 'pending'; // Status awal $validated['authorization_status'] = 'approved'; // Status otorisasi awal $validated['total_accounts'] = 1; // Untuk single account @@ -110,7 +110,7 @@ ini_set('max_execution_time', 300000); $validated['failed_count'] = 0; $validated['stmt_sent_type'] = $request->input('stmt_sent_type') ? implode(",",$request->input('stmt_sent_type')) : ''; $validated['branch_code'] = $validated['branch_code'] ?? $branch_code; // Awal tidak tersedia - + // Create the statement log $statement = PrintStatementLog::create($validated); @@ -825,9 +825,6 @@ ini_set('max_execution_time', 300000); $period = $statement->period_from; $format='pdf'; - - - // Generate nama file PDF $filename = $this->generatePdfFileName($norek, $period); @@ -967,10 +964,11 @@ ini_set('max_execution_time', 300000); Browsershot::html($html) ->showBackground() ->setOption('addStyleTag', json_encode(['content' => '@page { margin: 0; }'])) + ->setOption('protocolTimeout', 2147483) // 120000 ms = 2 menit ->format('A4') ->margins(0, 0, 0, 0) - ->waitUntilNetworkIdle() - ->timeout(6000) + ->waitUntil('load') + ->timeout(2147483) ->save($tempPath); // Verifikasi file berhasil dibuat @@ -1281,30 +1279,30 @@ ini_set('max_execution_time', 300000); try { // DB::beginTransaction(); - + Log::info('Starting statement processing', [ 'statement_id' => $statement->id, 'request_type' => $statement->request_type, 'stmt_sent_type' => $statement->stmt_sent_type, 'branch_code' => $statement->branch_code ]); - - + + if ($statement->request_type === 'multi_account') { return $this->processMultiAccountStatement($statement); } else { return $this->processSingleAccountStatement($statement); } - + } catch (\Exception $e) { //DB::rollBack(); - + Log::error('Failed to process statement', [ 'error' => $e->getMessage(), 'statement_id' => $statement->id, 'trace' => $e->getTraceAsString() ]); - + return response()->json([ 'success' => false, 'message' => 'Failed to process statement', @@ -1323,22 +1321,22 @@ ini_set('max_execution_time', 300000); { try { $period = $statement->period_from ?? date('Ym'); - + // Validasi stmt_sent_type if (empty($statement->stmt_sent_type)) { throw new \Exception('stmt_sent_type is required for multi account processing'); } - + // Decode stmt_sent_type jika dalam format JSON array $stmtSentTypes = explode(',', $statement->stmt_sent_type); - + Log::info('Processing multi account statement', [ 'statement_id' => $statement->id, 'branch_code' => $statement->branch_code, 'stmt_sent_types' => $stmtSentTypes, 'period' => $period ]); - + $clientName = $statement->branch_code.'_'.$period.'_';//.implode('_'.$stmtSentTypes); @@ -1352,13 +1350,13 @@ ini_set('max_execution_time', 300000); if ($accounts->isEmpty()) { throw new \Exception('No accounts found for the specified criteria'); } - + Log::info('Found accounts for processing', [ 'total_accounts' => $accounts->count(), 'branch_code' => $statement->branch_code, 'stmt_sent_types' => $stmtSentTypes ]); - + // Update statement log dengan informasi accounts $accountNumbers = $accounts->pluck('account_number')->toArray(); @@ -1368,7 +1366,7 @@ ini_set('max_execution_time', 300000); 'status' => 'processing', 'started_at' => now() ]); - + // Dispatch job untuk generate PDF multi account $job = GenerateMultiAccountPdfJob::dispatch( $statement, @@ -1376,16 +1374,16 @@ ini_set('max_execution_time', 300000); $period, $clientName ); - + DB::commit(); - + Log::info('Multi account PDF generation job dispatched', [ 'job_id' => $job->job_id ?? null, 'statement_id' => $statement->id, 'total_accounts' => $accounts->count(), 'period' => $period ]); - + return response()->json([ 'success' => true, 'message' => 'Multi account statement processing queued successfully', @@ -1398,16 +1396,16 @@ ini_set('max_execution_time', 300000); 'client_name' => $clientName ] ]); - + } catch (\Exception $e) { DB::rollBack(); - + Log::error('Failed to process multi account statement', [ 'error' => $e->getMessage(), 'statement_id' => $statement->id, 'trace' => $e->getTraceAsString() ]); - + throw $e; } } @@ -1491,24 +1489,24 @@ ini_set('max_execution_time', 300000); { try { $statement = PrintStatementLog::findOrFail($statementId); - + if ($statement->request_type !== 'multi_account') { return response()->json([ 'success' => false, 'message' => 'This statement is not a multi account request' ], 400); } - + if (!$statement->is_available) { return response()->json([ 'success' => false, 'message' => 'Statement files are not available for download' ], 404); } - + // Find ZIP file $zipFiles = Storage::disk('local')->files("statements/{$statement->period_from}/multi_account/{$statementId}"); - + $zipFile = null; foreach ($zipFiles as $file) { if (pathinfo($file, PATHINFO_EXTENSION) === 'zip') { @@ -1516,39 +1514,39 @@ ini_set('max_execution_time', 300000); break; } } - + if (!$zipFile || !Storage::disk('local')->exists($zipFile)) { return response()->json([ 'success' => false, 'message' => 'ZIP file not found' ], 404); } - + $zipPath = Storage::disk('local')->path($zipFile); $filename = basename($zipFile); - + // Update download status $statement->update([ 'is_downloaded' => true, 'downloaded_at' => now() ]); - + Log::info('Multi account ZIP downloaded', [ 'statement_id' => $statementId, 'zip_file' => $zipFile, 'user_id' => Auth::id() ]); - + return response()->download($zipPath, $filename, [ 'Content-Type' => 'application/zip' ]); - + } catch (Exception $e) { Log::error('Failed to download multi account ZIP', [ 'statement_id' => $statementId, 'error' => $e->getMessage() ]); - + return response()->json([ 'success' => false, 'message' => 'Failed to download ZIP file', diff --git a/app/Jobs/ExportStatementPeriodJob.php b/app/Jobs/ExportStatementPeriodJob.php index c2a5261..5035a0b 100644 --- a/app/Jobs/ExportStatementPeriodJob.php +++ b/app/Jobs/ExportStatementPeriodJob.php @@ -4,20 +4,32 @@ namespace Modules\Webstatement\Jobs; use Carbon\Carbon; use Exception; -use Illuminate\Bus\Queueable; +use Illuminate\Bus\{ + Queueable +}; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Queue\{ + InteractsWithQueue, + SerializesModels +}; +use Illuminate\Support\Facades\{ + DB, + Log, + Storage +}; +use Spatie\Browsershot\Browsershot; +use Modules\Webstatement\Models\{ + PrintStatementLog, + ProcessedStatement, + StmtEntry, + TempFundsTransfer, + TempStmtNarrFormat, + TempStmtNarrParam, + Account, + Customer +}; +use Modules\Basicdata\Models\Branch; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Storage; -use Modules\Webstatement\Models\PrintStatementLog; -use Modules\Webstatement\Models\ProcessedStatement; -use Modules\Webstatement\Models\StmtEntry; -use Modules\Webstatement\Models\TempFundsTransfer; -use Modules\Webstatement\Models\TempStmtNarrFormat; -use Modules\Webstatement\Models\TempStmtNarrParam; class ExportStatementPeriodJob implements ShouldQueue { @@ -92,6 +104,10 @@ class ExportStatementPeriodJob implements ShouldQueue if($this->toCsv){ $this->exportToCsv(); } + + // Generate PDF setelah data diproses + $this->generatePdf(); + Log::info("Export statement period job completed successfully for account: {$this->account_number}, period: {$this->period}"); } catch (Exception $e) { Log::error("Error in ExportStatementPeriodJob: " . $e->getMessage()); @@ -110,10 +126,10 @@ class ExportStatementPeriodJob implements ShouldQueue $existingDataCount = $this->getExistingProcessedCount($accountQuery); // Only process if data is not fully processed - //if ($existingDataCount !== $totalCount) { + if ($existingDataCount !== $totalCount) { $this->deleteExistingProcessedData($accountQuery); $this->processAndSaveStatementEntries($totalCount); - //} + } } private function getTotalEntryCount(): int @@ -398,6 +414,166 @@ class ExportStatementPeriodJob implements ShouldQueue return str_replace('', ' ', $result); } + /** + * Generate PDF statement untuk account yang diproses + * Menggunakan data yang sudah diproses dari ProcessedStatement + * + * @return void + * @throws Exception + */ + private function generatePdf(): void + { + try { + DB::beginTransaction(); + + Log::info('ExportStatementPeriodJob: Memulai generate PDF', [ + 'account_number' => $this->account_number, + 'period' => $this->period, + 'statement_id' => $this->statementId + ]); + + // Ambil data account dan customer + $account = Account::where('account_number', $this->account_number)->first(); + if (!$account) { + throw new Exception("Account tidak ditemukan: {$this->account_number}"); + } + + $customer = Customer::where('customer_code', $account->customer_code)->first(); + if (!$customer) { + throw new Exception("Customer tidak ditemukan untuk account: {$this->account_number}"); + } + + // Ambil data branch + $branch = Branch::where('code', $account->branch_code)->first(); + if (!$branch) { + throw new Exception("Branch tidak ditemukan: {$account->branch_code}"); + } + + // Ambil statement entries yang sudah diproses + $stmtEntries = ProcessedStatement::where('account_number', $this->account_number) + ->where('period', $this->period) + ->orderBy('sequence_no') + ->get(); + + if ($stmtEntries->isEmpty()) { + throw new Exception("Tidak ada data statement yang diproses untuk account: {$this->account_number}"); + } + + // Prepare header table background (convert to base64 if needed) + $headerImagePath = public_path('assets/media/images/bg-header-table.png'); + $headerTableBg = file_exists($headerImagePath) + ? base64_encode(file_get_contents($headerImagePath)) + : null; + + // Hitung saldo awal bulan + $saldoAwalBulan = (object) ['actual_balance' => (float) $this->saldo]; + + // Generate filename + $filename = "statement_{$this->account_number}_{$this->period}.pdf"; + + // Tentukan path storage + $storagePath = "statements/{$this->period}/{$this->account_number}"; + $tempPath = storage_path("app/temp/{$filename}"); + $fullStoragePath = "{$storagePath}/{$filename}"; + + // Pastikan direktori temp ada + if (!file_exists(dirname($tempPath))) { + mkdir(dirname($tempPath), 0755, true); + } + + // Pastikan direktori storage ada + Storage::makeDirectory($storagePath); + + $period = $this->period; + + // Render HTML view + $html = view('webstatement::statements.stmt', compact( + 'stmtEntries', + 'account', + 'customer', + 'headerTableBg', + 'branch', + 'period', + 'saldoAwalBulan' + ))->render(); + + Log::info('ExportStatementPeriodJob: HTML view berhasil di-render', [ + 'account_number' => $this->account_number, + 'html_length' => strlen($html) + ]); + + // Generate PDF menggunakan 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($tempPath); + + // Verifikasi file berhasil dibuat + if (!file_exists($tempPath)) { + throw new Exception('PDF file gagal dibuat'); + } + + $fileSize = filesize($tempPath); + + // Pindahkan file ke storage permanen + $pdfContent = file_get_contents($tempPath); + Storage::put($fullStoragePath, $pdfContent); + + // Update print statement log + $printLog = PrintStatementLog::find($this->statementId); + if ($printLog) { + $printLog->update([ + 'is_available' => true, + 'is_generated' => true, + 'pdf_path' => $fullStoragePath, + 'file_size' => $fileSize + ]); + } + + // Hapus file temporary + if (file_exists($tempPath)) { + unlink($tempPath); + } + + Log::info('ExportStatementPeriodJob: PDF berhasil dibuat dan disimpan', [ + 'account_number' => $this->account_number, + 'period' => $this->period, + 'storage_path' => $fullStoragePath, + 'file_size' => $fileSize, + 'statement_id' => $this->statementId + ]); + + DB::commit(); + + } catch (Exception $e) { + DB::rollBack(); + + Log::error('ExportStatementPeriodJob: Gagal generate PDF', [ + 'error' => $e->getMessage(), + 'account_number' => $this->account_number, + 'period' => $this->period, + 'statement_id' => $this->statementId, + 'trace' => $e->getTraceAsString() + ]); + + // Update print statement log dengan status error + $printLog = PrintStatementLog::find($this->statementId); + if ($printLog) { + $printLog->update([ + 'is_available' => false, + 'error_message' => $e->getMessage() + ]); + } + + throw new Exception('Gagal generate PDF: ' . $e->getMessage()); + } + } + /** * Export processed data to CSV file */