<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AeroSapiens Engineering]]></title><description><![CDATA[AeroSapiens Engineering]]></description><link>https://aerosapiens.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 05 Nov 2026 05:09:43 GMT</lastBuildDate><atom:link href="https://aerosapiens.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[CGST + SGST or IGST? Getting the GST Split Right in Code]]></title><description><![CDATA[How an invoicing app can choose between CGST + SGST/UTGST and IGST from the place of supply, and compute each amount in paise so totals add up.]]></description><link>https://aerosapiens.hashnode.dev/cgst-sgst-or-igst-getting-the-gst-split-right-in-code</link><guid isPermaLink="true">https://aerosapiens.hashnode.dev/cgst-sgst-or-igst-getting-the-gst-split-right-in-code</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[GST]]></category><category><![CDATA[india]]></category><category><![CDATA[invoicing]]></category><dc:creator><![CDATA[Team Aero Sapiens]]></dc:creator><pubDate>Sun, 01 Nov 2026 13:22:11 GMT</pubDate><content:encoded><![CDATA[<p>Every Indian GST invoice has to answer one question before it can show any tax: is this supply within a state, or between states? The answer decides whether the invoice carries CGST plus SGST (or UTGST), or a single IGST line. This post shows how to model that decision and the CGST SGST IGST calculation in code, and where the edge cases are.</p>
<p>This is engineering guidance, not tax advice. The rules come from the <a href="https://cbic-gst.gov.in/">IGST Act and the CGST Act on the CBIC GST portal</a>; check unusual cases with a tax professional.</p>
<h2>The rule in one sentence</h2>
<p>Compare the supplier's location with the place of supply. If both are in the same state or union territory, it is an intra-state supply and the rate is split equally between the central tax (CGST) and the state tax (SGST, or UTGST in a union territory without its own legislature). If they differ, it is an inter-state supply and the whole rate is charged as IGST. Sections 7 and 8 of the IGST Act define the two kinds of supply.</p>
<p>So an 18% item becomes either 9% CGST + 9% SGST, or 18% IGST. The total tax is the same; what changes is who collects it, and which lines appear on the invoice.</p>
<h2>Place of supply is an input, not a guess</h2>
<p>The hard part is not the arithmetic but the place of supply. For goods it generally follows where the movement of the goods ends; for most services to a registered business it is the recipient's registered location. The detailed rules are in the place-of-supply sections of the IGST Act and have many special cases (events, transport, property, online services).</p>
<p>In software, that means: do not derive the place of supply silently from a billing address. Store it as an explicit field on the invoice, default it sensibly, and let the user change it. The calculation then depends only on two state codes.</p>
<h2>Money in paise, rates in basis points</h2>
<p>Floating-point rupees will eventually produce a total that is one paisa off. Store amounts as integer paise and rates as integer basis points (18% = 1800).</p>
<pre><code class="language-js">function taxOn(paise, rateBasisPoints) {
  // Round half up to the nearest paisa; amounts here are never negative.
  return Math.floor((paise * rateBasisPoints + 5000) / 10000);
}

function gstLines(taxablePaise, rateBasisPoints, supplierState, placeOfSupply, isUnionTerritoryWithoutLegislature) {
  if (supplierState !== placeOfSupply) {
    return [{ name: 'IGST', rate: rateBasisPoints, amount: taxOn(taxablePaise, rateBasisPoints) }];
  }
  const half = rateBasisPoints / 2;
  const local = isUnionTerritoryWithoutLegislature ? 'UTGST' : 'SGST';
  return [
    { name: 'CGST', rate: half, amount: taxOn(taxablePaise, half) },
    { name: local, rate: half, amount: taxOn(taxablePaise, half) },
  ];
}
</code></pre>
<p>Two details matter here.</p>
<p><strong>Compute each half from the taxable value.</strong> Do not compute 18% and then divide by two: an odd number of paise cannot be split equally, and the invoice would show two different amounts for two equal rates. Calculating CGST and SGST separately at 9% each gives identical lines.</p>
<p><strong>Rates like 0.25% and 3% exist.</strong> Half of 25 basis points is 12.5, so either allow fractional basis points as above or store rates in hundredths of a basis point. Whatever you choose, keep one rounding function and use it everywhere.</p>
<h2>Line by line, then the invoice total</h2>
<p>Apply the split to each line item (items can carry different rates and HSN/SAC codes), then sum the tax per head across lines.</p>
<pre><code class="language-js">function invoiceTotals(items, supplierState, placeOfSupply, isUtWithoutLegislature) {
  const totals = { taxable: 0, taxes: {} };
  for (const item of items) {
    const taxable = item.quantity * item.unitPaise - (item.discountPaise || 0);
    totals.taxable += taxable;
    for (const line of gstLines(taxable, item.rateBasisPoints, supplierState, placeOfSupply, isUtWithoutLegislature)) {
      totals.taxes[line.name] = (totals.taxes[line.name] || 0) + line.amount;
    }
  }
  const tax = Object.values(totals.taxes).reduce((a, b) =&gt; a + b, 0);
  const exact = totals.taxable + tax;
  const rounded = Math.round(exact / 100) * 100;
  return { ...totals, tax, roundOff: rounded - exact, grandTotal: rounded };
}
</code></pre>
<p>Showing the round-off as its own line keeps the arithmetic visible: anyone checking the invoice can add the lines and get the printed total.</p>
<h2>Edge cases worth a test each</h2>
<ul>
<li><strong>Same state, different city.</strong> Still intra-state. The comparison is by state or union-territory code, not by address.</li>
<li><strong>Union territories.</strong> Those without their own legislature use UTGST instead of SGST; Delhi, Puducherry and Jammu and Kashmir have legislatures and use SGST. Keep this as data, not as a hard-coded list in the calculation.</li>
<li><strong>Exports and supplies to SEZ units.</strong> The IGST Act treats these as inter-state supplies, and they are zero-rated, which affects whether tax is charged at all. That needs its own handling and an accountant's input.</li>
<li><strong>Reverse charge and cess.</strong> Both change what the invoice must show. Model them as explicit flags rather than special-casing in the arithmetic.</li>
<li><strong>Rate changes.</strong> Store the rate on each invoice line at the time of issue, never look it up again later.</li>
</ul>
<h2>How I handle it in Movi Invoice</h2>
<p>I build Movi Invoice, an online invoicing tool, and its India GST template follows the approach above: it works out CGST + SGST/UTGST or IGST from the place of supply, supports HSN/SAC codes, cess, reverse charge, round-off and the amount in words, and keeps rates configurable rather than hard-coded. It is not certified by any tax authority and does not file returns or generate e-invoice IRNs, so the same caveat as this post applies: the software does the arithmetic; the tax decisions stay with the business. It is at <a href="https://invoice.aerosapiens.in">invoice.aerosapiens.in</a> if you want to see the result on a real invoice.</p>
<p>This post was drafted with AI assistance and reviewed by me before publication.</p>
<hr />
<p><em>Bhupendra Varshney is the founder of AeroSapiens, a software company in Noida, India.</em></p>
]]></content:encoded></item></channel></rss>