From 8cdb4bd72bd4b705b5914310e3f1d6ad5f326b6c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 1 Jun 2026 17:51:52 +0000 Subject: [PATCH] Fix FX rate convention to match market-standard quoting Users were confused by the "X foreign = 1 local" convention, which is the inverse of how exchange rates are normally quoted (e.g. "1 USD = 35 THB"). Flip to the market-standard "1 foreign = X local" direction, updating the rate label, the price calculation (per * rate instead of per / rate), and the PDF note. Note: existing localStorage data using the old convention will produce incorrect prices until users re-enter their exchange rates. https://claude.ai/code/session_0151QtsUhzXmgzEhSvXG2SDt --- CLAUDE.md | 2 +- app/config.yml | 8 ++++---- app/index.html | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index b975b60..8410bf2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,7 +60,7 @@ Do not break these behaviours: - **`addLine()` must `return i`** — `loadLines()` depends on the returned ID. - **`saveStorage()`** is triggered by `change` events on all `[data-ls]` elements; do not remove that listener. - **Invoice number bump:** occurs on the next page load after Generate via the `inv_generated_v1` flag; do not bump on Generate itself. -- **FX rate convention:** X foreign units = 1 local unit. `price_local = price_foreign / exchange_rate`. +- **FX rate convention:** 1 foreign unit = X local units (market-standard quote). `price_local = price_foreign * exchange_rate`. ## Config structure (`config.yml`) diff --git a/app/config.yml b/app/config.yml index aa7ff85..bd20241 100644 --- a/app/config.yml +++ b/app/config.yml @@ -360,10 +360,10 @@ translations: fr: Code devise "no": Valutakode exchange-rate: - en: "Exchange rate (X foreign = 1 local)" - de: "Wechselkurs (X Fremd = 1 Inland)" - fr: "Taux de change (X étranger = 1 local)" - "no": "Valutakurs (X utenlandsk = 1 lokal)" + en: "Exchange rate (1 foreign = X local)" + de: "Wechselkurs (1 Fremd = X Inland)" + fr: "Taux de change (1 étranger = X local)" + "no": "Valutakurs (1 utenlandsk = X lokal)" per-item: en: Price per item (foreign currency) de: Preis je Einheit (Fremdwährung) diff --git a/app/index.html b/app/index.html index 80121eb..5b4bd44 100644 --- a/app/index.html +++ b/app/index.html @@ -881,7 +881,7 @@ function toggleFx(i) {
-
Exchange rate (X ${h(defaultFcy)} = 1 ${h(lcy)})
+
Exchange rate (1 ${h(defaultFcy)} = X ${h(lcy)})
@@ -900,7 +900,7 @@ function calcFxFromPer(i) { const per = pn(document.getElementById(`fper-${i}`)?.value); const rate = pn(document.getElementById(`frate-${i}`)?.value) || 1; const prEl = document.getElementById(`price-${i}`); - if (prEl) prEl.value = (per / rate).toFixed(6); + if (prEl) prEl.value = (per * rate).toFixed(6); calcLine(i); saveLines(); } @@ -1327,7 +1327,7 @@ function buildPDF() { let fxH = 0; if (row.fxNote) { const fxStr = `${td("per-item")}: ${row.fxNote.cur} ${fmt(row.fxNote.per)} ` - + `(${(+row.fxNote.rate).toFixed(5)} ${row.fxNote.cur} = 1 ${iCur})`; + + `(1 ${row.fxNote.cur} = ${(+row.fxNote.rate).toFixed(5)} ${iCur})`; const fxLines = sp(fxStr, CD + CP - 4); fxH = fxLines.length * 3.5 + 1; } @@ -1360,7 +1360,7 @@ function buildPDF() { if (row.fxNote) { const fxStr = `${td("per-item")}: ${row.fxNote.cur} ${fmt(row.fxNote.per)} ` - + `(${(+row.fxNote.rate).toFixed(5)} ${row.fxNote.cur} = 1 ${iCur})`; + + `(1 ${row.fxNote.cur} = ${(+row.fxNote.rate).toFixed(5)} ${iCur})`; fn(7); tc(107,114,128); const fxLines = sp(fxStr, CD + CP - 4); fxLines.forEach((fl, fi) => tL(fl, xD+2, y + ROW_H + descH + 3.2 + fi * 3.5)); @@ -1410,7 +1410,7 @@ function updateFxLabels(i) { const lcy = document.getElementById("icur")?.value || ""; const rl = document.getElementById(`frate-lbl-${i}`); const pl = document.getElementById(`fper-lbl-${i}`); - if (rl) rl.textContent = `Exchange rate (X ${fcy} = 1 ${lcy})`; + if (rl) rl.textContent = `Exchange rate (1 ${fcy} = X ${lcy})`; if (pl) pl.textContent = `Price per item in ${fcy}`; }