# E-Factura VAT Calculation in TypeScript and React

> E-Factura VAT calculation in TypeScript: Romania's 21% and 11% rates, VAT rounded once per rate, the totals ANAF checks to the cent and a React line editor.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Oct 9, 2026
- Category: [Build it: screens and apps](https://wingo-ui.com/blog/category/build)
- Reading time: 11 min
- Canonical: https://wingo-ui.com/blog/e-factura-vat-calculation-javascript

## TL;DR

An e-Factura VAT calculation groups the invoice lines by VAT category and rate, adds their net amounts, and rounds base times rate once per group to two decimals; per-line VAT is informative only. Every document total then has to come from those same rounded numbers, because ANAF's validation checks the sums to the cent even though it lets the VAT of a rate drift by less than 1 leu. Romania's rates since August 1, 2025 are 21% standard and 11% reduced.

Your invoice editor shows a VAT figure, the PDF matches it, and the XML you upload to ANAF still has to follow one rule that line-by-line code misses: an e-Factura VAT calculation groups the lines by VAT rate, adds their net amounts, and rounds the VAT once per group. Every total in the file then has to come from those rounded numbers, to the cent. This tutorial builds that in TypeScript: Romania's VAT rates with their dates, VAT per rate in integer cents, the validation rules that compare totals, and an invoice line editor in React. The UI is [Invoice Line Items](https://wingo-ui.com/components/invoice-line-items) from Wingo UI, the library we build, so we are not neutral. The per-rate math is plain TypeScript you can paste anywhere.

## What are the Romanian VAT rates in 2026?

Two rates, both in force since August 1, 2025: 21% standard and 11% reduced. Law 141/2025 raised the standard rate from 19% and replaced the old 9% and 5% reduced rates with a single 11% rate, apart from a transitional 9% for some housing bought under contracts signed by August 1, 2025, which Law 161/2026 extended from July 31 to September 30, 2026 ([Contzilla, August 2026](https://www.contzilla.ro/oficial-legea-161-2026-extinde-aplicarea-cotei-de-9-la-unele-locuinte-intre-7-august-si-30-septembrie-2026/)); [ANAF's before-and-after table of the Fiscal Code](https://static.anaf.ro/static/10/Brasov/Brasov/tva_2025.pdf) shows both versions of article 291. As of October 2026, [ANAF's consolidated Fiscal Code](https://static.anaf.ro/static/10/Anaf/legislatie/Cod_fiscal_norme_2023.htm), last updated by Law 170/2026, still reads 21% and 11%.

| Rate | Code | Use it for |
|---|---|---|
| 21% | S | everything that is not exempt or reduced (19% until Jul 31, 2025) |
| 11% | S | the list in art. 291 (2), including medicines for human use, most food, water and sewerage, books and press, hotel stays, restaurant and catering |
| 0% | E | exempt operations, with an exemption reason |
| 0% | AE | reverse charge, with the buyer's VAT id |
| 0% | K | intra-community supplies, with the buyer's VAT id |

The code is the VAT category an e-Factura line carries (BT-151): S for standard rated, which covers the reduced rate too, E for exempt, AE for reverse charge and K for intra-community supplies.

Two details matter for code. The 11% list has exceptions inside it: alcoholic drinks, non-alcoholic drinks under NC code 2202, food with added sugar and at least 10 g of sugar per 100 g, and food supplements stay at 21%. So store the rate on each product. And the rate depends on a date: article 291 (4) applies the rate in force when the chargeable event happens, usually the delivery. A correction you issue today for a July 2025 delivery carries 19%, so keep dated rates and let the VAT select offer the old one when a correction needs it:

```ts
import { RO_VAT_RATES, type VatRateOption } from "@/lib/invoicing";

// the standard rate by the date of the chargeable event (Fiscal Code art. 291 (4))
const RO_STANDARD_RATE = [
  { from: "2017-01-01", rate: 0.19 },
  { from: "2025-08-01", rate: 0.21 },
];

export const standardRateOn = (isoDate: string) =>
  RO_STANDARD_RATE.findLast((entry) => entry.from <= isoDate)?.rate;

// the VAT select for corrections of supplies made before August 1, 2025
export const RATES_FOR_CORRECTIONS: VatRateOption[] = [
  ...RO_VAT_RATES,
  { value: "19", rate: 0.19, category: "S", label: "19%", description: "Standard rate until Jul 31, 2025" },
];
```

`RO_VAT_RATES` from the [invoicing](https://wingo-ui.com/components/invoicing) lib holds the current five rows of the table above, and the line editor takes any list through its `vatRates` prop.

## Why is e-Factura VAT computed per rate instead of per line?

Because the standard defines it that way. Rule BR-CO-17 of EN 16931, which e-Factura applies, reads: the VAT category tax amount (BT-117) equals the VAT category taxable amount (BT-116) times the rate (BT-119), rounded to two decimals. The Fiscal Code asks for the same on paper: article 319 (20) requires the taxable base for each rate and the VAT amount in lei for each rate. Adding up rounded line VAT gives a different invoice VAT per rate whenever several lines round in the same direction.

Three 21% lines and one 11% line show it:

| Line | Net | VAT | Rounded |
|---|---|---|---|
| Courier delivery, 21% | 22.50 | 4.725 | 4.73 |
| Packaging, 21% | 7.50 | 1.575 | 1.58 |
| Label printing, 21% | 4.50 | 0.945 | 0.95 |
| Sum of the 21% lines | 34.50 | | 7.26 |
| 21% computed once on 34.50 | 34.50 | 7.245 | 7.25 |
| Books, 3 x 14.50, 11% | 43.50 | 4.785 | 4.79 |

Each line rounds its half cent up, three times over; the rate rounds once. 7.25 is the VAT for 21% on this invoice, and the line figures can stay on the PDF as information.

## How do I compute VAT per rate in TypeScript without float errors?

Keep money in integer cents and divide integers. `Math.round(22.5 * 0.21 * 100) / 100` returns 4.72, because 22.5 x 0.21 is stored as 4.7249999999999996. We ran every base from 0.01 to 1,000.00 lei: 1,000 of them have a 21% VAT ending in exactly half a cent, and the float formula rounds 188 of those the wrong way, while `toFixed(2)` gets 718 wrong. When the numerator is an integer, a half cent is exactly .5:

```ts
// lib/vat-per-rate.ts
export type VatLine = {
  quantity: number; // up to 3 decimals: 1.5 hours
  unitPrice: number; // without VAT, in lei, up to 2 decimals
  vatRate: number; // a percent: 21, 11 or 0
};

export type VatRow = { rate: number; netCents: number; vatCents: number };

// an integer divided by a power of 10: half a cent is exactly .5 and rounds away from zero
export const divRound = (numerator: number, denominator: number) =>
  Math.sign(numerator) * Math.round(Math.abs(numerator) / denominator);

export const lineNetCents = (line: VatLine) =>
  divRound(Math.round(line.quantity * 1000) * Math.round(line.unitPrice * 100), 1000);

// 21% of 3450 cents: 3450 x 2100 / 10000 = 724.5, so 725
export const vatCents = (netCents: number, rate: number) =>
  divRound(netCents * Math.round(rate * 100), 10_000);

export function vatPerRate(lines: VatLine[]): VatRow[] {
  const nets = new Map<number, number>();
  for (const line of lines) nets.set(line.vatRate, (nets.get(line.vatRate) ?? 0) + lineNetCents(line));
  return [...nets]
    .map(([rate, netCents]) => ({ rate, netCents, vatCents: vatCents(netCents, rate) }))
    .sort((a, b) => b.rate - a.rate);
}
```

```ts
vatPerRate([
  { quantity: 1, unitPrice: 22.5, vatRate: 21 },
  { quantity: 1, unitPrice: 7.5, vatRate: 21 },
  { quantity: 1, unitPrice: 4.5, vatRate: 21 },
  { quantity: 3, unitPrice: 14.5, vatRate: 11 },
]);
// [{ rate: 21, netCents: 3450, vatCents: 725 }, { rate: 11, netCents: 4350, vatCents: 479 }]
```

A reversal (storno) line carries negative amounts. Rounding the absolute value keeps -7.245 at -7.25, the same magnitude the validator computes, since BR-CO-17 compares absolute values.

## Which e-Factura validation errors check the totals?

BR-CO-10, BR-CO-11, BR-CO-13, BR-CO-14 and BR-CO-15 compare the document totals to the cent, and only BR-S-08, BR-CO-17 and BR-S-09 allow any slack: less than one unit of the invoice currency. We read the rules in the CIUS-RO validation package the Ministry of Finance publishes for e-Factura, [ro16931-ubl 1.0.9](https://mfinante.gov.ro/static/10/eFactura/ro16931-ubl-1.0.9.zip), which embeds the EN 16931 rules from the [European Commission's validation artefacts](https://github.com/ConnectingEurope/eInvoicing-EN16931/tree/validation-1.3.8/ubl/schematron) (version 1.3.8; we rechecked the tolerances below in that release in October 2026):

| Rule | What it checks | Slack |
|---|---|---|
| BR-CO-10 | sum of line nets (BT-106) = the line net amounts (BT-131) added up | none |
| BR-CO-11 | document allowances (BT-107) = the allowance amounts (BT-92) added up | none |
| BR-CO-13 | total without VAT (BT-109) = BT-106 - BT-107 + charges | none |
| BR-CO-14 | VAT total (BT-110) = the VAT of each rate (BT-117) added up | none |
| BR-CO-15 | total with VAT (BT-112) = BT-109 + BT-110 | none |
| BR-S-08 | each rate's base (BT-116) = its lines - its allowances + its charges | under 1 unit |
| BR-CO-17 | each rate's VAT = base x rate, rounded to 2 decimals | under 1 unit |
| BR-S-09 | the same, for standard rated rows | under 1 unit |
| BR-E-08 | the base of the exempt row | none |
| BR-AE-08 | the base of the reverse charge row | none |
| UBL-DT-01 | every amount has at most 2 decimals; unit prices are excluded | none |

BR-CO-17 accepts a VAT that is less than 1 leu (one unit of the invoice currency) away from base times rate. Summing rounded line VAT therefore passes it on most invoices: each line drifts by half a cent at most, so it takes about 200 lines rounding the same way to reach 1 leu. The e-Factura validation errors come from mixing the two paths: BT-117 from the per-rate breakdown while BT-110 is the sum of line VAT, or BT-112 as the sum of line totals with VAT. Those sums have no tolerance, every rule above is flagged fatal, and one cent apart means an error message comes back instead of a validated invoice. Do not build on the slack either. Your PDF, your database and the buyer's books should all show 7.25.

## How do I turn the totals into e-Factura fields?

Derive every field from the same integers, in one order: line nets, the document discount per rate, the base and VAT of each rate, then the document totals. A discount on the whole invoice is the step that breaks mixed-rate invoices. BR-S-08 counts only the allowances that carry the same category and rate as the row, so a 100 lei discount on an invoice with 21% and 11% lines becomes two allowances, one per rate.

`computeInvoiceTotals` from the invoicing lib already shares a document discount across the lines by value, and the last line takes the remainder so the cents add up. The function below keeps each line's own discount inside BT-131, reads the document share back out per rate, and recomputes the VAT of each rate in cents:

```ts
// lib/efactura-totals.ts
import { computeInvoiceTotals, type VatCategory } from "@/lib/invoicing";
import type { InvoiceDocumentDiscount, InvoiceLine } from "@/components/ui/invoice-line-items";
import { vatCents } from "@/lib/vat-per-rate";

type Group = { rate: number; category: VatCategory };

// every amount in cents; rate is the percent the XML carries (BT-119)
export type EFacturaTotals = {
  lines: (Group & { id: string; net: number })[]; // BT-131
  allowances: (Group & { amount: number })[]; // BG-20, one per rate
  breakdown: (Group & { taxable: number; vat: number })[]; // BG-23: BT-116, BT-117
  lineNet: number; // BT-106
  allowanceTotal: number; // BT-107
  taxExclusive: number; // BT-109
  vat: number; // BT-110
  taxInclusive: number; // BT-112
};

const cents = (amount: number) => Math.round(amount * 100);
const percent = (rate: number) => Number((rate * 100).toFixed(2));
const sum = (values: number[]) => values.reduce((a, b) => a + b, 0);
const categoryOf = (rate: number, line: InvoiceLine): VatCategory =>
  line.vatCategory ?? (rate > 0 ? "S" : "E");

// B2B: unit prices without VAT
export function toEFacturaTotals(lines: InvoiceLine[], discount: InvoiceDocumentDiscount | null): EFacturaTotals {
  const totals = computeInvoiceTotals(lines, {
    discount,
    categoryFor: (rate, line) => categoryOf(rate, line as InvoiceLine),
  });
  const groups = new Map<string, Group & { taxable: number; allowance: number }>();

  const outLines = totals.lines.map((line, i) => {
    const group = { rate: percent(line.vatRate), category: categoryOf(line.vatRate, lines[i]) };
    // BT-131 keeps the line's own discount; its share of the document discount becomes an allowance
    const net = cents(line.gross) - cents(line.discount);
    const key = `${group.rate}|${group.category}`;
    const entry = groups.get(key) ?? { ...group, taxable: 0, allowance: 0 };
    entry.taxable += cents(line.net);
    entry.allowance += net - cents(line.net);
    groups.set(key, entry);
    return { ...group, id: line.id, net };
  });

  const breakdown = [...groups.values()].map(({ rate, category, taxable }) => ({
    rate,
    category,
    taxable,
    vat: category === "S" ? vatCents(taxable, rate) : 0,
  }));
  const allowances = [...groups.values()]
    .filter((group) => group.allowance > 0)
    .map(({ rate, category, allowance }) => ({ rate, category, amount: allowance }));

  const lineNet = sum(outLines.map((line) => line.net));
  const allowanceTotal = sum(allowances.map((allowance) => allowance.amount));
  const vat = sum(breakdown.map((row) => row.vat));
  const taxExclusive = lineNet - allowanceTotal;
  return { lines: outLines, allowances, breakdown, lineNet, allowanceTotal, taxExclusive, vat, taxInclusive: taxExclusive + vat };
}
```

Take the sample invoice from the editor demo further down: 25 licenses at 250 lei with 10% off, 6 hours of training at 320 lei and an e-Factura integration at 1,500 lei, all at 21%, plus 12 printed guides at 64.90 lei at 11%. With a 100 lei document discount, the function returns allowances of 92.07 lei at 21% and 7.93 lei at 11%, bases of 8,952.93 and 770.87, VAT of 1,880.12 and 84.80, and 11,688.72 lei in total.

> Live demo (Invoicing): A $100 document discount is already set: press + or − on a quantity and the 21% and 11% rows each recompute their VAT from their own base (this demo prices in dollars). Try it at [Invoicing](https://wingo-ui.com/components/invoicing) and install it with `npx wingo-ui@latest add invoicing`.

Then check the numbers the XML will carry, whatever produced them: an old system, a stored draft, a hand edit. The check runs on the server right before you build the file and answers with the rule ids ANAF's validator uses:

```ts
// lib/efactura-preflight.ts
import type { EFacturaTotals } from "@/lib/efactura-totals";
import { vatCents } from "@/lib/vat-per-rate";

const sum = (values: number[]) => values.reduce((a, b) => a + b, 0);

export function preflight(t: EFacturaTotals): string[] {
  const problems: string[] = [];
  if (t.lineNet !== sum(t.lines.map((l) => l.net))) problems.push("BR-CO-10");
  if (t.allowanceTotal !== sum(t.allowances.map((a) => a.amount))) problems.push("BR-CO-11");
  if (t.taxExclusive !== t.lineNet - t.allowanceTotal) problems.push("BR-CO-13");
  if (t.vat !== sum(t.breakdown.map((r) => r.vat))) problems.push("BR-CO-14");
  if (t.taxInclusive !== t.taxExclusive + t.vat) problems.push("BR-CO-15");
  for (const row of t.breakdown) {
    const same = (x: { rate: number; category: string }) => x.rate === row.rate && x.category === row.category;
    const base = sum(t.lines.filter(same).map((l) => l.net)) - sum(t.allowances.filter(same).map((a) => a.amount));
    // intra-community rules are named IC, the others after their category code
    if (row.taxable !== base) problems.push(`BR-${row.category === "K" ? "IC" : row.category}-08 at ${row.rate}%`);
    // ANAF lets this drift by less than 1 leu; we accept 0
    if (row.category === "S" && row.vat !== vatCents(row.taxable, row.rate)) problems.push(`BR-CO-17 at ${row.rate}%`);
  }
  return problems;
}
```

## How do I build the editable invoice lines in React?

Give the editor the lines and read the totals it computes, then recompute on the server. [Invoice Line Items](https://wingo-ui.com/components/invoice-line-items) edits the lines like a small spreadsheet: a product search that fills unit, price and VAT, a VAT select that defaults to `RO_VAT_RATES`, a discount per line with a % / lei switch, and drag to reorder. The price cells are a [Currency Input](https://wingo-ui.com/components/currency-input), so 1234.5 becomes 1,234.5 as you type and 1,234.50 on blur. Under the lines, the totals show the taxable base, one VAT row per rate and the grand total, all from one `computeInvoiceTotals` call on every keystroke, and `onValueChange(lines, totals)` hands you the same numbers. When the editor is narrower than 768px, every line becomes a card: tap it to edit the line in a bottom sheet on phones (a dialog on larger screens), and the totals fold into a Total row that opens the breakdown.

> Live demo (Invoice line items): Tap a line, change its VAT from 21% to 11% and save it, then open the breakdown under Total: the base and VAT of each rate follow every edit. Try it at [Invoice line items](https://wingo-ui.com/components/invoice-line-items) and install it with `npx wingo-ui@latest add invoice-line-items`.

In a form, the editor's `name` prop writes the lines as JSON into a hidden input, so a server action receives them with the rest of the form:

```tsx
// app/invoices/new/invoice-form.tsx
"use client";

import { useActionState, useState } from "react";
import { Alert } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import {
  InvoiceLineItems,
  type InvoiceDocumentDiscount,
  type InvoiceProduct,
} from "@/components/ui/invoice-line-items";
import { issueInvoice } from "./actions";

export function InvoiceForm({ products }: { products: InvoiceProduct[] }) {
  const [discount, setDiscount] = useState<InvoiceDocumentDiscount | null>(null);
  const [state, formAction, pending] = useActionState(issueInvoice, { problems: [] });

  return (
    <form action={formAction} className="flex flex-col gap-4">
      <InvoiceLineItems
        name="lines"
        products={products}
        documentDiscount={discount}
        onDocumentDiscountChange={setDiscount}
        currency="RON"
        locale="en-US"
      />
      <input type="hidden" name="discount" value={JSON.stringify(discount)} />
      {state.problems.length > 0 && (
        <Alert tone="danger" title="These totals would fail e-Factura" description={state.problems.join(", ")} />
      )}
      <Button type="submit" loading={pending} className="self-end">
        Issue invoice
      </Button>
    </form>
  );
}
```

```ts
// app/invoices/new/actions.ts
"use server";

import type { InvoiceLine } from "@/components/ui/invoice-line-items";
import { preflight } from "@/lib/efactura-preflight";
import { toEFacturaTotals } from "@/lib/efactura-totals";

export async function issueInvoice(_previous: { problems: string[] }, form: FormData) {
  // parse with zod in a real app; totals from the browser are for display only
  const lines = JSON.parse(String(form.get("lines") ?? "[]")) as InvoiceLine[];
  const discount = JSON.parse(String(form.get("discount") || "null"));
  const totals = toEFacturaTotals(lines, discount);
  const problems = preflight(totals);
  if (problems.length > 0) return { problems };
  // build the UBL from `totals`, store it with the invoice number, then upload it to SPV
  return { problems: [] };
}
```

The whole screen around it, with a client picker and CUI lookup, the series and number, due dates, an A4 preview and the e-Factura switch, is the [Invoice Page](https://wingo-ui.com/components/invoice-page) block. Its `onIssue` receives the document with `lines` and `documentDiscount`, the two inputs `toEFacturaTotals` needs.

## What changes for EUR invoices and 0% lines?

The invoice can be in euros; its VAT must also exist in lei. Article 319 (23) of the Fiscal Code lets the amounts use any currency as long as the VAT is expressed in lei at the exchange rate of article 290. In the XML, rule BR-RO-030 sets the VAT accounting currency (BT-6) to RON on any non-RON invoice, and BR-53 then requires the VAT total in that currency (BT-111). The Invoice Page shows the RON equivalent of a EUR total at the BNR rate; compute BT-111 on the server from the same per-rate VAT.

The 0% rows need more than a zero:

- **Exempt (E)**: an exemption reason code or text on the breakdown row (BR-E-10), and a VAT of 0 (BR-E-09).
- **Reverse charge (AE)**: the reason "Reverse charge" (BR-AE-10), a VAT of 0 (BR-AE-09), the seller's VAT id and the buyer's VAT id or registration number (BR-AE-02).
- **Intra-community (K)**: the reason "Intra-community supply" (BR-IC-10) and the VAT ids of the seller and the buyer (BR-IC-02).

The ids those rules ask for have their own guides: [Romanian CUI validation](https://wingo-ui.com/blog/romanian-cui-validation-javascript) for a company's tax and VAT id, the [Romanian CNP validator](https://wingo-ui.com/blog/romanian-cnp-validator-javascript) for B2C buyers (e-Factura accepts 13 zeros when the buyer gives none), and [IBAN validation in JavaScript](https://wingo-ui.com/blog/iban-validation-javascript) for the account you print under payment terms.

## Should I copy this code or install the components?

Copy the math. `vat-per-rate.ts` has no imports and the preflight needs only its `vatCents` and one type, so both paste into any TypeScript project; `efactura-totals.ts` builds on `computeInvoiceTotals` from the invoicing lib and two types from the line editor. If you want React invoice line items that already show VAT per rate, the editor above installs with `npx wingo-ui@latest add invoice-line-items` and brings the invoicing lib and Currency Input with it. All three, and the Invoice Page, are part of [Wingo UI Pro](https://wingo-ui.com/pricing).

For the rest of an admin app around this form, the [Next.js admin dashboard tutorial](https://wingo-ui.com/blog/nextjs-admin-dashboard-tutorial) builds the shell, tables and settings, and more [step-by-step React builds](https://wingo-ui.com/blog/category/build) live in the Build it category.

## Components in this post

- [Invoice line items](https://wingo-ui.com/components/invoice-line-items) (Pro): The lines of an invoice, quote or order edited like a small spreadsheet, with VAT per rate computed the way e-Factura checks it. Install: `npx wingo-ui@latest add invoice-line-items`
- [Invoicing](https://wingo-ui.com/components/invoicing) (Pro): Invoice math and the ids an invoice carries: VAT per rate the way e-Factura checks it, Romanian VAT rates, CUI, IBAN with its bank, CNP. Install: `npx wingo-ui@latest add invoicing`
- [Invoice Page](https://wingo-ui.com/components/invoice-page) (Pro): An invoice editor and viewer for Romanian B2B: client, series, dates, VAT lines, totals, an A4 preview, sending and e-Factura status. Install: `npx wingo-ui@latest add invoice-page`
- [Currency Input](https://wingo-ui.com/components/currency-input) (Pro): A money field formatted per locale as you type, with the symbol on the right side, an optional currency selector and quick amounts. Install: `npx wingo-ui@latest add currency-input`

## FAQ

### What are the VAT rates in Romania in 2026?

21% standard and 11% reduced, both in force since August 1, 2025 under Law 141/2025 and article 291 of the Fiscal Code. Before that date the standard rate was 19% and the reduced rates were 9% and 5%; a transitional 9% for some housing, extended by Law 161/2026, ended with deliveries on September 30, 2026. Exempt, reverse charge and intra-community lines go on an e-Factura invoice at 0% with the category codes E, AE and K.

### Is e-Factura VAT calculated per line or per rate?

Per rate. Rule BR-CO-17 defines the VAT of each rate as the rate's taxable amount times the rate, rounded to two decimals, and article 319 (20) of the Fiscal Code asks for the VAT amount in lei for each rate. VAT shown on a line is informative and never feeds the totals.

### Why does ANAF reject my e-Factura with BR-CO-14 or BR-CO-15?

Both rules compare sums to the cent. BR-CO-14 needs the invoice VAT total to equal the sum of the VAT per rate, and BR-CO-15 needs the total with VAT to equal the total without VAT plus that VAT. They fail when one total comes from rounded line amounts and another from the per-rate breakdown.

### Does e-Factura accept a one-cent difference in VAT?

For the VAT of a rate, yes: in the ro16931-ubl 1.0.9 validation package, BR-CO-17 accepts a value less than one currency unit away from base times rate. The rules that add amounts up (BR-CO-10, BR-CO-11, BR-CO-13, BR-CO-14 and BR-CO-15) have no tolerance at all.

### Which VAT rate applies when a July 2025 delivery is invoiced in August 2025?

Generally the rate in force when the chargeable event happened, which for a delivery is the delivery date, per article 291 (4) of the Fiscal Code. That makes it 19% for a July 2025 delivery, with exceptions for advances and invoices issued before the delivery.

---

Source: https://wingo-ui.com/blog/e-factura-vat-calculation-javascript
