Skip to content

feat(invoices): store invoice money as NUMERIC(15,4) — money migration phase 1#340

Merged
snowsky merged 1 commit into
mainfrom
feat/invoice-money-numeric-phase1
Jun 2, 2026
Merged

feat(invoices): store invoice money as NUMERIC(15,4) — money migration phase 1#340
snowsky merged 1 commit into
mainfrom
feat/invoice-money-numeric-phase1

Conversation

@snowsky
Copy link
Copy Markdown
Owner

@snowsky snowsky commented Jun 2, 2026

Phase 1 of the invoice money floatDecimal migration (plan: docs/todos/invoice-money-decimal-migration-plan.md). Storage foundation only — by design this does not change the arithmetic or the API contract yet.

What changed

  • models_per_tenant.py: invoice money columns FloatNumeric(15, 4):
    • Invoice.amount, Invoice.subtotal, Invoice.discount_value
    • InvoiceItem.quantity, InvoiceItem.price, InvoiceItem.amount
    • Payment.amount
  • Per-tenant migration scripts/migrate_invoice_money_to_numeric.pyALTER COLUMN ... TYPE NUMERIC(15,4) USING col::numeric(15,4) across all tenant DBs (idempotent; skips columns already numeric). The cast also cleans existing binary-float drift (e.g. 19.989999…19.9900).

Why asdecimal=False (important)

The columns use Numeric(15, 4, asdecimal=False), so the ORM keeps returning float. This is deliberate: flipping to Decimal now would make existing Decimal * float arithmetic across reports/stats/services throw TypeError, and would change the float-typed Pydantic response schemas. Keeping asdecimal=False means:

  • DB gains exact fixed-precision storage now (no float tails persisted).
  • Existing code and the API contract are unaffected — no behavior change.
  • Phase 2 flips to asdecimal=True + Decimal math with boundary rounding (ROUND_HALF_UP, quantize 0.01), and updates PDF/email/UI.

Deploy ordering ⚠️

Run the migration before deploying this model change:

docker compose exec api python scripts/migrate_invoice_money_to_numeric.py

Scope / not in this PR

  • No arithmetic changes (Phase 2), no UI changes (Phase 3), no reconciliation script (Phase 4).
  • Only invoice/payment/item money columns — other tables (expenses, inventory, discount_rules, etc.) are out of scope for the invoice-money migration.

Verification

Model imports clean and all 7 columns reflect as Numeric(precision=15, scale=4, asdecimal=False). Full end-to-end (insert/read round-trip) needs the stack up; flagging for review before Phase 2.

… phase 1)

Phase 1 of the float->Decimal money migration (see
docs/todos/invoice-money-decimal-migration-plan.md): convert the invoice,
payment and invoice-item money columns (amount, subtotal, discount_value,
quantity, price) from double-precision float to Numeric(15, 4) so the database
stores exact fixed-precision values.

Columns use asdecimal=False for now, so the ORM keeps returning float and the
existing float-based arithmetic / float API schemas are unaffected. Phase 2 will
switch to asdecimal=True and Decimal math with boundary rounding.

Includes a per-tenant migration (scripts/migrate_invoice_money_to_numeric.py)
that ALTERs the column types with a numeric cast — run it BEFORE deploying.
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
yourfinanceworks Ready Ready Preview, Comment, Open in v0 Jun 2, 2026 2:21pm

@snowsky snowsky merged commit bc53193 into main Jun 2, 2026
3 checks passed
@snowsky snowsky deleted the feat/invoice-money-numeric-phase1 branch June 2, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant