Skip to content

Deposit & Final Invoices (Abschlagsrechnung / Schlussrechnung) #326

Description

@clstaudt

Problem

Tuttle has no concept of linked partial invoices. A deposit invoice and its final invoice are currently two independent, unrelated invoices. The final invoice cannot automatically deduct previously paid deposits, and there is no chain showing they belong together.

Payment schedule at the contract level

The payment structure (e.g. "50% on commissioning, 50% on delivery") is defined in the contract/offer, not invented at invoice time. New table:

class PaymentMilestone(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    contract_id: int = Field(foreign_key="contract.id", ondelete="CASCADE")
    title: str                        # e.g. "Upon commissioning"
    percentage: Optional[Decimal]     # e.g. 50 — alternative to amount
    amount: Optional[Decimal]         # absolute amount — alternative to percentage
    position: int                     # ordering
    invoiced: bool = Field(default=False)

    contract: "Contract" = Relationship(
        back_populates="payment_milestones",
        sa_relationship_kwargs={"lazy": "subquery"},
    )

When creating invoices, Tuttle pulls the milestone schedule as a template, ensuring consistency between offer and invoices.

Proposed changes

Extend Invoice.document_type (currently "invoice" | "reminder") with two new values:

  • "deposit" — deposit / advance payment invoice
  • "final" — final settlement invoice (deducts prior deposits)

Self-referential chain (analogous to the existing reminder chain):

deposit_for_id: Optional[int] = Field(
    default=None, foreign_key="invoice.id",
)

deposits: List["Invoice"] = Relationship(...)   # on final invoice
final_invoice: Optional["Invoice"] = Relationship(...)  # on deposit invoice

Final invoice rendering (tax-law requirement)

The final invoice must show the full contract amount, not just the remainder:

Total fee                              10,000.00
plus 19% VAT                            1,900.00
Gross                                  11,900.00
less deposit per invoice no. 2025-01   -5,950.00
  (VAT included therein: 950.00)
Remaining balance                       5,950.00

Correct VAT handling across the chain

  • Deposit invoices: VAT on the partial amount
  • Final invoice: VAT on the full amount, minus VAT already invoiced on deposits
  • The rendering logic must settle VAT amounts, not just net amounts

UI behavior

  • "Create final invoice" auto-marks linked deposits as paid (or warns if still unpaid)
  • Clear project/client attribution in invoice list, independent of numbering
  • Deposit chain visualization on invoice detail view
  • Overview of upcoming deposit/final invoices derived from contract milestones

Scope

  • Schema: PaymentMilestone table, new document_type values, deposit chain FK, Alembic migration
  • Invoicing logic: final invoice with deduction lines + correct VAT settlement
  • Rendering: legally compliant PDF layout for deposit and final invoices
  • UI: milestone editor on contract, deposit chain, "create final invoice" action, open-items consistency

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions