Skip to content

feat(db-kit): storage for finance-kit, lock dates and a posting hook - #7

Merged
BackStacked merged 3 commits into
mainfrom
claude/finance-kit-phase-1-6ltkku
Oct 14, 2026
Merged

BackStacked merged 3 commits into
mainfrom
claude/finance-kit-phase-1-6ltkku

Conversation

@BackStacked

@BackStacked BackStacked commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Himanshu · project thread

What and why

Before: db-kit had no place to store B2B customers, collection-centre partners, settlements, cash closings, bank statements, lock dates, GST tax rules or credit notes. Invoices could not record a customer or a place of supply, and nothing stopped a voucher from being posted into a closed period.

After: all of that storage exists on both MongoDB and PostgreSQL, with contract tests. postVoucher and cancelVoucher refuse a locked period, even under concurrency. The billing workflows post through a replaceable PostingHook, so finance-kit (and tally-kit later) can take over posting without a dependency cycle.

This is the storage half of finance-kit Phase 1 (brief, section 3). The finance rules themselves live in finance-kit.

How:

  • New repositories: taxRules, customers (with getForUpdate), partners, partnerAgreements, settlements (with getForUpdate), cashClosings, bankStatements, bankLines, periodLocks (with hold), creditNotes (with listByDate). They use UUID ids, integer paise and Sequelize factories. Optional unique keys use partialFilterExpression on MongoDB (settlement periodKey).
  • New fields: Branch.stateCode. On Invoice: customerId, placeOfSupply, buyerGstin and creditedPaise, with due = total − credited − paid and a new credited status. Invoice lines get hsnSac and taxability.
  • New voucher type closing, left out of profitAndLoss and gstSummary. New queries invoices.listByDate and excludeVoucherTypes on ledger-entry queries.
  • Lock dates: assertPeriodOpen holds the org and branch lock rows inside the posting transaction. On PostgreSQL that is FOR SHARE for postings and FOR UPDATE for lock changes. On MongoDB it is a write on the lock document, so concurrent changes conflict and retry. It throws PeriodLockedError, which is a ConflictError (409).
  • PostingHook: set with createDb({ posting }). defaultPostingHook keeps today's behaviour exactly.
  • Docs: docs/FINANCE-STORAGE.md has the entity table, the lock-date design, the hook, and SQL for upgrading an existing PostgreSQL database.

Type

  • Bug fix
  • Feature
  • Test data (ranges, units, prices) — source linked below
  • Docs
  • Refactor / tooling

Checklist

  • On a branch (not main)
  • npm run typecheck passes
  • The repo's tests pass: npm test runs 194 checks (was 134) on real MongoDB and real PostgreSQL
  • Docs updated (README, AGENTS.md, llms.txt, llms-full.txt, docs/FINANCE-STORAGE.md)
  • HTTP contract changed? (no)
  • No real patient data, contact details or signatures

Open question for the owner, not changed here: should the "Doctor Referral Commission" and "Doctors Payable" default ledgers be removed? Nothing is built on them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PGpx9NTQ79qLEpq7CafibQ


Generated by Claude Code

Adds, on both MongoDB and PostgreSQL with contract tests:
- tax rules, B2B customers, partners and agreements, settlements, cash closings,
  bank statements and lines, period locks, credit notes;
- invoice fields for GST (customer, place of supply, buyer GSTIN, credited amount,
  line SAC/HSN and taxability) and a "closing" voucher type;
- lock dates checked inside postVoucher / cancelVoucher, safe under concurrency;
- a PostingHook for the billing workflows (default: current behaviour).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGpx9NTQ79qLEpq7CafibQ
@BackStacked BackStacked self-assigned this Oct 14, 2026
seedOrganization accepts the branch stateCode; a refund that empties a
credited invoice marks it refunded; Mongo updates use returnDocument
instead of the deprecated new option.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGpx9NTQ79qLEpq7CafibQ
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGpx9NTQ79qLEpq7CafibQ
@BackStacked
BackStacked marked this pull request as ready for review October 14, 2026 09:51
@BackStacked
BackStacked merged commit 9528b2f into main Oct 14, 2026
1 check passed
@BackStacked
BackStacked deleted the claude/finance-kit-phase-1-6ltkku branch October 14, 2026 09:52
BackStacked added a commit to mgpl-diagnostics/finance-kit that referenced this pull request Oct 14, 2026
<!-- claude-projects-attribution: {"github_login":"BackStacked"} -->
_Requested by **Himanshu** · [project
thread](https://claude.ai/code/project/chan_016JXeBZ2UxSi4N5VU2C26fQ?thread=cmsg_016JXeBZ2UxSi4N5VU2C26fQ2dDpGc3Gmn22ZukJMnCK6M)_

## What and why

Before: finance-kit was an empty repo; GST returns, B2B receivables,
collection-centre settlements, cash closing, bank reconciliation, lock
dates and year-end close did not exist anywhere.

After: `@mgpl-diagnostics/finance-kit` is a library on db-kit's
repositories (MongoDB or PostgreSQL) that provides Phase 1 of the brief:
- GST tax rules as data (SAC/HSN, taxability, rate, effective date),
CGST/SGST vs IGST by place of supply, GSTR-1 (B2B, B2CL, B2CS, CDNR,
CDNUR, nil, HSN, docs) and GSTR-3B (3.1a/c/e, 3.2) data;
- B2B customers with credit limits (concurrency-safe), statements with
ageing, credit notes that reduce output tax;
- collection-centre / franchise settlements: revenue share or fixed rate
with effective dates, configurable TDS, accrual and payout vouchers;
- day-end cash closing (short/excess, the closed day is locked), bank
CSV import and reconciliation with card/UPI MDR posted to Bank Charges;
- lock dates per org or branch (checked inside the posting transaction)
and year-end close;
- CSV exports of books, settlements and GST returns with TOTAL rows
equal to the reports.

Every voucher goes through `src/posting.ts`, so the later switch to
tally-kit touches one file (a test enforces it). No server, no GraphQL,
no Tally XML. Calls are `fn(repos, input, actorId)`; every id is checked
against `orgId`.

How: storage lives in db-kit (mgpl-diagnostics/db-kit#7, which must
merge and publish as v1.1.0 first). `scripts/test.ts` runs one scenario
on in-process MongoDB (replica set) and PostgreSQL: 262 checks, trial
balance difference 0 after each stage, concurrency races for lock dates,
credit limits and cash closing, and golden CSV files.

Compliance values (GST exemptions and rates, SAC/HSN, B2CL threshold,
TDS section/rate/threshold) ship as configuration marked "verify with
CA" in `docs/gst-notes.md`. That file also flags that invoice numbers
exceed the GST portal's 16-character limit, and the Doctor Referral
Commission ledger in db-kit (not used, not built).

## Type

- [ ] Bug fix
- [x] Feature
- [ ] Test data (ranges, units, prices) — source linked below
- [x] Docs
- [ ] Refactor / tooling

## Checklist

- [x] On a branch (not `main`)
- [x] `npm run typecheck` passes
- [x] The repo's tests pass (`npm test`, `npm run build`)
- [x] Docs updated (README, `docs/api.md`, `docs/gst-notes.md`,
AGENTS.md, llms.txt, llms-full.txt)
- [ ] HTTP contract changed? (no HTTP surface)
- [x] **No real patient data, contact details or signatures** in code,
tests, fixtures or screenshots

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01PGpx9NTQ79qLEpq7CafibQ

---
_Generated by [Claude
Code](https://claude.ai/code/session_01PGpx9NTQ79qLEpq7CafibQ)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants