Add Excel export to Billplz Transaction Summary

This commit is contained in:
Afiq Hamzah
2026-06-24 15:34:26 +08:00
parent 403459ca2f
commit eea205ef65
5 changed files with 293 additions and 17 deletions
@@ -0,0 +1,28 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
class BillplzTransactionSummaryExport implements WithMultipleSheets
{
protected $bill_plz_transaction_summary_pkb;
protected $bill_plz_transaction_summary_koop;
protected $date;
public function __construct($bill_plz_transaction_summary_pkb, $bill_plz_transaction_summary_koop, $date = null)
{
$this->bill_plz_transaction_summary_pkb = $bill_plz_transaction_summary_pkb;
$this->bill_plz_transaction_summary_koop = $bill_plz_transaction_summary_koop;
$this->date = $date;
}
public function sheets(): array
{
return [
new BillplzTransactionSummarySheet($this->bill_plz_transaction_summary_pkb, 'PKB (v2)', $this->date),
new BillplzTransactionSummarySheet($this->bill_plz_transaction_summary_koop, 'KOPKB (v3)', $this->date),
];
}
}
@@ -0,0 +1,180 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithColumnWidths;
use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class BillplzTransactionSummarySheet extends DefaultValueBinder implements FromArray, WithColumnFormatting, WithColumnWidths, WithCustomValueBinder, WithEvents, WithStrictNullComparison, WithStyles, WithTitle
{
protected $summary_data;
protected $sheet_title;
protected $date;
public function __construct($summary_data, $sheet_title, $date = null)
{
$this->summary_data = $summary_data;
$this->sheet_title = $sheet_title;
$this->date = $date;
}
public function title(): string
{
return $this->sheet_title;
}
public function bindValue(Cell $cell, $value)
{
if ($cell->getColumn() === 'J' && $value !== '' && $value !== null) {
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
return true;
}
return parent::bindValue($cell, $value);
}
public function columnFormats(): array
{
return [
'J' => NumberFormat::FORMAT_TEXT,
];
}
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
$sheet = $event->sheet->getDelegate();
$highest_column = $sheet->getHighestColumn(8);
$sheet->mergeCells('A1:' . $highest_column . '1');
$sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet->getStyle('B6')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
},
];
}
public function columnWidths(): array
{
return [
'A' => 25,
'B' => 22,
'C' => 15,
'D' => 22,
'E' => 15,
'F' => 22,
'G' => 20,
'H' => 15,
'I' => 30,
'J' => 16,
'K' => 20,
];
}
public function styles(Worksheet $sheet)
{
return [
1 => ['alignment' => ['wrapText' => true, 'horizontal' => Alignment::HORIZONTAL_CENTER]],
3 => ['alignment' => ['wrapText' => true]],
4 => ['alignment' => ['wrapText' => true]],
5 => ['alignment' => ['wrapText' => true]],
6 => ['alignment' => ['wrapText' => true]],
8 => ['alignment' => ['wrapText' => true]],
];
}
public function array(): array
{
$title = 'Billplz Transaction Summary' . ($this->date ? ' (' . $this->date . ')' : '');
$rows = [];
$rows[] = [$title];
$rows[] = ['', '', '', '', '', '', '', '', '', '', ''];
$rows[] = ['Sum of Billplz Transactions', $this->summary_data['sumOfBillplzTransactions'] ?? ''];
$rows[] = ['Sum of Arrahn Transactions', $this->summary_data['sumOfArrahnTransactions'] ?? ''];
$rows[] = ['Payment Gateway Cost', $this->summary_data['paymentGatewayCost'] ?? ''];
$rows[] = ['No of Arrahn Transactions', $this->summary_data['numberOfArrahnTransactions'] ?? ''];
$rows[] = ['', '', '', '', '', '', '', '', '', '', ''];
$rows[] = [
'Bil',
'Billplz ID',
'Billplz Status',
'Billplz Paid Amount (RM)',
'Arrahn Status',
'Arrahn Total Payment',
'Arrahn Norujukan',
'Arrahn Kodcaw',
'Arrahn Customer Name',
'Arrahn No. KP',
'Arrahn Paid At',
];
$bil = 1;
$details = $this->summary_data['billPlzTransactionSummary'] ?? [];
foreach ($details as $data) {
$billplz_bill = $data['billplz_bill'] ?? null;
$arrahn_transactions = $data['arrahn_transactions'] ?? [];
$billplz_id = $billplz_bill['id'] ?? '';
$billplz_status = $billplz_bill['state'] ?? '';
$billplz_paid_amount = '';
if (isset($billplz_bill['paid_amount']['amount'])) {
$billplz_paid_amount = floatval($billplz_bill['paid_amount']['amount']) / 100;
}
if (empty($arrahn_transactions)) {
$rows[] = [
$bil++,
$billplz_id,
$billplz_status,
$billplz_paid_amount,
'',
'',
'',
'',
'',
'',
'',
];
continue;
}
foreach ($arrahn_transactions as $transaction) {
$rows[] = [
$bil++,
$billplz_id,
$billplz_status,
$billplz_paid_amount,
$transaction['status'] ?? '',
$transaction['total_payment'] ?? '',
$transaction['norujukan'] ?? '',
$transaction['kodcaw'] ?? '',
$transaction['customer_name'] ?? '',
$transaction['nokp'] ?? '',
$transaction['paid_at'] ?? '',
];
}
}
return $rows;
}
}
@@ -5,6 +5,10 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\BillplzTransactionSummaryExport;
use App\Exports\BillplzTransactionSummarySheet;
class BillplzTransactionSummaryController extends Controller class BillplzTransactionSummaryController extends Controller
{ {
@@ -12,6 +16,46 @@ class BillplzTransactionSummaryController extends Controller
{ {
$date = $request->selectedDate ?? Carbon::now()->toDateString(); $date = $request->selectedDate ?? Carbon::now()->toDateString();
[$billPlzTransactionSummaryPKB, $billPlzTransactionSummaryKOOP] = $this->getSummaryData($date);
return view('online.billplz-transaction-summary.index', compact('billPlzTransactionSummaryPKB', 'billPlzTransactionSummaryKOOP', 'date'));
}
public function export(Request $request)
{
$date = $request->selectedDate ?? Carbon::now()->toDateString();
$tab = $request->tab ?? 'all';
[$billPlzTransactionSummaryPKB, $billPlzTransactionSummaryKOOP] = $this->getSummaryData($date);
if ($tab === 'kopkb') {
$file_name = 'billplz_transaction_summary_kopkb_' . $date . '.xlsx';
return Excel::download(
new BillplzTransactionSummarySheet($billPlzTransactionSummaryKOOP, 'KOPKB (v3)', $date),
$file_name
);
}
if ($tab === 'pkb') {
$file_name = 'billplz_transaction_summary_pkb_' . $date . '.xlsx';
return Excel::download(
new BillplzTransactionSummarySheet($billPlzTransactionSummaryPKB, 'PKB (v2)', $date),
$file_name
);
}
$file_name = 'billplz_transaction_summary_' . $date . '.xlsx';
return Excel::download(
new BillplzTransactionSummaryExport($billPlzTransactionSummaryPKB, $billPlzTransactionSummaryKOOP, $date),
$file_name
);
}
private function getSummaryData($date)
{
$transactionDataPKB = collect( Http::arrahn()->get('/api/transaction/online/query', ['date' => $date, 'status' => 'paid'])->json() ); $transactionDataPKB = collect( Http::arrahn()->get('/api/transaction/online/query', ['date' => $date, 'status' => 'paid'])->json() );
$transactionDataPKB = $transactionDataPKB->map(function($x){ $transactionDataPKB = $transactionDataPKB->map(function($x){
$x['account_own_by'] = 'PKB'; $x['account_own_by'] = 'PKB';
@@ -88,9 +132,11 @@ class BillplzTransactionSummaryController extends Controller
$data = array_map(fn($x, $y) => $this->processBillplzBills($x, $y), $billplzBills, [$transactionDataKOOP, $transactionDataPKB]); $data = array_map(fn($x, $y) => $this->processBillplzBills($x, $y), $billplzBills, [$transactionDataKOOP, $transactionDataPKB]);
[$billPlzTransactionSummaryKOOP, $billPlzTransactionSummaryPKB] = $data; [$billPlzTransactionSummaryKOOP, $billPlzTransactionSummaryPKB] = $data;
return view('online.billplz-transaction-summary.index', compact('billPlzTransactionSummaryPKB' ,'billPlzTransactionSummaryKOOP', 'date')); return [$billPlzTransactionSummaryPKB, $billPlzTransactionSummaryKOOP];
} }
public function processArrahnTransactions($transactionData) public function processArrahnTransactions($transactionData)
@@ -10,8 +10,9 @@
</breadcrumb> </breadcrumb>
<div class="container p-4 mx-auto"> <div class="container pt-4 mx-auto">
<div class="flex mb-0 border-b border-gray-200"> <div class="flex mb-0 border-b border-gray-200 justify-between items-center">
<div class="flex">
<button class="flex items-center tab-button active" data-tab="pkb-transactions-summary"> <button class="flex items-center tab-button active" data-tab="pkb-transactions-summary">
<span>PKB (v2)</span> <span>PKB (v2)</span>
</button> </button>
@@ -19,6 +20,14 @@
<span>KOPKB (v3)</span> <span>KOPKB (v3)</span>
</button> </button>
</div> </div>
<a id="export-summary-btn"
href="{{ route('transaction.online.billplz-transaction-summary.export', ['selectedDate' => $date, 'tab' => 'pkb']) }}"
data-date="{{ $date }}"
class="bg-gray-500 text-white py-2 px-6 font-medium rounded-xl hover:bg-gray-600 transition-all duration-300 mb-1">
<i class="fas fa-file-excel mr-2"></i> <span id="export-summary-text">Export Excel for v2</span>
</a>
</div>
</div>
<div> <div>
<div id="pkb-transactions-summary" class="container p-4 mx-auto bg-yellow-50 tab-content active"> <div id="pkb-transactions-summary" class="container p-4 mx-auto bg-yellow-50 tab-content active">
<x-filter-date-card title="Billplz Transaction Summary" <x-filter-date-card title="Billplz Transaction Summary"
@@ -210,6 +219,17 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const tabButtons = document.querySelectorAll('.tab-button'); const tabButtons = document.querySelectorAll('.tab-button');
const tabContents = document.querySelectorAll('.tab-content'); const tabContents = document.querySelectorAll('.tab-content');
const exportBtn = document.getElementById('export-summary-btn');
const exportText = document.getElementById('export-summary-text');
const selectedDate = exportBtn.getAttribute('data-date');
const exportBaseUrl = "{{ route('transaction.online.billplz-transaction-summary.export') }}";
function updateExportButton(tabId) {
const tab = tabId === 'kopkb-transactions-summary' ? 'kopkb' : 'pkb';
const version = tabId === 'kopkb-transactions-summary' ? 'v3' : 'v2';
exportBtn.href = exportBaseUrl + '?selectedDate=' + encodeURIComponent(selectedDate) + '&tab=' + tab;
exportText.textContent = 'Export Excel for ' + version;
}
tabButtons.forEach(button => { tabButtons.forEach(button => {
button.addEventListener('click', () => { button.addEventListener('click', () => {
@@ -221,8 +241,9 @@
// Activate the clicked tab // Activate the clicked tab
button.classList.add('active'); button.classList.add('active');
console.log(tabId)
document.getElementById(tabId).classList.add('active'); document.getElementById(tabId).classList.add('active');
updateExportButton(tabId);
}); });
}); });
}); });
+1
View File
@@ -183,6 +183,7 @@ Route::get('/transaction/online/trail', OnlineTransactionTrailController::class)
Route::get('/transaction/online/billplz-transaction-review', [BillplzTransactionReviewController::class, 'index'])->name('transaction.online.billplz-transaction-review.index'); Route::get('/transaction/online/billplz-transaction-review', [BillplzTransactionReviewController::class, 'index'])->name('transaction.online.billplz-transaction-review.index');
Route::get('/transaction/online/billplz-transaction-review/{bill_id}', [BillplzTransactionReviewController::class, 'show'])->name('transaction.online.billplz-transaction-review.show'); Route::get('/transaction/online/billplz-transaction-review/{bill_id}', [BillplzTransactionReviewController::class, 'show'])->name('transaction.online.billplz-transaction-review.show');
Route::get('/transaction/online/billplz-transaction-summary', [BillplzTransactionSummaryController::class, 'index'])->name('transaction.online.billplz-transaction-summary.index'); Route::get('/transaction/online/billplz-transaction-summary', [BillplzTransactionSummaryController::class, 'index'])->name('transaction.online.billplz-transaction-summary.index');
Route::get('/transaction/online/billplz-transaction-summary/export', [BillplzTransactionSummaryController::class, 'export'])->name('transaction.online.billplz-transaction-summary.export');
Route::get('/transaction/online/log', [OnlineTransactionLogController::class, 'index'])->name('transaction.online.log.index'); Route::get('/transaction/online/log', [OnlineTransactionLogController::class, 'index'])->name('transaction.online.log.index');
Route::get('/transaction/online/log/{bill_id}', [OnlineTransactionLogController::class, 'show'])->name('transaction.online.log.show'); Route::get('/transaction/online/log/{bill_id}', [OnlineTransactionLogController::class, 'show'])->name('transaction.online.log.show');
Route::get('/transaction/online/gadaian/{norujukan}', OnlineTransactionGadaianController::class)->name('transaction.online.gadaian'); Route::get('/transaction/online/gadaian/{norujukan}', OnlineTransactionGadaianController::class)->name('transaction.online.gadaian');