Skip to content

✨ server: support many cards in the statement#893

Draft
nfmelendez wants to merge 1 commit intomainfrom
statement
Draft

✨ server: support many cards in the statement#893
nfmelendez wants to merge 1 commit intomainfrom
statement

Conversation

@nfmelendez
Copy link
Contributor

@nfmelendez nfmelendez commented Mar 16, 2026

Summary by CodeRabbit

  • New Features
    • Statements now display multiple cards with separate purchase and payment breakdowns by card.
    • Redesigned statement layout featuring account details, summary totals, and improved transaction organization.

@changeset-bot
Copy link

changeset-bot bot commented Mar 16, 2026

🦋 Changeset detected

Latest commit: aa5c7f8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

Walkthrough

This PR refactors the statement generation flow to support multiple cards per account. It introduces a nested, card-centric data structure where purchases are grouped by card with lastFour identifier, updates the Statement React component's props to accept card arrays with nested purchases and payment information, and modifies the activity API to construct purchases data with card ID references for PDF and JSON statement outputs.

Changes

Cohort / File(s) Summary
Changeset Entry
.changeset/soft-trains-dig.md
Marks a patch release for @exactly/server with note about supporting multiple cards in statement generation.
Statement API Logic
server/api/activity.ts
Refactors card query to include id and lastFour fields; replaces statementCards/statementTransactions logic with purchases-based approach; builds hash-to-cardId map for grouping purchases by card; injects cardId into transaction items and derives payments section separately.
Statement Tests
server/test/api/activity.test.ts, server/test/utils/statement.test.ts
Seed data updated to include second card; transaction cardId selection now dynamic per index; test input shape changed from flat array to nested structure with account, cards (containing lastFour and purchases), and payments; expectations updated for per-card rendering, "Statement"/"Account" headers, and summary/due balance sections.
Statement Component
server/utils/Statement.tsx
Component signature refactored from data/lastFour to account/cards/maturity/payments; introduces currency formatter (USD) and computed totals (totalSpent, totalPayments, dueBalance); UI expanded with per-card purchase tables, summary section, payments section, and due balance display bar; date formatting updated to epoch-based toLocaleDateString.

Sequence Diagram(s)

sequenceDiagram
    participant API as Activity API
    participant DB as Database
    participant Component as Statement Component
    participant PDF as PDF/JSON Output

    API->>DB: Query credentials with cards (id, lastFour)
    DB-->>API: Cards with id & lastFour
    
    API->>DB: Fetch transactions for each card
    DB-->>API: Transactions data
    
    API->>API: Build purchases per card<br/>(group by cardId)
    API->>API: Create hash-to-cardId map<br/>(from purchases)
    API->>API: Extract payments section<br/>(from response items)
    
    API->>Component: Pass account, cards[{lastFour,<br/>purchases[]}], maturity,<br/>payments[]
    
    Component->>Component: Calculate totals<br/>(totalSpent, totalPayments,<br/>dueBalance)
    Component->>Component: Render per-card<br/>purchase tables
    Component->>Component: Render payments section<br/>& summary
    
    Component->>PDF: Serialize to PDF/JSON<br/>with multi-card layout
    PDF-->>Component: Statement output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • cruzdanilo
  • dieguezguille
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly describes the main change: adding support for multiple cards in the statement component, which is the core focus of the changeset across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch statement
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enables the generation of comprehensive user statements that can include transactions from multiple cards. The changes involve updating the data retrieval process in the API to fetch all relevant card data and refactoring the PDF rendering component to display this multi-card information in a structured and user-friendly format. This enhancement provides users with a more complete overview of their financial activity across all their associated cards.

Highlights

  • Multi-Card Statement Support: The server-side logic and PDF generation for user statements have been updated to support displaying transactions from multiple cards within a single statement.
  • API Refactoring: The activity.ts API endpoint was refactored to correctly query and aggregate transaction data for all cards associated with a credential, removing a previous limitation to a single card for PDF statements.
  • Enhanced PDF Statement Layout: The Statement.tsx component was significantly redesigned to present a clear, organized view of purchases grouped by individual cards, along with a summary of total spending and payments.
  • Comprehensive Testing: API and statement rendering tests were expanded to include scenarios with multiple cards, ensuring the new functionality works as expected.
Changelog
  • .changeset/soft-trains-dig.md
    • Added a new changeset entry for multi-card statement support.
  • server/api/activity.ts
    • Modified the cards query to explicitly select id and lastFour.
    • Refactored the logic for fetching card purchases to support multiple cards and their associated transactions.
    • Updated the PDF generation to process and group purchases by card.
    • Removed the restriction that prevented PDF generation for statements with more than one card.
  • server/test/api/activity.test.ts
    • Added a second card to the test setup.
    • Updated transaction assignments to distribute them across multiple cards for testing.
  • server/test/utils/statement.test.ts
    • Updated existing test cases to match the new statement data structure.
    • Added a new test case to verify rendering of statements with multiple cards.
    • Modified assertions to reflect changes in the rendered PDF text.
  • server/utils/Statement.tsx
    • Updated the Statement component's props to accept account, an array of cards, maturity, and payments.
    • Implemented new rendering logic to display purchases grouped by individual cards.
    • Introduced a summary section for total spent and due balance.
    • Refactored date formatting functions.
    • Updated styling for a more structured and multi-card friendly layout.
Activity
  • The pull request was created by nfmelendez.
  • A new changeset file was added, indicating a patch release for @exactly/server.
  • The core logic for generating activity statements was updated to handle multiple cards.
  • Corresponding test files were modified to reflect and validate the new multi-card statement functionality.
  • The UI component responsible for rendering PDF statements was significantly refactored to display the new multi-card data structure.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for multiple cards in PDF statements, which is a significant feature enhancement. The changes are well-implemented across the API, statement generation component, and tests. The refactoring in server/api/activity.ts to handle data fetching for multiple cards is more efficient than the previous implementation. The Statement.tsx component has been completely redesigned, resulting in a much-improved and more professional-looking statement PDF. The accompanying test updates are thorough. I have one minor suggestion to improve code maintainability. Overall, this is an excellent contribution.

Comment on lines 172 to 178
export function format(timestamp: number) {
return new Date(timestamp * 1000).toISOString().slice(0, 10);
return new Date(timestamp * 1000).toLocaleDateString("en-US", { month: "short", day: "2-digit", year: "numeric" });
}

function formatDate(iso: string) {
return new Date(iso).toLocaleDateString("en-US", { month: "short", day: "2-digit", year: "numeric" });
}

Choose a reason for hiding this comment

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

medium

To improve maintainability and reduce code duplication, you can extract the Intl.DateTimeFormatOptions into a constant, as both format and formatDate functions use the same formatting options.

const dateFormatOptions: Intl.DateTimeFormatOptions = { month: "short", day: "2-digit", year: "numeric" };

export function format(timestamp: number) {
  return new Date(timestamp * 1000).toLocaleDateString("en-US", dateFormatOptions);
}

function formatDate(iso: string) {
  return new Date(iso).toLocaleDateString("en-US", dateFormatOptions);
}

@sentry
Copy link

sentry bot commented Mar 16, 2026

✅ All tests passed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 10f80b4e-01f7-4d80-a553-f9c0d21d0758

📥 Commits

Reviewing files that changed from the base of the PR and between f9f0d0a and aa5c7f8.

📒 Files selected for processing (5)
  • .changeset/soft-trains-dig.md
  • server/api/activity.ts
  • server/test/api/activity.test.ts
  • server/test/utils/statement.test.ts
  • server/utils/Statement.tsx

Comment on lines +82 to +93
{cards.map((card) => {
const cardTotal = card.purchases.reduce(
(sum, p) => sum + p.installments.reduce((a, installment) => a + installment.amount, 0),
0,
);
return (
<View key={card.lastFour} style={styles.summaryRow}>
<Text style={styles.summaryLabel}>Card **** {card.lastFour} purchases</Text>
<Text style={styles.summaryAmount}>{currency.format(cardTotal)}</Text>
</View>
);
})}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Duplicate cardTotal calculation; consider extracting.

The same cardTotal calculation appears at lines 83-86 and 106-109. Consider computing card totals once during the initial data processing to avoid duplication.

Additionally, using card.lastFour as the React key could cause issues if two cards share the same last four digits (rare but possible). Consider using a more unique identifier if available.

♻️ Suggested refactor
-  const totalSpent = cards.reduce(
-    (sum, card) =>
-      sum +
-      card.purchases.reduce((s, p) => s + p.installments.reduce((a, installment) => a + installment.amount, 0), 0),
-    0,
-  );
+  const cardsWithTotals = cards.map((card) => ({
+    ...card,
+    total: card.purchases.reduce((s, p) => s + p.installments.reduce((a, i) => a + i.amount, 0), 0),
+  }));
+  const totalSpent = cardsWithTotals.reduce((sum, card) => sum + card.total, 0);

Then use cardsWithTotals in both the summary and per-card sections, referencing card.total directly.

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