Fix PDF invoice false discount styling on repriced charge lines

Objective

  • Stop PDF invoices from showing crossed-out prices, “Discount applied” copy, and discount savings when the customer did not receive a real promotion—especially on orders where internal pricing adjustments (travel rebalance, modifiers, overrides) were applied.
  • Keep legitimate bundle and discount-code savings visible on invoices where those discounts actually apply.
  • Align client-facing invoice PDFs with the total amount due teams expect, without misleading discount styling on individual lines.

Background

  • Live example (RI5887): Travel-related admin-fee modifiers were used so total price would increase without affecting inspector commission. The total due looked correct on the client-facing side, but the PDF invoice showed the original amount crossed out with a lower amount beneath it (~$90), as if a customer discount had been applied.
  • The team was not certain whether RI5887 was caused only by admin-fee modifier setup or by another pricing interaction on that order. Code review shows admin-only modifiers do not change customer amount—they adjust admin/commission math in modifierPriceCalc.ts. Misleading discount styling is driven by invoice display heuristics, not admin modifier evaluation itself.
  • Current invoice behavior: In InvoicePDF.tsx and InvoiceAttachmentPDF.tsx, a charge line is treated as discounted when originalBasePrice > amount, or when any discount metadata is present (discountAmount, bundle/code discount fields, _discountCodeId). Matching lines render strikethrough, a bold final amount, and generic “Discount applied” when no bundle/code name is available.
  • The same originalBasePrice > amount strikethrough appears on the workorder Charges & Payments table in ServicesPayments.tsx (comment in PDF code says to keep in sync).
  • Surfaces: PDF download/preview via InvoiceDrawer.tsx (tools workorder and client portal); email attachments via backend buildTemplateEmailAttachments.ts using InvoiceAttachmentPDF.tsx.

Product Decisions

Locked

  1. Problem type — This is incorrect customer-facing presentation on PDF invoices, not a request to change how admin modifiers calculate payroll.
  2. Legitimate discounts — Bundle and discount-code savings that are real customer promotions should continue to show appropriate discount styling on invoices when product confirms the attribution fields are populated.
  3. Template parity — Frontend InvoicePDF.tsx and backend InvoiceAttachmentPDF.tsx must stay aligned; both are in scope.

Open

  1. RI5887 trigger validation — Which charge fields on RI5887 caused the display (originalBasePrice, amount, preTaxAmount, discountAmount, bundle/code IDs, modifier types on the service)? Needed to confirm whether the fix is display-only or also charge persistence.
  2. Manual price overrides — When staff set a price override below list/modifier-calculated price, should the invoice show strikethrough/discount styling, a single flat amount, or something else?
  3. Charges & Payments table — Should the same discount-display fix apply to the workorder Charges & Payments strikethrough (ServicesPayments.tsx), or PDF/email only in v1?
  4. Generic “Discount applied” copy — When strikethrough is warranted, should generic fallback copy remain, or should strikethrough appear only when bundle name or discount code name is available?
  5. originalBasePrice vs amount comparisonoriginalBasePrice is pre-tax starting fee; amount includes tax. Should invoice discount detection require explicit discount fields (bundle/code/discountAmount) rather than any case where stored base exceeds final amount?

Scope

Frontend

  • Invoice PDF (preview/download): attik-frontend/src/components/invoice/InvoicePDF.tsxgetBaseOriginalTotal, per-line hasDiscount, strikethrough rendering, subtotal/savings section (showDiscountUI, totalSavings).
  • Invoice drawer entry points: attik-frontend/src/components/invoice/InvoiceDrawer.tsx — used from ServicesPayments.tsx (tools) and JobAccordion.tsx (client portal).
  • Workorder charges table (likely same fix): attik-frontend/src/app/tools/inspections/[id]/components/ServicesPayments.tsxoriginalBasePrice > charge.amount strikethrough; keep in sync with PDF per code comment.

Backend

  • Email/PDF attachments: attik-backend/src/util/functions/pdf/templates/InvoiceAttachmentPDF.tsx — duplicate discount logic; must match frontend fix.
  • Attachment builder: attik-backend/src/util/functions/emailBuilder/buildTemplateEmailAttachments.ts — renders InvoiceAttachmentPDF for template emails.

Context (not modifier logic changes unless validation requires)

  • Charge shape: attik-backend/src/models/chargeSchema.tsoriginalBasePrice, discountAmount, bundle/code discount fields, amount, preTaxAmount.
  • Charge creation: attik-frontend/src/util/functions/quote/createChargeObjects.ts — sets originalBasePrice from startingFee, amount from totalPrice / priceOverride.
  • Modifier evaluation: attik-backend/src/util/functions/schedule/modifierPriceCalc.tsaddType: 'admin' affects finalAdminAddition only; addType: 'cost' affects customer finalPrice. Display bug is separate from admin modifier math unless charge fields are written incorrectly on save.

Out of scope (unless product expands)

  • Changing how admin, cost, or travel modifiers calculate prices or payroll.
  • Client portal non-PDF pricing displays (unless tied to the same shared discount helper after PDF fix).
  • ATT-1892 service-area travel modifier work—related operationally but separate delivery.

References

  • Example order discussed: RI5887
  • attik-frontend/src/components/invoice/InvoicePDF.tsx
  • attik-backend/src/util/functions/pdf/templates/InvoiceAttachmentPDF.tsx
  • attik-frontend/src/app/tools/inspections/[id]/components/ServicesPayments.tsx
  • attik-backend/src/util/functions/schedule/modifierPriceCalc.ts
  • attik-frontend/src/util/functions/quote/createChargeObjects.ts

Please authenticate to join the conversation.

Upvoters
Status

Planned

Board
🏠

Main App

Date

About 14 hours ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.