Payments & compliance

Invoicing

VAT invoicing in a Next.js app: gapless numbers per series and year, invoices from a sale or standalone, correcting invoices, a register, an A4 PDF, and an optional bridge to KSeF.

Released
October 7, 2026
npx skills add timerise-ai/invoicing
v0.1.2
Current release
14
Reference docs
6
Non-negotiables
MIT
License

The problem

What problem does Invoicing solve?

A booking, rental or membership system that takes money has to hand the buyer a VAT invoice. Staff issue it from a sale or on its own, correct it when something was wrong, and download or e-mail it as a PDF.

The tax rules make three things hard. Numbers must run without gaps for each series and year, even when two people issue invoices at the same moment. An issued invoice must never change, so every mistake is fixed with a correcting invoice that cancels the original to the last digit. And the PDF must show every name and amount in full, however long.

This skill gives a coding agent the invoicing module the way we build it: the database stamps the number inside the write that creates the invoice, each invoice keeps its own copy of the seller and the buyer, nothing edits it after issue, and the PDF comes from a writer with no dependencies. In Poland it can also send each invoice to KSeF through our ksef skill.

The module

What does the skill build?

An agent with this skill builds one module for a Next.js App Router app, on your stack. For Invoicing that module consists of:

  • 1. Gapless numbering per series and year stamped inside the insert
  • 2. Invoices issued standalone or from a sale
  • 3. Correcting invoices with signed deltas
  • 4. Seller and buyer snapshots
  • 5. Immutability after issue
  • 6. A register
  • 7. An A4 PDF from a dependency-free writer
  • 8. An optional bridge to KSeF through the ksef skill

What it needs from you

  • A Next.js App Router app. The route template uses the promise form of params from Next.js 15 and 16.
  • Postgres for production. The module runs on the in-memory store with no database, for tests and a first build only.
  • No runtime dependency for the core module. The optional KSeF bridge needs qrcode and the ksef skill.

Provenance

Where do the rules come from?

This skill is written by the engineer who has shipped this module, an invoicing section in a multi-tenant business application on Postgres. The templates hold these properties: a number is consumed only by a document that exists, a document and its lines are one write, an issued document cannot be updated or deleted, a correction's negated line cancels the original to the last digit, no text on the PDF is cut, and the verification code is never overprinted. Each is verified by the suites in references/testing.md and references/testing-service.md or by the database checks. The record of what the audit changed, what was kept and what is new is in references/provenance.md.

The engineering ledger for whoever edits this skill. The templates were written by the engineer who has shipped this module, and audited against the earlier implementation: an invoicing section inside a multi-tenant Next.js business application on Postgres, with VAT invoices, corrections, a PDF and a KSeF integration. Four lists follow and they are kept apart: what the audit changed, what was kept on purpose, what is new in the skill and has never run outside its own tests, and the defects that agent evals found in a released template.

The ledger separates what the audit changed, what was kept on purpose, and what has not run in production yet.

  1. Changed by the audit

    Sixteen entries. Each says what the earlier implementation did, what the templates do, and what verifies it.

    • The seller is a snapshot The earlier implementation copied the buyer onto the invoice and read the seller from the tenant profile every time a PDF or an e-invoice file was built. Editing the company name, address or bank account changed every past document on its next render. Shipped: SellerSnapshot on the invoice, written at issue (data-model.md). service.test.ts changes the profile after issue and reads the document back.
    • A document and its lines are one write The earlier implementation inserted the invoice, then its lines, then the e-invoice job, as three calls. A failure after the first left a numbered document with no lines and no way to remove it. Shipped: invoicing_create (postgres.md). The database checks count the lines and show that a refused insert returns its number.
    • A correction is one write, and one per document The earlier implementation inserted the correction, then its lines, then marked the original, with no unique constraint on the link. Two operators correcting the same invoice at once could both succeed. Shipped: a row lock on the original and a unique index on corrects_invoice_id. Four parallel corrections of one document were run against the scratch database: one succeeded and the correction series advanced by one.
    • Payment and correction are separate facts The earlier implementation stored one status, issued, paid or corrected. Correcting a paid invoice overwrote paid, and a corrected invoice could never be marked paid. Shipped: paidAt and correctedByInvoiceId, with invoiceStatus derived. service.test.ts corrects a paid invoice and finds it still paid.
    • Immutability is enforced by the database The earlier implementation relied on the application never updating an invoice, while the row-level policy allowed any billing role to update any column through the data API. Shipped: guard triggers on both tables. The database checks try to edit a total, edit a line, delete a document and undo a payment.
    Show 11 more
    • The register filters and pages on the server The earlier implementation loaded the newest 200 invoices and filtered them in the browser, so a search for an older document returned nothing. Shipped: InvoiceFilters and a cursor in the store. service.test.ts pages through seven documents three at a time and filters by text, date range and status.
    • Rounding is symmetric The earlier implementation rounded with Math.round, which rounds a half toward positive infinity. With a fractional quantity, a correction's negated line could differ from the original by one unit in the last place. The measured rate on a sweep of fractional quantities and two-decimal prices was more than a quarter of cases. Shipped: roundMoney (model.md). model.test.ts sweeps five quantities across two thousand prices.
    • Text wraps instead of being cut The earlier implementation cut a line name at 52 characters with an ellipsis and drew party names, addresses and the correction reason on one row each, so a long seller name ran into the buyer column. Shipped: wrapText (pdf-writer.md) and its use in the layout. invoice-pdf.test.ts checks that every word of a long name is printed.
    • The verification code is never overprinted The earlier implementation let table rows run to 130 points from the bottom of the first page while the code's top edge was at 146, so the last rows of a long invoice were drawn across it. Shipped: the floor function in the layout (pdf.md). The test renders five document lengths and fails when the floor is removed.
    • No verification code without e-invoicing The earlier implementation computed the e-invoice hash at issue for every tenant with a tax identifier, whether or not it had credentials, and built the link with a default test environment. Its invoices also stayed in the pending state for good. Shipped: isEnabled decides pending or not_required at issue, and verification returns null without a stored hash (ksef-bridge.md). bridge.test.ts covers the disabled tenant.
    • The hash is of the enqueued bytes, stored once The earlier implementation built the XML twice, at issue for the hash and again at send time, and relied on the two builds being identical. They read the live seller profile, so an edit in between produced a link that did not verify. Shipped: the bridge hashes the buffer it hands to enqueue, and the builder reads the snapshot.
    • The exemption basis is printed The earlier implementation stored the legal basis for exempt lines and sent it in the e-invoice, and did not print it on the PDF. Shipped: the payment details block prints it. invoice-pdf.test.ts checks the line.
    • Input is validated before a number is reserved The earlier implementation checked the VAT rate and that there was at least one line. A zero or negative quantity, an unparseable price, a blank line name and a malformed date reached the database. Shipped: validateLines and isIsoDate, called in the service. The form helper turns an unparseable number into NaN, not 0, so it is refused.
    • Nothing after the insert fails the request The earlier implementation ran the e-invoice enqueue and the audit after the insert and let their errors reach the form. The operator saw a failure, retried, and issued a second document. Shipped: afterCreate in service.md. Two tests in service.test.ts hold it.
    • Expected failures are return values with codes The earlier implementation threw Error with a sentence in one language from its Server Actions. Shipped: InvoiceError codes and InvoiceActionResult, following the Next.js error-handling guide, which says to model expected errors as return values.
    • The picker asks about the ids on screen The earlier implementation loaded every invoiced source id in one unbounded query to filter its picker, through a data API that caps a response at a fixed number of rows. Shipped: invoicedSourceIds(tenantId, kind, ids).
Read the full record in provenance.md

Non-negotiables

What are the 6 rules the module never breaks?

Every module built from this skill holds these, whoever builds it. The same list is in the skill's README and SKILL.md, so the agent reads it before it writes a line.

  1. Assign the number inside the insert.

    Never compute it in application code or a separate round trip: the counter must roll back with the document it numbered. The database checks refuse an insert and find the next document holding the number it would have had; twenty parallel issues produce twenty consecutive numbers.

  2. Write a document and its lines in one transaction

    , and a correction with the link on its original. A numbered document with no lines cannot be removed. invoicing_create is one call, and four parallel corrections of one document produce exactly one.

  3. Never update or delete an issued document.

    Both parties are snapshots, a mistake is fixed by a correction, and database triggers enforce it against any client. The checks try to edit a total, edit a line, delete a document and undo a payment.

  4. Take the tenant and the permissions from the server-side actor on every call.

    No route, action or store method accepts a tenant id from input. The service suite shows a second tenant nothing and lets it correct nothing.

  5. Compute every amount and every correction delta on the server

    , from the stored lines. The client sends lines and a corrected state, never totals or deltas. The model suite holds the arithmetic, including a negated line that cancels its original for fractional quantities.

  6. Let nothing fail the request once the document exists.

    A hand-off or audit failure is recorded and reported; an error would send the operator back to issue a second invoice. Two tests in the service suite fail the audit and the e-invoice hand-off and still receive the invoice.

Fit

When should you use it, and when not?

Use it for

  • A Next.js App Router app that sells something and must hand the buyer a VAT invoice: issued by staff in an admin area, standalone or from a sale, order or booking the app already holds; corrected when wrong; downloaded or e-mailed as PDF; and, in Poland, sent to KSeF.

Not for

  • Charging a card, a checkout, subscriptions, dunningInsteadThe payment provider's invoices, or stripe-connect-subscriptions
  • Talking to KSeF: auth, encryption, sessions, UPO, purchase invoicesInsteadksef; this skill builds the file and joins to it
  • A general ledger, VAT returns, JPK filesInsteadAn accounting system
  • Deciding a rate, an exemption or whether to correctInsteadAn accountant; this skill covers the module, not interpretations of the VAT Act
  • Fiscal receipts from a cash registerInsteadThe fiscal device's integration

Build it yourself

How do I install it?

One command. The skills.sh CLI installs the skill into every skills-compatible agent it finds.

$ npx skills add timerise-ai/invoicing

Claude Code

Invoke with /invoicing

Codex CLI

Invoke with $invoicing

Gemini CLI

Invoke with /skills

Name the agents instead with -a, for example npx skills add timerise-ai/invoicing -a claude-code -a codex. Or clone the repository into your agent's skills folder. Nothing in it is agent-specific.

What is inside the repository (20 entries)
  • SKILL.mdEntry point: architecture diagram, seven critical facts, six hard rules, the quick start, and the reference directory
  • README.mdThis file: the human-facing front door
  • CHANGELOG.mdEvery release, newest first
  • CLAUDE.mdWhat the repository is and its editing conventions, for an agent editing the skill itself
  • references/adaptation.mdThe seam contract with the host app: actor, seller profile, store, clock, audit, strings, sources, the domain rename, the order of work
  • references/model.mdThe pure model: rates, symmetric rounding, line and document totals, the number format, correction deltas, validation, the tax identifier
  • references/data-model.mdThe entities, the InvoiceStore interface and the in-memory store
  • references/postgres.mdThe migration with the numbering trigger, the immutability triggers and invoicing_create, the Postgres store, notes for other data layers
  • references/service.mdThe service: issue, correct, mark paid, list, render data; the wiring file; the Server Actions
  • references/pdf-writer.mdThe dependency-free PDF writer: pages, two built-in fonts, Polish diacritics, wrapped text
  • references/pdf.mdThe invoice layout with its label tables, the download response and the route
  • references/admin-ui.mdThe register, the issue form and the correction editor: structure, states and behaviour, with one unstyled line editor
  • references/fa3-xml.mdOptional: the FA(3) builder and its rate buckets, with its tests
  • references/ksef-bridge.mdOptional: the bridge to the ksef skill, the verification code, with its tests
  • references/operations.mdEnvironment, delivery by e-mail, the go-live list, traps, extensions
  • references/testing.mdThe model and PDF suites and how to run them
  • references/testing-service.mdThe service suite and the SQL checks for the schema
  • references/provenance.mdThe engineering ledger: what the audit of the earlier implementation changed and how the templates verify it, what was kept on purpose, and what is new in the skill
  • evals/The prompts an operator types after installing (prompts.md) and one file per agent eval: the skill installed into an empty Next.js app, one prompt, no help, then type-checked, built and tested
  • .github/workflows/agent-eval.ymlThe caller of the index's eval workflow, the same in every skill

Recent releases

  1. v0.1.2October 7, 2026

    Fix release, from scoring the prompt-1 agent eval runs against 0.1.1.

  2. v0.1.1October 6, 2026

    Documentation-only release: the templates, the references' rules and the non-negotiables are unchanged from 0.1.0. Wording brought in line with the skill standard.

  3. v0.1.0October 3, 2026

    First release. A VAT invoicing module for a Next.js App Router app: numbering, corrections, a register, a PDF, and an optional bridge to the ksef skill.

After installing

What do I tell my agent?

Say what you need in your own words; the skill supplies the how. These are starting points, and the ones we tested say how it went.

  1. Add invoicing to this app: staff issue VAT invoices with numbers that never skip, can issue a correction when one is wrong, see them all in a list, and download each as a PDF.

  2. Our invoices need to go to KSeF. Build the FA(3) file for each invoice we issue and put the KSeF QR code on the PDF.

    Postgres
  3. Audit the invoices module in this app: can a number be skipped or used twice, and can an invoice change after it was issued?

Tested

How does it do in each agent?

We install the skill into an empty Next.js app, give the agent one of the prompts above and no further help, then type-check, build and run the tests it left behind. Nothing is fixed by hand before the checks, and a failing run is published like a passing one. The procedure and every result are public, and the first prompt runs again before each release.

  • Claude Code2.1.292

    claude-opus-5-5

    Built, checks pass
    Add invoicing to this app: staff issue VAT invoices with numbers that never skip, can issue a correction when one is wrong, see them all in a list, and download each as a PDF.
    Typecheck: passBuild: passTests: pass
    Time
    3 min
    Changed
    29 files, +4,209 lines
    Stack
    No data store
    Skill
    v0.1.2
    Run
    Oct 7, 2026

    Result fileAgent log

  • Gemini CLI0.63.0

    gemini-3.8-flash

    Built, checks pass
    Add invoicing to this app: staff issue VAT invoices with numbers that never skip, can issue a correction when one is wrong, see them all in a list, and download each as a PDF.
    Typecheck: passBuild: passTests: pass
    Time
    13 min
    Changed
    27 files, +4,581 lines
    Stack
    No data store
    Skill
    v0.1.2
    Run
    Oct 7, 2026

    Result fileAgent log

    What we saw

    Rubric 8/8, scored from the summary. The shipped suites ran under vitest (57), the in-memory store is wired as shipped, and getInvoiceActor keeps returning null "rather than inventing arbitrary default users". Only the eight INVOICE_* variables, with the template's own defaults, are listed empty in .env.example. The handover section states the 401, the store that forgets on restart, and the seller name that issuing needs.

  • Codex CLIcodex-cli 0.161.0

    gpt-6.1-sol

    Built, checks pass
    Add invoicing to this app: staff issue VAT invoices with numbers that never skip, can issue a correction when one is wrong, see them all in a list, and download each as a PDF.
    Typecheck: passBuild: passTests: pass
    Time
    2 min
    Changed
    34 files, +4,353 lines
    Stack
    No data store
    Skill
    v0.1.2
    Run
    Oct 7, 2026

    Result fileAgent log

    What we saw

    Rubric 8/8, scored from the diff. The model changed since the baseline (gpt-6-astra at medium effort to gpt-6.1-sol at none), so part of the change may be the model's. It installed server-only and vitest from the registry, ran the shipped suites unchanged (57), copied every template as written apart from passing INVOICE_PDF_STRINGS_EN in the PDF route, which the skill documents as the strings seam, and kept the null actor and the memory store. It added a proxy.ts that calls the same getInvoiceActor for /invoices; this adds nothing the actor does not already refuse, so it is noted, not scored. .env.example holds exactly the eight variables, empty, and the final message states all three handover facts.

Build it with Timerise

How long does it take, and what does it cost?

We quote this module per project. The price depends on what it has to connect to. The path to a number is short and free:

  1. Step 1

    Brief

    Tell us what the module must connect to. Takes minutes, in a chat.

  2. Step 2

    Prototype in 48 hours

    A clickable prototype of your system and a quote, at no cost.

  3. Step 3

    Build and handoff

    One project price. Source code, documentation and IP are yours.

Two ways to get Invoicing

Build it yourself

Install the skill. Your own agent builds the module.

  • MIT licensed, no strings
  • Runs in Claude Code, Codex CLI and Gemini CLI
  • The same rules our engineers build by
$ npx skills add timerise-ai/invoicing

Build it with Timerise

Send a brief. We build Invoicing into a system you own.

  • Clickable prototype and a quote within 48 hours, free
  • One project price, no subscription, no commission
  • Source code, documentation and IP handed over
3 years of support included.

Generated from the skill's own files at commit 7b4c3de. Every rule above links to where the repository says it. All skills