Every scenario we handle -- not a highlight reel.
GST-correct bookkeeping for a Shopify seller is genuinely fiddly: place of supply, B2B vs B2C, exports under an LUT, returns that shouldn’t net against sales, multiple GST rates in one day. This page is long on purpose -- it's the real list of what's actually built, including the edge cases, not a marketing summary of them.
GST place-of-supply & tax split
The core mechanic everything else builds on -- get this wrong and every downstream number is wrong too.
Ship-to state, not billing state
Place of supply for goods is the customer's ship-to state -- a gift order, a different delivery address on file, or a centrally-billed corporate purchase never changes this.
Per-fulfillment ship-from state
The seller-side state is resolved from the specific warehouse that actually fulfilled that order, not a single shop-wide constant -- a genuinely multi-warehouse seller gets the right split even when different orders ship from different states on the same day.
Same state -> CGST+SGST, different state -> IGST
Each half of the applicable rate for intra-state, full rate as IGST for inter-state -- computed per fulfillment, then grouped for posting.
Multiple GST rates within one state-day
A state's daily aggregate can span 5%, 12%, 18%, 28% items in the same day -- each rate gets its own Sales + CGST/SGST(or IGST) line-group within the one voucher, never blended into a single number.
Credit notes use the return date's state
A return posts against the state the return date resolves to -- not silently re-using the original shipment's state -- and never nets against the original Sales entry.
B2B orders & GSTIN handling
The moment an order carries a buyer GSTIN, it needs to become a completely different kind of invoice -- not a variant of the same one.
Full B2B deep diveThree ways to capture a GSTIN
Shopify's checkout has no native GSTIN field -- Ledgeree reads it from a checkout note attribute, an order or company metafield, or Shopify's native B2B/Wholesale company (Shopify Plus), configurable per shop.
Matched by GSTIN, never by name
Customer names vary and typo across orders ("Acme Pvt Ltd" vs "ACME Private Limited") -- the one reliable identifier is the GSTIN, so party-ledger lookup is always keyed on it, checked before ever creating a new ledger.
Auto-created Sundry Debtor / Contact
The first order from a new B2B customer creates their dedicated ledger (name + GSTIN + state) on the fly -- idempotent, so a hundred repeat orders from the same customer never create a hundred ledgers.
Never folded into the B2C aggregate
B2B orders are excluded from the standard state-day gateway/website aggregate entirely and post against the customer's own ledger -- unlike at least one competing Shopify-to-Zoho connector that reportedly drops the captured GSTIN and books every buyer as a plain consumer.
One voucher per fulfillment, never aggregated
Unlike the B2C daily aggregate, every B2B fulfillment gets its own voucher -- so a tax-credit invoice always maps to exactly one real shipment, never a blended multi-order total.
Its own Sales / Sales Return ledger pair
B2B sales and returns post to dedicated ledgers, kept separate from ordinary retail Sales/Sales Return -- your B2C and B2B revenue never mix in one account.
Configurable GSTIN-source-not-enabled fallback
If auto-ledger-creation is switched off, a captured GSTIN is simply ignored and the order falls back to plain B2C -- a deliberate escape hatch, not a silent failure.
Export orders
Zero-rated, but not a simple 'set GST to 0%' toggle -- exports have their own invoicing rules and their own failure modes.
Full export deep diveAutomatic detection by ship-to country
Any order shipping outside India is treated as an export -- no manual flagging per order.
Zero-rated under your LUT, blocked without one
Every line item is posted at 0% GST regardless of its normal domestic rate -- but only once a valid LUT (Letter of Undertaking) number is configured; without one, the export order is blocked (not silently posted at the wrong rate).
LUT financial-year staleness handling
A LUT is filed per financial year and goes stale every April 1st. Ledgeree records which FY your LUT covers, blocks an export order whose FY doesn't match, and nudges you every January-March to file the next year's LUT before the current one expires.
One voucher per shipment, never aggregated
Like B2B, export orders are never folded into a daily aggregate -- a Shipping Bill (customs) is issued per physical shipment, so one voucher always maps to exactly one shipping bill, never several blended together.
Its own export Sales / Sales Return ledger pair
Kept out of your domestic Sales accounts entirely, same principle as B2B.
Shipping Bill number & port code, recorded after the fact
A Shipping Bill doesn't exist until customs actually clears the shipment, days after the order syncs -- so Ledgeree lets you record the Shipping Bill number, date, and port code against the already-posted voucher once you have them, for your Rule 46 compliance records.
Foreign-currency total captured for your records
When a customer is charged in a foreign currency, that currency and total are recorded alongside the INR figures your books actually need (already correctly converted by Shopify's own payment processing) -- so you have both figures on file without a manual note.
Skip toggle if you invoice exports manually
Don't want export orders synced at all? A single toggle excludes them from sync entirely, with no blocking errors generated for orders you've chosen to handle yourself.
Returns & credit notes
A return is not a reversal -- your books need to show what actually happened, when it happened.
Same-day returns never post at all
If an order is returned before its shipment's daily aggregate has even posted, it's excluded before ever reaching your books -- no Sales entry, no credit note, no cleanup needed.
Next-day-or-later returns become real Sales Return vouchers
Posted against the return date's own state as their own daily aggregate -- gross sales and returns both stay visible on their own lines, exactly what a GST return expects, instead of silently netting against the original entry.
Dedicated Sales Return ledger, never the Sales ledger
Returns never reverse into the Sales account directly -- always their own ledger, so a glance at Sales alone never overstates true net revenue.
Party ledger granularity
How finely your B2C sales get broken out by payment method or courier -- a bookkeeping-depth choice, not a correctness one.
Single ledger (default)
Every B2C sale posts against one default party/clearing ledger -- simplest books, right for most sellers.
Per-gateway ledgers
Split B2C sales by payment gateway (Razorpay, COD, etc.) into their own ledgers -- useful when you reconcile gateway settlements separately.
Per-gateway + per-COD-carrier ledgers
COD orders further split by the courier/tracking company handling cash collection -- for sellers who reconcile COD remittances by courier.
Graceful fallback, never a blocked sync
An unmapped gateway or carrier falls back to the next level up (carrier -> gateway -> shop default) automatically -- routing granularity is a bookkeeping preference, never allowed to block a real sync.
Product mapping & GST rates
Configuring GST% shouldn't mean filling in a spreadsheet row for every SKU you sell.
Three-tier GST rate resolution
Set a shop-wide default GST rate, override it per Shopify product type, or override it per individual SKU -- resolved in that order, so you only configure exceptions, not every product.
HSN/SAC code per SKU
Mapped alongside each SKU, mandatory on a compliant tax invoice -- captured once per product, never re-entered per order.
Blocks, never guesses, on missing configuration
If a SKU truly has no resolvable GST rate (no per-SKU, no product-type, no shop default), the order is blocked with a clear error rather than posted at an invented rate.
Itemized billing (optional)
Post one lump Sales figure per day, or go all the way to per-SKU inventory lines in Tally -- your choice.
Off by default: one Sales figure per state/day/rate
The simplest, fastest-to-reconcile posting shape -- right for sellers who don't need per-product inventory tracking inside Tally itself.
On: every order line item becomes its own inventory line
Turns on real Stock Item-level posting in Tally (ALLINVENTORYENTRIES), for sellers who want product-level inventory movement tracked directly in their books.
Auto-mapped against your real Tally Stock Item list
A one-click action fetches your actual Stock Item master list from Tally and matches Shopify SKUs to it by name -- no manual typing of item names into a spreadsheet.
Hard block on an unmapped SKU, never a silent guess
If itemized billing is on and a SKU doesn't match a real Stock Item, the push is blocked with a clear error -- Ledgeree deliberately never auto-creates a Stock Item master on your behalf; you map or create it, then it flows.
Voucher numbering & multi-company
Invoice sequences are expected to be gap-free and explainable -- not an accident of retry timing.
Tally's own auto-numbering, or an app-controlled sequence
Default lets Tally number vouchers itself; switch to app-controlled numbering for a custom prefix/suffix/padding and your own counter.
Monthly or financial-year counter resets
An app-controlled sequence can restart every calendar month or every FY (1st April) -- configurable per voucher type.
A number is only consumed once Tally confirms the push
The counter never increments before a push is confirmed successful -- so a failed or retried push can never burn a real invoice number that never actually appears in Tally.
Multi-company / FY-rollover routing
For sellers who create a new Tally company every financial year, vouchers route to the correct company automatically based on a configurable naming pattern -- no manual company-switching.
Multi-instance / multi-port Tally routing
Running more than one Tally instance (or company on different ports) on the same machine or network -- Ledgeree probes and posts to the right one automatically.
Reliability & safety guards
The parts you never see until something would otherwise go quietly wrong.
Won't post real vouchers to an unlicensed Tally
If your Tally instance is running in demo/educational mode, a voucher write is refused outright -- so a licensing gap gets caught immediately, not discovered later as vouchers that silently never actually saved.
No cloud round-trip for Tally data
A small local relay agent talks to your Tally Prime installation directly over your own network -- your ledger and voucher data reaches Tally without needing to pass through a third-party server that also has to read your books.
Cap reached? Pause & prompt, never silent overage billing
At 80% of your plan's shipment cap you get a warning; at 100%, new fulfillments are still captured and cached -- never dropped -- while sync pauses until you upgrade, then flushes through automatically in order.
Automatic retry with backoff, then a clear failure log
A failed push retries automatically; if it keeps failing, sync pauses and the real ERP error message is logged -- not just "something went wrong."
Reconciliation view
A dedicated view compares Shopify's own totals against what actually posted in Tally/Zoho, to catch sync drift -- separate from payment/settlement reconciliation, which is out of scope.
Tally Prime or Zoho Books
One canonical order model, two very different ERPs underneath.
All integrationsTally Prime: local relay agent, no cloud API required
Tally has no cloud API of its own -- the relay agent bridges your local Tally installation to Ledgeree's sync engine.
Zoho Books: direct cloud API, no local agent needed
Zoho's own REST API is called directly -- GST itself is computed by Zoho from each line's tax percentage, so the same canonical order model drives both adapters without duplicating tax logic.
Same GST correctness either way
Place-of-supply splits, B2B GSTIN handling, export zero-rating, and return handling are identical regardless of which ERP you connect -- the adapter changes, the correctness doesn't.
From fulfillment to booked voucher
- 01
Connect
Install from the Shopify App Store, pick Tally Prime or Zoho Books, and pair the local relay agent (Tally) or authorize your org (Zoho) in a couple of minutes.
- 02
Map once
Tell Ledgeree which Shopify location maps to which warehouse/state, and your GST rates per product or product type. Most of this only needs setting up once.
- 03
Ship orders as usual
The moment an order is fulfilled, it's captured. Nothing posts on unfulfilled or pre-ship edits -- only what actually shipped.
- 04
Wake up to booked vouchers
Each day's shipments post as correctly GST-split vouchers in your books, with returns credit-noted separately -- ready for your accountant, not a pile of CSVs.
Ledgeree Books is the standalone answer.
Sole proprietors and partnerships without any accounting software get a read-only daybook, a running GST-liability tracker, and a downloadable GSTR-1/3B-ready export -- no relay agent, no ERP required.
See Ledgeree BooksSee it against your own orders.
A 5-day free trial is enough to watch a real day’s shipments post correctly.
Start free trial