FinzBooksDevelopers

Reading tax legs

Every line item carries three per-leg tax amounts plus a derived tax_percentage. The amounts are the source of truth — don't re-derive them from rate × taxable on your side.

The four fields

"line_items": [
  {
    "name":           "Consulting services",
    "quantity":       1,
    "rate":           10000.00,
    "discount":       0,
    "taxable_value":  10000.00,
    "cgst_amount":    900.00,
    "sgst_amount":    900.00,
    "igst_amount":    0.00,
    "cess_amount":    0.00,
    "tax_percentage": 18,
    "item_total":     11800.00
  }
]
FieldMeaning
cgst_amountCentral GST — intra-state supplies
sgst_amountState/UT GST — intra-state supplies
igst_amountIntegrated GST — inter-state supplies
cess_amountGST compensation cess — on specific HSN codes
taxable_valuePre-tax line total (after discount, before any tax)
tax_percentageDerived: (cgst + sgst + igst) / taxable × 100, snapped to integer ±0.1

Intra vs inter

For a single line you'll see EITHER CGST+SGST OR IGST, never both. If place_of_supplymatches the supplier branch's state → intra (CGST+SGST split half-half). Otherwise → inter (IGST = full rate).

// Intra-state (Karnataka supplier → Karnataka customer @ 18%)
{ "cgst_amount": 900, "sgst_amount": 900, "igst_amount": 0,    "tax_percentage": 18 }

// Inter-state (Karnataka supplier → Maharashtra customer @ 18%)
{ "cgst_amount": 0,   "sgst_amount": 0,   "igst_amount": 1800, "tax_percentage": 18 }

If you ever see both populated on the same line, it's a data anomaly — open a ticket with the line_item_id.

Rate snapping

tax_percentageis computed from real per-leg amounts (not the line's stored rate field, which can be stale on legacy data). The raw division can produce values like 17.99 due to rounding — we snap to nearest integer when within ±0.1, so you get 18 back. Fractional rates like 12.5 stay unchanged.

Why this matters: GSTR-1 buckets supplies by integer rate (5, 12, 18, 28). A fractional rate on a line would land in "no bucket" on the return.

Don't reconstruct legs

Don't do this on your side:

// WRONG — re-derived from rate, loses rounding precision and per-line cess
const cgst = taxable * tax_percentage / 200;
const sgst = taxable * tax_percentage / 200;

The amount columns are already correct, rounded to 2dp, and include any per-line cess. Use them directly.

Totals

At the document level you also get totals:

{
  "sub_total":  10000.00,
  "tax_total":  1800.00,
  "total":      11800.00,
  "round_off":  0
}

total = sub_total + tax_total + round_off. Round-off absorbs sub-rupee differences when the org's roundInvoiceTotals preference is on.