Skip to content

feat: add order invoice component#282

Open
tomrndom wants to merge 13 commits into
mainfrom
feat/order-pdf
Open

feat: add order invoice component#282
tomrndom wants to merge 13 commits into
mainfrom
feat/order-pdf

Conversation

@tomrndom

@tomrndom tomrndom commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

ref: https://app.clickup.com/t/9014802374/86baq9key

Signed-off-by: Tomás Castillo tcastilloboireau@gmail.com

Summary by CodeRabbit

  • New Features
    • Added printable order invoices in PDF format with order details, customer/venue info, line items, fees, discounts, payments, refunds, notes, and calculated totals.
    • Supports both direct downloads and in-browser preview, including reconciliation and cancellation/refund impacts.
  • Tests
    • Added a comprehensive Jest suite validating invoice row generation and the PDF download/preview flows.
  • Maintenance
    • Upgraded the PDF rendering library and included the new order-invoice PDF bundle in the build output.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new order invoice PDF module with row construction, React-PDF rendering, reconciliation display, browser download and preview helpers, webpack entry wiring, dependency updates, and comprehensive Jest coverage.

Changes

Order PDF invoice generation

Layer / File(s) Summary
Invoice formatting and row construction
src/components/order-invoice-pdf/helpers.js
Adds date, address, venue, font, currency, and invoice-row helpers covering items, cancellations, discounts, fees, payments, refunds, and notes.
PDF styles and rendering components
src/components/order-invoice-pdf/styles.js, src/components/order-invoice-pdf/components/*
Adds invoice styles and reusable PDF components for fields, icons, transaction rows, and reconciliation details.
Invoice component and browser utilities
src/components/order-invoice-pdf/index.js, webpack.common.js, src/components/index.js, package.json
Adds OrderPdf, PDF download and preview functions, a webpack entry, a commented component export, and React-PDF version updates.
Invoice PDF test coverage
src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js
Adds mocked rendering tests for row construction, invoice output, reconciliation labels, downloads, previews, errors, and delayed URL revocation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: smarcet

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant previewPDF
  participant OrderPdf
  participant BrowserTab
  Caller->>previewPDF: order, summit, options
  previewPDF->>BrowserTab: open blank tab synchronously
  previewPDF->>OrderPdf: render invoice to PDF blob
  OrderPdf-->>previewPDF: PDF blob
  previewPDF->>BrowserTab: navigate to blob URL
  previewPDF-->>BrowserTab: revoke blob URL after delay
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a new order invoice component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/order-pdf

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/__tests__/order-pdf-.test.js`:
- Line 16: The test import is wrong in order-pdf-.test.js: it only brings in
buildOrderPdfRows and OrderPdf from the components barrel, but the suite
actually calls buildRows, and importing via the barrel also pulls in react-dnd
through SortableTable. Update the test to import the order-pdf module directly
and use the matching exported helper name (or import the alias used by the
tests) so buildRows is defined and the react-dnd dependency chain is avoided.

In `@src/components/order-pdf/index.js`:
- Around line 82-101: The item filtering in the order PDF is inconsistent with
the displayed default quantity: the current filter in the item iteration drops
rows when quantity is missing, but the row rendering in the same block (`qty` in
the `form.items` processing) treats missing quantity as 1. Update the
`form.items` filter logic so optional/missing quantities are still included when
they should render as default quantity 1, and keep the balance calculation and
PDF rows aligned with the `rows.push` item mapping.
- Around line 804-805: The previewPDF flow is creating a blob URL and opening it
in a new tab without releasing it afterward, which can leak memory on repeated
previews. Update the URL handling around the object URL creation in previewPDF
so the URL is revoked after the new window/tab has been opened, while keeping
the current open-in-new-tab behavior intact.
- Around line 32-33: The formatDate helper currently calls .format()
unconditionally on the result of epochToMomentTimeZone, which can be falsy for
missing or 0 epochs and crash invoice rendering. Update formatDate to guard the
value returned by epochToMomentTimeZone before formatting, and return a safe
fallback when no valid moment-like value exists. Keep the fix localized to
formatDate so callers of the order PDF rendering path continue working without
changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b0cf3870-2447-4bcc-95d2-77eafc9856b4

📥 Commits

Reviewing files that changed from the base of the PR and between d418849 and 58c7524.

📒 Files selected for processing (5)
  • package.json
  • src/components/__tests__/order-pdf-.test.js
  • src/components/index.js
  • src/components/order-pdf/index.js
  • webpack.common.js

Comment thread src/components/__tests__/order-pdf-.test.js Outdated
Comment thread src/components/order-pdf/index.js Outdated
Comment thread src/components/order-invoice-pdf/index.js Outdated
Comment thread src/components/order-invoice-pdf/index.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reusable “order invoice PDF” component to uicore, enabling apps that consume this library to generate and preview an order invoice PDF (with line items, fees, discounts, payments/refunds, notes, reconciliation, and amount due).

Changes:

  • Introduces OrderPdf + helpers (buildRows, generateInvoicePDF, previewPDF) under src/components/order-invoice-pdf/.
  • Exposes the new component/helpers via the central src/components/index.js export surface and adds a webpack entrypoint.
  • Updates @react-pdf/renderer to v4.x and adds unit tests for row-building and rendering.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
webpack.common.js Adds a new webpack entry for the invoice PDF component bundle.
src/components/order-invoice-pdf/index.js Implements PDF layout, row building, and generate/preview helpers.
src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js Adds tests for row formatting and basic render behavior.
src/components/index.js Exports the new invoice PDF APIs from the library’s main component index.
package.json Bumps @react-pdf/renderer devDependency and broadens peer dependency to allow v4.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/components/order-invoice-pdf/index.js Outdated
Comment thread src/components/order-invoice-pdf/index.js Outdated
Comment thread src/components/index.js Outdated
Comment thread package.json Outdated

@smarcet smarcet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomrndom please review

Comment thread src/components/order-invoice-pdf/index.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/order-invoice-pdf/helpers.js (1)

88-114: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Quantity filter still drops items that the row renderer treats as qty=1 — previously flagged, now regressed.

Line 90 filters out any item with a falsy quantity (covers undefined/null/0), yet Line 108 renders missing quantity as 1 via item.quantity ?? 1. Since the filter runs first, items with undefined/null quantity never reach the render step, so this fallback is dead code and those legacy/optional-quantity items silently disappear from both the invoice table and the running balance (Line 99 balanceCents += item.amount) instead of showing with default qty 1. This is the exact issue previously raised on an earlier revision of this component (then at src/components/order-pdf/index.js) and marked resolved — it appears to have regressed during the file split into helpers.js/styles.js/components.js/index.js.

🐛 Proposed fix
     (form.items || [])
-      .filter((item) => item.quantity)
+      .filter((item) => (item.quantity ?? 1) > 0)
       .forEach((item) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/order-invoice-pdf/helpers.js` around lines 88 - 114, The item
iteration in the order invoice row builder is filtering out any entry with a
falsy quantity before rendering, which causes legacy items with missing quantity
to disappear even though the row logic already defaults qty to 1. Update the
item-processing path in the form/items loop so that undefined or null quantity
values are allowed through while still excluding only truly invalid cases if
needed, and keep the existing qty fallback used when building each row. Ensure
the fix preserves the balance update and row creation for those items in the
helpers.js item row construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/order-invoice-pdf/index.js`:
- Around line 36-108: The OrderPdf component is only partially guarded: the
totals destructure uses order || {}, but later reads still access order.client,
order.address, order.number, order.purchased_date, summit.time_zone_id, and
summit.name directly. Update OrderPdf to consistently handle missing order and
summit data by defaulting the props up front or using optional
chaining/fallbacks at each access so rendering does not throw when either prop
is absent.

---

Duplicate comments:
In `@src/components/order-invoice-pdf/helpers.js`:
- Around line 88-114: The item iteration in the order invoice row builder is
filtering out any entry with a falsy quantity before rendering, which causes
legacy items with missing quantity to disappear even though the row logic
already defaults qty to 1. Update the item-processing path in the form/items
loop so that undefined or null quantity values are allowed through while still
excluding only truly invalid cases if needed, and keep the existing qty fallback
used when building each row. Ensure the fix preserves the balance update and row
creation for those items in the helpers.js item row construction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc0a5572-6fe6-46c0-a3ca-9e4a0688b776

📥 Commits

Reviewing files that changed from the base of the PR and between 58c7524 and 32793f5.

📒 Files selected for processing (8)
  • package.json
  • src/components/index.js
  • src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js
  • src/components/order-invoice-pdf/components.js
  • src/components/order-invoice-pdf/helpers.js
  • src/components/order-invoice-pdf/index.js
  • src/components/order-invoice-pdf/styles.js
  • webpack.common.js

Comment thread src/components/order-invoice-pdf/index.js

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread src/components/order-invoice-pdf/styles.js
Comment thread src/components/order-invoice-pdf/helpers.js Outdated
Comment thread src/components/order-invoice-pdf/index.js
Comment thread package.json
Comment thread src/components/order-invoice-pdf/index.js Outdated
Comment thread src/components/order-invoice-pdf/index.js
Comment thread src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js Outdated
Comment thread src/components/order-invoice-pdf/helpers.js Outdated
Comment thread src/components/order-invoice-pdf/styles.js
Comment thread src/components/order-invoice-pdf/components.js Outdated
Comment thread src/components/order-invoice-pdf/components.js Outdated
Comment thread src/components/order-invoice-pdf/components.js Outdated
Comment thread src/components/order-invoice-pdf/components.js Outdated
Comment thread src/components/order-invoice-pdf/components.js Outdated
Comment thread src/components/order-invoice-pdf/helpers.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/order-invoice-pdf/index.js (1)

176-230: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate PDF-blob construction across generateInvoicePDF and previewPDF.

Both functions build the identical pdf(<OrderPdf order={order} summit={summit} logoSrc={logoSrc} theme={theme} />).toBlob() call. Extracting a shared helper (e.g. buildInvoicePdfBlob(order, summit, options)) would remove the duplication and keep the two call sites in sync going forward.

♻️ Proposed refactor
+const buildInvoicePdfBlob = (order, summit, { logoSrc, theme } = {}) =>
+  pdf(
+    <OrderPdf order={order} summit={summit} logoSrc={logoSrc} theme={theme} />
+  ).toBlob();
+
 export const generateInvoicePDF = async (
   order,
   summit,
   { logoSrc, theme } = {}
 ) => {
   try {
-    const blob = await pdf(
-      <OrderPdf
-        order={order}
-        summit={summit}
-        logoSrc={logoSrc}
-        theme={theme}
-      />
-    ).toBlob();
+    const blob = await buildInvoicePdfBlob(order, summit, { logoSrc, theme });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/order-invoice-pdf/index.js` around lines 176 - 230, Extract
the duplicated OrderPdf-to-blob construction from generateInvoicePDF and
previewPDF into a shared helper such as buildInvoicePdfBlob, accepting order,
summit, and the logoSrc/theme options. Replace both inline pdf(...).toBlob()
calls with the helper while preserving each function’s existing download,
preview, cleanup, and error behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/order-invoice-pdf/index.js`:
- Around line 206-221: Update window.open in previewPDF to use only the blank
URL and _blank target, removing the noreferrer feature so it returns a
WindowProxy. Preserve the existing newTab.location.href navigation after the PDF
blob URL is created.

---

Nitpick comments:
In `@src/components/order-invoice-pdf/index.js`:
- Around line 176-230: Extract the duplicated OrderPdf-to-blob construction from
generateInvoicePDF and previewPDF into a shared helper such as
buildInvoicePdfBlob, accepting order, summit, and the logoSrc/theme options.
Replace both inline pdf(...).toBlob() calls with the helper while preserving
each function’s existing download, preview, cleanup, and error behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e2b88bf2-022d-4a63-8853-0609987037c6

📥 Commits

Reviewing files that changed from the base of the PR and between 32793f5 and de411bc.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (11)
  • package.json
  • src/components/index.js
  • src/components/order-invoice-pdf/__tests__/order-invoice-pdf.test.js
  • src/components/order-invoice-pdf/components/field-row.js
  • src/components/order-invoice-pdf/components/pdf-icon.js
  • src/components/order-invoice-pdf/components/pdf-table-row.js
  • src/components/order-invoice-pdf/components/reconciliation-block.js
  • src/components/order-invoice-pdf/helpers.js
  • src/components/order-invoice-pdf/index.js
  • src/components/order-invoice-pdf/styles.js
  • webpack.common.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • webpack.common.js
  • src/components/order-invoice-pdf/styles.js

Comment thread src/components/order-invoice-pdf/index.js

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

const rows = buildRows(order, summit);

const {
amount_due: total = 0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomrndom Quick question on total vs amount_due: OrderPdf reads order.amount_due here for the AMOUNT DUE line, but SponsorOrderGrid (src/components/mui/SponsorOrderGrid/index.js:86) reads order.total for the same concept. I checked purchases-api's purchase_v2_serializer.py — the raw v2 response has amount_due (computed as max(raw_amount - discount_amount + charges_amount - payments_total, 0)) but no total field at all, so amount_due matches the backend contract exactly, same as the other 4 reconciliation fields this component already reads (cancelled_total, refunds_total, retained, credited_to_payment_method).

Can you confirm whether the order prop this component receives is the raw purchases-api v2 response, or whether it goes through the same mapping/normalization used to build SponsorOrderGrid's order prop (which would carry total instead)? If it's the latter, AMOUNT DUE will always render $0.00 in production and amount_due should be total here.

@tomrndom tomrndom Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This takes the raw amount_due prop directly from the order API response where total not exists, based on this change at sponsor-services https://github.com/fntechgit/sponsor-services/pull/100/changes

This components is migrated from sponsor-services, and reused part of the structure from the order-details page. On a deep look, that page the data comes from the reducer after a normalizeOrder function that assign the prop total from the value of amount_due or net_amount (https://github.com/fntechgit/sponsor-services/blob/main/src/utils/methods.js#L92), We should probably need to adjust this line to support both options like the reducer

@smarcet smarcet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomrndom please review

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
tomrndom added 10 commits July 24, 2026 15:16
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
…s, add tests

Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
tomrndom added 2 commits July 24, 2026 16:37
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants