'1080425781', 'period' => '2025012', 'saldo' => '23984352604' ],[ 'account_number' => '1080425781', 'period' => '2025013', 'saldo' => '13984352604' ]]; $jobIds = []; // Process each data entry foreach ($data as $entry) { // Dispatch job for each entry $job = new ExportStatementJob( $entry['account_number'], $entry['period'], $entry['saldo'] ); $jobIds[] = app(Dispatcher::class)->dispatch($job); } return response()->json([ 'message' => 'Statement export jobs have been queued', 'jobs' => array_map(function($index, $jobId) use ($data) { return [ 'job_id' => $jobId, 'account_number' => $data[$index]['account_number'], 'period' => $data[$index]['period'], 'file_name' => "{$data[$index]['account_number']}_{$data[$index]['period']}.csv" ]; }, array_keys($jobIds), $jobIds) ]); } /** * Download a previously exported statement */ public function downloadStatement(Request $request) { $account_number = $request->input('account_number', '1080425781'); $period = $request->input('period', '20250512'); $fileName = "{$account_number}_{$period}.csv"; $filePath = "statements/{$fileName}"; if (!Storage::disk('local')->exists($filePath)) { return response()->json([ 'message' => 'Statement file not found. It may still be processing.' ], 404); } return Storage::disk('local')->download($filePath, $fileName, [ "Content-Type" => "text/csv", ]); } /** * Generate statement on-demand and return as download */ public function generateAndDownload(Request $request) { $account_number = $request->input('account_number', '1080425781'); $period = $request->input('period', '20250512'); $saldo = $request->input('saldo', '23984352604'); $stmt = StmtEntry::with(['ft', 'transaction']) ->where('account_number', $account_number) ->where('booking_date', $period) ->orderBy('date_time', 'ASC') ->orderBy('trans_reference', 'ASC') ->get(); if ($stmt->isEmpty()) { return response()->json([ 'message' => 'No statement data found for the specified account and period.' ], 404); } $runningBalance = (float) $saldo; // Map the data to transform or format specific fields $mappedData = $stmt->sortBy(['ACTUAL.DATE', 'REFERENCE.NUMBER']) ->map(function ($item, $index) use (&$runningBalance) { $runningBalance += (float) $item->amount_lcy; return [ 'NO' => 0, // Will be updated later 'TRANSACTION.DATE' => Carbon::createFromFormat('YmdHi', $item->booking_date . substr($item->ft?->date_time ?? '0000000000', 6, 4)) ->format('d/m/Y H:i'), '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' => Carbon::createFromFormat('ymdHi', $item->ft?->date_time ?? '2505120000') ->format('d/m/Y H:i'), ]; }) ->values(); // Then apply the sequential numbers $mappedData = $mappedData->map(function ($item, $index) { $item['NO'] = $index + 1; return $item; }); $csvFileName = $account_number . "_" . $period . ".csv"; $headers = [ "Content-Type" => "text/csv", "Content-Disposition" => "attachment; filename={$csvFileName}" ]; $callback = function () use ($mappedData) { $file = fopen('php://output', 'w'); // Write headers without quotes, using pipe separator fputs($file, implode('|', array_keys($mappedData[0])) . "\n"); // Write data rows without quotes, using pipe separator foreach ($mappedData as $row) { fputs($file, implode('|', $row) . "\n"); } fclose($file); }; return response()->stream($callback, 200, $headers); } /** * Generate narrative for a statement entry */ private function generateNarrative($item) { $narr = ''; if ($item->transaction && $item->transaction->narr_type) { $narr .= $item->transaction->stmt_narr . ' '; $narr .= $this->getFormatNarrative($item->transaction->narr_type, $item); } else if ($item->transaction) { $narr .= $item->transaction->stmt_narr . ' '; } if ($item->ft && $item->ft->recipt_no) { $narr .= 'Receipt No: ' . $item->ft->recipt_no; } return $narr; } /** * Get formatted narrative based on narrative type */ private 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.IN'; } 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') { $result .= $this->getTransaction($item->trans_reference, $fieldName) . ' '; } } } } return $result; } /** * Get transaction data by reference and field */ private function getTransaction($ref, $field) { $trans = TempFundsTransfer::where('ref_no', $ref)->first(); return $trans ? ($trans->$field ?? "") : ""; } /** * Queue a statement export job and return job ID */ public function queueExport(Request $request) { $account_number = $request->input('account_number', '1080425781'); $period = $request->input('period', '20250512'); $saldo = $request->input('saldo', '23984352604'); // Dispatch the job and get the job ID $job = new ExportStatementJob($account_number, $period, $saldo); $jobId = app(Dispatcher::class)->dispatch($job); return response()->json([ 'message' => 'Statement export job has been queued', 'job_id' => $jobId, 'account_number' => $account_number, 'period' => $period, 'file_name' => "{$account_number}_{$period}.csv" ]); } /** * Check the status of an export job */ public function checkExportStatus(Request $request, $jobId) { // Get job status from the queue $job = DB::table('jobs') ->where('id', $jobId) ->first(); if (!$job) { // Check if job is completed $completedJob = DB::table('job_batches') ->where('id', $jobId) ->first(); if ($completedJob) { return response()->json([ 'status' => 'completed', 'message' => 'Export job has been completed' ]); } return response()->json([ 'status' => 'not_found', 'message' => 'Export job not found' ], 404); } return response()->json([ 'status' => 'pending', 'message' => 'Export job is still processing' ]); } }