Skip to content
Open
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
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": "29d49a8d8cf10c3415c7d487dfb7419e69ea581f035ec1b3d82788f2ea25f25e",
"compatibility_surface_sha256": "ac5d44785fa061e2694384d5ad5fe8f6a001ceb3226119404a00cba6cc7c8684",
"claims": [
{
"claim_id": "erp9-6-6-3-windows-education-xml-one-company",
Expand Down
10 changes: 5 additions & 5 deletions docs/tally/compatibility/compatibility-surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
},
{
"path": "src-tauri/src/commands.rs",
"sha256": "d12c341e5f5fecd6e41436293699bd8f318f0ae2127ccd7ede29e0af2db9d19e"
"sha256": "052b45f94751a01a1b7960cd861d1c122b49f0b80088b0434fc33e958396992d"
},
{
"path": "src-tauri/src/db/encrypted.rs",
Expand Down Expand Up @@ -303,7 +303,7 @@
},
{
"path": "src-tauri/src/lib.rs",
"sha256": "62c950aacaff7b1b431b65f32e14ead421d87a0bd3cd44a580912adfb5bf3730"
"sha256": "5582994d94163235a885bce0d44eadc11b6d8eb9a36898610e94c2b1141efbae"
},
{
"path": "src-tauri/src/sync/coordinator.rs",
Expand Down Expand Up @@ -359,7 +359,7 @@
},
{
"path": "src/OutstandingsScreen.tsx",
"sha256": "5fbb3bc7a47487cf5a5d9f5bc527c5a7e01940589fd519c4a07c1e6c6f094cf3"
"sha256": "6835d23a31eb92e1037e8f1e9c268ea86c59554a6b7060009e4dfa086bfa03d6"
},
{
"path": "src/TallyReadinessFlow.tsx",
Expand All @@ -379,7 +379,7 @@
},
{
"path": "src/styles.css",
"sha256": "b488d5305557b4c356e4d2e16971d387724e9be17ede1d8c05213f64c9661146"
"sha256": "e567fc5c8afebc047a8f8d59c7c7399dfefcb5e018aa337004fabf1ad8f03e4f"
},
{
"path": "src/tally-company-selection.ts",
Expand Down Expand Up @@ -434,5 +434,5 @@
"sha256": "a27f294ee15e407b69fdfc73609e8708ac0509b6e6a8872daef5451fde61a8db"
}
],
"manifest_sha256": "29d49a8d8cf10c3415c7d487dfb7419e69ea581f035ec1b3d82788f2ea25f25e"
"manifest_sha256": "ac5d44785fa061e2694384d5ad5fe8f6a001ceb3226119404a00cba6cc7c8684"
}
92 changes: 92 additions & 0 deletions scripts/client-grouping.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: Apache-2.0

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";

import { applyClientGroupLabel, groupClientRows, rollbackFailedClientGroupLabel, sumExactDecimals } from "../src/client-grouping.ts";

test("a failed optimistic label save restores only the value that actually failed", () => {
const persisted = { "synthetic-company-guid": "Original" };
const optimistic = applyClientGroupLabel(persisted, "synthetic-company-guid", "North");
assert.deepEqual(
rollbackFailedClientGroupLabel(optimistic, "synthetic-company-guid", "North", persisted),
persisted,
);

const newerEdit = applyClientGroupLabel(optimistic, "synthetic-company-guid", "Newer edit");
assert.deepEqual(
rollbackFailedClientGroupLabel(newerEdit, "synthetic-company-guid", "North", persisted),
newerEdit,
"a late failure must not erase typing that happened after the failed attempt",
);
assert.deepEqual(
rollbackFailedClientGroupLabel({ "synthetic-company-guid": "North" }, "synthetic-company-guid", "North", {}),
{},
"a failed first save returns the company to ungrouped",
);
});

test("applying a group label preserves every company figure byte-for-byte", () => {
const row = {
companyGuid: "synthetic-company-guid",
company: "Synthetic Components Ltd",
exactAmounts: { receivable: "482001.25", overdue: "120400.5", unallocated: "7580.75" },
oldest: 97,
complete: true,
};
const before = Buffer.from(JSON.stringify(row));

const grouped = groupClientRows([row], { "synthetic-company-guid": "North practice" });
const groupedRow = grouped.groups[0].rows[0];

assert.deepEqual(Buffer.from(JSON.stringify(groupedRow)), before);
assert.deepEqual(grouped.groups[0].totals, {
receivable: "482001.25",
overdue: "120400.5",
unallocated: "7580.75",
});
assert.equal(grouped.ungroupedRows.length, 0);
});

test("ungrouped companies remain separate and receive no synthetic total", () => {
const rows = [
{ companyGuid: "synthetic-a", exactAmounts: { receivable: "10", overdue: "2", unallocated: "1" } },
{ companyGuid: "synthetic-b", exactAmounts: { receivable: "20", overdue: "3", unallocated: "4" } },
];

const grouped = groupClientRows(rows, {});

assert.deepEqual(grouped.groups, []);
assert.equal(grouped.ungroupedRows.length, 2);
});

test("group totals preserve decimal precision without IEEE-754 rounding", () => {
assert.equal(sumExactDecimals(["9007199254740993", "0.01", "-1"]), "9007199254740992.01");
assert.equal(sumExactDecimals(["42.00", "0.10"]), "42.1");
assert.equal(sumExactDecimals(["10", "not-an-amount"]), undefined);
});

test("client amount views fail visibly instead of coercing or flipping amounts", async () => {
const [allClients, outstandings] = await Promise.all([
readFile(new URL("../src/AllClientsScreen.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/OutstandingsScreen.tsx", import.meta.url), "utf8"),
]);

assert.match(allClients, /Amount unavailable/);
assert.doesNotMatch(allClients, /Math\.abs/);
assert.match(outstandings, /Bridge could not read an outstandings amount/);
assert.doesNotMatch(outstandings, /Math\.abs/);
Comment thread
lamemustafa marked this conversation as resolved.
});

test("all-client responses carry the pinned GUID back to the open action", async () => {
const [allClients, commands] = await Promise.all([
readFile(new URL("../src/AllClientsScreen.tsx", import.meta.url), "utf8"),
readFile(new URL("../src-tauri/src/commands.rs", import.meta.url), "utf8"),
]);

assert.match(commands, /pub company_guid: String/);
assert.match(commands, /company_guid: entry\.expected_company_guid/);
assert.match(allClients, /companyGuid: entry\.company_guid/);
assert.doesNotMatch(allClients, /companies\.find\(\(company\) => company\.name === entry\.company\)/);
});
Loading
Loading