Skip to content

feat: defer invoices below 5 EUR with carry-over#3525

Draft
dkrizan wants to merge 10 commits intomainfrom
dkrizan/invoice-carry-over
Draft

feat: defer invoices below 5 EUR with carry-over#3525
dkrizan wants to merge 10 commits intomainfrom
dkrizan/invoice-carry-over

Conversation

@dkrizan
Copy link
Contributor

@dkrizan dkrizan commented Mar 13, 2026

Summary by CodeRabbit

  • New Features
    • Added Invoices administration page with comprehensive invoice listing, organization filtering capabilities, and pagination support.
    • Implemented PDF invoice download functionality for easy access to invoice documents.
    • Added carry-over management interface with separate tabs for active and historical carry-overs.
    • Enhanced billing information display with deferred amount tracking and usage threshold notifications.

dkrizan and others added 6 commits March 13, 2026 07:25
Add an administration invoices page that shows per-organization invoices
and carry-over history. Refactor ProportionalUsageItemRow to accept
organizationId as a prop so the row is reusable outside the org context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add minInvoiceAmount to UsageModel (null for invoice views, populated
for expected-usage endpoint). When usage-only total is below the
threshold, a LabelHint question-mark tooltip on the Total row explains
that the amount will be carried over and billed later.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…odel

Move the carry-over threshold from the per-request UsageModel response
into PublicBillingConfigurationDTO (served via init-data). TotalRow now
reads the value from useGlobalContext rather than receiving it as a prop.
Remove minInvoiceAmount from UsageModel and the overloaded toModel().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add carryOverTotal to UsageData/UsageModel and display a 'Deferred from
previous periods' row in UsageTable when carry-over is present. The
dialog total now matches the actual invoice total for invoices that
settled previously deferred usage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 90a57591-da15-426d-8a9b-693255c38257

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds billing carry-overs and invoices management features across backend and frontend. Introduces carryOverTotal and minUsageInvoiceAmount data fields, new API endpoints for retrieving invoices and carry-overs, and administration dashboard components for browsing organization invoices, viewing usage details, downloading PDF invoices, and tracking deferred billing amounts.

Changes

Cohort / File(s) Summary
Backend Data Models
backend/api/src/main/kotlin/io/tolgee/hateoas/ee/uasge/proportional/UsageModel.kt, ee/backend/app/src/main/kotlin/io/tolgee/ee/data/UsageData.kt
Added carryOverTotal field to track deferred amounts from previous billing periods. Updated total calculation in UsageData to include carry-over amounts.
Backend Configuration DTOs
backend/data/src/main/kotlin/io/tolgee/dtos/response/PublicBillingConfigurationDTO.kt
Added minUsageInvoiceAmount property to expose minimum invoice threshold configuration to frontend.
Backend Assemblers
ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/hateoas/assemblers/UsageModelAssembler.kt
Updated to map carryOverTotal from data object to UsageModel constructor.
Frontend Route Constants
webapp/src/constants/links.tsx
Added ADMINISTRATION_BILLING_INVOICES link constant for invoices administration page route.
Frontend Admin Invoices Components
webapp/src/ee/billing/administration/invoices/AdministrationInvoicesView.tsx, webapp/src/ee/billing/administration/invoices/OrgInvoicesSection.tsx, webapp/src/ee/billing/administration/invoices/CarryOversSection.tsx, webapp/src/ee/billing/administration/invoices/AdminDownloadButton.tsx, webapp/src/ee/billing/administration/invoices/AdminInvoiceUsage.tsx
New administration dashboard components: main invoices page layout, organization invoice browser with pagination and filtering, carry-over history tabs, invoice PDF download button, and usage details modal dialog.
Frontend Usage/Billing Components
webapp/src/ee/billing/common/usage/CarryOverRow.tsx, webapp/src/ee/billing/common/usage/TotalRow.tsx, webapp/src/ee/billing/common/usage/PropertionalUsageItemRow.tsx, webapp/src/ee/billing/common/usage/UsageTable.tsx, webapp/src/ee/billing/common/usage/ExpectedUsageDialogButton.tsx, webapp/src/ee/billing/Invoices/InvoiceUsage.tsx
Added CarryOverRow component for displaying deferred amounts; enhanced TotalRow with minUsageInvoiceAmount threshold hints; threaded organizationId prop through usage components; integrated CarryOverRow into UsageTable; added usageOnlyTotal tracking; simplified JSX in ExpectedUsageDialogButton.
Frontend Application Setup
webapp/src/eeSetup/eeModule.ee.tsx
Registered new AdministrationInvoicesView route and invoices menu item in administration navigation, gated by billing.enabled feature flag.
Generated API Schemas
webapp/src/service/apiSchema.generated.ts, webapp/src/service/billingApiSchema.generated.ts
Expanded schema definitions with new API endpoints (/v2/administration/billing/carry-overs, /v2/administration/billing/invoices), CarryOverModel type, and fields for UsageModel.carryOverTotal and PublicBillingConfigurationDTO.minUsageInvoiceAmount. Extended InvoiceModel with organization metadata (organizationId, organizationName, organizationSlug) and StatsUserModel with name field.

Sequence Diagram(s)

sequenceDiagram
    participant User as Admin User
    participant UI as AdministrationInvoicesView
    participant OrgSec as OrgInvoicesSection
    participant CarrySec as CarryOversSection
    participant API as Billing API
    participant DB as Backend

    User->>UI: Navigate to Invoices Page
    UI->>OrgSec: Render Organization Invoices Tab
    UI->>CarrySec: Render Carry-Overs Tab
    
    OrgSec->>API: Fetch Organizations
    API->>DB: Query Organizations
    DB-->>API: Return Organizations List
    API-->>OrgSec: Organizations Data
    OrgSec->>API: Fetch Invoices (with pagination)
    API->>DB: Query Invoices
    DB-->>API: Return Invoices
    API-->>OrgSec: Invoices List + Metadata

    User->>OrgSec: Click Invoice Download
    OrgSec->>API: GET /invoices/{id}/pdf
    API->>DB: Retrieve Invoice PDF
    DB-->>API: PDF Binary
    API-->>OrgSec: PDF Blob
    OrgSec->>User: Trigger Browser Download

    User->>OrgSec: Click Invoice Usage Details
    OrgSec->>API: GET /invoices/{id}/usage
    API->>DB: Fetch Usage Data
    DB-->>API: UsageData (with carryOverTotal)
    API-->>OrgSec: Usage Details
    OrgSec->>User: Display UsageTable (including CarryOverRow)

    CarrySec->>API: Fetch Active Carry-Overs
    API->>DB: Query Carry-Overs
    DB-->>API: CarryOverModel List
    API-->>CarrySec: Carry-Over Data
    CarrySec->>User: Display Carry-Over History
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • JanCizmar
  • gabrielshanahan

Poem

🐰 Hop, hop! New invoices appear,
With carry-overs crystal clear,
Billing features, row by row,
Let the deferred amounts flow!
PDFs download with a click,
Admin dashboard, done up quick!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: defer invoices below 5 EUR with carry-over' accurately describes the main feature being implemented across the codebase, which adds carry-over functionality for deferred invoices.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dkrizan/invoice-carry-over
📝 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.

@dkrizan dkrizan marked this pull request as ready for review March 13, 2026 07:17
@dkrizan dkrizan marked this pull request as draft March 13, 2026 07:27
Copy link
Contributor

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
webapp/src/ee/billing/common/usage/ProportionalUsageItemRow.tsx (1)

9-15: ⚠️ Potential issue | 🟠 Major

Type-safe invoice downloads by requiring both IDs together.

The optional organizationId prop causes silent feature loss. AdminInvoiceUsage.tsx (lines 55–59) passes invoiceId but omits organizationId, and the runtime guard on lines 23–25 silently disables the download action. This compiles cleanly despite being incomplete.

Model these props as a union type so invoice-backed rows require both IDs together—making the error obvious at compile time instead of discovering it at runtime:

💡 Tighten the prop contract
-export const ProportionalUsageItemRow = (props: {
-  item: components['schemas']['AverageProportionalUsageItemModel'];
-  invoiceId?: number;
-  invoiceNumber?: string;
-  type: ProportionalUsageType;
-  organizationId?: number;
-}) => {
+type ProportionalUsageItemRowProps =
+  | {
+      item: components['schemas']['AverageProportionalUsageItemModel'];
+      invoiceId: number;
+      invoiceNumber?: string;
+      type: ProportionalUsageType;
+      organizationId: number;
+    }
+  | {
+      item: components['schemas']['AverageProportionalUsageItemModel'];
+      invoiceId?: undefined;
+      invoiceNumber?: undefined;
+      type: ProportionalUsageType;
+      organizationId?: undefined;
+    };
+
+export const ProportionalUsageItemRow = (
+  props: ProportionalUsageItemRowProps
+) => {

Fix AdminInvoiceUsage.tsx by passing organizationId to UsageTable, or exclude the download feature for admin invoices if the organization context is unavailable.

Also applies to: 23–30

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/common/usage/ProportionalUsageItemRow.tsx` around lines
9 - 15, The component ProportionalUsageItemRow currently accepts invoiceId? and
organizationId? independently which allows passing invoiceId without
organizationId and causes the download guard to silently disable the feature;
change the props to a discriminated union so that either both invoiceId and
organizationId are present (required together) or neither is present (e.g. {
invoiceId: number; invoiceNumber?: string; organizationId: number; type:
ProportionalUsageType; item: ... } | { invoiceId?: undefined; invoiceNumber?:
string; organizationId?: undefined; type: ProportionalUsageType; item: ... }),
update the ProportionalUsageItemRow signature accordingly, and then fix callers
(e.g. AdminInvoiceUsage -> UsageTable) to pass organizationId when they pass
invoiceId or intentionally omit invoice-related props if organization context is
unavailable so the compiler surfaces the missing organizationId instead of
failing at runtime.
🧹 Nitpick comments (6)
webapp/src/ee/billing/administration/invoices/OrgInvoicesSection.tsx (2)

79-112: Add data-cy attribute for E2E testing.

The organization filter Autocomplete should have a data-cy attribute to enable E2E test selectors.

Proposed fix
         <Autocomplete<OrgItem>
           options={orgItems}
           getOptionLabel={(o) => o.name}
           isOptionEqualToValue={(a, b) => a.id === b.id}
           loading={orgsLoadable.isFetching}
           value={selectedOrg}
           onChange={(_, value) => {
             setSelectedOrg(value);
             setInvoicePage(0);
           }}
           onInputChange={(_, value) => setSearch(value)}
           sx={{ width: 280 }}
+          data-cy="admin-invoices-org-filter"
           renderInput={(params) => (

As per coding guidelines: "STRICTLY use data-cy attributes for E2E selectors".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/administration/invoices/OrgInvoicesSection.tsx` around
lines 79 - 112, Add the missing data-cy attribute to the organization filter
Autocomplete's input so E2E tests can target it: update the Autocomplete
renderInput/TextField block (in the component using Autocomplete<OrgItem> and
renderInput) to include a data-cy prop (e.g.,
data-cy="administration-invoices-filter-org") on the TextField (or its input
props) so the selector is present while preserving existing params and
InputProps, value/onChange handlers, and loading behavior.

114-175: Add data-cy attribute to the list component.

The invoice list should have a data-cy attribute for E2E testing purposes.

Proposed fix
       <PaginatedHateoasList
         onPageChange={(p) => setInvoicePage(p)}
         listComponent={Paper}
         listComponentProps={{
           variant: 'outlined',
           sx: {
             display: 'grid',
             gridTemplateColumns: 'auto auto 1fr auto auto auto',
             alignItems: 'center',
           },
+          'data-cy': 'admin-invoices-list',
         }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/administration/invoices/OrgInvoicesSection.tsx` around
lines 114 - 175, The PaginatedHateoasList rendering the invoices is missing a
data-cy attribute for E2E tests; add a data-cy (e.g. data-cy="invoices-list") to
the list component props by updating the PaginatedHateoasList call where
listComponent={Paper} — put the attribute inside listComponentProps (the same
object with variant/sx) so the rendered Paper element includes the data-cy for
testing.
webapp/src/ee/billing/administration/invoices/CarryOversSection.tsx (1)

161-170: Add data-cy attributes for E2E testing.

The Tabs component should have data-cy attributes for E2E test selectors.

Proposed fix
-      <Tabs value={tab} onChange={(_, value) => setTab(value)} sx={{ mb: 2 }}>
+      <Tabs value={tab} onChange={(_, value) => setTab(value)} sx={{ mb: 2 }} data-cy="carry-overs-tabs">
         <Tab
           value="active"
           label={t('administration_carry_overs_tab_active', 'Active')}
+          data-cy="carry-overs-tab-active"
         />
         <Tab
           value="history"
           label={t('administration_carry_overs_tab_history', 'History')}
+          data-cy="carry-overs-tab-history"
         />
       </Tabs>

As per coding guidelines: "STRICTLY use data-cy attributes for E2E selectors".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/administration/invoices/CarryOversSection.tsx` around
lines 161 - 170, Add data-cy attributes to the Tabs and each Tab for E2E
selectors: update the Tabs component (where value={tab} onChange={(_, value) =>
setTab(value)}) to include a data-cy like data-cy="carry-overs-tabs" and add
data-cy attributes to the two Tab components (e.g.,
data-cy="carry-overs-tab-active" and data-cy="carry-overs-tab-history") so tests
can reliably target them; keep the existing props (value, label, sx) and only
add the data-cy attributes to Tabs and Tab.
webapp/src/ee/billing/administration/invoices/AdminDownloadButton.tsx (2)

34-36: Redundant type assertion on blob.

res.blob() already returns Promise<Blob>, so the as unknown as Blob cast is unnecessary.

Proposed fix
         async onSuccess(response) {
           const res = response as unknown as Response;
           const data = await res.blob();
-          const url = URL.createObjectURL(data as unknown as Blob);
+          const url = URL.createObjectURL(data);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/administration/invoices/AdminDownloadButton.tsx` around
lines 34 - 36, The blob cast is redundant: res.blob() returns a Promise<Blob>,
so remove the unnecessary "as unknown as Blob" and pass the returned blob
directly to URL.createObjectURL; ensure the earlier cast of response to Response
(const res = response as unknown as Response) remains appropriate or replace
with a direct Response typing for the response variable so that data is used as
a Blob when calling URL.createObjectURL(data).

56-65: Add data-cy attribute for E2E testing.

The download button should have a data-cy attribute to enable reliable E2E test selectors.

Proposed fix
     <LoadingButton
       disabled={!invoice.pdfReady}
       loading={pdfMutation.isLoading}
       onClick={onDownload}
       size="small"
+      data-cy="admin-invoice-download-button"
     >
       PDF
     </LoadingButton>

As per coding guidelines: "STRICTLY use data-cy attributes for E2E selectors".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/administration/invoices/AdminDownloadButton.tsx` around
lines 56 - 65, The LoadingButton rendered in AdminDownloadButton lacks the
required E2E selector; add a data-cy attribute to the LoadingButton (the
component using props: disabled={!invoice.pdfReady},
loading={pdfMutation.isLoading}, onClick={onDownload})—e.g.
data-cy="admin-invoice-download-pdf"—so tests can reliably target the PDF
download button.
webapp/src/ee/billing/common/usage/TotalRow.tsx (1)

21-26: Optional simplification: Redundant truthy check.

The condition usageOnlyTotal && is redundant when followed by usageOnlyTotal > 0, since the latter already handles undefined, null, and 0 cases by returning false. However, this is a common defensive pattern for explicitness.

♻️ Optional simplification
   const showHint = Boolean(
     minUsageInvoiceAmount &&
-      usageOnlyTotal &&
       usageOnlyTotal > 0 &&
       usageOnlyTotal < minUsageInvoiceAmount
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/ee/billing/common/usage/TotalRow.tsx` around lines 21 - 26,
Simplify the showHint boolean expression in the TotalRow component by removing
the redundant "usageOnlyTotal &&" check; keep the Boolean(...) wrapper and the
remaining conditions "minUsageInvoiceAmount && usageOnlyTotal > 0 &&
usageOnlyTotal < minUsageInvoiceAmount" so the logic is unchanged but less
verbose and still handles undefined/null correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@backend/data/src/main/kotlin/io/tolgee/dtos/response/PublicBillingConfigurationDTO.kt`:
- Around line 5-7: PublicBillingConfigurationDTO adds minUsageInvoiceAmount but
no provider populates it; update the provider(s) to supply this value or remove
the field. Concretely, either (A) remove minUsageInvoiceAmount from
PublicBillingConfigurationDTO if unused, or (B) modify
BasePublicBillingConfProvider and all concrete providers that return
PublicBillingConfigurationDTO (look for the constructor call in
BasePublicBillingConfProvider and other providers) to pass the actual billing
minimum invoice amount (e.g., read from your billing configuration/service and
call PublicBillingConfigurationDTO(enabled, minUsageInvoiceAmount)) so the DTO
is not always null.

In `@webapp/src/ee/billing/administration/invoices/AdminInvoiceUsage.tsx`:
- Around line 55-59: The UsageTable is rendered without the optional
organizationId prop so ProportionalUsageItemRow instances receive no
organizationId and CSV download buttons remain disabled; update the UsageTable
invocation in AdminInvoiceUsage to pass organizationId={invoice.organizationId}
so the prop flows into ProportionalUsageItemRow and enables CSV downloads.
- Around line 40-51: The IconButton opening the usage dialog (the element using
setOpen(true) and labelled via t('billing_invoices_show_usage_button')) needs a
data-cy attribute for E2E selection and all translation calls must include
default values; add a data-cy (e.g. billing-invoice-show-usage-button) to the
IconButton and update the t() usages around the IconButton (aria-label and
Tooltip title) and the DialogTitle call (t('invoice_usage_dialog_title')) to
supply sensible defaultValue strings so meaningful text appears before
translations load.

In
`@webapp/src/ee/billing/administration/invoices/AdministrationInvoicesView.tsx`:
- Around line 23-28: The navigation entry in AdministrationInvoicesView uses
t('administration_organizations') without a defaultValue; update the call to
include a defaultValue (e.g., defaultValue: 'Organizations') so the navigation
label renders before translations load—locate the navigation prop in
AdministrationInvoicesView.tsx and add the defaultValue option to the t(...)
invocation (or to the T component if used) for the
'administration_organizations' key.

In `@webapp/src/ee/billing/administration/invoices/CarryOversSection.tsx`:
- Around line 76-89: Replace the hardcoded "Settled by" string in
CarryOversSection's JSX (inside the conditional that checks showSettledBy and
item.resolvedByInvoiceNumber) with a call to the project's translation function
(e.g., t('...') or i18n.t('...')); import/use the same translation hook used
elsewhere in this component (or module) and pick an appropriate key (for example
'billing.settledBy' or 'invoices.settledBy') so the Box displaying the label
becomes translated while keeping the surrounding layout and Link logic intact.
- Around line 91-97: The AmountItem label strings in CarryOversSection are
hardcoded; update the four labels ("Credits", "Seats", "Translations", "Keys")
to use the i18n translation helper used in this component (e.g., the t function
or <Trans>) so they are localized; locate the CarryOversSection component and
replace the literal label props passed to AmountItem with translated keys (e.g.,
t('billing.credits'), t('billing.seats'), t('billing.translations'),
t('billing.keys')) ensuring the appropriate translation keys are added to the
locale files.
- Around line 67-72: The Chip labels are hardcoded ("Cloud" / "Self-hosted EE");
replace them with the translation helper (use the T component or t() function)
and provide defaultValue strings per guidelines. Update the label prop in the
Chip rendering (the conditional using isCloud) to call t('Cloud', {
defaultValue: 'Cloud' }) and t('Self-hosted EE', { defaultValue: 'Self-hosted
EE' }) or wrap with <T defaultValue="..."> accordingly so both branches use
i18n.

In `@webapp/src/eeSetup/eeModule.ee.tsx`:
- Around line 141-143: The Administration invoices route is currently only
protected by PrivateRoute (authentication) and needs an admin permission guard;
update the route handling for LINKS.ADMINISTRATION_BILLING_INVOICES.template so
only admin/supporter users can access AdministrationInvoicesView. Either wrap
that <PrivateRoute> with an admin-check component (e.g., AdminGuard) or replace
PrivateRoute with an AdminRoute that calls useIsAdminOrSupporter() and redirects
non-admins to a safe page; ensure the check happens in RootRouter around the
route for AdministrationInvoicesView so direct URL access is blocked.

---

Outside diff comments:
In `@webapp/src/ee/billing/common/usage/ProportionalUsageItemRow.tsx`:
- Around line 9-15: The component ProportionalUsageItemRow currently accepts
invoiceId? and organizationId? independently which allows passing invoiceId
without organizationId and causes the download guard to silently disable the
feature; change the props to a discriminated union so that either both invoiceId
and organizationId are present (required together) or neither is present (e.g. {
invoiceId: number; invoiceNumber?: string; organizationId: number; type:
ProportionalUsageType; item: ... } | { invoiceId?: undefined; invoiceNumber?:
string; organizationId?: undefined; type: ProportionalUsageType; item: ... }),
update the ProportionalUsageItemRow signature accordingly, and then fix callers
(e.g. AdminInvoiceUsage -> UsageTable) to pass organizationId when they pass
invoiceId or intentionally omit invoice-related props if organization context is
unavailable so the compiler surfaces the missing organizationId instead of
failing at runtime.

---

Nitpick comments:
In `@webapp/src/ee/billing/administration/invoices/AdminDownloadButton.tsx`:
- Around line 34-36: The blob cast is redundant: res.blob() returns a
Promise<Blob>, so remove the unnecessary "as unknown as Blob" and pass the
returned blob directly to URL.createObjectURL; ensure the earlier cast of
response to Response (const res = response as unknown as Response) remains
appropriate or replace with a direct Response typing for the response variable
so that data is used as a Blob when calling URL.createObjectURL(data).
- Around line 56-65: The LoadingButton rendered in AdminDownloadButton lacks the
required E2E selector; add a data-cy attribute to the LoadingButton (the
component using props: disabled={!invoice.pdfReady},
loading={pdfMutation.isLoading}, onClick={onDownload})—e.g.
data-cy="admin-invoice-download-pdf"—so tests can reliably target the PDF
download button.

In `@webapp/src/ee/billing/administration/invoices/CarryOversSection.tsx`:
- Around line 161-170: Add data-cy attributes to the Tabs and each Tab for E2E
selectors: update the Tabs component (where value={tab} onChange={(_, value) =>
setTab(value)}) to include a data-cy like data-cy="carry-overs-tabs" and add
data-cy attributes to the two Tab components (e.g.,
data-cy="carry-overs-tab-active" and data-cy="carry-overs-tab-history") so tests
can reliably target them; keep the existing props (value, label, sx) and only
add the data-cy attributes to Tabs and Tab.

In `@webapp/src/ee/billing/administration/invoices/OrgInvoicesSection.tsx`:
- Around line 79-112: Add the missing data-cy attribute to the organization
filter Autocomplete's input so E2E tests can target it: update the Autocomplete
renderInput/TextField block (in the component using Autocomplete<OrgItem> and
renderInput) to include a data-cy prop (e.g.,
data-cy="administration-invoices-filter-org") on the TextField (or its input
props) so the selector is present while preserving existing params and
InputProps, value/onChange handlers, and loading behavior.
- Around line 114-175: The PaginatedHateoasList rendering the invoices is
missing a data-cy attribute for E2E tests; add a data-cy (e.g.
data-cy="invoices-list") to the list component props by updating the
PaginatedHateoasList call where listComponent={Paper} — put the attribute inside
listComponentProps (the same object with variant/sx) so the rendered Paper
element includes the data-cy for testing.

In `@webapp/src/ee/billing/common/usage/TotalRow.tsx`:
- Around line 21-26: Simplify the showHint boolean expression in the TotalRow
component by removing the redundant "usageOnlyTotal &&" check; keep the
Boolean(...) wrapper and the remaining conditions "minUsageInvoiceAmount &&
usageOnlyTotal > 0 && usageOnlyTotal < minUsageInvoiceAmount" so the logic is
unchanged but less verbose and still handles undefined/null correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a1abfb5-1fb7-4a98-929b-5a2ad9ef9646

📥 Commits

Reviewing files that changed from the base of the PR and between 50ca4d4 and 203893f.

📒 Files selected for processing (19)
  • backend/api/src/main/kotlin/io/tolgee/hateoas/ee/uasge/proportional/UsageModel.kt
  • backend/data/src/main/kotlin/io/tolgee/dtos/response/PublicBillingConfigurationDTO.kt
  • ee/backend/app/src/main/kotlin/io/tolgee/ee/api/v2/hateoas/assemblers/UsageModelAssembler.kt
  • ee/backend/app/src/main/kotlin/io/tolgee/ee/data/UsageData.kt
  • webapp/src/constants/links.tsx
  • webapp/src/ee/billing/Invoices/InvoiceUsage.tsx
  • webapp/src/ee/billing/administration/invoices/AdminDownloadButton.tsx
  • webapp/src/ee/billing/administration/invoices/AdminInvoiceUsage.tsx
  • webapp/src/ee/billing/administration/invoices/AdministrationInvoicesView.tsx
  • webapp/src/ee/billing/administration/invoices/CarryOversSection.tsx
  • webapp/src/ee/billing/administration/invoices/OrgInvoicesSection.tsx
  • webapp/src/ee/billing/common/usage/CarryOverRow.tsx
  • webapp/src/ee/billing/common/usage/ExpectedUsageDialogButton.tsx
  • webapp/src/ee/billing/common/usage/ProportionalUsageItemRow.tsx
  • webapp/src/ee/billing/common/usage/TotalRow.tsx
  • webapp/src/ee/billing/common/usage/UsageTable.tsx
  • webapp/src/eeSetup/eeModule.ee.tsx
  • webapp/src/service/apiSchema.generated.ts
  • webapp/src/service/billingApiSchema.generated.ts

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