Dentolize · Invoice Discount Percentage Fix Walkthrough
On this pageThe change under testCore happy pathEdge cases to coverRegression checks (make sure nothing else moved)The open definitional questionData‑integrity note

For Quality

What to test, and where the edges are. The change is tiny, but it has a few real edge cases worth exercising.

The change under test

  • Web: packages/clinic-web/.../invoices/InvoiceInfo.js:118
  • Mobile: packages/clinic-mobile/.../invoices/InvoicesInfo.js:99
  • New formula: percentage = total ? (discount / (total + discount)) * 100 : undefined, rounded with .toFixed(1).

Core happy path

  1. Open Finances → Invoices, click Show Analytics, click the Discount

tile to filter to discounted invoices.

  1. Read the Total and Discount amounts from the tiles.
  2. Verify the tile percentage equals Discount ÷ (Total + Discount) × 100,

rounded to one decimal.

Known‑good reference (sandbox, discounted filter): Discount ₺2,373.25, Total ₺18,301.75 → 2373.25 / (18301.75 + 2373.25) = 11.48 % → 11.5 %. (The old code gave 2373.25 / 18301.75 = 13.0 %.)

Edge cases to cover

#ScenarioExpected
1No tax (VAT‑Exempt) invoices onlyTile equals the true "percent off": discount ÷ subtotal. This is the exact case.
2Taxed invoices in scopeTile is slightly lower than discount ÷ subtotal, because the denominator is subtotal + tax. Confirm it's between the old (too high) and the pre‑tax true value.
3Zero discount in scopeThe Discount tile is hidden entirely ({discount ? … : null}).
4Discount present but total is 0 (e.g. only balance invoices, all filtered out of total)Percentage is undefined / blank — no divide‑by‑zero, no Infinity, no NaN.
5Balance invoices in the mixtotal excludes balanceInvoice rows (filter(p => !p.balanceInvoice)) but discount sums all rows. Verify the intended scope: a discount sitting only on a balance invoice still contributes to the numerator while its total is excluded from the denominator. Flag if this skews the percentage.
6100 % discount (total after discount = 0 but discount > 0, non‑balance)Denominator total + discount = discount, so tile shows 100.0 %. Confirm it doesn't read blank or error.
7Web vs mobile paritySame filter/date range should yield the same percentage on both clients.
8Rounding displayWhole values print without a trailing .0 (e.g. 1, not 1.0), because of Number(...). Fractional values keep one decimal (11.5).
9Rounding to 0A very small discount (e.g. the unfiltered set: ₺2,373 on ₺246,439) rounds to 1. Confirm tiny discounts round sanely and never negative.

Regression checks (make sure nothing else moved)

  • Other tiles unchanged: Paid, Required To Pay, Remaining, Tax, Insurance,

Diagnostic Fee, Released still use value / total — their percentages must be identical to before this change.

  • Money amounts unchanged: every tile's currency figure is untouched.
  • Invoice detail unchanged: expanding a row still shows the same

Subtotal/Discount/Tax/Total (e.g. 500 − 75 + 63.76 = 488.76).

The open definitional question

The per‑invoice stored discountPercent is discount ÷ subtotal (invoiceUtils.js:145), a pre‑tax base. The tile now uses discount ÷ (total + discount) = discount ÷ (subtotal + tax), a tax‑inclusive base. They diverge on taxed invoices. This is not a bug in the fix, but QA should confirm with product which basis is canonical for the summary bar. If the tile should match the per‑invoice figure exactly, the denominator would need to subtract tax (total + discount − tax).

Data‑integrity note

No database writes are involved, so there is nothing to verify at the storage layer — only the rendered number. Confirm the value recomputes correctly on filter/date changes without a page reload.