Repository navigation
feat(db-kit): storage for finance-kit, lock dates and a posting hook - #7
Merged
Merged
Conversation
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
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
7 of 11 tasks
BackStacked
marked this pull request as ready for review
October 14, 2026 09:51
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
postVoucherandcancelVoucherrefuse a locked period, even under concurrency. The billing workflows post through a replaceablePostingHook, 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:
taxRules,customers(withgetForUpdate),partners,partnerAgreements,settlements(withgetForUpdate),cashClosings,bankStatements,bankLines,periodLocks(withhold),creditNotes(withlistByDate). They use UUID ids, integer paise and Sequelize factories. Optional unique keys usepartialFilterExpressionon MongoDB (settlementperiodKey).Branch.stateCode. OnInvoice:customerId,placeOfSupply,buyerGstinandcreditedPaise, with due = total − credited − paid and a newcreditedstatus. Invoice lines gethsnSacandtaxability.closing, left out ofprofitAndLossandgstSummary. New queriesinvoices.listByDateandexcludeVoucherTypeson ledger-entry queries.assertPeriodOpenholds the org and branch lock rows inside the posting transaction. On PostgreSQL that isFOR SHAREfor postings andFOR UPDATEfor lock changes. On MongoDB it is a write on the lock document, so concurrent changes conflict and retry. It throwsPeriodLockedError, which is aConflictError(409).PostingHook: set withcreateDb({ posting }).defaultPostingHookkeeps today's behaviour exactly.Type
Checklist
main)npm run typecheckpassesnpm testruns 194 checks (was 134) on real MongoDB and real PostgreSQLOpen 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