A bill number a tax invoice may carry - #794
Merged
sridharkalaibala merged 1 commit intoSep 16, 2026
Merged
Conversation
Owner: "we need year pattern required in the sales bill number example attached have 26 in the year. and then number increasing would be okay. you suggest per day increase or year wise reset better tell me international standards." And on the answer: "i accept recommandation and may configurable if people from EU and international. Blocker fix that. i dont know how its got SB1d14 and all." SB1D14-000051 is S, the branch's code, this till's gateway-assigned code, and a running number. The till code is what stops two tills in one shop minting the same number; it has been there since bill numbers went atomic. THE RULE. India, CGST Rule 46(b): a consecutive serial number, NOT EXCEEDING SIXTEEN CHARACTERS, of letters, digits, "-" and "/", UNIQUE FOR A FINANCIAL YEAR. The reference invoice he sent is exactly sixteen - VR26-27VIR006782 - because whoever built that hit the same wall. The EU VAT Directive asks only for a sequential number, with no length limit and no prescribed reset, so the year and the reset are settings and the charset is not. Not per day. A daily reset repeats numbers inside the year unless the date is in the number, and a date costs six to eight of the sixteen. It also destroys what an auditor actually uses: one consecutive run per year has visible gaps when something is cancelled, and three hundred and sixty-five short runs do not. utils/bill-number.js builds the number and shortens the RUNNING NUMBER when something has to give - never the year, which uniqueness depends on, and never the till code, without which two tills collide. With no year asked for it produces exactly what the template literal it replaced produced, pinned over 72 combinations. WHOSE MIDNIGHT. A bill rung up at 00:30 on 1 April in Chennai belongs to the new financial year. A cloud instance running in UTC would still call it March and number it into a year that closed half an hour ago. The period is worked out in the shop's own clock. THE ROLL-OVER IS ONE ATOMIC STEP. A read, a compare and a write would be three, and a year turns over at midnight in a restaurant that is still serving. One pipeline update on the counter row, so two tills billing in that second cannot both be given number one. Checked against a real mongod, not a mock of one. A counter written before any of this reads as "no period" rather than as a mismatch, or the first bill on every shop in the estate would restart at one. And it found a real one: the counter sale formatted its own number while the customer's ordering page called generateSalesIdForBranch, with both drawing on one counter. A shop turning the year on would have had it on its online orders and not on its counter bills, and the reset would have had the till reissuing numbers it had already given out. Both now come through one door. Off by default. Ninety shops are mid-year with a running series on their invoices; switching them on an upgrade would change the shape of every number overnight and restart the count in the middle of a year. A shop turns it on, best on the first day of its own year.
Contributor
|
Merged to Try it at https://develop.posnic.io, or run it yourself: git fetch origin develop && git checkout develop
npm install && npm --prefix api install
npm run dev # then http://localhost:3000When you have tested it, say what you did and what happened, and set Reporting that something is broken is as useful as fixing it. It is |
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.
Your ask: "we need year pattern required in the sales bill number example attached
have 26 in the year. and then number increasing would be okay. you suggest per day
increase or year wise reset better tell me international standards" and "i accept
recommandation and may configurable if people from EU and international. Blocker fix
that. i dont know how its got SB1d14 and all."
SB1D14 first
SB1D14-000051is S + B1, the branch's code + D14, this till'sgateway-assigned code + the running number. The till code is what stops two tills in
one shop minting the same number; it has been there since bill numbers went atomic.
Nothing was wrong with it - it just has no year in it.
Year, not day
India, CGST Rule 46(b): a consecutive serial number, not exceeding sixteen
characters, of letters, digits,
-and/, unique for a financial year. Theinvoice you sent is exactly sixteen -
VR26-27VIR006782- because whoever built thathit the same wall.
EU VAT Directive 2006/112/EC Art 226(2): a sequential number, no length limit, no
prescribed reset.
So the year and the reset are settings, and the charset is not.
Not per day: a daily reset repeats numbers inside the year unless the date is in the
number, and a date costs six to eight of the sixteen. It also destroys what an auditor
actually uses - one consecutive run per year has visible gaps when something is
cancelled, and 365 short runs do not.
What a shop sets
Settings → beside Sale Prefix:
SB1D14-000051SB1D14-27-000051SB1D14-26-000051Plus the month a financial year starts in - April in India, January in much of the EU.
The month names come from the browser's own calendar rather than from 17 translation
packs.
Off by default. Ninety shops are mid-year with a running series on their invoices.
Switching them on an upgrade would change the shape of every number overnight and
restart the count in the middle of a year, which is the one thing an auditor reads a
series for. Turn it on per shop, best on the first day of that shop's year.
The three things that were hard
Whose midnight. A bill rung up at 00:30 on 1 April in Chennai belongs to the new
financial year. A cloud instance running in UTC would still call it March and number it
into a year that closed half an hour ago. The period is worked out in the shop's own
clock.
The roll-over is one atomic step. A read, a compare and a write would be three, and
a year turns over at midnight in a restaurant that is still serving: two tills billing
in that second would both read the old year and both be given number one. It is a
single pipeline update on the counter row, checked against a real mongod rather than a
mock of one. A counter written before this change reads as "no period" rather than as a
mismatch - otherwise the first bill on every shop in the estate would restart at one.
Sixteen characters is a ceiling, not a guideline. When something has to give it is
the padding on the running number - never the year, which uniqueness depends on, and
never the till code, without which two tills collide. The shop is told, in words it can
act on.
A real bug found on the way
sale.service.js- the counter sale, which is most of the bills in the estate - took anumber from the counter and formatted it itself, while the customer's ordering page
called
generateSalesIdForBranch. Both draw on the same counter.A shop turning the year on would have got the year on its online orders and not on its
counter bills - and the moment the reset put that counter back to one, the till would
have started reissuing numbers it had already given out, caught only by the unique index,
as a failed sale in front of a customer. Both now come through one door, and a test says
so.
Writing the pinning test also found that the unused
isReturnpath producedR-SB12D999-000001- seventeen characters, not a number a tax invoice may carry. It isnow fifteen and legal.
Checks
api: 373 suites, 10,800 tests (17 of them against a real mongod)tests/: the settings chain, all five links, plus i18n 50/50npm run check: 7/7with "bill_number_reset is stored but never read back" - the silent failure this whole
area is prone to.
One thing to tell me
The two-character year is the year the financial year ends in, which is how India
says it (FY27 = April 2026 to March 2027). Your reference bill wrote 26-27, both
years, using five of its sixteen characters. If you would rather see
26or26-27,say which and it is a small change - I picked the convention over the sample.