Digi@Flow Transaction Engine — 12→16 Column Processor with Deterministic RoundingIntroduction

63 views
Skip to first unread message

digico Iwate (Digico)

unread,
Apr 16, 2026, 7:29:21 AM (12 days ago) Apr 16
to mementodatabase

This engine is part of a production workflow built on MementoDB.

It converts a 12-column merged transaction CSV into a deterministic 16-column Digi@Flow format, with a focus on reproducibility and stable financial outcomes in a mobile execution environment.

I’m documenting an approach and interested in how others think about rounding, tax normalization, and transaction modeling.


Snapshot Model (Input Layer)

The engine operates on a snapshot of merged transaction data.

In this model:

  • each row is treated as an immutable input state

  • no external lookups or cross-row dependencies

  • all calculations are finalized per row

  • upstream inconsistencies are normalized at this layer

This keeps the output deterministic regardless of how the source data was produced.


Why This Exists

In practice, financial pipelines in MementoDB often require:

  • deterministic rounding at the smallest currency unit (0.01)

  • row-level finalization (no cross-row adjustments)

  • tolerance for inconsistent tax rate formats (0.08 / 8 / 1.08)

  • safe handling of quoted CSV fields

  • predictable behavior under mobile constraints

This engine is used in production, so operational stability is prioritized over mathematical symmetry.


Design PrinciplesDeterministic Rounding

All monetary values are finalized per row using Math.floor() with a small epsilon to suppress floating-point noise.

Tax Rate Normalization

Accepts multiple formats (0.08, 8, 1.08) and normalizes to 0.08.

Service vs. Product Logic

If cost == unit price and margin resolves to zero,
the full ex-tax amount is treated as profit (service/labor model).

CSV Handling

A lightweight parser handles quoted fields.
Optimized for controlled input, not full RFC4180 compliance.

Execution Context

Designed for MementoDB’s JavaScript runtime with mobile-first constraints.


Trade-offs

These choices are intentional:

  • floor-based rounding ensures consistent accumulation

  • epsilon is used only to neutralize floating-point artifacts

  • date formatting follows the local runtime timezone

  • service detection is heuristic and model-dependent


Code// Digi@Flow Transaction Processor (International Edition) // Input: 12-column merged transaction CSV ("取引結合") // Output: 16-column Digi@Flow CSV // Version: 2026-01-31_2045 Copilot Assisted — Digico Edition // Columns: // 1 Customer, 2 CustomerID, 3 Date, 4 VoucherID, 5 BranchID, // 6 Item, 7 Qty, 8 UnitPrice, 9 Unit, 10 TaxRate, // 11 Cost, 12 AmountExTax, 13 TaxAmount, 14 AmountIncTax, // 15 Profit, 16 Note function parseCSV(line){ var r = [], c = "", q = false; for (var i = 0; i < line.length; i++) { var x = line[i]; if (x === '"') { q = !q; continue; } if (x === "," && !q) { r.push(c); c = ""; } else c += x; } r.push(c); return r; } function toDateString(sec){ var n = Number(sec) || 0; if (n > 1000000000000) n = Math.floor(n / 1000); var d = new Date(n * 1000); var y = d.getFullYear(); var m = ("0" + (d.getMonth() + 1)).slice(-2); var da = ("0" + d.getDate()).slice(-2); return y + "/" + m + "/" + da; } function num(x){ return Number(String(x == null ? "" : x).trim()) || 0; } function str(x){ return String(x == null ? "" : x).trim(); } var EPS = 0.0000001; var raw = entry().field("取引結合") || ""; var rows = raw.split(/\r?\n/); var out = rows.reduce(function(a, line){ line = line.trim(); if (!line) return a; while (line.charAt(0) === "*") { line = line.substring(1); } var cols = parseCSV(line); if (cols.length < 12) return a; var c1 = str(cols[0]); var c2 = num(cols[1]); var c3s = cols[2]; var c4 = num(cols[3]); var c5 = num(cols[4]); var c6 = str(cols[5]); var c7 = num(cols[6]); var c8 = num(cols[7]); var c9 = str(cols[8]); var c10 = num(cols[9]); var c11 = num(cols[10]); var c16 = str(cols[11]); var c3 = c3s ? toDateString(c3s) : ""; var ex = Math.floor(c7 * c8 + EPS); var rate = c10; if (rate >= 2) { rate = rate / 100; } else if (rate > 1 && rate < 2) { rate = rate - 1; } var tax = Math.floor(ex * rate + EPS); var inc = ex + tax; var prof = ex - Math.floor(c11 * c7 + EPS); if (c7 > 0 && prof === 0 && c11 === c8) { prof = ex; } var lineOut = "*" + "\"" + c1 + "\"," + c2 + "," + c3 + "," + c4 + "," + c5 + "," + "\"" + c6 + "\"," + c7 + "," + c8 + "," + "\"" + c9 + "\"," + c10 + "," + c11 + "," + ex + "," + tax + "," + inc + "," + prof + "," + "\"" + c16 + "\""; a.push(lineOut); return a; }, []); out.join("\n");
Closing

Curious how others approach this layer:

  • Do you prefer floor, round, or banker’s rounding for accumulation consistency?

  • How do you normalize tax inputs in loosely structured data?

  • Do you model service vs. product explicitly, or infer it like this?

Interested in different trade-offs and design directions.


Bill Crews

unread,
Apr 16, 2026, 11:20:01 AM (12 days ago) Apr 16
to digico Iwate (Digico), mementodatabase
WTF?

--
You received this message because you are subscribed to the Google Groups "mementodatabase" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mementodataba...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mementodatabase/e9d51d17-4a53-4c1f-ab58-f4fb573741b2n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages