add route preview-unfinished-payment-orders

This commit is contained in:
Edmond Lang
2025-07-25 18:59:02 +08:00
parent ed317ed176
commit 1bb0a73bd9
2 changed files with 162 additions and 45 deletions
@@ -0,0 +1,155 @@
<?php
namespace App\Http\Controllers\Reports;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPaidAmount;
use App\Http\Controllers\Controller;
use App\Models\Booking;
use Illuminate\Http\Request;
use Carbon\Carbon;
class UnfinishedPaymentOrders extends Controller
{
public function execute(Request $request)
{
$page = (int) $request->input('page', 1);
$perPage = 5000;
$offset = ($page - 1) * $perPage;
$cutOffDate = $request->cut_off_date ? Carbon::parse($request->cut_off_date) : null;
$baseQuery = Booking::with('company')
->when($cutOffDate, fn($q) => $q->where('created_at', '<=', $cutOffDate))
->orderByDesc('id');
$total = $baseQuery->count();
$bookings = $baseQuery->offset($offset)->limit($perPage)->get();
$calculator = new CalculatesBookingPaidAmount();
$filtered = $bookings->filter(function ($b) use ($calculator, $cutOffDate) {
$paid = $calculator->executeUntilDate($b, $cutOffDate);
$outstanding = $b->fix_amount - $paid;
return $paid > 0 && $outstanding > 0;
});
return response()->json([
'success' => true,
'current_page' => $page,
'next_page' => ($offset + $perPage < $total) ? $page + 1 : null,
'count' => $filtered->count(),
'data' => $filtered->map(function ($b) use ($calculator, $cutOffDate) {
$paid = $calculator->executeUntilDate($b, $cutOffDate);
return [
'id' => $b->id,
'order_ref' => $b->marking ?? $b->id,
'booking_amount' => number_format($b->fix_amount, 2),
'paid_amount' => number_format($paid, 2),
'outstanding_amount' => number_format($b->fix_amount - $paid, 2),
'customer' => optional($b->company)->reference,
'created_at' => $b->created_at->toDateTimeString(),
];
})->values(),
]);
}
public function loadView(Request $request)
{
$cutOffDateString = $request->cut_off_date ?? '';
$cutOffDateParsed = $cutOffDateString ? Carbon::parse($cutOffDateString)->toDateString() : '-';
echo <<<HTML
<p>Cut Off Date: {$cutOffDateParsed}</p>
<div class="log">🔄 Processing... Total 0</div>
<br>
<style>
table {
border-collapse: collapse;
width: 100%;
font-family: Arial, sans-serif;
font-size: 14px;
}
th, td {
padding: 6px 10px;
border: 1px solid #ccc;
}
thead {
background: #f1f1f1;
}
.log {
margin-top: 15px;
font-family: monospace;
white-space: pre-line;
}
</style>
<table id="results-table">
<thead>
<tr>
<th>Order Ref</th>
<th>Booking Amount</th>
<th>Paid Amount</th>
<th>Outstanding Amount</th>
<th>Customer</th>
<th>Order Created Date</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
let currentPage = 1;
const cutOffDate = encodeURIComponent("{$cutOffDateString}");
const tableBody = document.querySelector('#results-table tbody');
const log = document.querySelector('.log');
const bookingUrlTemplate = "/transfer/__ORDER_REF__";
const customerUrlTemplate = "/customer/__ORDER_REF__";
let totalRows = 0;
function updateLogMessage() {
log.textContent = `🔄 Processing... Total \${totalRows}`;
}
function runNextBatch() {
const url = `/run-batch-unfinished-payment-orders?page=\${currentPage}&cut_off_date=\${cutOffDate}`;
fetch(url)
.then(res => {
if (!res.ok) throw new Error("404 or server error");
return res.json();
})
.then(data => {
if (data.success) {
totalRows += data.count;
updateLogMessage();
data.data.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
<td><a href="\${bookingUrlTemplate.replace('__ORDER_REF__', item.order_ref)}" target="_blank">\${item.order_ref}</a></td>
<td>RM \${item.booking_amount}</td>
<td>RM \${item.paid_amount}</td>
<td>RM \${item.outstanding_amount}</td>
<td><a href="\${customerUrlTemplate.replace('__ORDER_REF__', item.customer)}" target="_blank">\${item.customer ?? '-'}</a></td>
<td>\${item.created_at}</td>
`;
tableBody.appendChild(row);
});
if (data.next_page) {
currentPage = data.next_page;
runNextBatch();
} else {
log.textContent = `✅ Completed. Total \${totalRows}`;
}
}
})
.catch(err => {
log.textContent = "❌ Error: " + err + "\\n";
});
}
runNextBatch();
</script>
HTML;
}
}
+7 -45
View File
@@ -36,7 +36,7 @@ use App\Classes\Modules\Transactions\Services\DeletesTransaction;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Classes\General\AWSS3Helper;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPaidAmount;
use App\Http\Controllers\Reports\UnfinishedPaymentOrders;
use Illuminate\Support\Facades\File;
@@ -1371,49 +1371,11 @@ Route::get('/maintenance', function () {
return response()->view('errors.503', [], 503);
});
// web route to view the result
Route::get('/preview-unfinished-payment-orders', function (Request $request) {
$cutOffDate = $request->cut_off_date ?? null;
if ($cutOffDate) {
echo 'Cut Off Date: ' . $cutOffDate;
echo '<br><br>';
$cutOffDate = Carbon::parse($cutOffDate);
}
echo '<table style="border-collapse: collapse; width: 100%;" border="1">';
echo '<thead><tr>
<th>Order Ref</th>
<th>Booking Amount</th>
<th>Paid Amount</th>
<th>Outstanding Amount</th>
<th>Customer</th>
<th>Order Created Date</th>
</tr></thead>';
echo '<tbody>';
$bookings = Booking::where('status', '<', ApprovalStatus::COMPLETED);
if ($cutOffDate) {
$bookings->where('created_at', '<=', $cutOffDate);
}
$bookings = $bookings->orderBy('id', 'desc')->get();
$calculateBookingPaidAmount = new CalculatesBookingPaidAmount();
foreach ($bookings as $booking) {
$paid = $calculateBookingPaidAmount->executeUntilDate($booking, $cutOffDate);
$outstanding = $booking->fix_amount - $paid;
if ($paid > 0 && $outstanding > 0) {
echo '<tr>';
echo '<td><a href="'.route('booking.details', $booking->marking).'" target="_blank">'.$booking->marking.'</a></td>';
echo '<td>' . number_format($booking->fix_amount, 2) . '</td>';
echo '<td>' . number_format($paid, 2) . '</td>';
echo '<td>' . number_format($outstanding, 2) . '</td>';
echo '<td><a href="'.route('customer.profile', $booking->company->reference).'" target="_blank">'.$booking->company->reference.'</a></td>';
echo '<td>' . $booking->created_at->format('d-m-Y') . '</td>';
echo '</tr>';
}
}
echo '</tbody></table>';
return (new UnfinishedPaymentOrders())->loadView($request);
});
// web route to run the logic
Route::get('/run-batch-unfinished-payment-orders', function (Request $request) {
return (new UnfinishedPaymentOrders())->execute($request);
});