← SIMRANJAISWAL.INTHE ENGINE ROOM · 06 · BILLING

The credit note that explains itself

In the billing-reversals case, one invoice in six on a real wholesaler's ledger was later credited back, and a model could say which ones would be. This engine is the other half: the seven questions a credit note must answer before it exists — which invoice, how much is still open on it, why, has this been asked already, how often has this customer been credited, who may say yes, and is the goodwill budget spent. It is running below, in your browser, on 500 requests you can make as generous as you like.

◆ LIVE ENGINE · RUNS IN YOUR BROWSER · PYTHON REFERENCE PUBLIC
0CREDIT-NOTE REQUESTS · 1,500 INVOICES · 60 CUSTOMERS · 90 DAYS
0IN A FIXED ORDER · THE FIRST FAILURE WINS · EVERY ANSWER ON THE REQUEST
0STOPPED AT THE GATE · REFUSED + DECLINED + HELD · REFERENCE RUN · ₹ LAKH
0REQUESTS THAT DID NOT BECOME A CREDIT NOTE, EACH WITH THE GATE THAT SAID SO
01 · THE JOB

A credit note is the easiest way to move money out of a company, because nobody thinks of it as money. It is a correction, a favour, a way to close a ticket. On the wholesaler's ledger one invoice in six came back that way; the model said which. This engine asks, before the credit exists, whether it should.

Every credit note is a decision that someone did not have to defend. The invoice was wrong, so credit it; the customer was unhappy, so credit it; the salesperson promised, so credit it. Each one is small and each one is reasonable, and the sum is a leak that never shows up as a loss because it was never booked as revenue that stayed. In the billing-reversals case the reversals were found after the fact, tied back to the invoices they unwrote, and a model learned to predict them at the moment of invoicing. The Credit Note Gate is the SEAL that follows the TRACE: a credit note cannot exist until seven questions are answered on the request itself, in a fixed order, with the first failure winning — so the note that does get raised carries its own explanation, and the one that does not carries the sentence that stopped it.

02 ·THE ENGINEONE REQUEST, SEVEN GATES, THREE LANES, ONE LEDGER
AT REQUEST TIME · G1 AN INVOICE · G2 ROOM ON IT · G3 A REASON · G4 NOT ALREADY ASKED · G5 NOT TOO OFTEN · G6 WHO MAY SAY YES · G7 THE GOODWILL BUDGET · REFUSALS ARE FINAL, HOLDS GO TO REVIEW, APPROVALS MOVE THE LEDGER SO THE NEXT REQUEST SEES LESS ROOM.

The engine is deliberately boring: no model of which customers deserve goodwill, no fuzzy matching, no threshold tuned by feel. Each gate is a sentence a finance head can read and disagree with, the limits are numbers printed on the sliders, and the goodwill cap is arithmetic on the requester's own book. That is what makes a refusal survivable: when the gate says no, the requester can see which sentence said it and what would have passed.

03 ·THE RULESWHAT "CREDITED" MEANS, EXACTLY
GATERULEWHAT IT DOES
G1 · an invoiceThe request must reference an invoiceElse REFUSED · NO_INVOICE. A credit against nothing is the purest form of the leak.
G2 · room on itAmount ≤ what is still open on the invoice (earlier credits already deducted)Else REFUSED · OVER_REMAINING. You cannot give back more than was billed.
G3 · a reasonA reason code is present: PRICING_ERROR · SHORT_SHIPPED · QUALITY · GOODWILL · DUPLICATE_BILLINGElse HELD · NO_REASON — back to the requester, not refused. The word is the point.
G4 · not already askedNo earlier request on the same invoice with an amount within 2% inside the duplicate window (default 10 days)Else REFUSED · DUPLICATE, with the earlier request named.
G5 · not too oftenThe customer has received fewer than N − 1 credit notes in the last 30 days (default N = 3; counts notes actually issued)Else HELD · FREQUENCY — a customer credited every fortnight is a pricing or a quality problem, not a credit problem.
G6 · who may say yes≤ ₹5,000 AUTO · ≤ ₹50,000 MANAGER · above FINANCE_HEADThe tier, not the answer. AUTO goes out with no human; the rest wait for one.
G7 · the goodwill budgetA GOODWILL credit may not take the requester's goodwill credits this quarter above 1% of the invoiced value in their nameElse HELD · GOODWILL_CAP. Goodwill is a budget, and the budget belongs to the person spending it.
The approverAnswers within 24 h with p = 0.85, else the request ESCALATES one level and is answered there in 24–72 h; grants with p = 0.9 PRICING_ERROR · 0.9 SHORT_SHIPPED · 0.8 QUALITY · 0.95 DUPLICATE_BILLING · 0.5 GOODWILLThe synthetic approver. In life the probabilities are the approvers' own history. Declined → nothing is credited.
The ledgerAn approved credit reduces the invoice's remainingSo the next request on that invoice meets G2 with less room. The gate remembers.
₹ preventedRefused + declined + held-not-yet-approved valueThe number the page leads with. Holds are money that is waiting for a sentence, not money that is gone.
Tie-out500 = auto + approved + declined + escalated + held + refused; ₹ requested = ₹ credited + ₹ preventedIf either breaks, the quarter's credit report is not published.
# the gate, in this order — first failure wins (engine.py)
if Q["f_no_invoice"]:  Q["decision"] = "REFUSED"; Q["code"] = "NO_INVOICE"      # G1
elif Q["f_over"]:      Q["decision"] = "REFUSED"; Q["code"] = "OVER_REMAINING"  # G2
elif Q["reason"] == "": Q["decision"] = "HELD"; Q["code"] = "NO_REASON"  # G3 · to the requester
elif Q["f_dup"]:       Q["decision"] = "REFUSED"; Q["code"] = "DUPLICATE"       # G4
else:
    n30 = credit notes this customer received in the last 30 days
    if n30 + 1 >= freq_n: Q["decision"] = "HELD"; Q["code"] = "FREQUENCY"      # G5
    else:
        Q["tier"] = tier_of(Q["amount"], auto_limit, manager_limit)   # G6 · the tier
        if Q["reason"] == "GOODWILL" and gw_credited[r] + Q["amount"] > cap * inv_value[r]:
            Q["decision"] = "HELD"; Q["code"] = "GOODWILL_CAP"           # G7
        elif Q["tier"] == "AUTO": Q["decision"] = "AUTO"
        else:
            answered = rng2.random() < SLA_P                  # within 24 h with p 0.85
            granted  = rng2.random() < GRANT_P[Q["reason"]]    # 0.9 · 0.9 · 0.8 · 0.95 · 0.5
04 ·RUN ITTHE ENGINE, LIVE, ON A STREAM YOU CONTROL
SEED
A 1,500-INVOICE BOOK AND 500 REQUESTS ARE GENERATED FROM THE PRINTED RULES AND PUT THROUGH THE GATE IN YOUR BROWSER. NOTHING LEAVES THIS PAGE.
STOPPED AT THE GATE

Run the quarter to see what the gate stops.

THE DECISION LADDER

Two things to try. Switch the gate off and the big number turns into what gets credited when nobody asks: the same 500 requests, ₹7.4 lakh of them against no invoice at all, ₹2.3 lakh asked twice, and ₹2.7 lakh credited beyond what was ever billed. Then drag the goodwill cap down to 0.5%: at 1% it never fires on this stream, because the frequency guard and a 50-50 approver catch goodwill first; at 0.5% it starts naming requesters. A cap that never fires is not wrong — it is a budget nobody has reached yet — but it is a slider, not a constant, because the day someone reaches it is the day you want to know.

05 ·WHAT IT FINDSTHE REFERENCE RUN · SEED 42

What happened to each reason

REQUESTS BY REASON CODE · CREDITED · HELD · DECLINED · REFUSED
CREDITEDHELDDECLINEDREFUSED

How long a decision took

HOURS FROM REQUEST TO ANSWER · THE GATE ANSWERS INSTANTLY, APPROVERS IN 1–72 H

Who asks, and what they get

₹ REQUESTED (GREY) · ₹ CREDITED (GOLD) · GOODWILL SHARE OF EACH REQUESTER'S ASKS AT THE RIGHT

By reason code

By requester

06 ·WATCHHOW IT RUNS WHEN NOBODY IS LOOKING
WHENWHAT HAPPENSWHO SEES IT
At request timeG1–G5 run inside the ticket the moment it is raised. A refusal names the gate and the earlier request it clashes with; a hold goes back to the requester or to review with the sentence that held it.The requester, in under a second, with what would have passed.
Within 24 hThe approver at the tier answers. Unanswered requests escalate one level and are counted as escalations against the approver.The manager, then the finance head. The escalation count is how you find the approver who is the bottleneck.
NightlyApproved credits are posted and the invoice's remaining is reduced; the day's decisions are tied out: requests = auto + approved + declined + escalated + held + refused. Any gap → no post.The controller. A tie-out failure is the engine finding a bug in itself.
WeeklyThe goodwill ledger by requester against their cap, the frequency holds by customer, the duplicates caught. A customer held twice for frequency in a month is handed to pricing or quality, not to credit control.Finance and sales, with the by-requester table on the table.
QuarterlyThe caps reset. The approval rate by reason and the ₹ prevented are the two numbers leadership sees. Limits move only when that page moves.Leadership — the WATCH stage of the Leak Ledger.
07 · THE TAKEAWAY

A credit note that cannot explain itself should not exist. The value is not the ₹47 lakh this stream stopped in a quarter — most of it is holds, money waiting for a reason, and much of it will be credited once the reason arrives. The value is that every credit note that does get raised carries an invoice, a remaining balance, a reason, a tier and a budget, and every one that does not carries the sentence that stopped it. Make the gates readable, the limits sliders, the goodwill a budget with a name on it, and the tie-out refuse to lie, and the credit note stops being the leak.

THE BOOK AND THE REQUEST STREAM ARE SYNTHETIC AND GENERATED FROM THE RULES PRINTED IN engine.py (SEED 42; LIMITS AS SHOWN ON THE SLIDERS). NO CLIENT DATA, NO REAL REQUESTERS, NO REAL INVOICES. THE ENGINE IS THE GATE THAT FOLLOWS FROM THE BILLING-REVERSALS WORK, WHERE ONE INVOICE IN SIX ON A WHOLESALER'S LEDGER WAS LATER CREDITED BACK; THAT CASE'S FIGURES ARE AS REPORTED THERE. THE APPROVER PROBABILITIES ARE ASSUMPTIONS, PRINTED. THE BROWSER ENGINE IS A LINE-FOR-LINE PORT OF THE PYTHON AND REPRODUCES results.json EXACTLY ON THE REFERENCE SETTINGS — THE STATUS LINE ABOVE SAYS SO WHEN IT DOES.

Credit notes nobody can explain? A credit note gate is a two-to-four-week SEAL: your reason codes, your tiers, your goodwill budgets — running inside the ticket, with a tie-out that refuses to post a credit that does not add up.

Start with a TRACE → engine.py README results.json The case it came from →