77 lines
2.9 KiB
PHP
77 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Livewire\Concerns\ResolvesCustomer;
|
|
use App\Models\Invoice;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Pagination\LengthAwarePaginator as Paginator;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Component;
|
|
use Livewire\WithPagination;
|
|
|
|
/**
|
|
* The customer's own documents, newest first.
|
|
*
|
|
* This page used to be a mock that outlived its purpose: five invented invoices
|
|
* numbered CP-2026-0003 to CP-2026-0007 — a prefix belonging to no configured
|
|
* series — all "paid", a spend curve rising from 179 to 198 € and a next charge
|
|
* of 198 € on 01.08.2026. Every signed-in customer saw it, and their REAL
|
|
* invoices, which exist in `invoices` and are mailed to them as PDFs, appeared
|
|
* nowhere in the portal at all. The paperwork was real and the page lied about
|
|
* it.
|
|
*
|
|
* What is here now is the same rows the operator console lists (App\Livewire\
|
|
* Admin\Invoices), scoped to the one customer. Read-only, like the console's,
|
|
* and for the same reason: an issued invoice is never edited or deleted, so
|
|
* there is no button here whose absence needs explaining.
|
|
*
|
|
* **No next charge, and no spend chart.** Both were invented, and neither is a
|
|
* figure this application actually holds: what Stripe will take on the next
|
|
* cycle depends on prorations, credit on the account and discounts, and nothing
|
|
* computes it. A number nobody has worked out is worse than a blank space, so
|
|
* the space stays blank — what the customer pays per month is on the billing
|
|
* page, from their contract, where it is true.
|
|
*/
|
|
#[Layout('layouts.portal-app')]
|
|
class Invoices extends Component
|
|
{
|
|
use ResolvesCustomer;
|
|
use WithPagination;
|
|
|
|
public function render()
|
|
{
|
|
$customer = $this->customer();
|
|
|
|
return view('livewire.invoices', [
|
|
'invoices' => $this->invoices($customer?->id),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* This customer's invoices, and nobody else's.
|
|
*
|
|
* Scoped by `customer_id` in the query rather than filtered afterwards, and
|
|
* an empty page for somebody with no customer record at all — an operator
|
|
* signed into the portal, an account whose customer has been deleted. The
|
|
* document itself is scoped again at the download route: a list is a list,
|
|
* and hiding a link has never stopped anybody fetching a URL.
|
|
*
|
|
* @return LengthAwarePaginator<int, Invoice>
|
|
*/
|
|
private function invoices(?int $customerId): LengthAwarePaginator
|
|
{
|
|
if ($customerId === null) {
|
|
return new Paginator([], 0, 12);
|
|
}
|
|
|
|
return Invoice::query()
|
|
->where('customer_id', $customerId)
|
|
->orderByDesc('issued_on')
|
|
// Then by id, because a day can hold several documents and a list
|
|
// whose order changes between two page loads reads as a fault.
|
|
->orderByDesc('id')
|
|
->paginate(12);
|
|
}
|
|
}
|