• Mahmoud Aglan's avatar
    feat(expenses): let one expense carry many receipt images · b3c232ab
    Mahmoud Aglan authored
    An expense is one payment, not one piece of paper. A month of fuel, or a kit
    order settled across a dozen shops, arrives at the desk as a single expense
    with a hundred receipts behind it. The schema had one set of `attachment_*`
    columns on `expenses`, which encoded the opposite assumption, and
    attachReceipt() deleted the file it displaced — so uploading page two of an
    invoice destroyed page one and nothing said so.
    
    Receipts move to their own `expense_attachments` table. The migration is
    additive and idempotent (guarded by hasTable, backfill deduped by path, so it
    is safe against the migrate-on-every-boot entrypoint), and it copies the
    existing single attachments across, defaulting the pre-metadata rows to the
    `public` disk exactly as attachmentDiskName() did in PHP. The old columns are
    deliberately left in place rather than dropped: a client DB is migrated in
    place on a live container and a dropped column is not recoverable.
    
    The desk can now select several files at once and keep adding in batches —
    selections accumulate instead of replacing, which is what a hundred-receipt
    expense actually needs. Deleting one receipt leaves the others alone, and the
    delete re-resolves the id through the expense's own relation so an id off the
    wire cannot erase another expense's evidence. Streaming moves to the
    attachment's own uuid route, with the branch check read from the parent
    expense without global scopes so the guard cannot fail open.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    b3c232ab
Name
Last commit
Last update
..
console.php Loading commit data...
web.php Loading commit data...