Certainly! Here’s a structured approach to generate a customer statement in Laravel (with Livewire), including all invoices (orders/sales), receipts, balances, and a running total, with support for a date range and opening balance.
1. Define the Statement Query Logic
You want to:
- Get all OrderInvoices and SalesInvoices for a customer within a date range, with their receipts.
- Get all OrderInvoiceReceipts and SalesInvoiceReceipts for that customer within the same range.
- Calculate the opening balance (before
from_date). - Show a running balance.
2. Sample Livewire Component Method
public function getCustomerStatement($customerId, $fromDate, $toDate)
{
// 1. Calculate opening balance (before fromDate)
$orderInvoicesBefore = OrderInvoice::where('customer_id', $customerId)
->where('issue_date', '<', $fromDate)
->sum('total_amount');
$salesInvoicesBefore = SalesInvoice::where('customer_id', $customerId)
->where('issue_date', '<', $fromDate)
->sum('total_amount');
$orderReceiptsBefore = OrderInvoiceReceipt::whereHas('orderInvoice', function($q) use ($customerId) {
$q->where('customer_id', $customerId);
})
->where('paid_date', '<', $fromDate)
->sum('paid_amount');
$salesReceiptsBefore = SalesInvoiceReceipt::where('customer_id', $customerId)
->where('paid_date', '<', $fromDate)
->sum('paid_amount');
$openingBalance = ($orderInvoicesBefore + $salesInvoicesBefore) - ($orderReceiptsBefore + $salesReceiptsBefore);
// 2. Get all relevant transactions within date range
$orderInvoices = OrderInvoice::where('customer_id', $customerId)
->whereBetween('issue_date', [$fromDate, $toDate])
->get();
$salesInvoices = SalesInvoice::where('customer_id', $customerId)
->whereBetween('issue_date', [$fromDate, $toDate])
->get();
$orderReceipts = OrderInvoiceReceipt::whereHas('orderInvoice', function($q) use ($customerId) {
$q->where('customer_id', $customerId);
})
->whereBetween('paid_date', [$fromDate, $toDate])
->get();
$salesReceipts = SalesInvoiceReceipt::where('customer_id', $customerId)
->whereBetween('paid_date', [$fromDate, $toDate])
->get();
// 3. Merge all transactions into a single collection
$transactions = collect();
foreach ($orderInvoices as $invoice) {
$transactions->push([
'date' => $invoice->issue_date,
'reference' => $invoice->invoice_no,
'type' => 'Order Invoice',
'description' => 'Order Invoice',
'amount' => $invoice->total_amount,
'payment' => 0,
]);
}
foreach ($salesInvoices as $invoice) {
$transactions->push([
'date' => $invoice->issue_date,
'reference' => $invoice->invoice_no,
'type' => 'Sales Invoice',
'description' => 'Sales Invoice',
'amount' => $invoice->total_amount,
'payment' => 0,
]);
}
foreach ($orderReceipts as $receipt) {
$transactions->push([
'date' => $receipt->paid_date,
'reference' => $receipt->receipt_no,
'type' => 'Order Receipt',
'description' => 'Order Payment',
'amount' => 0,
'payment' => $receipt->paid_amount,
]);
}
foreach ($salesReceipts as $receipt) {
$transactions->push([
'date' => $receipt->paid_date,
'reference' => $receipt->receipt_no,
'type' => 'Sales Receipt',
'description' => 'Sales Payment',
'amount' => 0,
'payment' => $receipt->paid_amount,
]);
}
// 4. Sort by date, then by type (so invoices come before receipts on same day)
$transactions = $transactions->sortBy([
['date', 'asc'],
['type', 'asc'],
])->values();
// 5. Calculate running balance
$balance = $openingBalance;
$statement = [];
// Add opening balance row
$statement[] = [
'date' => $fromDate,
'reference' => '',
'type' => 'Opening Balance',
'description' => 'Balance Forwarded',
'amount' => '',
'payment' => '',
'balance' => $openingBalance,
];
foreach ($transactions as $row) {
$balance += $row['amount'];
$balance -= $row['payment'];
$statement[] = array_merge($row, ['balance' => $balance]);
}
return $statement;
}
3. How to Use in Livewire/View
You can call this method in your Livewire component and pass the result to your view for display as a table.
public $customerId, $fromDate, $toDate, $statement;
public function mount($customerId)
{
$this->customerId = $customerId;
$this->fromDate = now()->subMonth()->toDateString();
$this->toDate = now()->toDateString();
$this->loadStatement();
}
public function loadStatement()
{
$this->statement = $this->getCustomerStatement($this->customerId, $this->fromDate, $this->toDate);
}
And in your Blade view:
<table>
<thead>
<tr>
<th>Date</th>
<th>Reference</th>
<th>Description</th>
<th>Amount</th>
<th>Payment</th>
<th>Balance</th>
</tr>
</thead>
<tbody>
@foreach($statement as $row)
<tr>
<td>{{ $row['date'] }}</td>
<td>{{ $row['reference'] }}</td>
<td>{{ $row['description'] }}</td>
<td>{{ $row['amount'] ?: '' }}</td>
<td>{{ $row['payment'] ?: '' }}</td>
<td>{{ $row['balance'] }}</td>
</tr>
@endforeach
</tbody>
</table>
4. Notes
- You can add more fields (like invoice status, etc.) as needed.
- This approach is flexible: you can add filters, export to PDF, etc.
- For performance, consider eager loading relationships if you have a lot of data.
Summary:
This solution gives you a full, date-ranged customer statement with opening balance, all invoices, all receipts, and a running balance, ready to display or send to your customers.