GST invoice data model basics: the minimum fields, HSN handling, and admin screens needed to generate compliant invoices and simplify reconciliation.

Most GST invoice problems are not “complex tax” problems. They are missing or inconsistent data problems. An audit fails when the invoice cannot be tied cleanly to what was sold, to whom, where it was supplied, and how tax was computed.
A common trigger is HSN being absent, outdated, or applied at the wrong level. Teams may store an HSN on the product, but the invoice line is created from a different SKU name or variant, so the HSN never makes it onto the final document. Another frequent issue is the wrong tax split: charging IGST when it should be CGST and SGST (or the reverse) because “place of supply” was guessed from the shipping address without storing the state codes used for the decision.
Finance teams feel this immediately. Reconciliation becomes a daily cleanup job: invoice totals do not match the order, the order does not match the payment gateway settlement, and refunds become a chain of manual notes. Even small rounding differences across line items can create mismatches between the invoice PDF, GST reports, and the ledger.
Here are the patterns that cause most mismatch pain:
The goal of a GST invoice data model is simple: store a minimum set of order, product, party, tax, invoice, and credit note fields so every number can be reproduced and explained later. Keep it small, but do not drop legally important fields that decide tax type, rate, and reporting.
If you want GST invoices to be easy to generate and easy to reconcile later, start with a small set of objects and make each one do a single job. A clean GST invoice data model is less about having many tables and more about keeping facts stable over time.
Here are the core records most teams need on day one:
An Invoice should be separate from an Order. Orders can change (edited address, canceled items, partial fulfillment). Invoices should not. They need stable numbering, dates, and totals that never “drift” because someone updated an order later.
The anchor for tax accuracy is Line Items. Each order line item (and later each invoice line item) should hold the exact quantity, unit price, discount, and tax breakdown for that specific item. That is where HSN/SAC and GST rates actually get applied.
One detail that saves finance teams: store snapshots. When you generate an invoice, copy the product description, HSN/SAC, tax rate, and pricing into invoice line items. Do not rely on the current product master, because rates and names change.
Optional but often worth adding early are Returns, Refunds, and Credit Notes as separate records. Example: if a customer returns one item from a two-item order, you want a credit note that references the original invoice line, while the payment refund record references the gateway transaction. Keeping these as explicit objects prevents month-end “manual fixes” in GST registers.
If you build this in Koder.ai, treat each object as a simple screen first (create, view, edit), then add invoice generation only after snapshots and line-level fields are in place.
HSN (for goods) and SAC (for services) are not “invoice-only” details. They start at the product or service definition, and then get copied onto each invoice line at the moment you raise the invoice. This keeps mixed carts correct and makes audits easier because each line stands on its own.
A practical minimum data model is:
Putting HSN/SAC on the Product helps your admin team maintain it in one place. Copying it to InvoiceLine is what makes past invoices stable. Even if the product later changes, the invoice still shows what was true at the time of sale. This is the heart of a GST invoice data model that does not break during reconciliation.
For HSN storage, keep it simple: code is required, description is optional, and an effective_from date is optional if you want a change history. Most teams do not need the description on every line item, but it can help when finance checks exceptions.
Mixed carts are normal: one invoice can have multiple line items and therefore multiple HSN/SAC codes. Do not try to force one code per invoice. Totals roll up at the invoice level, while classification stays at the line level.
Change management is where people get into trouble. Use a small rule set:
Admin screen wise, you only need one place to edit Product tax fields, plus a read-only view on the invoice line to confirm what was captured when the invoice was created. If you are building these screens quickly, tools like Koder.ai can generate the basic CRUD pages and data tables from this model with minimal effort.
A GST invoice data model fails most often on party details. If the buyer or seller identity is even slightly off, your invoice may be valid on paper but painful in returns and reconciliation.
Start by treating “seller”, “buyer”, and “ship-to” as separate parties, even when they are the same person. This prevents later hacks when a customer adds a different shipping address or when you sell from more than one GST registration.
Keep the fields boring and explicit. These are the ones that usually end up needed on the invoice and in reports:
Store state as both a human-readable name and a state code, because reporting and place-of-supply rules often rely on the code.
Capture both billing and shipping addresses on the order, not just on the customer profile. Profiles change; invoices should not.
Place of supply should be stored as a specific state code on the invoice (copied from the order at invoice time). Do not “recalculate” it later. If your rule is “ship-to state”, store that result, plus the state used to decide it. This makes audits and disputes easier.
For B2B, buyer GSTIN is normally required and should be validated for length and format at entry time. For B2C, GSTIN can stay empty, but you still need a full address and state to determine whether CGST/SGST or IGST applies.
A simple rule that works in most systems: if buyer GSTIN is present, treat as B2B; if not, treat as B2C. If you need exceptions, store an explicit customer_type field.
If you have branches or business units with different GST registrations, model “Seller Entity” as its own record with its own GSTIN and address. Each order should reference exactly one seller entity, and each invoice should copy those details so historic invoices stay accurate even if the seller address changes later.
Tools like Koder.ai can generate the admin forms for these records quickly, but the key is the structure: separate seller entity, order-time snapshots, and an explicit place-of-supply state code.
The most common split is simple: if the place of supply is in the same state as the supplier, tax is CGST + SGST. If it is a different state, tax is IGST. Your system should not “recompute later from totals” because tiny differences (rounding, discounts, shipping) are exactly what cause mismatches.
At minimum, store tax numbers at the invoice line level, not just the invoice header. That way you can explain every rupee on the invoice and match it back to product, HSN, and revenue.
A practical minimum per invoice line in your GST invoice data model looks like this:
Discounts are where systems get messy. Decide one rule and store it clearly. If discounts reduce the price before tax (typical for item discounts and coupons), store the original gross amount, the discount amount, and the resulting taxable value. If you have an order-level coupon, allocate it across lines (usually proportional to each line’s pre-discount taxable value) and store each line’s allocated discount so your tax math remains explainable.
Rounding should be consistent and recorded. Choose whether you round at the line level or only at the invoice level, then store the rounded results you printed. Many teams compute tax per line, round to 2 decimals, sum, then apply a final invoice_rounding_adjustment field to reach the exact payable amount.
Shipping and handling should not be a hidden add-on. Treat them as a separate invoice line with its own HSN/service code and tax rate rules. For example, an order with two products and a shipping fee becomes three lines, each with stored taxable value and tax component amounts, making finance reconciliation much easier.
Once tax is calculated, the invoice still needs “document” fields that make it valid, auditable, and easy to reconcile later. In a GST invoice data model, treat the invoice header like a legal record: it should be stable even if product or customer data changes in the future.
Start with the header basics: invoice number, issue date (the date on the invoice), invoice type (tax invoice, export, B2B, B2C, etc.), and currency. Even if you mostly invoice in INR, storing currency avoids messy edge cases for exports or multi-currency marketplaces.
Numbering is where teams get burned. Keep a series or prefix (for example “FY25-INV-”), store the financial year, and enforce uniqueness at the database level. Also store “next number” controls per series in admin so two admins cannot issue the same number at the same time.
Totals should be stored explicitly, not only derived. Save subtotal (taxable value), total tax, grand total, and a separate round-off amount. If you recalculate later from line items, a small rule change can make old invoices stop matching the filed return.
Statuses should reflect the real lifecycle and lock the record when needed:
Finally, store generated artifact metadata: PDF template version, generation timestamp, and a file identifier. A hash is optional, but useful if you need to prove the PDF was not altered.
Example: if a support agent regenerates a PDF after a template update, the invoice totals and number should stay identical, but the stored template version explains why the PDF layout looks different.
If you want clean GST invoices, don’t start in the invoice screen. Start with the admin pages that feed it. A good GST invoice data model stays small when these inputs are controlled and consistent.
The product master is where most future mismatches begin, so keep it strict. Each SKU should have exactly one default HSN (or SAC for services), plus a default GST rate and any exceptions that apply only for certain dates.
A practical product screen usually needs:
Avoid a “calculator” UI. Instead, store inputs your system can apply consistently: rate tables, place of supply rules you follow, and how you decide intra-state vs inter-state (usually by comparing supplier state and ship-to state).
Keep the tax screen focused on: tax rate by category/HSN group, effective dates, and what should happen when the buyer provides a valid GSTIN vs not.
The customer screen should capture GSTIN and its validation status, plus default billing and shipping addresses. Don’t let users type freeform states; use a controlled list so “KA” and “Karnataka” don’t become two different values.
Your company profile screen is equally important: legal name, GSTIN, registered address, and invoice series settings (prefix, next number, and financial year boundaries). Lock this down with permissions because changes affect every future document.
You don’t need a complex system, but you do need a trail. Log who changed HSN/SAC, GST rates, invoice series settings, or company GSTIN, along with the old value, new value, timestamp, and reason.
If you’re building these screens in a tool like Koder.ai, treat audit logging and effective dates as first-class fields from day one. They cost little to add early and save hours during finance review later.
A compliant invoice is less about fancy formatting and more about freezing the right facts at the right time. If you design your GST invoice data model around this flow, finance work becomes a simple match, not a weekly investigation.
Before you calculate tax, lock an order snapshot: items, quantities, unit prices, discounts, shipping/handling charges, customer GSTIN (if any), billing and shipping addresses, and place of supply signals. The snapshot should not change even if the product price or HSN mapping changes later.
Compute taxes and generate invoice lines from the snapshot. Each invoice line should copy the HSN/SAC, tax rate(s), taxable value, and tax amounts used at that moment, instead of looking them up live later.
Assign the invoice number and issue date, then mark the invoice as issued. From this point, block edits to pricing, tax rates, HSN codes, and addresses on the invoice record. If you need to allow anything, limit it to non-financial notes and internal tags.
Generate the PDF/print view from the issued invoice, then store the final totals you will report: taxable total, CGST/SGST/IGST totals, rounding, and grand total. If you want extra safety, store a document version or checksum so you can prove the printout matches the stored numbers.
After issue, changes should follow rules, not edits:
If you build this flow into your admin screens (Koder.ai-style planning mode is helpful for mapping the steps before building), your team can generate invoices quickly without breaking reconciliation later.
Reconciliation gets messy when payments are treated as a single “paid/unpaid” flag on the order. Keep payments and refunds as separate records that point to the order and the invoice, so finance can match bank settlements without rewriting history.
A compliant invoice should stay stable after it’s issued. If a customer pays in parts, or you refund later, record that movement as a payment or refund entry, not as a change to the invoice totals.
Minimum fields that usually make reconciliation easy:
If the customer returns one item, don’t “reduce the invoice.” Issue a credit note and link it to the original invoice. The invoice register stays clean, and the refund ties back to the credit note.
Give finance a single screen that answers: what was issued, what got paid, what is still open, and what was reversed. Include ageing (0-7, 8-30, 31-60, 60+ days) and drill-down to the related payment and refund entries.
Exports most teams need every month:
Example: an order is Rs 10,000, paid Rs 6,000 today and Rs 4,000 next week. The invoice stays Rs 10,000. Your finance view shows balance Rs 4,000 until the second settlement arrives, then marks it fully paid without altering the issued document.
Most GST invoice problems are not “tax logic” problems. They are record keeping problems: the numbers on the PDF do not match what finance exports, or the invoice cannot be explained months later.
The first trap is calculating GST only at view time. If you compute CGST/SGST/IGST every time someone opens an invoice, you will eventually get different results after a rate change, rounding change, or a bug fix. Store the computed tax breakdown that was used when the invoice was issued, even if you also store the inputs.
A second trap is allowing edits to an issued invoice. Once an invoice is final, changes should happen through a credit note or a replacement flow with an audit trail. Otherwise, you will see “why does the customer PDF differ from the books?” arguments.
Here are the mismatch patterns that show up most often in a GST invoice data model:
A quick example: you sell to a customer in Karnataka, but the shipping address is in Maharashtra. If your system picks the billing state for place of supply by mistake, you may charge CGST+SGST instead of IGST. If you also recalculate tax on the fly, that error can silently “fix itself” later, leaving finance with numbers that do not match the issued document.
When you build admin screens (whether custom or via a platform like Koder.ai), add small guardrails: lock issued invoices, show place-of-supply inputs next to the computed tax type, and keep an immutable snapshot of HSN, rate, and rounding used at issue time.
Before you send an invoice to a customer or mark it as “issued”, run a fast set of checks. This is where most small mistakes turn into big reconciliation headaches later. If you’re building a GST invoice data model, it’s worth baking these checks into both validation rules and your admin UI.
A simple example: a customer updates their shipping address after payment, and the state changes. If you re-issue the same invoice number with new tax, your register and payment records stop matching. The safer approach is to keep the original invoice immutable and create an adjustment document.
Next steps: implement the screens and validations first, then iterate. In Koder.ai, start with Planning Mode to sketch the records and admin screens (products with HSN/SAC mapping, customer/GSTIN details, tax rules, and invoices). Generate the app, test a few real orders end-to-end, then use snapshots and rollback to safely refine the workflow. When you need deeper customization or reviews, export the source code and continue evolving it with your usual process.