Skip to content

fix: PR D — decimal.js monetary math migration (round 4, bug 11)#81

Merged
telivity-otaip merged 1 commit into
mainfrom
fix/pr-d-decimal-migration
Apr 18, 2026
Merged

fix: PR D — decimal.js monetary math migration (round 4, bug 11)#81
telivity-otaip merged 1 commit into
mainfrom
fix/pr-d-decimal-migration

Conversation

@telivity-otaip
Copy link
Copy Markdown
Collaborator

Summary

Final PR from round-4 codex review. All monetary arithmetic now runs on `decimal.js` over Drizzle's string numeric representation.

Rule: Drizzle returns `numeric` as strings. Keep them as strings. Math via `new Decimal(str).plus/times/div(...).toFixed(2)`. Round only at the persist/display boundary.

Scope

Primary (Bug 11): tax engine — compound base, percentage rates, inclusive back-calc.

Full sweep across 11 business-logic files: folio, payment, reservation, reports, night-audit, connect-booking/search/insights, channel/ari + rate-parity.

`parseFloat` on money: 44 → 19 (25 removed). Remaining 19 intentionally kept (tests, statistical/ML agent inputs, OTA XML boundary parsers, probability values) — documented by decision rule.

Silent correctness wins

  • Compound tax base no longer accumulates per-step rounding error
  • Tax-inclusive back-calc (`gross / (1 + 0.13)`) now bit-exact
  • Stripe partial-refund idempotency keys stable across retries
  • Rate parity override (`base * (1 + 10/100)`) exact
  • Cancellation penalty + refund sum exactly to totalAmount

Test plan

  • `pnpm build` green
  • `pnpm typecheck` green
  • `pnpm test` — 562/562 unchanged (existing assertions matched Decimal output exactly)

Round 4 complete

PRs A (#78), B (#79), C (#80), D (this) address all 12 new bugs from the fourth codex review.

🤖 Generated with Claude Code

Replace float arithmetic on monetary values with decimal.js operating on
Drizzle's string numeric representation. Round only at persist/display boundary.

Primary fix (Bug 11): tax engine
- calculateTaxes compound base: runningBase.plus(rounded) exact each step
- backCalculateFromInclusive: gross / (1 + rate) now bit-exact
- Percentage rate application Decimal-safe

Full sweep — 11 business-logic files:
- folio.service (reversal/negation, tax charge reversal)
- payment.service (gateway amount, partial-refund idempotency key stability)
- reservation.service (120% deposit, express-checkout balance guard)
- reports.service (daily/financial/trend aggregation, ADR, RevPAR)
- night-audit.service (totalRoom/totalTax, ADR, RevPAR)
- connect-booking/search/insights.service (rate shopping, nightly breakdown,
  cancellation first-night penalty sums to totalAmount exactly)
- channel/ari.service + rate-parity.service (override percentage math exact)

parseFloat instances: 44 → 19 (25 removed). Remaining 19 intentionally kept:
- tests (6), statistical/ML agent inputs (9), OTA XML boundary parsers (2),
  confidence probabilities (2). Documented by decision rule.

562/562 tests pass unchanged — existing tax assertions lived in float-safe
zone; Decimal output matched at 2-decimal precision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@telivity-otaip telivity-otaip merged commit e36ef6d into main Apr 18, 2026
2 of 3 checks passed
@telivity-otaip telivity-otaip deleted the fix/pr-d-decimal-migration branch April 18, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant