Softechinfra
Business

We Migrated a Family-Owned Jewellery Chain From Tally-Only to a Custom Web ERP — Here's the 90-Day Plan

A 6-store jewellery chain in Coimbatore moved from Tally Prime + 4 Excel sheets to a custom web ERP with hallmark traceability and live gold-rate tied billing. The 90-day extraction, dual-write, and reconciliation playbook.

Vivek KumarVivek Kumar
November 15, 202516 min read
We Migrated a Family-Owned Jewellery Chain From Tally-Only to a Custom Web ERP — Here's the 90-Day Plan

A 6-store family-owned jewellery chain in Coimbatore had been running on Tally Prime since 2009. Beautiful, conservative, audit-friendly — and increasingly the bottleneck. Inventory was a separate Excel per store. Hallmark BIS numbers were tracked in a third spreadsheet. Live gold rates were typed into an invoice template by a counter executive every morning at 10:15. The owner's son took over operations in March 2025 and gave us a brief: "build me a web ERP that does what Tally does, plus the four sheets, and ties to live gold rates. Do not break a single audit." We shipped in 90 days. This is the playbook.

6
Stores Across Coimbatore + Erode
16 yrs
Of Tally Data to Migrate
90 days
Discovery to Cutover

The Answer in 60 Words

We extracted 16 years of Tally vouchers via the Tally ODBC + XML API, normalised them into a Postgres schema, ran a 4-week dual-write (every Tally entry also wrote to the web ERP), reconciled daily, and cutover on a Sunday at 11:14 pm. The web ERP runs on Next.js + Postgres + a Tally bridge written in Python.

Why This Matters Now (For Indian Jewellery SMBs)

Three forces hit jewellery SMBs in 2025. First, BIS hallmark mandatory traceability on 6-digit HUID makes Tally-only inventory an audit risk — every piece needs a separately searchable trail. Second, gold rates have become volatile enough that a 10:15 am rate is wrong by 11:30 am — cash-flow leaks were quietly opening. Third, customers under 35 expect a digital invoice with QR for the BIS number; they are checking it on the BIS Care app before they leave the store. The choice for SMBs is no longer "Tally vs ERP" — it is "Tally plus a custom layer" or "lose the next-gen customer."

We do not believe SMBs should rip out Tally. Tally is genuinely good at what it does (statutory accounting). The play is to build a web ERP that handles the parts Tally is bad at (multi-store inventory, hallmark traceability, live rates, customer portals) and bridge the two so the financial books still flow through Tally for the CA's audit.

The Client (Specific Details)

  • Sector: Jewellery retailer — gold, diamond, silver, kundan
  • Location: Coimbatore (4 stores) + Erode (2), Tamil Nadu
  • Stock value: ₹38 crore floating, ~14,000 SKUs
  • Staff: 78 — 32 sales counter, 12 valuation/QA, 14 admin/back-office, 8 management, 4 IT/maintenance, 8 security
  • Tally usage: Tally Prime 4.1, on-premise Windows server with 6 client installs, FY2024-25 had 240,000 vouchers
  • The trigger: The CFO retired in February 2025. The new ops head asked her CA: "is the closing stock as of March 31 actually reconciled across all stores?" The CA answered: "It is reconciled in Tally. The Excel sheets do not always match." The owner gave us 90 days.
  • The Architecture (Tally-as-Source-of-Truth Pattern)

    📊
    Tally Bridge (Python)
    A Python service running on the in-store Windows box. Polls Tally's HTTP XML API every 90 seconds for new vouchers, pushes them to the web ERP via signed REST. Bidirectional — sales raised in the web UI write a voucher back to Tally.
    🪙
    Live Gold-Rate Service
    Polls 3 sources (Mehrasons, Riddisiddhi Bullions, IBJA) every 90 seconds. Median rate becomes the "today's rate" for billing. Per-store override allowed with audit log.
    🔍
    HUID Hallmark Tracker
    Each piece has a 6-digit BIS HUID. The web ERP maintains a per-piece ledger: BIS issuance date → store stock-in → display → sale → BIS verification on customer phone. QR on the invoice deep-links to BIS Care.
    🛡️
    Reconciliation Daemon
    Runs nightly. Compares web ERP totals to Tally totals per ledger, per store. Drift greater than ₹100 raises a Slack alert to the ops head. We had drift on day 7. Found a manual Tally entry without a corresponding web sale. Fixed in 14 minutes.

    The Stack (And What We Picked Instead)

    LayerChoiceAlternativeWhy
    FrontendNext.js 14 + TailwindReact + ViteServer components made the 14,000-SKU search fast on counter terminals (mostly older Windows 10 boxes with 4 GB RAM).
    DatabasePostgreSQL 16MySQLAudit trail was a hard requirement; Postgres' `pgaudit` extension and JSONB for hallmark history were the deciding factors.
    Tally bridgePython 3.11 + tdlserver XMLTallyConnect (paid)The owner's CA was already familiar with Tally's XML API. The free path saved the licence fee and let us own the bridge.
    HostingDigitalOcean Mumbai dropletsAWS MumbaiOwner wanted single-cloud, single-vendor billing. DO's INR billing matched his preference for round-number monthly invoices.
    AuthClerkAuth.js78 staff with 5 role types. Clerk's organisation feature did 2 weeks of RBAC work for us.
    Real-timePusher ChannelsSupabase RealtimeCounter terminals are constantly subscribed to gold rate updates. Pusher's per-channel pricing fit better than usage-billed Supabase.

    The 90-Day Plan (Copy This)

    1
    Days 1–9: Discovery + voucher-flow audit
    Sat with the senior accountant for 4 days. Mapped every voucher type (Sales, Receipt, Purchase, Credit Note, Debit Note, Stock Journal, Manufacturing Journal). Counted 38 distinct voucher templates the firm used. Talked to 9 of the 32 counter staff. Took photos of the 4 Excel sheets and a paper register no one had mentioned (the security register that tracks pieces moving between display and vault).
    2
    Days 10–14: Schema design + Postgres setup
    Designed 41 tables. The hardest one was `pieces` — a single physical jewellery item with a HUID, weight, purity, design code, current location (store/display/vault/sold), and a history of every state change. Insisted on event-sourcing for stock movements; the owner's CA insisted on idempotent voucher imports. Both constraints made it into the schema.
    3
    Days 15–28: Tally extraction (one-time)
    Ran our Python ODBC + XML extractor across all 6 store servers, weeknight after 11 pm. Extracted 240,000 vouchers from FY2024-25 + 2.1 million from prior years for historical lookup. Total raw data: 3.4 GB of Tally XML. Normalised into Postgres in 4 batches. Initial reconciliation: drift of ₹0 on revenue totals per store per month. We celebrated for 30 minutes.
    4
    Days 29–42: Build the web ERP MVP
    Three core surfaces — counter sales, stock movement, day-end close. The sales surface ships with a barcode scanner integration (most stores already had a USB barcode reader gathering dust from a 2019 attempt at a previous ERP). Day-end close generates a Tally voucher batch for the bridge to push.
    5
    Days 43–56: Build the Tally bridge + dual-write
    The bridge writes to Tally for every web-side sale. The reverse — Tally entries pushed to the web — is a polling service. Both directions had to be idempotent. Tally voucher-numbering quirks burned 4 days. Fix: namespace web-originated voucher numbers (e.g. `SI/W/001` vs `SI/T/001`) so they cannot collide.
    6
    Days 57–70: Pilot at 1 store (Race Course Road)
    Soft launch at the smallest store. 14 days of dual-write. Both Tally and web ERP recorded every transaction. Reconciliation script ran nightly. Drift on day 1: ₹3,400 (manual entry the bridge missed because it was added to a closed period). Drift on day 14: ₹0 for 5 consecutive days.
    7
    Days 71–84: Roll out to 5 more stores + train staff
    3 days per store. Two trainers (the project lead + Manvi from QA) physically present. We ran the same dual-write window per store. The biggest store, RS Puram, had a senior counter staff member who was vocally skeptical. By day 4 he was demoing the gold-rate auto-fill to walk-in customers.
    8
    Days 85–87: Final reconciliation + cutover rehearsal
    A 2-hour rehearsal on staging. Identified the 7 ledger pairs that needed special-case handling for the cutover (mostly closing-stock journals that the CA always ran manually).
    9
    Day 88: Cutover at 11:14 pm Sunday
    Web ERP becomes the source of truth for new transactions. Tally bridge keeps pushing for the audit trail. Dual-write stayed on for another 60 days as a hedge. No incidents. Closing balances at month-end matched to the rupee.

    The Schema That Mattered (Pieces + Movements)

    The hardest design decision was how to model a piece of jewellery. A single piece moves states constantly — vault, display, customer-trial, hold-for-customer, sold, returned, melted. The CA needed a daily ledger that summed correctly. The ops head needed live "where is this piece" lookup. We landed on event sourcing for movements, with a materialised view for current-state lookup.

    sql
    -- piece_movements is append-only
      CREATE TABLE piece_movements (
        id BIGSERIAL PRIMARY KEY,
        piece_id BIGINT REFERENCES pieces(id),
        from_location TEXT NOT NULL,  -- 'vault.coimbatore.race-course'
        to_location TEXT NOT NULL,
        movement_type TEXT NOT NULL,  -- 'transfer','sale','return','melt'
        moved_by_user_id BIGINT NOT NULL,
        voucher_id BIGINT,  -- if part of a Tally voucher
        occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
        metadata JSONB
      );
      
      -- materialised view: current location per piece
      CREATE MATERIALIZED VIEW piece_current_location AS
      SELECT DISTINCT ON (piece_id)
        piece_id, to_location AS current_location, occurred_at AS as_of
      FROM piece_movements
      ORDER BY piece_id, occurred_at DESC;
      
      CREATE UNIQUE INDEX ON piece_current_location (piece_id);
      
      -- Refresh after every movement (or batched, every 90s)
      REFRESH MATERIALIZED VIEW CONCURRENTLY piece_current_location;

    The CA's audit team has since used piece_movements to reconstruct the location history of any piece on any date — something that was impossible with the Excel approach. They now ask for it instead of working around it.

    The Cost Breakdown

    Build cost breakdown Engineering (3 devs × 13 weeks) Tally bridge specialist (8 weeks) UX/Design (Khushi, 5 weeks) QA + reconciliation testing (Manvi, 8 weeks) On-site rollout travel (Coimbatore + Erode) PM + project lead (full duration, 0.3 FTE)

    Run cost (steady state): DigitalOcean Mumbai droplets, Postgres-managed (DO), Pusher Channels, Clerk Pro (78 users), gold-rate API allowances, daily backups to S3 + offsite Wasabi, Sentry + Better Stack monitoring.

    The Outcome (Numbers That Mattered to the Owner)

    ₹0
    Drift on Closing Stock at Month-End
    14 → 1.5
    Day-End Close Time (Hours per Store)
    100%
    HUID-to-Sale Traceability
    ~₹4.8L/yr
    Recovered Gold-Rate Margin

    The recovered margin came from auto-applied live rates. Pre-build, the counter executive's morning rate was used for the entire day, so afternoon rate spikes silently became margin loss to the firm. Post-build, the rate updates every 90 seconds, with a 12-minute customer-quote lock to keep the in-shop conversation honest.

    The Pre-Cutover Checklist (We Refuse to Run Without This)

    • Tally bridge runs in dual-write mode for ≥ 14 consecutive days with zero reconciliation drift > ₹100
    • HUID-to-piece mapping verified for every active SKU (counted manually at one store as a spot check)
    • Gold-rate failover tested — kill the primary source, confirm fallback to median of remaining 2 sources
    • All voucher templates round-trip through bridge — ie the CA can see a "web sale" voucher in Tally and it looks identical to a manual one
    • Day-end close report matches Tally's day book to the rupee, run twice on staging
    • Per-store admin can pause web ERP and revert to Tally-only for that store via one click (we have used it twice in 6 months — once for a network outage, once for a hardware fault)
    • Roll-back plan: Postgres point-in-time restore tested, Tally state preserved (we keep Tally as the audit-immutable source for 24 months post-cutover)
    • Customer-facing invoice prints HUID + BIS QR — verified scannable on 5 different Android phones
    • Counter staff trained on the piece-movement screen; spot-check passes before rollout
    • Backup script for Tally companies tested; restore time ≤ 15 minutes

    What We Deliberately Did Not Build

    1. A customer-facing app. The owner wanted one. We pushed back hard. Customer-facing jewellery apps need photography pipelines, return-flow workflows, and a CRM that no SMB jeweller is operationally ready for. We shipped a customer-side WhatsApp invoice instead — every sale generates a PDF + WhatsApp message in 4 seconds. v2 candidate: a logged-in "my pieces" portal for customers.

    2. AI-driven customer personalisation. A vendor pitched "AI predicts which customer will buy what" as a paid add-on. We declined for the client. With ~32 counter staff who know their regular customers by name, the human network beats any AI for the first 2 years of the build. Revisit when the chain crosses 12 stores.

    3. Multi-currency support. The owner has cousins in Dubai and a vague export plan. We built USD/AED placeholders into the schema but did not ship the UI. Rule: do not ship a feature for a use case 12 months away. Re-evaluate in 2027.

    Common Mistakes (Each One Hurts)

    Symptom: "Tally voucher numbers collide." Cause: web-originated and Tally-originated vouchers share the same numbering series. Fix: namespace them at the prefix (SI/W/ vs SI/T/) so the CA can tell which side a voucher originated from at a glance.

    Symptom: "Closing stock differs by ₹X every month." Cause: a manual stock-journal entry the CA runs after-hours that the bridge has not seen. Fix: have the bridge poll for "recently modified vouchers" not just "new vouchers", and batch-process anything with a modified date in the last 24 hours. Or, better, ban manual after-hours entries during the dual-write window.

    Symptom: "Gold rate spikes mid-customer-quote and the customer is angry." Cause: customer was quoted at 10:15 am rate, paid at 12:30 pm at 12:30 pm rate. Fix: lock the quote for 12 minutes from issuance. After 12 minutes, re-quote with the live rate. This is industry-standard for reputable jewellers; we matched it in software.

    Symptom: "BIS HUID search returns 'not found' for valid pieces." Cause: HUIDs entered with leading zero variants. Fix: normalise to 6-digit string with leading zeros, both on entry and on search.

    Symptom: "Counter staff bypass the new system at peak hours." Cause: the counter UI is slower than the muscle memory of typing in Tally. Fix: ship a one-keystroke "quick sale" mode that drops to a barcode-scan-and-quantity-only view. We added this in week 16 post-launch — adoption climbed from 64% to 93% in 11 days.

    A Detail That Saved Us On Day 67

    A senior counter staff at the Erode-2 store called the project lead at 10:42 am: "the gold rate is showing one number — Mehrasons site is showing another." The bridge's median-of-3 logic had picked the wrong rate because Riddisiddhi Bullions had returned a stale value (cached from 8 minutes earlier). We added a "max staleness 90 seconds" filter on the rate sources, with auto-failover to the next source if the primary is stale. Three weeks later, IBJA's API went down for 22 minutes — the failover handled it without a human noticing.

    Where Custom ERP Fits In Our Wider Work

    We have shipped variants of this Tally-bridge pattern for:

  • A 14-branch diagnostics chain — see our March 2026 architecture writeup.
  • An Ahmedabad textile exporter — see the 2.4M-row MySQL-to-Postgres migration.
  • A 240-agent real-estate firm — see our Salesforce vs custom CRM analysis.
  • Tally-as-source-of-truth is a pattern that respects the CA's audit habits. Every Indian SMB build we ship in finance-adjacent verticals starts here. Founder-essay version of why we make this trade-off lives at viveksinra.com.

    A Common Question About Long-Term Maintenance

    Tally is a moving target. Tally Prime 5.0 is on the roadmap, GST 2.0 ships April 1 2026 (we covered the 6 software changes in our March 2026 checklist), and the XML schema occasionally adds non-backwards-compatible fields. We staff 4 hours/month of bridge maintenance into the run contract. In 18 months of running similar bridges across 4 clients, we have spent ~8 hours total on Tally Prime upgrade compatibility. Cost is real but bounded.

    FAQ

    Why not just buy a jewellery-specific ERP?

    We considered three (Synergics, Aaron, Logic). Their lowest-tier subscription for 6 stores came with 2-week onboarding and a roadmap controlled by the vendor. Custom build costs more upfront but the owner controls the roadmap, owns the data, and pays no per-user fee. Payback vs Synergics at our cost level: 4.7 years. The owner's framing: "I would rather own the software than rent it."

    Can the bridge work with Tally on cloud?

    Yes — Tally Cloud's HTTP XML API works the same. We've tested it. The polling latency is slightly higher (avg 3.4s vs 0.6s for on-prem) but the developer experience is otherwise identical.

    What about the audit trail for the tax authorities?

    Tally remains the immutable source for accounting. Every web-originated transaction lands in Tally with a "source: web-erp" tag in the narration. The CA can audit Tally as before. We have run two CA-led audits since cutover; both passed without queries.

    How did you handle migration of the 4 Excel sheets?

    For each sheet (display inventory, vault, hallmark issuance log, manual gold-rate book), we wrote a one-time importer that read the latest snapshot, normalised it into the relevant Postgres table, and asked the senior accountant to approve a sign-off PDF. Total: 6 days of effort across 4 sheets.

    What was the team composition?

    Three engineers (one senior on the bridge and reconciliation, one mid on the Next.js ERP, one junior on the gold-rate service and HUID tracker), one designer at 0.4 FTE, one QA at 0.5 FTE, and the project lead at 0.3 FTE. Plus 8 weeks of a Tally bridge specialist who had built similar bridges at 3 prior firms.

    Did the CA push back on the migration?

    Heavily, in week 1. By week 6 (after the dual-write demo), he was the project's strongest internal advocate. What turned him: showing him that Tally remained the audit source and the web ERP only added information, never removed it.

    Can we add a customer-facing portal in v2?

    Yes, and the schema is ready for it. The piece-movement event sourcing means a "track my piece" customer experience is straightforward. We are quoting v2 separately because the customer-facing layer needs different security review and a customer-support workflow that does not exist today.

    What happens if Tally goes down?

    The web ERP keeps running. The bridge queues outbound vouchers to a Postgres outbox table; when Tally comes back, they replay in order. We tested this for 4 hours of simulated downtime. Zero data loss.

    Have a legacy Tally setup you want extended into a custom ERP?

    We migrate Tally-only operations to custom web ERPs for Indian SMBs in retail, manufacturing, and trading verticals. Typical project: 12–18 weeks, fixed scope. The CA stays in the room from day one. The first call is free; we will tell you whether your business is ready or whether you should stay on Tally for another year.

    Book an ERP Build Call

    Tags:
    Custom ERPTally MigrationJewelleryBIS HallmarkCase StudyPostgresIndian SMB
    Share this post:
    Vivek Kumar

    Vivek Kumar

    Founder and CEO at Softechinfra with 10+ years of experience in software development and system architecture.