On this page
What changed, in one paragraphWhy it mattersScope of this PRA behavior worth knowing up frontOverview
This PR (#367, mo/fix_quotation_missing_fields_and_add_new_design) reworks how Quotations — the treatment cost estimates clinic staff prepare for a patient before treatment happens — calculate tax, and adds an optional new printable layout for handing that estimate to a patient.
It touches the web clinic app (clinic-web), the mobile clinic app (clinic-mobile), the GraphQL server, and the Quotation database table.
Status: unreleased. Everything described here lives on a feature branch and has not shipped to clinics yet.
What changed, in one paragraph
Quotations used to apply one flat discount % and one flat tax % to the whole estimate, regardless of which procedures were covered by insurance and which weren't. That's wrong whenever the insurance company's tax rate differs from the patient's own tax rate — the total tax came out incorrect, and quotations could disagree with the invoice they later became. This PR rebuilds the quotation editor around per-procedure line items: every procedure in the estimate now carries its own discount amount, its own insurance-covered amount, and its own tax toggle, and the quotation's tax is calculated by splitting each procedure's taxable amount into an insurance-covered part (taxed at the insurance company's rate) and a patient-paid part (taxed at the patient's/clinic's rate), then summing the two. This mirrors the model Dentolize Invoices already use — patientTaxPercent/insuranceTaxPercent already existed on the Invoice table; this PR adds the same two columns to Quotation (packages/prisma/schema.prisma:2764-2765) so quotations and their converted invoices agree. Alongside the fix, clinics can opt a branch into a redesigned, itemized printable quotation layout.
Why it matters
- Correctness: clinics quoting insured patients could show — and later invoice — the wrong total tax. This is money a patient is told they owe.
- Consistency: quotations now compute tax the same way invoices do, so a quotation's total should match the invoice it's converted into.
- Control: front-desk and insurance staff can now adjust discount, insurance coverage, and the tax toggle per procedure, instead of only at the whole-quotation level.
- Presentation: a new itemized print design gives patients a clearer paper estimate — per-procedure pricing before/after discount, an insurance column, and signature blocks.
Scope of this PR
| Area | What changed |
|---|---|
| Web quotation editor | QuotationDrawer.js rewritten around per-line-item discount/insurance/tax (was flat quotation-level fields) |
| Mobile quotation editor | NewQuotationScreen.js rewritten with the same per-line-item model |
| Database | Quotation.patientTaxPercent / Quotation.insuranceTaxPercent columns added (packages/prisma/migrations/20260806203102/migration.sql) |
| Server recalculation | handleUpdateQuotations (packages/server/src/resolvers/mutations/mutationUtils/quotationUtils.js) rewritten to recompute subtotal/discount/tax/insurance per procedure instead of from one quotation-level rate |
| Print settings | New "Quotation Design" option (Settings → Print → Invoice/Quotation form) with a free-text "Quotation Details" field, stored per branch |
| Print output | New NewQuotationDesign.js itemized layout, opt-in per branch — still shows a single combined tax line, not a patient/insurance split |
| Convert to invoice | ConvertInvoiceButton.js passes per-procedure discount through instead of one aggregate discount/insurance blob |
A behavior worth knowing up front
Editing the top-level tax % field in either editor (instead of a per-procedure tax) resets both the patient rate and the insurance rate to that one typed value, collapsing any existing split. This is intentional (it's the "set one flat rate for everything" escape hatch) but it's easy to trigger by accident — see Patient vs. Insurance Tax Split for where this happens in the code and why it matters for support and QA.
Read the Walkthrough for a guided tour with real screenshots, or jump straight to the Feature breakdown for the mechanics.