Skip to content

Preserve sign and honor currency exponents in money formatting#232

Open
roncodes wants to merge 1 commit into
dev-v1.6.55from
claude/adoring-shirley-f94522
Open

Preserve sign and honor currency exponents in money formatting#232
roncodes wants to merge 1 commit into
dev-v1.6.55from
claude/adoring-shirley-f94522

Conversation

@roncodes

Copy link
Copy Markdown
Member

Problem

Utils::numbersOnly() normalized input with preg_replace('/[^0-9]/', '', ...), which stripped the leading minus sign. Because Utils::moneyFormat() (and the Money cast) route their input through it, negative monetary amounts silently lost their sign — e.g. moneyFormat(-500, 'USD') rendered as $5.00 instead of -$5.00. This is a real bug for refunds and adjustments.

While investigating, the money-format path was also checked against currencies whose minor-unit exponent isn't 2 — zero-decimal (JPY/KRW) and three-decimal (BHD/KWD).

Changes

  • numbersOnly() now preserves a minus sign that appears before the first digit, so signed values keep their sign. A hyphen occurring after digits (e.g. in a phone number like 276-7156) is deliberately not treated as a sign, keeping existing non-monetary usage intact. Empty / non-numeric / null still collapse to 0.
  • moneyFormat() documents and relies on the minor-units contract: the amount is an integer number of the currency's smallest unit, and the money adapter derives decimal placement from each currency's ISO-4217 exponent. No 2-decimal assumption — JPY/KRW format with no decimals, BHD/KWD with three (500 fils → 0.500).
  • Behavior of the Money cast follows automatically: it will now store negative amounts instead of flipping them positive.

Tests

  • Added cknow/laravel-money to require-dev so the money tests run against the real Cknow\Money\Money adapter instead of a stub.
  • Rewrote UtilsMoneyFormatTest to cover positive / zero / negative USD, formatted-string input, JPY/KRW (0-decimal), BHD/KWD (3-decimal), and non-numeric / null. Assertions target the numeric core of the output so they stay stable across ICU symbol/spacing differences.
  • Added dedicated numbersOnly sign-preservation coverage in UtilsTest.
  • Updated ReportQueryExporterTest, which had a cross-file dependency on the old stub, to assert the real adapter output.

Verification

  • Full suite: 1329 passed, 0 failed (pre-existing deprecations/warnings unchanged).
  • php-cs-fixer clean on all changed files; no new PHPStan findings on the changed methods.

🤖 Generated with Claude Code

Utils::numbersOnly() stripped the leading minus sign via
preg_replace('/[^0-9]/', ...), so negative monetary amounts lost their
sign (e.g. moneyFormat(-500, 'USD') rendered as $5.00 instead of
-$5.00). numbersOnly() now preserves a minus that appears before the
first digit, while still ignoring hyphens that occur after digits
(e.g. phone numbers like "276-7156"), and collapses empty/non-numeric
input to 0.

moneyFormat() treats its amount as an integer number of the currency's
smallest (minor) unit; the money adapter derives decimal placement from
each currency's ISO-4217 exponent, so zero-decimal (JPY/KRW) and
three-decimal (BHD/KWD) currencies format correctly rather than assuming
two places. Docblocks updated to document the contract.

Tests now exercise the real Cknow\Money\Money adapter (added
cknow/laravel-money to require-dev) instead of a stub, covering
negative/zero/JPY/KRW/BHD/KWD/non-numeric/null cases, plus dedicated
numbersOnly sign-preservation coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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