From 677f468d63072515b84f01f84217fee12c65fe23 Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Tue, 10 Nov 2026 03:21:10 +0530 Subject: [PATCH] fix(invoices): store time_entry_ids as text A grouped invoice line joins every entry id, so about 100 entries overflowed VARCHAR(500) and generation failed on PostgreSQL. --- CHANGELOG.md | 1 + app/models/invoice.py | 2 +- .../207_widen_invoice_item_time_entry_ids.py | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 migrations/versions/207_widen_invoice_item_time_entry_ids.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e399d78..21c8fa5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Invoices** — Generating an invoice from many time entries no longer fails on PostgreSQL: `invoice_items.time_entry_ids` is now `TEXT` (was `VARCHAR(500)`). - **Payments** — Stripe checkout now sends JPY and other zero-decimal currencies (and three-decimal ones such as KWD) in the unit Stripe expects; before, every amount was multiplied by 100, so a JPY invoice was charged 100 times its balance, and the webhook amount was divided by 100 the same way. - **Peppol** — The UBL invoice now reports payments already recorded on the invoice as `PrepaidAmount` and sets `PayableAmount` to the total minus those payments (BR-CO-16), never below zero, as the Factur-X/ZUGFeRD export already does; before, a partly paid invoice sent or exported as UBL asked the buyer for the full total. diff --git a/app/models/invoice.py b/app/models/invoice.py index 83c9bf77..d4280071 100644 --- a/app/models/invoice.py +++ b/app/models/invoice.py @@ -360,7 +360,7 @@ class InvoiceItem(db.Model): total_amount = db.Column(db.Numeric(10, 2), nullable=False) # Time entry reference (optional) - time_entry_ids = db.Column(db.String(500), nullable=True) # Comma-separated IDs + time_entry_ids = db.Column(db.Text, nullable=True) # Comma-separated IDs # Inventory integration stock_item_id = db.Column(db.Integer, db.ForeignKey("stock_items.id"), nullable=True, index=True) diff --git a/migrations/versions/207_widen_invoice_item_time_entry_ids.py b/migrations/versions/207_widen_invoice_item_time_entry_ids.py new file mode 100644 index 00000000..354dc0f1 --- /dev/null +++ b/migrations/versions/207_widen_invoice_item_time_entry_ids.py @@ -0,0 +1,23 @@ +"""Widen invoice_items.time_entry_ids so a grouped line can hold many entries. + +Revision ID: 207_invoice_item_time_entry_ids +Revises: 206_add_bank_transactions +""" + +import sqlalchemy as sa +from alembic import op + +revision = "207_invoice_item_time_entry_ids" +down_revision = "206_add_bank_transactions" +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table("invoice_items") as batch_op: + batch_op.alter_column("time_entry_ids", existing_type=sa.String(length=500), type_=sa.Text()) + + +def downgrade(): + with op.batch_alter_table("invoice_items") as batch_op: + batch_op.alter_column("time_entry_ids", existing_type=sa.Text(), type_=sa.String(length=500))