| name | quarterly-invoice |
| description | Create, render, and issue outgoing invoices (facturas) for the QuarterlyAssistant project. Use whenever the user asks to write or create an invoice, bill a client, mentions a missing outgoing invoice for a payment they received, wants to issue/finalize/lock an invoice draft, re-render an invoice PDF, create a factura rectificativa, or add a new invoicing client — including casual phrasings like "Rechnung an X", "invoice Montana for June", "I got paid but never invoiced". Also use when an invoice number matching the tenant's series is mentioned. Requires the QuarterlyAssistant folder to be connected. |
Outgoing invoices — QuarterlyAssistant
Create an invoice as a locked, numbered YAML+PDF pair in the tenant's invoicing/issued/ folder. The YAML is the bookkeeping record: the quarterly pipeline (AGENTS.md Stage 2/3) ingests it as income and matches it against the bank payment — creating the invoice here IS the bookkeeping entry, no second step exists.
Orientation
The repo is the connected folder containing AGENTS.md and tools/render-invoice.py. Tenant is a folder under tenants/; if more than one exists, ask which. Read documentation/USAGE.md if anything below seems out of date — that file wins.
Dependencies (sandbox, once per session): pip install --break-system-packages pyyaml jinja2 weasyprint. Rendering works anywhere if the tenant's template embeds its fonts via @font-face (the reference setup does).
Workflow
-
Resolve the client. Look up the slug in tenants/<tenant>/invoicing/clients.yaml. New client → collect name, address lines, NIF or VAT-ID, country, language (de/es/en), and tax_treatment (igic for Canarias/ES clients, export for clients outside the Canaries, none only with a stated reason), then add the entry. If tax treatment is unclear, ask — never assume.
-
Collect the line items. Three forms, mixable:
- work:
description, qty (number or "95:30" = h:mm), unit_price (EUR, tax-exclusive)
- pass-through expense (Auslagen):
description + amount only
- optional
description_sub for a gray subline (second language)
If the user references a received payment ("I got 3.200 € from X"), work backwards: the line items must sum to the amount actually received, accounting for IGIC/retención. Show the arithmetic.
-
Date. Today unless the user specifies otherwise. Never backdate: numbering is gapless and must stay chronological. A late invoice for old work is dated today — mention the service period in service_period or the line description instead.
-
Create and render the draft:
python3 tools/render-invoice.py new <tenant> --client <slug> [--date YYYY-MM-DD]
Edit the created YAML in invoicing/issued/<year>/ with the real lines, then:
python3 tools/render-invoice.py render <tenant> <number>
Drafts carry a DRAFT watermark.
-
Show the PDF to the user with a one-line summary: number, client, base, IGIC, retención, total. Wait for approval; apply corrections by editing the YAML and re-rendering — drafts are freely editable.
-
On approval, issue: set issued: true in the YAML, re-render (this freezes issued_hash and removes the watermark), then run:
python3 tools/render-invoice.py check <tenant>
check must print OK. Present the final PDF.
Hard rules
- Never edit an issued invoice. The tool blocks it (lock hash); don't work around it. Corrections = factura rectificativa: a new invoice referencing the original number, with negative or corrected amounts — confirm details with the user before creating one.
- Never assign numbers by hand. Only
render-invoice.py new assigns numbers (respects seq_start in config for pre-tool invoices).
- Money is in the YAML in EUR decimals; the script computes cents. Don't compute totals yourself in the YAML — the renderer freezes
*_cents fields on render; trust those and quote them in the summary.
- Template changes (
invoicing/template/invoice.html.j2) affect all future renders; don't touch it for a one-off unless asked. Reference design: template/sample.pdf.