You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In v1, every invoice PDF generation writes a dated, immutable copy to uploads/archive/ (application/helpers/mpdf_helper.php:144-217: <Y-m-d>_<trans('invoice')>_<number>.pdf), and users can browse (application/modules/invoices/controllers/Invoices.php:78-91 + Mdl_invoices::get_archives()), filter, and securely download (Invoices.php:93-118) archived copies. This satisfies legal retention: the archive proves exactly what was sent to the client, even after the invoice record changes. v2 has no trace of this — PDFs are streamed and discarded.
Specific
On every invoice PDF generation (download action, email attach per #526, and future guest access), persist the rendered bytes to a company-scoped archive disk path (e.g. storage/app/archive/{company_id}/{Y-m-d}_{invoice_number}.pdf, or an Upload-model-backed store consistent with Modules/Core uploads). Add a Filament Invoice Archive page (Company panel) listing archived PDFs (date, invoice number, filename) with search by invoice number and a per-row download action. Repeat generations on the same day overwrite that day's copy; different days accumulate history (v1 behaviour).
Measurable
Generating an invoice PDF creates exactly one archive file per invoice per day, named <Y-m-d>_<invoice_number>.pdf, scoped to the invoice's company.
The archive page lists entries newest-first and filters by invoice number.
Download returns the archived bytes (not a re-render) with Content-Type: application/pdf.
Users of company A can never list or download company B's archives (path traversal and cross-tenant access rejected).
New Filament page Modules/Invoices/Filament/Company/Pages/InvoiceArchive.php backed by a disk listing (or an invoice_archives table if metadata querying is preferred).
Download via Storage::download() with basename validation — mirror v1's validate_file_access() hardening.
Gate with a new VIEW_INVOICE_ARCHIVE permission (PermissionsSeeder).
Relevant
Retention of sent invoices is a legal requirement in several jurisdictions and a v1 feature users rely on; without it, editing an invoice silently rewrites history.
Time-Bound
After PDF list-action wiring (#200) and alongside email attach (#526), which should archive at send time exactly as v1 does.
In v1, every invoice PDF generation writes a dated, immutable copy to
uploads/archive/(application/helpers/mpdf_helper.php:144-217:<Y-m-d>_<trans('invoice')>_<number>.pdf), and users can browse (application/modules/invoices/controllers/Invoices.php:78-91+Mdl_invoices::get_archives()), filter, and securely download (Invoices.php:93-118) archived copies. This satisfies legal retention: the archive proves exactly what was sent to the client, even after the invoice record changes. v2 has no trace of this — PDFs are streamed and discarded.Specific
On every invoice PDF generation (download action, email attach per #526, and future guest access), persist the rendered bytes to a company-scoped archive disk path (e.g.
storage/app/archive/{company_id}/{Y-m-d}_{invoice_number}.pdf, or anUpload-model-backed store consistent withModules/Coreuploads). Add a Filament Invoice Archive page (Company panel) listing archived PDFs (date, invoice number, filename) with search by invoice number and a per-row download action. Repeat generations on the same day overwrite that day's copy; different days accumulate history (v1 behaviour).Measurable
<Y-m-d>_<invoice_number>.pdf, scoped to the invoice's company.Content-Type: application/pdf.Achievable
InvoiceService::generatePdf()/PdfGenerationService::invoicePdf()after PR [IP-130]: Report Builder — bands + bricks (IP-130, Phases 1-4 + Browsershot driver) #608) to write the output viaStorage::disk(...)before streaming.Modules/Invoices/Filament/Company/Pages/InvoiceArchive.phpbacked by a disk listing (or aninvoice_archivestable if metadata querying is preferred).Storage::download()with basename validation — mirror v1'svalidate_file_access()hardening.VIEW_INVOICE_ARCHIVEpermission (PermissionsSeeder).Relevant
Retention of sent invoices is a legal requirement in several jurisdictions and a v1 feature users rely on; without it, editing an invoice silently rewrites history.
Time-Bound
After PDF list-action wiring (#200) and alongside email attach (#526), which should archive at send time exactly as v1 does.
Arrange
Company + invoice (
Invoice::factory()->for($this->company)->create(['invoice_number' => 'INV-2026-001'])), fake storage disk, Carbon frozen (base TestCase freezes 2026-01-01).Act
Call the invoice PDF generation service method; then load the archive page component and call its download action for the created entry.
Assert
storagecontainsarchive/{company_id}/2026-01-01_INV-2026-001.pdfafter generation.../../etc/passwd) is rejected.