CLI ยท commit 5c68b7e

einvoice

GST e-invoice

Download einvoice.zip
Quickstart
unzip einvoice.zip && cd einvoice
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/python -m pytest -q

einvoice

GST e-invoice (IRN) JSON builder, validator and signed-QR toolkit for Indian businesses that must report B2B invoices to the Invoice Registration Portal (IRP). It converts ERP CSV/Excel exports into schema 1.1 e-invoice JSON through TOML mapping profiles, checks the result against the schema and the portal's business rules before upload, computes IRN hashes for duplicate pre-checks, and decodes and renders the signed QR code.

Unofficial tool. It is not affiliated with or endorsed by GSTN, NIC or the IRP. The schema is a documented subset of version 1.1 and the rule codes are modelled on the portal's, not copied from it. Always validate on the official sandbox before relying on it.

Features

Install

Python 3.12+.

cd einvoice
uv venv .venv
uv pip install -p .venv/bin/python -e '.[dev]'
source .venv/bin/activate        # or call .venv/bin/einvoice directly

Quickstart

einvoice build examples/invoices.csv -p examples/profile.toml -o out/invoices
einvoice validate out/invoices examples/resubmitted.json -c examples/einvoice.toml --errors-csv out/errors.csv
einvoice report   out/invoices examples/resubmitted.json -c examples/einvoice.toml
einvoice irn --gstin 29AAGCB7383J1Z4 --fy 2025-26 --type INV --no INV/001
einvoice qr decode examples/irp_response.json --public-key examples/qr_public_key.pem
einvoice qr render examples/irp_response.json -o out/qr.png

The examples contain 50 synthetic invoices (B2B, SEZ, exports, deemed exports, credit notes) with planted errors. They are dated October-November 2025, so examples/einvoice.toml pins "today" to 2025-11-15; without it the reporting-window rule would flag everything.

Usage

build

einvoice build SOURCE.csv|xlsx -p PROFILE.toml -o OUT

OUT ending in .json produces one array file, otherwise a directory with one file per invoice (<seller gstin>_<doc no>.json). Rows are grouped into invoices by the profile's invoice_key. Exit code 1 if any invoice could not be mapped (the others are still written).

$ einvoice build examples/invoices.csv -p examples/profile.toml -o out/invoices
built 50 invoice(s), 0 failed -> out/invoices

Excerpt of a generated file (27XDAJH4657E1ZG_INV_2511_0013.json; the profile left the tax amounts empty, so they were computed):

"ItemList": [
  {
    "SlNo": "1",
    "PrdDesc": "Aerated water 200ml crate",
    "IsServc": "N",
    "HsnCd": "22021010",
    "Qty": 5.0,
    "Unit": "XYZ",
    "UnitPrice": 600.0,
    "TotAmt": 3000.0,
    "Discount": 150.0,
    "AssAmt": 2850.0,
    "GstRt": 28.0,
    "IgstAmt": 798.0,
    "CesRt": 12.0,
    "CesAmt": 342.0,
    "TotItemVal": 3990.0
  }
],

validate

einvoice validate PATH... [-c CONFIG] [--turnover-crore N] [--window-days N] [--item-tolerance N]
                  [--invoice-tolerance N] [--today YYYY-MM-DD] [--errors-csv FILE]
                  [--format text|json] [-q]

PATH is a JSON file (one invoice or an array) or a directory of them. Exit code 1 if any invoice has errors; warnings alone exit 0. Duplicate document numbers are detected across everything in one run.

$ einvoice validate out/invoices examples/resubmitted.json -c examples/einvoice.toml --errors-csv out/errors.csv
(output trimmed to six of the 23 reported invoices)
07ACGHT1434U1ZC_INV_2510_0009.json  [INV/2510/0009]  INVALID
  E 2103  BuyerDtls.Gstin: GSTIN '27ABCDE1234F1Z' does not match the 15-character GSTIN pattern
07ACGHT1434U1ZC_INV_2511_0012.json  [INV/2511/0012]  WARNINGS
  W 2186  ExpDtls: ExpDtls is only meaningful for export supplies, not B2B
07ACGHT1434U1ZC_INV_2510_0015.json  [INV/2510/0015]  INVALID
  E 2161  DocDtls.Dt: Document date 20/11/2025 is after 15/11/2025
07ACGHT1434U1ZC_INV_2511_0039.json  [INV/2511/0039]  INVALID
  E 2123  ItemList[0]: SEZWOP supplies must carry zero CGST/SGST/IGST
27XDAJH4657E1ZG_INV_2511_0007.json  [INV/2511/0007]  INVALID
  E 2104  BuyerDtls.Gstin: GSTIN 36GCBYH5741C1ZA fails the checksum (expected check character E)
29DYUCV7912B1ZG_INV_2511_0017.json  [INV/2511/0017]  INVALID
  E 2162  DocDtls.Dt: Document is 75 days old; reporting window is 30 days
resubmitted.json  [INV/2511/0028]  INVALID
  E 2171  DocDtls.No: Document INV/2511/0028 already used in FY 2025-26 (first seen in 27XDAJH4657E1ZG_INV_2511_0028.json)
51 invoice(s): 28 valid, 2 with warnings, 21 invalid

The error CSV has one row per issue: source,doc_no,status,severity,code,path,message.

report

$ einvoice report out/invoices examples/resubmitted.json -c examples/einvoice.toml
Invoices: 51   valid 28   warnings 2   invalid 21

By supply type
  B2B    invalid 16  valid 19  warnings 2
  DEXP   valid 2
  EXPWOP valid 2
  EXPWP  invalid 2  valid 2
  SEZWOP invalid 1  valid 1
  SEZWP  invalid 2  valid 2

By rule
  2001  x1   JSON schema violation
  2103  x1   Buyer GSTIN has invalid format
  2104  x1   Buyer GSTIN checksum is wrong
  ...

--format json prints the same data as JSON. einvoice rules lists the catalog.

irn

$ einvoice irn --gstin 29AAGCB7383J1Z4 --fy 2025-26 --type INV --no INV/001
72242e6acf3b05a1b7f258e60c7a66dcbda2d4371755cd1677cfa9fba2c03b99

$ einvoice irn out/invoices/27XDAJH4657E1ZG_INV_2511_0028.json examples/resubmitted.json
27XDAJH4657E1ZG_INV_2511_0028.json  de1734373b099bb3c530f950329e99d90f8ddb698a9a26a89fbcade279e98374
resubmitted.json  de1734373b099bb3c530f950329e99d90f8ddb698a9a26a89fbcade279e98374  DUPLICATE of 27XDAJH4657E1ZG_INV_2511_0028.json

With files, the financial year comes from the document date. --known FILE (one IRN per line) flags ALREADY REGISTERED. Exit code 1 on any duplicate.

qr decode and qr render

SOURCE can be a bare JWT, a file containing one, - for stdin, or an IRP response JSON with a SignedQRCode field (the response's Irn is cross-checked against the QR's).

$ einvoice qr decode examples/irp_response.json --public-key examples/qr_public_key.pem
Header: {"alg": "RS256", "typ": "JWT", "kid": "sample-key"}
  SellerGstin   27XDAJH4657E1ZG
  BuyerGstin    24MWJBR9785D1ZF
  DocNo         INV/2511/0001
  DocTyp        INV
  DocDt         03/11/2025
  TotInvVal     123456.0
  ItemCnt       2
  MainHsnCode   84136010
  Irn           6666b1189bfd2bf265cb31dcf888683c6d8b16a47efd1a006a4d799cb0fca26c
  IrnDt         2025-11-03 11:42:10
Signature: VALID

Without --public-key the signature line reads not checked (no --public-key). A failed verification prints INVALID and exits 1. --json prints header, payload and decoded data.

$ einvoice qr render examples/irp_response.json -o out/qr.png     # .png or .svg
wrote out/qr.png
$ einvoice qr render examples/signed_qr.jwt --terminal

Configuration

Validation settings come from a TOML [validate] table (-c), overridable per flag:

Key Default Meaning
turnover_crore 5.0 Aggregate turnover; above 5 crore HSN needs 6+ digits, otherwise 4+
window_days 30 Max age of a document at reporting time; 0 disables the rule
item_tolerance 1.0 Rupee tolerance for item level arithmetic
invoice_tolerance 1.0 Rupee tolerance for invoice level sums
today system date Reference date for the future/window rules

Mapping profiles are documented in docs/mapping.md; the rule catalog is in docs/rules.md; algorithms and design decisions are in docs/design.md.

Architecture

src/einvoice/
  cli.py        click commands (build, validate, report, irn, qr, rules)
  mapping.py    TOML profile, CSV/XLSX reading, grouping, coercion, amount computation
  schema.py     schema loading and error formatting
  data/         einvoice-1.1.schema.json
  rules.py      business rules, issue catalog, batch duplicate detection
  gstin.py      GSTIN format and checksum
  irn.py        IRN hash
  qr.py         JWT decode/verify/sign, QR rendering
  batch.py      loading invoice files, error CSV, summaries
  config.py     [validate] settings
  dates.py      portal date handling and financial year
  constants.py  state codes, UQC, supply types, GST slabs
examples/       sample data, profile, config, signed QR, generator script
tests/          pytest suite

Tests

.venv/bin/python -m pytest -q

134 tests cover each business rule (positive and negative), schema errors, rounding tolerance boundaries, mapping profiles (types, dates, grouping, computation, errors), CSV/XLSX equivalence, IRN vectors, JWT decode/verify/tamper, QR rendering and the CLI end to end. examples/make_examples.py regenerates the sample data (--qr also makes a new key pair and signed QR).

Limitations