Skip to content

fix: pick the quote currency from a dropdown instead of a text field - #9

Merged
bariscelik merged 3 commits into
pigontech:mainfrom
NoelBank:fix/currency-dropdown-in-quote-form
Aug 11, 2026
Merged

fix: pick the quote currency from a dropdown instead of a text field#9
bariscelik merged 3 commits into
pigontech:mainfrom
NoelBank:fix/currency-dropdown-in-quote-form

Conversation

@NoelBank

@NoelBank NoelBank commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Changing a quote's currency from USD to EUR means typing over a three-character text field, so autosave stores the half-typed values E and EU on the way. The item schema only caps the length (z.string().max(3)), so both pass validation, and every later read that runs an amount through Intl.NumberFormat throws RangeError: currency is not a well-formed currency code. That shows up as a server error, and the quote keeps failing until the stored code is corrected by hand.

The quote form now uses the same CurrencyCombobox the invoice form already uses, so there are no half-typed states to save.

Both formatCurrency helpers, backend and frontend, also fall back to 12.50 EU instead of throwing. The dropdown only fixes new input; without the fallback a document that already carries a broken code keeps erroring.

Not in this PR: tightening the Zod rule to a real currency list. .max(3) currently also accepts an empty string and I didn't want to change what the API accepts inside a bug fix. Happy to follow up if you want that closed too.

bun run check passes.

NoelBank and others added 2 commits August 4, 2026 07:46
The quote form had a plain three-character text input for the currency, so
changing USD to EUR went through the half-typed values "E" and "EU". Autosave
stores those (the schema only caps the length), and every later read that runs
the amount through Intl.NumberFormat throws, which surfaces as a server error.

The form now uses the CurrencyCombobox the invoice form already uses, and both
currency formatters fall back to "12.50 XY" instead of throwing, so documents
that already carry a broken code stay readable.
@bariscelik

Copy link
Copy Markdown
Contributor

Reviewed and pushed one commit (b708faa) to this branch.

One formatting site was still unguarded. einvoice-pdf.service.ts has its own fmtMoney calling Intl.NumberFormat directly, so a document carrying a broken code still threw on the ZUGFeRD PDF path, the same symptom the fallback fixes everywhere else. It now delegates to formatCurrency.

The fallback keeps the configured number format. amount.toFixed(2) always emitted a dot decimal, so a 1.000,00 install would render healthy rows as 1.234,50 EUR and degraded ones as 1234.50 EU in the same document. It now formats the number in the resolved locale and appends the raw code, with toFixed(2) as the inner fallback when the locale itself is malformed (your de_DE test still passes unchanged).

The backend logs a warning when it falls back, so a bad code is discoverable instead of quietly shipping 12.50 EU onto a customer-facing PDF or email.

CurrencyCombobox shows the stored code when it is not in CURRENCIES. Opening a quote already holding EU rendered the "Select currency" placeholder while state still carried EU and autosave kept writing it, which is exactly the affected-document case.

Verified against a quote seeded with currency: "EU": the selector reads EU, the subtotal renders 12.50 EU with no error, and picking Euro repairs it to €12.50 on autosave. bun run check passes, 381 tests.

Agreed on leaving the Zod rule out of this. For the follow-up it is four routes, not one: customers.ts:158, invoices.ts:241, quotes.ts:78, expenses.ts:204, all z.string().max(3).

@bariscelik
bariscelik merged commit d7e7adc into pigontech:main Aug 11, 2026
2 checks passed
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.

2 participants