CluPilotCloud/app/Livewire/Invoices.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);
}
}