Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/tally/TALLY_PROTOCOL_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,24 @@ with the gateway healthy):
**Batching is valid here because unknown elements are silently ignored** — a batch that still
reports the same "required" error disproves every name in it at once.

#### Correction — 2026-08-23: base-currency fields are Currency-master properties

The negative object-export result above remains valid, but its conclusion was too broad.
`TYPE=Object` / `SUBTYPE=Company` with `<FETCH>*</FETCH>` does **not** emit the base-currency
fields: they are not properties of the `Company` object. It does not establish that Tally never
exports them.

The Currency master collection does return them. The request rendered by
`render_company_currency_request` is exactly `TYPE=Collection` with `<TYPE>Currency</TYPE>` and
fetches `NAME`, `MAILINGNAME`, and `DECIMALPLACES`. Three captures committed on this PR establish
that response shape. On the same licensed TallyPrime Silver 7.1 machine on 2026-08-23, current books
reported `I₹` (U+0049 followed by U+20B9) with `MAILINGNAME` `INR`; older books reported `Rs.`
with `MAILINGNAME` `Indian Rupees`.

The Company Creation formal name remains a property on a different master. That boundary explains
why the 19 earlier probes, sent as `Company` children, all failed; it does not justify treating
the fields as form-local or relying on a Company object export to recover them.

### 9.10b `ORIGINALNAME` at `COMPANY` level hangs the gateway — **TRAP**

**VERIFIED 2026-07-30.** A flat combination of
Expand Down
2 changes: 1 addition & 1 deletion docs/tally/compatibility/compatibility-matrix.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schema_version": 1,
"bridge_commit_sha": "be1c20cc3fd66fa1ece196505c69f26e555e4b8e",
"compatibility_surface_sha256": "c0d63b2ac1014e421c32f53b2087176901a3fbec3c59f52c1e91fccb6dc364f9",
"compatibility_surface_sha256": "7e034f2dc2438952c9d6b0fd842ea4a3627f54d5f805adb5483aa40b5caf3e6c",
"claims": [
{
"claim_id": "erp9-6-6-3-windows-education-xml-one-company",
Expand Down
18 changes: 9 additions & 9 deletions docs/tally/compatibility/compatibility-surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
},
{
"path": "scripts/outstandings-copy.test.mjs",
"sha256": "0f9b53d4b3e352fe5fff3c909cea5d10336cc07dc6db817a05761e2afc191463"
"sha256": "1fc0bc399a7ca6e70b1e8ba5dff83f40d214a8ca256e928d7c39979fd42fac98"
},
{
"path": "scripts/tally-company-selection.test.mjs",
Expand Down Expand Up @@ -159,11 +159,11 @@
},
{
"path": "src-tauri/crates/bridge-tally-protocol/src/native_outstandings/request.rs",
"sha256": "92d77ef47933a52510eb186768f550546b36de385eb15b1d019d065346d72bb6"
"sha256": "b909d3f9eb620c50365341528c1211568afa7690a3442653993f65a334438ec3"
},
{
"path": "src-tauri/crates/bridge-tally-protocol/src/native_outstandings/wire.rs",
"sha256": "266d4f04ac46452213e5dcfe41693272115c0f18704632119e2315dfca5ac4b8"
"sha256": "bf74bb87d9579ee43eca8b825ce05ab7f565ddc67262ac76fede6a4fa8fb47a6"
},
{
"path": "src-tauri/crates/bridge-tally-protocol/src/outstandings/completeness.rs",
Expand Down Expand Up @@ -247,7 +247,7 @@
},
{
"path": "src-tauri/src/commands.rs",
"sha256": "b0f1607b947b13066bb9030632bfe9f0c05ea9d710b007c4e4ff37a2a9ecd25c"
"sha256": "2feef66eaeff0eed11d4cc544e0d343fa7b023e9382add210ce79f134376ac1c"
},
{
"path": "src-tauri/src/db/encrypted.rs",
Expand Down Expand Up @@ -371,7 +371,7 @@
},
{
"path": "src-tauri/src/tally/runtime.rs",
"sha256": "760394d90156acb720a2a377959a8c356d5fc4061612b9e4d2d772ce374631a2"
"sha256": "cc1a81ddca0afd4bea56cee465cff19dc5c11d67f5b30d57619e773cc857aad4"
},
{
"path": "src-tauri/src/tally/serial_queue.rs",
Expand All @@ -395,11 +395,11 @@
},
{
"path": "src/AllClientsScreen.tsx",
"sha256": "f4cd60e3e777c71daa5eff330574a1988606f012da92133fd4b4f5ed8a27d8b7"
"sha256": "a376a574481347427ca986001effa2a3687bacf8f3082f4feaa2466818a505a8"
},
{
"path": "src/OutstandingsScreen.tsx",
"sha256": "d743d03cc9eac7223a1d8dae807463aaf6505c2244c2e09d8680b8db6ef300f9"
"sha256": "75a80eb258ebb78070b519637d5a8a62861d630554f652a064ee7764d48f37c0"
},
{
"path": "src/TallyReadinessFlow.tsx",
Expand All @@ -415,7 +415,7 @@
},
{
"path": "src/outstandings-copy.ts",
"sha256": "61811b284283bd3f7c0b8744c16fbc537295aa07bf5add3822cb85f775f8ee17"
"sha256": "ccf31b5ec13f731b272e4c1805bb4a6cd2b668276a5760ac58ec520cc2780ed5"
},
{
"path": "src/outstandings-csv.ts",
Expand Down Expand Up @@ -478,5 +478,5 @@
"sha256": "a8ac2714fecf51947f2822c8c46d7ce2e8602c732780ff60566a7771f0836f9a"
}
],
"manifest_sha256": "c0d63b2ac1014e421c32f53b2087176901a3fbec3c59f52c1e91fccb6dc364f9"
"manifest_sha256": "7e034f2dc2438952c9d6b0fd842ea4a3627f54d5f805adb5483aa40b5caf3e6c"
}
19 changes: 19 additions & 0 deletions scripts/outstandings-copy.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,28 @@ test("new native and sweep boundaries have operator-readable reasons", () => {
);
assert.match(outstandingsPartialReason("company_currency_probe_failed"), /base currency/i);
assert.match(outstandingsPartialReason("company_base_currency_not_inr"), /not INR/i);
assert.match(
outstandingsPartialReason("company_base_currency_undetermined"),
/multiple Currency masters.*confirm INR manually.*single-company read.*foreign-currency ledger/i,
);
assert.match(outstandingsPartialReason("company_outstandings_read_failed"), /company read failed/i);
});

test("a foreign-currency ledger names the blocked book without inviting a repeat", () => {
const state = outstandingsPartialState(
"company_foreign_currency_ledger_balance",
undefined,
undefined,
"Synthetic FX Debtor",
);

assert.match(state.title, /not available for this company/i);
assert.match(state.message, /Synthetic FX Debtor/);
assert.match(state.message, /rather than guessing a base-currency amount/i);
assert.equal(state.retryable, false);
assert.equal(state.tallyReadAttempted, true);
});

test("missing, empty, or zero-only counters name the unconfirmed effective-date boundary", () => {
const state = outstandingsPartialState(
"native_outstandings_as_of_unconfirmed_without_effective_date_evidence",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ pub enum NativeOutstandingsError {
/// date, or its lexeme did not match the observed `D-MMM-YY` shape.
InvalidDate(&'static str),
InvalidAmount,
/// A ledger's `CLOSINGBALANCE` was a foreign-currency display expression
/// rather than the base-currency decimal this read requires.
ForeignCurrencyLedgerBalance {
ledger_name: String,
},
/// Tally's response did not match the documented grammar. The code
/// identifies which structural rule was violated.
InvalidResponse(&'static str),
Expand All @@ -29,6 +34,10 @@ impl fmt::Display for NativeOutstandingsError {
write!(formatter, "native outstandings date invalid ({code})")
}
Self::InvalidAmount => formatter.write_str("Tally returned an invalid native amount"),
Self::ForeignCurrencyLedgerBalance { ledger_name } => write!(
formatter,
"Tally reported a foreign-currency closing balance for ledger {ledger_name}"
),
Self::InvalidResponse(code) => {
write!(formatter, "native outstandings response invalid ({code})")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ fn xml_escape(value: &str) -> String {
/// A company's base currency is a fact Tally holds, so asking the operator to
/// assert it is a step the product can answer for itself. Measured
/// 2026-08-07 on three lab companies: one `CURRENCY` row each, `NAME` `"Rs."`,
/// `MAILINGNAME` `"Indian Rupees"`.
/// `MAILINGNAME` `"Indian Rupees"` or `"INR"`.
Comment thread
lamemustafa marked this conversation as resolved.
pub fn render_company_currency_request(company: &str) -> String {
format!(
r#"<ENVELOPE><HEADER><VERSION>1</VERSION><TALLYREQUEST>Export</TALLYREQUEST><TYPE>Collection</TYPE><ID>BridgeCompanyCurrencies</ID></HEADER><BODY><DESC><STATICVARIABLES><SVEXPORTFORMAT>$$SysName:XML</SVEXPORTFORMAT><SVCURRENTCOMPANY>{company}</SVCURRENTCOMPANY></STATICVARIABLES><TDL><TDLMESSAGE><COLLECTION NAME="BridgeCompanyCurrencies" ISMODIFY="No"><TYPE>Currency</TYPE><FETCH>NAME, MAILINGNAME, DECIMALPLACES</FETCH></COLLECTION></TDLMESSAGE></TDL></DESC></BODY></ENVELOPE>"#,
Expand Down
Loading