Add feature for online transaction report

This commit is contained in:
Afiq Hamzah
2025-06-14 15:17:17 +08:00
parent b19a90eb9b
commit ee7a2df22f
6 changed files with 466 additions and 24 deletions
@@ -0,0 +1,118 @@
<?php
namespace App\Exports\LaporanKeseluruhan;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class BayaranOnlineExport extends DefaultValueBinder implements FromArray, WithStrictNullComparison, WithHeadings, ShouldAutoSize, WithColumnFormatting, WithStyles, WithCustomValueBinder
{
public $data;
public function __construct($data)
{
$this->data = $data;
}
public function headings(): array
{
return [
'Bil',
'Nama Pelanggan',
'No. KP',
'Total SAG',
'Cawangan',
'No. Rujukan',
'Bayaran',
'Jumlah Bayaran(RM)',
'Tarikh',
'Masa',
'Status',
];
}
public function array(): array
{
$transactions = $this->data['transactions'];
foreach ($transactions as $index => $transaction) {
$data[] = array_merge(
['nos' => $index + 1],
$transaction,
);
}
foreach($data as $customer){
foreach($customer['payments'] as $paymentIndex => $payment ){
foreach($payment['paymentDetails'] ?? [] as $paymentDetailIndex => $paymentDetail){
$row[] = [
'bil' => $paymentIndex == 0 && $paymentDetailIndex == 0 ? $customer['nos'] : '',
'customer_name' => $paymentIndex == 0 && $paymentDetailIndex == 0 ? $customer['customer_name'] : '',
'nokp' => $paymentIndex == 0 && $paymentDetailIndex == 0 ? $customer['nokp'] : '',
'paymentNos' => $paymentDetailIndex == 0 ? $payment['paymentNos'] : '',
'cawangan' => $paymentDetailIndex == 0 ? $paymentIndex : '',
'norujukan' => $paymentDetail['norujukan'] ?? '',
'total_payment' => $paymentDetail['total_payment'] ?? '',
'sum_total_payment' => $paymentDetailIndex == 0 ? $payment['paymentSum'] : '',
'tarikh' => $paymentDetail['tarikh'] ?? '',
'masa' => $paymentDetail['masa'] ?? '',
'status' => $paymentDetail['status'] ?? '',
];
}
}
}
$total_row = ['Total : ', '', '', $this->data['sumPaymentNos'], '', '', $this->data['sumTotalPayments'], $this->data['sumTotalPayments'], '', '', ''];
array_push($row, $total_row);
return $row;
}
public function startCell(): string
{
return 'B2';
}
public function columnFormats(): array
{
return [
'C' => '#',
];
}
public function styles(Worksheet $sheet)
{
return [
'G:H' => [
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT,
],
],
];
}
public function bindValue(Cell $cell, $value)
{
if (is_numeric($value)) {
$cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
return true;
}
// else return default behavior
return parent::bindValue($cell, $value);
}
}
@@ -0,0 +1,48 @@
<?php
namespace App\Http\Controllers\Reports;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Http;
class BayaranOnlineController extends Controller
{
public function __invoke(Request $request)
{
$startDate = $request->filled('startDate') ?
Carbon::parse($request->startDate)->startOfDay() :
Carbon::now()->startOfDay();
$endDate = $request->filled('endDate') ?
Carbon::parse($request->endDate)->startOfDay() :
Carbon::now()->startOfDay();
$responseTransactionSortedByTotalCawanganPayment = Http::get(config('api.url') . '/api/reports/bayaran-online', [
'startDate' => $startDate->toDateString(),
'endDate' => $endDate->toDateString(),
'orderBy' => 'totalCawanganPayment',
]);
$responseTransactionSortedByTotalReferenceNo = Http::get(config('api.url') . '/api/reports/bayaran-online', [
'startDate' => $startDate->toDateString(),
'endDate' => $endDate->toDateString(),
'orderBy' => 'totalReferenceNo',
]);
if($responseTransactionSortedByTotalCawanganPayment->failed() OR $responseTransactionSortedByTotalReferenceNo->failed()){
throw new \Exception("Something wrong with the API", 1);
}
$data = [
'sortedByTotalCawanganPayment' => $responseTransactionSortedByTotalCawanganPayment->json(),
'sortedByTotalReferenceNo' => $responseTransactionSortedByTotalReferenceNo->json(),
];
return view('reports.bayaran-online.index', compact('data', 'startDate', 'endDate'));
}
}
@@ -0,0 +1,49 @@
<?php
namespace App\Http\Controllers\Reports;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Http;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\LaporanKeseluruhan\BayaranOnlineExport;
class BayaranOnlineDetailController extends Controller
{
public function __invoke(Request $request)
{
$startDate = $request->filled('startDate') ?
Carbon::parse($request->startDate)->startOfDay() :
Carbon::now()->startOfDay();
$endDate = $request->filled('endDate') ?
Carbon::parse($request->endDate)->startOfDay() :
Carbon::now()->endOfDay();
$responseTransactions = Http::get(config('api.url') . '/api/reports/bayaran-online-detail', [
'startDate' => $startDate->toDateString(),
'endDate' => $endDate->toDateString(),
'status' => $request->status ?? 'PAID',
'kodcaw' => $request->kodcaw ?? '',
]);
if($responseTransactions->failed()){
throw new \Exception("Something wrong with the API", 1);
}
$data = $responseTransactions->json();
$title = 'laporan_bayaran_online';
$cawangan = $request->details_caw ?? 'Semua Cawangan';
$tarikh_carian = $startDate->toFormattedDateString() . ' hingga ' . $endDate->toFormattedDateString();
$tarikh_cetak = 'cetak pada ' . Carbon::now()->toDayDateTimeString();
$title = '--'.$title . '--' . $cawangan . '--' . $tarikh_carian . '--' . $tarikh_cetak . '--' . '.xlsx';
return Excel::download(new BayaranOnlineExport($data), $title);
}
}
+29 -23
View File
@@ -1,71 +1,77 @@
@extends('layouts.app')
@section('content')
<div class="bg-gray-900 opacity-50 hidden fixed inset-0 z-10" id="sidebarBackdrop"></div>
<div id="main-content" class="h-full w-full bg-gray-50 relative overflow-y-auto">
<div class="fixed inset-0 z-10 hidden bg-gray-900 opacity-50" id="sidebarBackdrop"></div>
<div id="main-content" class="relative w-full h-full overflow-y-auto bg-gray-50">
<main>
<div class="pt-6 px-4">
<div class="px-4 pt-6">
<breadcrumb :items="[{ 'title': 'Laporan Keseluruhan', 'icon': 'fas fa-file-alt' }]"></breadcrumb>
<div class="mx-auto content-center flex justify-center items-center">
<div class="flex items-center content-center justify-center mx-auto">
<div class="grid grid-rows-1">
<br>
<div class="ml-3 grid grid-cols-3">
<div class="grid grid-cols-3 ml-3">
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.gadaian.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Pembiayaan</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Pembiayaan</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.penebusan.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Penyelesaian Pembiayaan</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Penyelesaian Pembiayaan</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.ansuran.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Ansuran Pembiayaan</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Ansuran Pembiayaan</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.ringkasan-harian.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Ringkasan Harian</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Ringkasan Harian</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.stok-audit.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Stok Audit</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Stok Audit</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.lelongan.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Laporan Lelongan</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Laporan Lelongan</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.aging.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Laporan Aging</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Laporan Aging</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.outstanding.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Outstanding</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Outstanding</h2>
</a>
</div>
<div
class="bg-white ml-2 shadow border border-solid px-6 py-4 rounded-lg hover:bg-yellow-300 hover:transition-all mb-2 cursor-pointer">
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.petibesi.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-center font-bold text-2xl uppercase">Peti Besi Cawangan</h2>
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Peti Besi Cawangan</h2>
</a>
</div>
<div
class="px-6 py-4 mb-2 ml-2 bg-white border border-solid rounded-lg shadow cursor-pointer hover:bg-yellow-300 hover:transition-all">
<a href="{{ route('reports.bayaran-online.index', ['startDate' => now()->toDateString(), 'endDate' => now()->toDateString()]) }}" class="">
<h2 class="mx-auto text-2xl font-bold text-center uppercase">Bayaran Online</h2>
</a>
</div>
<div
@@ -0,0 +1,218 @@
@extends('layouts.app')
@section('content')
<div class="fixed inset-0 z-10 hidden bg-gray-900 opacity-50" id="sidebarBackdrop"></div>
<div id="main-content" class="relative w-full h-full overflow-y-auto bg-gray-50">
<main>
<div class="px-4 pt-6">
<breadcrumb
:items="[{ 'title': 'Laporan Keseluruhan', 'icon': 'fas fa-users', 'url': '{{ route('reports') }}' }, { 'title': 'Bayaran Online', 'icon': 'fas fa-users' }]">
</breadcrumb>
<div class="container">
<div class="flex w-full">
<div class="p-8 mx-auto xl:w-2/3 2xl:w-1/2">
<div class="px-6 py-4 text-lg text-gray-200 bg-yellow-600 rounded-t-lg">
<a href="{{ route('reports') }}" class="router-link-active">
<i class="text-white fas fa-backspace"></i>
</a>
<span class="mx-auto font-bold text-center text-white">Bayaran Online</span>
</div>
<form method="get" id="form-bayaran-online" action="{{ route('reports.bayaran-online.index') }}">
<input type="hidden" id="formAction" name="action" value="search"> <!-- Hidden field to store action -->
<div class="flex items-center justify-center p-6 bg-white rounded shadow">
<div class="pb-6">
<div class="flex gap-4">
<div>
<label class="block pb-1 font-semibold text-gray-700">Mula</label>
<input name="startDate" type="date"
value="{{ request()->startDate ?? $startDate }}"
class="w-full px-4 py-2 border border-2 rounded" placeholder="Dari">
</div>
<div>
<label class="block pb-1 font-semibold text-gray-700">Hingga</label>
<input name="endDate" type="date"
value="{{ request()->endDate ?? $endDate }}"
class="w-full px-4 py-2 border border-2 rounded" placeholder="Dari">
</div>
<button type="submit" onclick="setAction('search')" class="px-8 py-2 ml-5 font-medium text-yellow-600 transition-all duration-300 border border-yellow-600 font-montserrat rounded-xl hover:bg-yellow-600 hover:text-white">
<i class="mr-2 fas fa-magnifying-glass"></i> Search
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="mb-5 border border-gray-400 summary-table">
<x-table>
<x-slot name="header">
<x-table.header content="Dari Tarikh" />
<x-table.header content="Hingga Tarikh" />
<x-table.header content="Bilangan SAG" />
<x-table.header content="Jumlah Bayaran Online" />
<x-table.header content="Export PAID" />
<x-table.header content="Export UNSUCCESSFUL" />
</x-slot>
<x-slot name="body">
<tr>
<x-table.data :content="$startDate->toDateString()" />
<x-table.data :content="$endDate->toDateString()" />
<x-table.data :content="$data['sortedByTotalCawanganPayment']['nosPayments']" />
<x-table.data :content="$data['sortedByTotalReferenceNo']['sumTotalPayments']" />
<div>
<td class="py-5 text-sm bg-white border-b border-gray-200">
<div class="flex items-center">
<div class="ml-3">
<p class="text-gray-900 whitespace-no-wrap">
@if($data['sortedByTotalCawanganPayment']['nosPayments'] != 0)
<a href="{{ route('reports.bayaran-online-detail.index', ['startDate' => request()->startDate, 'endDate' => request()->endDate, 'status' => 'PAID']) }}" class="px-3 py-2 font-medium text-yellow-600 transition-all duration-300 border border-yellow-600 font-montserrat rounded-xl hover:bg-yellow-600 hover:text-white">
<i class="fas fa-file-excel"></i> Export
</a>
@else
<span class="pl-3">Tiada</span>
@endif
</p>
</div>
</div>
</td>
</div>
<div>
<td class="py-5 text-sm bg-white border-b border-gray-200">
<div class="flex items-center">
<div class="ml-3">
<p class="text-gray-900 whitespace-no-wrap">
@if($data['sortedByTotalCawanganPayment']['nosPayments'] != 0)
<a href="{{ route('reports.bayaran-online-detail.index', ['startDate' => request()->startDate, 'endDate' => request()->endDate, 'status' => 'UNSUCCESSFUL']) }}" class="px-3 py-2 font-medium text-yellow-600 transition-all duration-300 border border-yellow-600 font-montserrat rounded-xl hover:bg-yellow-600 hover:text-white">
<i class="fas fa-file-excel"></i> Export
</a>
@else
<span class="pl-3">Tiada</span>
@endif
</p>
</div>
</div>
</td>
</div>
</tr>
</x-slot>
</x-table>
</div>
<div class="flex gap-4">
<div class="w-1/2 m-4 border border-gray-400 sorted-by-payment">
<x-table>
<x-slot name="header">
<x-table.header content="No" />
<x-table.header content="Cawangan" />
<x-table.header content="Bilangan SAG" />
<x-table.header content="Jumlah Bayaran Online(RM)" />
<x-table.header content="Export" />
</x-slot>
<x-slot name="body">
@forelse($data['sortedByTotalCawanganPayment']['transactions'] as $transaction)
<tr>
<x-table.data :content="$loop->iteration" />
<x-table.data :content="$transaction['cawangan']['details_caw']" />
<x-table.data :content="$transaction['totalReferenceNo']" />
<x-table.data :content="$transaction['totalCawanganPayment']" />
<div>
<td class="text-sm bg-white border-b border-gray-200 ">
<div class="flex items-center">
<div class="ml-3">
<p class="text-gray-900 whitespace-no-wrap">
<a href="{{ route('reports.bayaran-online-detail.index', ['startDate' => request()->startDate, 'endDate' => request()->endDate, 'kodcaw' => $transaction['cawangan']['kodcaw'], 'details_caw' => $transaction['cawangan']['details_caw']]) }}" class="px-1 py-2 font-medium text-yellow-600 transition-all duration-300 border border-yellow-600 font-montserrat rounded-xl hover:bg-yellow-600 hover:text-white">
<i class="fas fa-file-excel"></i> Export
</a>
</p>
</div>
</div>
</td>
</div>
</tr>
@empty
<tr>
<div>
<td class="text-sm bg-white border-b border-gray-200" colspan="5">
<div class="flex items-center">
<div class="ml-3">
<p class="py-3 text-gray-900 whitespace-no-wrap">
Tiada transaksi pada tarikh yang dipilih
</p>
</div>
</div>
</td>
</div>
</tr>
@endforelse
</x-slot>
</x-table>
</div>
<div class="w-1/2 m-4 border border-gray-400 sorted-by-number-sag">
<x-table>
<x-slot name="header">
<x-table.header content="No" />
<x-table.header content="Cawangan" />
<x-table.header content="Bilangan SAG" />
<x-table.header content="Jumlah Bayaran Online(RM)" />
<x-table.header content="Export" />
</x-slot>
<x-slot name="body">
@forelse($data['sortedByTotalReferenceNo']['transactions'] as $transaction)
<tr>
<x-table.data :content="$loop->iteration" />
<x-table.data :content="$transaction['cawangan']['details_caw']" />
<x-table.data :content="$transaction['totalReferenceNo']" />
<x-table.data :content="$transaction['totalCawanganPayment']" />
<div>
<td class="text-sm bg-white border-b border-gray-200 ">
<div class="flex items-center">
<div class="ml-3">
<p class="text-gray-900 whitespace-no-wrap">
<a href="{{ route('reports.bayaran-online-detail.index', ['startDate' => request()->startDate, 'endDate' => request()->endDate, 'kodcaw' => $transaction['cawangan']['kodcaw'], 'details_caw' => $transaction['cawangan']['details_caw']]) }}" class="px-1 py-2 font-medium text-yellow-600 transition-all duration-300 border border-yellow-600 font-montserrat rounded-xl hover:bg-yellow-600 hover:text-white">
<i class="fas fa-file-excel"></i> Export
</a>
</p>
</div>
</div>
</td>
</div>
</tr>
@empty
<tr>
<div>
<td class="text-sm bg-white border-b border-gray-200" colspan="5">
<div class="flex items-center">
<div class="flex items-center ml-3">
<p class="py-3 text-gray-900 whitespace-no-wrap">
Tiada transaksi pada tarikh yang dipilih
</p>
</div>
</div>
</td>
</div>
</tr>
@endforelse
</x-slot>
</x-table>
</div>
</div>
</div>
</main>
@include('layouts.footer')
</div>
@endsection
<script>
function setAction(action) {
document.getElementById('formAction').value = action;
document.getElementById('form-bayaran-online').submit();
}
</script>
+4 -1
View File
@@ -7,6 +7,8 @@ use App\Http\Controllers\Reports\RingkasanHarianController;
use App\Http\Controllers\Reports\StokAuditController;
use App\Http\Controllers\Reports\LelonganController;
use App\Http\Controllers\Reports\AgingController;
use App\Http\Controllers\Reports\BayaranOnlineController;
use App\Http\Controllers\Reports\BayaranOnlineDetailController;
use App\Http\Controllers\Reports\OutstandingController;
use App\Http\Controllers\Reports\PetibesiController;
use App\Http\Controllers\Reports\AnalisaController;
@@ -38,5 +40,6 @@ Route::middleware(['roleChecker:admin,akaun,operasi,kpo, null'])->group(function
Route::get('bakilelongbelumtuntut/view', [BakiLelongController::class, 'index'])->name('reports.bakilelongbelumtuntut.index');
Route::get('bakilelongbelumtuntut/show', [BakiLelongController::class, 'show'])->name('reports.bakilelongbelumtuntut.show');
Route::get('bakilelongbelumtuntut/{kod_cawangan}', [BakiLelongController::class, 'downloadBakiLelongBelumTuntutCawanganExcel'])->name('reports.bakilelongbelumtuntut.cawangan.excel');
Route::get('bayaran-online/view', BayaranOnlineController::class)->name('reports.bayaran-online.index');
Route::get('bayaran-online-details/view', BayaranOnlineDetailController::class)->name('reports.bayaran-online-detail.index');
});