Skip to content

feat: add invoice pdf download on purchase lists#1021

Closed
tomrndom wants to merge 1 commit into
masterfrom
feat/invoice-order-pdf
Closed

feat: add invoice pdf download on purchase lists#1021
tomrndom wants to merge 1 commit into
masterfrom
feat/invoice-order-pdf

Conversation

@tomrndom

@tomrndom tomrndom commented Jul 24, 2026

Copy link
Copy Markdown

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

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

Summary by CodeRabbit

  • New Features

    • Added invoice PDF downloads for individual sponsor purchases.
    • Added invoice generation support for invoice-paid purchases.
    • Added clear error messaging when invoice generation fails.
  • Bug Fixes

    • Improved sponsor order retrieval when the current sponsor identifier is unavailable.
  • Improvements

    • Prevented duplicate invoice downloads while a file is being generated.

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

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Sponsor purchase list views now download invoice PDFs by fetching sponsor orders, generating PDFs with summit context, preventing concurrent downloads, and showing translated errors. Sponsor order retrieval accepts a fallback sponsor ID, and the UI foundation dependency is updated.

Changes

Sponsor invoice downloads

Layer / File(s) Summary
Sponsor order fetch fallback
src/actions/sponsor-purchases-actions.js
getSponsorOrder accepts an optional sponsor ID and uses it when the current Redux sponsor ID is unavailable.
Purchase list invoice flow
src/pages/sponsors/show-purchase-list-page/index.js, src/i18n/en.json, package.json
The purchase list downloads invoices with loading protection, summit context, translated error handling, and updated supporting dependency and locale text.
Sponsor purchases tab invoice flow
src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js
The sponsor purchases tab connects invoice retrieval and generation to its download action with loading protection and error handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PurchaseView
  participant getSponsorOrder
  participant SponsorOrdersAPI
  participant generateInvoicePDF
  participant Snackbar
  PurchaseView->>getSponsorOrder: fetch selected purchase order
  getSponsorOrder->>SponsorOrdersAPI: request order with computed sponsor_id
  SponsorOrdersAPI-->>getSponsorOrder: return sponsor order
  getSponsorOrder-->>PurchaseView: provide order data
  PurchaseView->>generateInvoicePDF: generate invoice PDF
  generateInvoicePDF-->>PurchaseView: complete or fail
  PurchaseView->>Snackbar: show translated error on failure
Loading

Possibly related PRs

Suggested reviewers: smarcet, santipalenque

🚥 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 invoice PDF downloads to purchase lists.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/invoice-order-pdf

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

package.json

Parsing error: Missing semicolon. (2:8)


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: 3

🤖 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/actions/sponsor-purchases-actions.js`:
- Around line 303-308: Update the sponsor ID selection in the purchase action
around getAccessTokenSafely so the explicit sponsorId argument is preferred,
falling back to sponsor.id only when it is absent. Preserve the existing
currentSponsorState lookup for that fallback and use the selected ID for
subsequent sponsor-specific requests.

In `@src/pages/sponsors/show-purchase-list-page/index.js`:
- Around line 202-208: Add the same localized invoice-download aria-label to
both IconButton controls: src/pages/sponsors/show-purchase-list-page/index.js
lines 202-208 and
src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js lines
185-191. Reuse the existing localization mechanism and keep the button behavior
unchanged.
- Around line 99-109: Update the failure handler in handleInvoiceDownload to use
the imported translation instance instead of the undefined t reference, while
preserving the existing invoice_generation error message and loading-state
cleanup.
🪄 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: 9ed2e073-a69c-4222-8d01-dcd070c01dd6

📥 Commits

Reviewing files that changed from the base of the PR and between 53b15bf and 3aeb075.

⛔ Files ignored due to path filters (2)
  • src/assets/fn-invoice-header.png is excluded by !**/*.png
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • package.json
  • src/actions/sponsor-purchases-actions.js
  • src/i18n/en.json
  • src/pages/sponsors/show-purchase-list-page/index.js
  • src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js

Comment on lines +303 to +308
const { currentSummitState, currentSponsorState } = getState();
const { currentSummit } = currentSummitState;
const { entity: sponsor } = currentSponsorState;
const accessToken = await getAccessTokenSafely();

return getRequest(
null,
createAction(RECEIVE_SPONSOR_ORDER),
`${window.PURCHASES_API_URL}/api/v2/summits/${currentSummit.id}/sponsors/${sponsor.id}/purchases/${orderId}`,
authErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
const sponsor_id = sponsor.id || sponsorId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prioritize the explicit purchase sponsor ID.

The global purchase list passes the row’s purchaseOrder.sponsor_id, but Line 308 ignores it whenever Redux still holds a previously viewed sponsor. That can fetch the order through the wrong sponsor URL. Prefer the explicit argument, with Redux only as the fallback.

Proposed fix
-    const sponsor_id = sponsor.id || sponsorId;
+    const sponsor_id = sponsorId || (sponsor && sponsor.id);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { currentSummitState, currentSponsorState } = getState();
const { currentSummit } = currentSummitState;
const { entity: sponsor } = currentSponsorState;
const accessToken = await getAccessTokenSafely();
return getRequest(
null,
createAction(RECEIVE_SPONSOR_ORDER),
`${window.PURCHASES_API_URL}/api/v2/summits/${currentSummit.id}/sponsors/${sponsor.id}/purchases/${orderId}`,
authErrorHandler
)(params)(dispatch).finally(() => {
dispatch(stopLoading());
});
};
const sponsor_id = sponsor.id || sponsorId;
const { currentSummitState, currentSponsorState } = getState();
const { currentSummit } = currentSummitState;
const { entity: sponsor } = currentSponsorState;
const accessToken = await getAccessTokenSafely();
const sponsor_id = sponsorId || (sponsor && sponsor.id);
🤖 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/actions/sponsor-purchases-actions.js` around lines 303 - 308, Update the
sponsor ID selection in the purchase action around getAccessTokenSafely so the
explicit sponsorId argument is preferred, falling back to sponsor.id only when
it is absent. Preserve the existing currentSponsorState lookup for that fallback
and use the selected ID for subsequent sponsor-specific requests.

Comment on lines +99 to +109
const handleInvoiceDownload = (purchaseOrder) => {
if (loadingPDF) return;
setLoadingPDF(true);
getSponsorOrder(purchaseOrder.id, purchaseOrder.sponsor_id)
.then(({ response: fetchedOrder }) =>
generateInvoicePDF(fetchedOrder, currentSummit, {
logoSrc: logoInvoice
})
)
.catch(() => errorMessage(t("errors.invoice_generation")))
.finally(() => setLoadingPDF(false));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the imported translation instance in the failure handler.

Line 108 references undefined t; a failed fetch/PDF generation throws instead of showing the snackbar.

-      .catch(() => errorMessage(t("errors.invoice_generation")))
+      .catch(() => errorMessage(T.translate("errors.invoice_generation")))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleInvoiceDownload = (purchaseOrder) => {
if (loadingPDF) return;
setLoadingPDF(true);
getSponsorOrder(purchaseOrder.id, purchaseOrder.sponsor_id)
.then(({ response: fetchedOrder }) =>
generateInvoicePDF(fetchedOrder, currentSummit, {
logoSrc: logoInvoice
})
)
.catch(() => errorMessage(t("errors.invoice_generation")))
.finally(() => setLoadingPDF(false));
const handleInvoiceDownload = (purchaseOrder) => {
if (loadingPDF) return;
setLoadingPDF(true);
getSponsorOrder(purchaseOrder.id, purchaseOrder.sponsor_id)
.then(({ response: fetchedOrder }) =>
generateInvoicePDF(fetchedOrder, currentSummit, {
logoSrc: logoInvoice
})
)
.catch(() => errorMessage(T.translate("errors.invoice_generation")))
.finally(() => setLoadingPDF(false));
🤖 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/pages/sponsors/show-purchase-list-page/index.js` around lines 99 - 109,
Update the failure handler in handleInvoiceDownload to use the imported
translation instance instead of the undefined t reference, while preserving the
existing invoice_generation error message and loading-state cleanup.

Comment on lines 202 to 208
<IconButton
size="large"
sx={{ color: "primary.main" }}
onClick={() => handleMenu(row)}
onClick={() => handleInvoiceDownload(row)}
>
<DownloadIcon fontSize="large" />
</IconButton>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add an accessible name to both invoice-download buttons.

Screen-reader users cannot identify icon-only controls without an aria-label.

  • src/pages/sponsors/show-purchase-list-page/index.js#L202-L208: add a localized invoice-download aria-label.
  • src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js#L185-L191: add the same localized aria-label.
📍 Affects 2 files
  • src/pages/sponsors/show-purchase-list-page/index.js#L202-L208 (this comment)
  • src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js#L185-L191
🤖 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/pages/sponsors/show-purchase-list-page/index.js` around lines 202 - 208,
Add the same localized invoice-download aria-label to both IconButton controls:
src/pages/sponsors/show-purchase-list-page/index.js lines 202-208 and
src/pages/sponsors/sponsor-page/tabs/sponsor-purchases-tab/index.js lines
185-191. Reuse the existing localization mechanism and keep the button behavior
unchanged.

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.

1 participant