Skip to content

invoice: invDueDate before invDate is silently accepted (no cross-field validation) #145

@CryptoJones

Description

@CryptoJones

Problem

app/schemas/invoice.schema.js validates invDate and invDueDate as ISO-date strings independently — no cross-field check. An invoice POST or PATCH where invDueDate < invDate (due date BEFORE the issue date) sails through validation. The DB row is created with both bounds intact and no signal to the operator that they wrote bookkeeping nonsense.

This is the same shape as the timeentry teEndedAt >= teStartedAt issue (#129, fixed in #130).

The bulk-create endpoint (POST /v1/invoice/bulk) also lacks the check — anyone could persist a batch of nonsense via the bulk envelope even after a single-entry validation tightens.

Proposed fix

Add a zod .refine() cross-field check to createInvoiceBody and updateInvoiceBody. The bulk schema uses z.array(createInvoiceBody) so it inherits the refinement automatically. Equality stays allowed — "Due on Receipt" / zero-day-net is a real billing term.

Single-bound PATCH (only invDueDate or only invDate) can't be validated at the schema layer without a DB read; leave that on the controller layer for a follow-up, matching how timeentry handled the same edge.

Pin behavior with five new tests covering: inverted single CREATE → 400, equality CREATE → schema-pass, inverted both-bound PATCH → 400, single-bound PATCH → schema-pass, inverted bulk-entry → 400.

Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions