mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-08-04 07:02:13 +00:00
fix(seeder): assign unique_hash to seeded documents (#698)
RealisticDemoSeeder builds invoices, payments and estimates with Model::create(), which bypasses both paths that normally set unique_hash — the factories set it directly, and InvoiceService and friends encode it from the id after insert. Nothing assigned it here, so every seeded document had it NULL. The PDF routes bind on that column, so the frontend built `/invoices/pdf/` with an empty segment. That 404s, and the only symptom is "Unable to load document preview" in the UI with nothing written to the log, which makes it a genuinely slow thing to track down. Anyone who seeds realistic demo data and opens a document hits it. Production is unaffected: documents created through the app go through the service layer, which assigns the hash. Existing seeded databases need a backfill, encoding each id the same way the services do. Uses Hashids, as the services do, rather than the factories' str_random, so demo data matches what the app itself would have produced.
This commit is contained in:
committed by
GitHub
parent
9a5731106e
commit
01da03624d
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Facades\Hashids;
|
||||
use App\Models\Address;
|
||||
use App\Models\AiConversation;
|
||||
use App\Models\CompanySetting;
|
||||
@@ -452,6 +453,10 @@ class RealisticDemoSeeder extends Seeder
|
||||
|
||||
// Touch timestamps to match the invoice_date so tool queries like
|
||||
// `latest('invoice_date')` match `latest('created_at')` plausibly.
|
||||
// The PDF routes bind on unique_hash. Creating through the model rather
|
||||
// than InvoiceService skips the one place that normally assigns it, so
|
||||
// set it here or every seeded document 404s on preview and download.
|
||||
$invoice->unique_hash = Hashids::connection(Invoice::class)->encode($invoice->id);
|
||||
$invoice->created_at = $invoiceDate;
|
||||
$invoice->updated_at = $invoiceDate;
|
||||
$invoice->save();
|
||||
@@ -515,6 +520,8 @@ class RealisticDemoSeeder extends Seeder
|
||||
'notes' => null,
|
||||
]);
|
||||
|
||||
// See seedInvoice(): the PDF routes bind on unique_hash.
|
||||
$payment->unique_hash = Hashids::connection(Payment::class)->encode($payment->id);
|
||||
$payment->created_at = $paymentDate;
|
||||
$payment->updated_at = $paymentDate;
|
||||
$payment->save();
|
||||
@@ -585,6 +592,8 @@ class RealisticDemoSeeder extends Seeder
|
||||
'notes' => null,
|
||||
]);
|
||||
|
||||
// See seedInvoice(): the PDF routes bind on unique_hash.
|
||||
$estimate->unique_hash = Hashids::connection(Estimate::class)->encode($estimate->id);
|
||||
$estimate->created_at = $estimateDate;
|
||||
$estimate->updated_at = $estimateDate;
|
||||
$estimate->save();
|
||||
|
||||
31
tests/Feature/Admin/RealisticDemoSeederTest.php
Normal file
31
tests/Feature/Admin/RealisticDemoSeederTest.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Estimate;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
beforeEach(function () {
|
||||
Artisan::call('db:seed', ['--class' => 'DatabaseSeeder', '--force' => true]);
|
||||
Artisan::call('db:seed', ['--class' => 'RealisticDemoSeeder', '--force' => true]);
|
||||
});
|
||||
|
||||
/**
|
||||
* The PDF routes bind on unique_hash (`/invoices/pdf/{invoice:unique_hash}`), and
|
||||
* the seeder builds its documents with Model::create() rather than through the
|
||||
* service layer that normally assigns it. When that was missed, every seeded
|
||||
* document produced a `/invoices/pdf/` URL with an empty segment — a 404, surfaced
|
||||
* in the UI only as "Unable to load document preview", with nothing in the log.
|
||||
*
|
||||
* Anything the seeder creates that the app then serves by hash has to carry one.
|
||||
*/
|
||||
test('every seeded document has the unique hash its pdf route binds on', function (string $model) {
|
||||
expect($model::count())->toBeGreaterThan(0);
|
||||
|
||||
expect($model::whereNull('unique_hash')->orWhere('unique_hash', '')->count())
|
||||
->toBe(0);
|
||||
})->with([
|
||||
Invoice::class,
|
||||
Estimate::class,
|
||||
Payment::class,
|
||||
]);
|
||||
Reference in New Issue
Block a user