Dentolize · Invoice Discount Percentage Fix Walkthrough
On this pageBusiness viewTechnical view

The discount percentage fix

Business view

What the number is supposed to mean

On the invoices screen, the Discount tile answers a simple question: "Of the work we billed, how much did we give away as discount?" The money figure (e.g. ₺2,373.25) is exact. The small percentage under it is meant to express that discount as a share — "we discounted about 12 % of what we billed."

What was wrong

The percentage was dividing the discount by the already‑discounted total. That is like saying "I gave you $20 off, and you paid $80, so that's a 25 % discount" — when in reality the item cost $100 and $20 off is a 20 % discount. Using the smaller, post‑discount number as the base always makes the percentage look bigger than the discount really was.

The more a clinic discounts, the more exaggerated the number became.

What it does now

The percentage is now calculated against the amount before the discount was applied. Using real sandbox data for the discounted invoices:

  • Old tile: 13.0 %
  • New tile: 11.5 %

The new figure is the true share of the pre‑discount price that was given away.

What this does not change

  • The money amount of the discount was always correct and is untouched.
  • The discount stored on each individual invoice is untouched.
  • Every other tile (Paid, Remaining, Tax, Insurance) is untouched.
  • No data is migrated or recalculated in the database — this is a display fix.

Technical view

The two changed lines

Both changes are identical in intent — one for web, one for mobile.

Webpackages/clinic-web/src/components/dashboard/finances/invoices/InvoiceInfo.js:118

<sub>{total ? `${Number(((discount / (total + discount)) * 100).toFixed(1))}%` : undefined}</sub>

Mobilepackages/clinic-mobile/src/components/dashboard/finances/invoices/InvoicesInfo.js:99

percentage={total ? Number(((discount / (total + discount)) * 100).toFixed(1)) : undefined}

Previously both used discount / total.

Where total and discount come from in this component

Both components aggregate the rows returned by the invoicesInfo query and reduce them into totals. In the web component (InvoiceInfo.js:16 and :22):

const total    = invoiceData.filter(p => !p.balanceInvoice).reduce((t, p) => t + p.total, 0)
const discount = invoiceData.reduce((t, p) => t + p.discount, 0)

So total and discount here are the sums across the currently filtered invoices — exactly the scope shown in the tile.

Why total is the wrong denominator

Each invoice's total is computed in the backend as net of discount:

packages/server/src/resolvers/mutations/actions/invoices/invoiceUtils.js:205

const total = Number(subtotal.toFixed(2)) - Number(discount.toFixed(2)) + Number(tax.toFixed(2))

That is total = subtotal − discount + tax. Rearranged:

total + discount = subtotal + tax

So total + discount reconstructs the pre‑discount, tax‑inclusive amount — the sensible base for "percent off". Dividing by total alone divides by a figure that has the discount subtracted out of it, which inflates the ratio.

The rounding

.toFixed(1) keeps one decimal place, then Number(...) strips a trailing zero (so 11.5 stays 11.5, but 1.0 prints as 1). The web version wraps the result in ${...}%; the mobile version passes the number to MoneyFormat's percentage prop. The total ? … : undefined guard avoids a divide‑by‑zero when there are no non‑balance invoices in scope.

One honest nuance: tax

The system's own stored per‑invoice discount percentage uses a pre‑tax base — discount / subtotal:

packages/server/src/resolvers/mutations/actions/invoices/invoiceUtils.js:145

const discountPercent = discount ? (allHasSameDiscountPercent ? firstDiscountPercent : (discount / subtotal) * 100) : 0

Because total + discount = subtotal + tax, the tile's new denominator is subtotal + tax, which is slightly larger than subtotal whenever tax is present. Consequences:

  • Tax‑exempt invoices (tax = 0): the tile matches the true "percent off"

exactly. Many sandbox invoices are marked VAT‑Exempt, so this is common.

  • Taxed invoices: the tile reads a touch lower than discount / subtotal.

On the sandbox's discounted set the true discount / subtotal is ≈ 12.0 %; the tile shows 11.5 %, while the old code showed 13.0 %. The new value is much closer to the truth, but not identical when tax is involved.

This is a summary‑bar display choice, not a change to any stored value. If the intent were to reproduce the stored discountPercent exactly, the denominator would need to be subtotal (i.e. total + discount − tax). See For Quality for the edge cases worth checking.