Skip to content

A foreign-currency party ledger makes the on-account residual invent money #170

Description

@lamemustafa

Summary

On a party ledger denominated in a foreign currency, CLOSINGBALANCE reads 0.00 while that party's
bill reports the full base-currency amount. Bridge computes the on-account residual as
CLOSINGBALANCE - sum(BILLCL), so it emits a residual equal to the entire outstanding with the
sign reversed
— money that does not exist, on a document a CA sends to a client.

Measured 2026-08-23 on licensed TallyPrime Silver against a purpose-built book.

Reproduction

BRIDGE CORPUS FOREX, base currency INR. A $ currency master created over XML, then a party ledger
created with <CURRENCYNAME>$</CURRENCYNAME> — which Tally honours (readback shows currency='$'
where INR ledgers show 'I₹'). One Sales voucher for $1,000 at 83.50 = ₹83,500.

read value
FX Sales CLOSINGBALANCE 115000.00 — includes the ₹83,500, so the voucher posted
Bills Receivable FXU-INV-001 BILLCL -83500.00
FX USD Debtor 01 CLOSINGBALANCE 0.00
residual = CLOSINGBALANCE - sum(BILLCL) = 0.00 - (-83500.00) = +83500.00

Positive, where the real exposure is negative, and equal in magnitude to the whole receivable.

Why it is reachable

Bridge sends currency_assertion: "INR" on outstandings requests, and nothing has ever tested a
non-INR book against that path because no non-INR book existed. Bridge is read-only, so the
import-side behaviour below is not reachable in production — but this one is. It needs only a
customer with a single foreign-currency debtor.

The residual is not a corner case either. On the one real book measured, ~98.4% of the party balance
sat as unnamed On Account residual; it is the dominant number in an outstandings report.

Root cause, and one honest uncertainty

The leading explanation is that CLOSINGBALANCE on a foreign-currency ledger is expressed in the
ledger's own currency, while BILLCL is in base currency — so the subtraction crosses currencies.

That is not proven, because it was not possible to post a genuine forex amount to test it:
Tally silently discards FOREIGNAMOUNT and RATEOFEXCHANGE on import (CREATED=1 ERRORS=0 EXCEPTIONS=0, both fields absent on readback), so the voucher posts as a plain base-currency entry
against a foreign-denominated ledger.

What is certain is the observable disagreement between the two reads for the same party.

Suggested direction

The first requirement is to refuse rather than misreport, not to support multi-currency:

  • detect a party ledger whose CURRENCYNAME differs from the company base currency
  • withhold the report, or mark it partial with a specific reason, rather than emitting a residual
    computed across mismatched currencies
  • CURRENCYNAME is fetchable on the ordinary ledger collection, so detection is cheap and needs no
    new request shape

This is the same principle the crosscheck work on #168 settled on: absence of contradiction is not
evidence, and a number that cannot be shown correct should not be published as complete.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions