# Invoicing

> Invoice math for React billing apps: VAT per rate the way e-Factura checks it, Romanian VAT rates, and CUI, IBAN and CNP validation.

- Type: React helper (Hooks & Utilities)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add invoicing`
- Docs: https://wingo-ui.com/components/invoicing

## Interaction

Change the quantities of the invoice lines with − and +: the net per line, the VAT breakdown per rate (21% and 11%), the discounts and the total are one computeInvoiceTotals call. Turn on prices with VAT, set a document discount in percent or as an amount, and see the VAT stay exact per rate. Type any CUI, IBAN or CNP in the checker: it recognises which one it is, validates the control digit, names the bank of a Romanian IBAN and reads the birth date and county from a CNP.

## Installation

```bash
npx wingo-ui@latest add invoicing
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/invoicing` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `lib/invoicing.ts`

Wingo UI dependencies (installed automatically):

- [Commerce](https://wingo-ui.com/components/commerce) ([Markdown](https://wingo-ui.com/components/commerce.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))

## Usage

```tsx
import { computeInvoiceTotals, formatVatRate, isValidCui } from "@/lib/invoicing"
import { formatPrice } from "@/lib/commerce"

const totals = computeInvoiceTotals(
  [
    { id: "l1", quantity: 3, unitPrice: 1250, vatRate: 0.21, discount: 10 },
    { id: "l2", quantity: 12, unitPrice: 64.9, vatRate: 0.11 },
  ],
  { discount: { type: "amount", value: 100 } },
)

totals.vatBreakdown.map((row) => `VAT ${formatVatRate(row.rate, "en-US")}: ${formatPrice(row.vat, { locale: "en-US", currency: "USD" })}`)
formatPrice(totals.total, { locale: "en-US", currency: "USD" }) // "$4,829.10"
isValidCui("RO12345674") // true
```

## API

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `pricesIncludeVat?` | `boolean` | `false` | options.pricesIncludeVat: false for B2B invoices (unit prices without VAT, VAT added), true for shops and B2C quotes (VAT inside the prices). |
| `discount?` | `number` | `0` | The demo's document discount, options.discount.value: shared across lines by value after their own discounts, so every VAT rate stays exact. |
| `discountType?` | `"percent" \| "amount"` | `"amount"` | options.discount.type: a percent of the subtotal or a fixed amount (dollars in the demo). |
| `locale?` | `string` | `"en-US"` | The demo's formatting locale (formatPrice from commerce, formatVatRate). |
| `code?` | `string` | `"RO12345674"` | The demo's id checker: a CUI (with or without RO), an IBAN or a CNP. |
| `computeInvoiceTotals?` | `(lines: InvoiceLineInput[], options?: InvoiceTotalsOptions) => InvoiceTotals` |  | Lines are { id, quantity, unitPrice, vatRate? (0.21), discount?, discountType?: "percent" \| "amount" }; options { pricesIncludeVat, discount: { type, value }, defaultVatRate, categoryFor, decimals }. Returns lines (gross, discount, net, vat, total each), gross, lineDiscount, documentDiscount, discount, net, vat, total and vatBreakdown (one row per rate: rate, category, net, vat, total, lineCount). VAT is rounded once per rate on its taxable amount (EN 16931 BR-CO-17, what e-Factura validates), so per-line VAT is informative and the totals always add up. |

## Parts

### computeInvoiceLine (function)

One line on its own (gross, discount, net, VAT and total), for the row of a line editor. Takes the same line and options as computeInvoiceTotals.

### RO_VAT_RATES (constant)

Options for a VAT select, each { value, rate, category, label, description }: 21% standard, 11% reduced, and 0% exempt (E), reverse charge (AE) and intra-community supply (K).

### RO_VAT_RATE (constant)

Romania's standard VAT rate as a fraction, 0.21, in force since Aug 1, 2025. Re-exported from commerce.

### RO_REDUCED_VAT_RATE (constant)

Romania's reduced VAT rate, 0.11, for food, medicines, books and accommodation. Re-exported from commerce.

### formatVatRate (function)

A VAT rate as text in the locale: formatVatRate(0.21, "en-US") is "21%" ("21 %" in ro-RO).

### incrementDocumentNumber (function)

The next number of an invoice series, keeping its padding and prefix: "2026-0412" becomes "2026-0413", "CRP 099" becomes "CRP 100".

### parseCui (function)

Reads a Romanian company code (CUI / CIF) such as "RO 12345674": { digits, vatPayer, valid } with the ANAF control digit checked, or null when it is not 2 to 10 digits.

### isValidCui (function)

Whether a CUI's control digit holds: RO12345674 is valid, RO12345678 is not.

### formatCui (function)

A CUI written the standard way: "RO12345674" for VAT payers, "12345674" otherwise. Returns the input unchanged when it is not a CUI.

### isValidIban (function)

The ISO 13616 mod-97 check with each country's length, for Romanian and other European IBANs.

### formatIban (function)

An IBAN in groups of four for display: "RO49 AAAA 1B31 0075 9384 0000".

### getIbanBank (function)

The bank of a Romanian IBAN from its four-letter code, e.g. { code: "BTRL", name: "Banca Transilvania" }; null for other countries or unknown codes.

### RO_BANKS (constant)

Romanian bank names by IBAN bank code (BTRL, RNCB, BRDE, INGB and more), the table getIbanBank reads.

### parseCnp (function)

Reads a Romanian personal numeric code (CNP): { valid, sex, birthDate, countyCode, countyName, anonymous } with the control digit, date and county checked, or null when it is not 13 digits.

### isValidCnp (function)

Whether a CNP is valid. "0000000000000", the e-Factura placeholder for a buyer who gave no CNP, counts as valid.

### VatCategory (type)

The UBL / EN 16931 VAT category codes an e-Factura line carries: S, Z, E, AE, K, G, O.

### InvoiceLineInput (type)

{ id, quantity, unitPrice, vatRate?, discount?, discountType? }, one line of an invoice or quote.

### InvoiceTotals (type)

What computeInvoiceTotals returns: lines, gross, discounts, net, VAT, total and the VAT breakdown per rate.

## Examples

- **VAT rate options**: RO_VAT_RATES for a line's VAT select, with the e-Factura category of each.
- **Invoice numbering**: incrementDocumentNumber keeps the prefix and the zero padding of a series.
- **CUI and IBAN checks**: Control digits, the standard written form and the bank of a Romanian IBAN.

## See also

- [Commerce](https://wingo-ui.com/components/commerce) ([Markdown](https://wingo-ui.com/components/commerce.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))
- [Masked Input](https://wingo-ui.com/components/masked-input) ([Markdown](https://wingo-ui.com/components/masked-input.md))

---

Canonical: https://wingo-ui.com/components/invoicing
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
