Menu โ–พ โ–ด

#173 ERPNext adapters: cast amounts to Decimal(38,9); the trial balance union fails with NO_COMMON_TYPE

closed
nobody
None
2026-09-17
2026-09-13
Anonymous
No

Originally created by: grynn-in

Found reviewing [#158] (PR [#168]).

The canonical staging models UNION ALL every enabled ERP. D365 amounts are Decimal(38, 9), and ClickHouse 24.8 refuses a union of Decimal(38,9) with Float64 (Code 386, NO_COMMON_TYPE), including the Nullable variants.

  • Trial balance, certain break: stg_erpnext__trial_balance.sql (around line 48) hard-codes toFloat64(0) as opening_balance. Enabling erpnext therefore breaks stg_trial_balance whatever type the feed lands as.
  • GL, likely break: stg_erpnext__gl_entries.sql (around line 46) doesn't cast amount. A Float64-landed ERPNext feed breaks stg_gl_entries for every source. An Airbyte-landed feed is probably Decimal, but nothing guarantees it.

Fix:

  • Cast in the adapters. For example, use toDecimal128(0, 9) for the opening balance, and cast amount and the sum(debit) / sum(credit) terms to Decimal(38, 9).
  • Add a test that builds the canonical unions with erpnext enabled over empty inputs.
  • Update the "Known gaps" list in models/staging/README.md (added in [#168]).

Related

Tickets: #158
Tickets: #168
Tickets: #173

Discussion

  • Anonymous

    Anonymous - 2026-09-15

    Originally posted by: grynn-in

    Verdict: LIVE, confirmed. stg_erpnext__trial_balance.sql:48 still reads toFloat64(0) as opening_balance, so the union with D365's Decimal(38, 9) still has the certain break this issue describes. Unchanged.

    ๐Ÿค– Triage against main โ€” Claude Code ยท https://claude.ai/code/session_01P3Pf9835FeLeXjRrYTTZ1M

     
  • Anonymous

    Anonymous - 2026-09-16

    Originally posted by: grynn-in

    Options scored

    criterion A. Close [#173], leave the path dormant B. Fix the cast C. Retire the ERP adapter path
    Product, not one-off Neutral Neutral Yes โ€” one ingestion story
    Technical debt Highest โ€” two architectures, one untested Adds a test to dead code Lowest โ€” removes the second
    Configurable Flag stays, meaning unclear Same Fewer knobs, clearer
    Not opinionated Implies ERP support that isn't real Same States the boundary
    Compliant n/a n/a n/a
    No silent fallback Poor โ€” a flag that breaks when used Better โ€” it would work Best โ€” the flag is gone
    Traceable Issue closed with a reason Fix without a decision Decision recorded
    Effective-dated n/a n/a n/a
    Bounded residual n/a n/a n/a
    Upgrade path None needed None needed Real work, and konsol#218 depends on it

    Reading

    C wins on every criterion that applies. Its only cost sits in the last row, and that row is not really a cost โ€” konsol#218 depending on it means C clears the ground [#218] builds on, rather than competing with it.

    A is the worst option and the most tempting. Closing the issue leaves erp_sources implying ERPNext support that does not work, so the next person to enable it hits the same NO_COMMON_TYPE break with no issue left to find. That is the "no silent fallback" row doing its job.

    B is a trap that looks like diligence. It adds a test to a code path nobody runs, on a layer the 15 Sep decision put out of scope. It makes the flag work without deciding whether the flag should exist.

    I recommended B. It is item 9 on the small-issues queue (konsol#235) as "fix the cast" โ€” chosen because it was small and self-contained, which is exactly the reasoning this framework is built to catch. Small and self-contained is not the same as worth doing. I will move it.

    Note on the three n/a rows

    Compliant, Effective-dated and Bounded residual are n/a for all three options โ€” the same pattern as the konsol#218 scoring, where Compliant was n/a on every question. These are accounting criteria, and this is a plumbing decision. Their being blank is informative rather than missing: it confirms this can be settled on engineering grounds without an accounting judgement.

    What C means concretely

    Related and already scoped: konsolidat#207 โ€” eleven bronze models ref() stg_d365_fo__* directly, so D365 cannot be switched off either. C is the ERPNext half of the same boundary. Both should be sequenced inside the dbt-into-the-app move rather than before it, since that work touches the same tree.

    ๐Ÿค– Claude Code ยท https://claude.ai/code/session_01P3Pf9835FeLeXjRrYTTZ1M

     

    Related

    Tickets: #173
    Tickets: #218

  • Anonymous

    Anonymous - 2026-09-17

    Originally posted by: grynn-in

    Closing โ€” the ERP staging tree is being removed (konsolidat#221, Deepak Pai, 17 September 2026).

    This defect's only site is stg_erpnext__trial_balance.sql, inside the tree being deleted. Fixing the cast would add a test to code scheduled for removal.

    This matches the scoring already on this issue: option C, retire the ERP adapter path, won on every criterion that applied. The decision now makes that concrete.

    If the ERP path ever returns, this defect is real and this issue is the record of it.

    ๐Ÿค– Claude Code ยท https://claude.ai/code/session_01P3Pf9835FeLeXjRrYTTZ1M

     
  • Anonymous

    Anonymous - 2026-09-17

    Ticket changed by: grynn-in

    • status: open --> closed
     

Log in to post a comment.