Skip to content

Implement receivables and ATO super metrics#19

Open
mgkcloud wants to merge 1 commit intomasterfrom
codex/implement-receivables-and-ato-super-owing-metrics
Open

Implement receivables and ATO super metrics#19
mgkcloud wants to merge 1 commit intomasterfrom
codex/implement-receivables-and-ato-super-owing-metrics

Conversation

@mgkcloud
Copy link
Copy Markdown
Owner

@mgkcloud mgkcloud commented May 21, 2025

Summary

  • compute totalReceivables and atoSuperOwing in metrics aggregation
  • expose new metrics from the API
  • show new metrics in dashboard and metrics explorer
  • add receivables widget and rename tax widget label
  • document metric ids in data model
  • add a unit test for receivables aggregation

Testing

  • npm run lint (fails: ESLint config missing)
  • npm run typecheck
  • npm test
  • npm run build (fails to fetch fonts during Next.js build)

Summary by CodeRabbit

  • New Features

    • Added support for displaying "Total Receivables" (unpaid invoices) and "ATO Super Owing" metrics in the dashboard and Today Overview.
    • Introduced a new "Unpaid Invoices" widget to the Today Overview section.
  • Improvements

    • Updated metric names and descriptions for clarity, including explicit labels for new metrics.
    • Enhanced API and mock data to include the new metrics and align naming conventions.
  • Bug Fixes

    • None.
  • Documentation

    • Expanded documentation to describe the new metric identifiers and their definitions.
  • Tests

    • Added tests to verify correct calculation of the "Total Receivables" metric.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2025

Walkthrough

The changes introduce two new financial metrics: totalReceivables and atoSuperOwing. These are implemented across backend aggregation, API schema, frontend display components, and documentation. The dashboard and widgets now support displaying unpaid invoices and ATO superannuation owing amounts, with updated mappings, descriptions, and user interface elements to reflect the new metrics.

Changes

File(s) Change Summary
docs/data-model.md Added documentation for new metric IDs: totalReceivables and atoSuperOwing, with definitions and internal mappings.
packages/backend/src/routes/metrics.ts Extended the list of numeric keys in the isResultEmpty function to include totalReceivables and atoSuperOwing for dashboard metrics.
packages/backend/src/services/metricsAggregation.receivables.test.ts Added a new test suite for the aggregateMetric function, verifying that totalReceivables correctly sums unpaid invoices.
packages/backend/src/services/metricsAggregation.ts Implemented aggregation logic for totalReceivables (sum of unpaid authorised invoices) and atoSuperOwing (latest value before end date). Updated dashboard and individual metric handling to support these. Updated comments to reflect implemented metrics.
packages/frontend/components/dashboard-metrics.tsx Updated formatMetricName and getMetricDescription functions to recognize and describe totalReceivables and atoSuperOwing. Adjusted mapping and descriptions for these metrics.
packages/frontend/components/metrics-explorer.tsx Added mappings for total receivables and ato super owing to their respective widget IDs in METRIC_TO_WIDGET_ID.
packages/frontend/components/today-overview.tsx Added totalReceivables and replaced taxAndSuper with atoSuperOwing in the metrics state and UI. Integrated TotalReceivablesWidget and updated data fetching, state, and rendering logic accordingly.
packages/frontend/components/today-widgets/TotalReceivablesWidget.tsx Introduced a new exported React component, TotalReceivablesWidget, to display unpaid invoices with tooltip and styling.
packages/frontend/lib/api.ts Updated the metrics API schema and mock data: replaced taxAndSuper with atoSuperOwing and added totalReceivables.
packages/frontend/lib/today-widgets.ts Added a new widget entry for "Unpaid Invoices" (id: "receivables") and updated the label for the "tax" widget to "ATO Super Owing" in the TODAY_WIDGETS array.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant DB

    User->>Frontend: Load dashboard
    Frontend->>Backend: GET /metrics?metric=dashboard
    Backend->>DB: Query unpaid authorised invoices (for totalReceivables)
    Backend->>DB: Query latest atoSuperOwing value
    DB-->>Backend: Return results
    Backend-->>Frontend: Respond with dashboard metrics (including totalReceivables, atoSuperOwing)
    Frontend-->>User: Display widgets for Unpaid Invoices and ATO Super Owing
Loading

Poem

In the warren where metrics hop and play,
Two new friends joined the dashboard today:
Receivables tallied, unpaid and bright,
ATO Super Owing, now in plain sight.
With widgets and docs, the numbers appear—
More carrot-crunching clarity for every bunny here!
🥕📊

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (3)
packages/backend/src/services/metricsAggregation.receivables.test.ts (1)

27-43: Test case correctly verifies receivables aggregation logic

This test case effectively validates that the aggregateMetric function correctly sums only unpaid invoices with the 'AUTHORISED' status. The test data is well-structured with both paid and unpaid invoices, ensuring the function properly filters out paid invoices.

However, you might want to add more test cases to cover edge cases:

+  it('handles empty result set', async () => {
+    const db = setupDb();
+    const start = Date.parse('2024-06-01');
+    const end = Date.parse('2024-06-30');
+    
+    const result = await aggregateMetric({ db: db as any, metric: 'totalReceivables', start, end });
+    
+    expect(result).toEqual({ value: 0 });
+  });
+
+  it('excludes invoices with zero amount due', async () => {
+    const db = setupDb();
+    const now = Date.now();
+    await db.insert(xero_invoices).values([
+      { id: '1', invoiceId: '1', contactId: 'A', date: '2024-06-05', dueDate: null, total: 100, status: 'AUTHORISED', amountDue: 0, lastUpdated: now, customFields: null, jobId: null, clientId: null },
+    ]);
+
+    const start = Date.parse('2024-06-01');
+    const end = Date.parse('2024-06-30');
+
+    const result = await aggregateMetric({ db: db as any, metric: 'totalReceivables', start, end });
+
+    expect(result).toEqual({ value: 0 });
+  });
packages/frontend/components/today-widgets/TotalReceivablesWidget.tsx (2)

42-44: Consider making tooltip description more detailed

The tooltip description could be enhanced to provide more context about what "unpaid invoices" represent in the business context.

-            <div>{getDescription("Total Receivables")}</div>
-            <div className="mt-1 text-xs text-gray-400">Outstanding sales invoices.<br/>Source: Xero, updated daily.</div>
+            <div>{getDescription("Total Receivables")}</div>
+            <div className="mt-1 text-xs text-gray-400">Sum of all outstanding sales invoices that have been authorized but not yet paid by customers.<br/>Source: Xero, updated daily.</div>

48-48: Use more robust currency formatting

The current implementation uses a simple dollar sign with toLocaleString(). Consider using Intl.NumberFormat for more robust currency formatting that respects locale settings, similar to what's used in the formatMetricValue function in dashboard-metrics.tsx.

-    <div className="mt-3 text-2xl font-bold text-white">${value.toLocaleString()}</div>
+    <div className="mt-3 text-2xl font-bold text-white">
+      {new Intl.NumberFormat("en-AU", {
+        style: "currency",
+        currency: "AUD",
+        maximumFractionDigits: 0,
+      }).format(value)}
+    </div>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8ba0313 and 581be22.

📒 Files selected for processing (10)
  • docs/data-model.md (1 hunks)
  • packages/backend/src/routes/metrics.ts (1 hunks)
  • packages/backend/src/services/metricsAggregation.receivables.test.ts (1 hunks)
  • packages/backend/src/services/metricsAggregation.ts (4 hunks)
  • packages/frontend/components/dashboard-metrics.tsx (2 hunks)
  • packages/frontend/components/metrics-explorer.tsx (1 hunks)
  • packages/frontend/components/today-overview.tsx (10 hunks)
  • packages/frontend/components/today-widgets/TotalReceivablesWidget.tsx (1 hunks)
  • packages/frontend/lib/api.ts (2 hunks)
  • packages/frontend/lib/today-widgets.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/backend/src/services/metricsAggregation.ts (1)
packages/backend/dist/services/metricsAggregation.js (7)
  • db (283-285)
  • db (290-292)
  • isoStart (19-19)
  • isoStart (23-23)
  • isoEnd (20-20)
  • isoEnd (24-24)
  • total (303-303)
packages/frontend/components/today-overview.tsx (1)
packages/frontend/components/today-widgets/WidgetContainer.tsx (1)
  • WidgetContainer (17-40)
🔇 Additional comments (26)
packages/backend/src/routes/metrics.ts (1)

36-37: LGTM! New metrics correctly integrated in isEmpty check.

The addition of 'totalReceivables' and 'atoSuperOwing' to the mainKeys array ensures the API properly handles these new financial metrics when determining if a dashboard result is empty.

packages/frontend/lib/today-widgets.ts (2)

7-7: LGTM! New receivables widget properly added.

The 'receivables' widget with label 'Unpaid Invoices' is correctly positioned between the existing widgets.


10-10: LGTM! Widget label updated to accurately reflect its content.

Renaming the 'tax' widget label from "Tax & Super" to "ATO Super Owing" properly aligns with the newly implemented metric.

docs/data-model.md (1)

119-123: LGTM! Clear documentation of the new metrics.

The documentation provides clear definitions for both new metrics:

  • totalReceivables: Sum of amount_due for authorised sales invoices with outstanding balances
  • atoSuperOwing: Latest balance of GST, PAYG and Super accounts from Xero

This documentation will help team members understand how these metrics are calculated.

packages/frontend/components/metrics-explorer.tsx (2)

23-23: LGTM! Metric mapping added for 'total receivables'.

The mapping from the user-facing 'total receivables' metric name to the 'receivables' widget ID is correctly implemented.


26-26: LGTM! Metric mapping added for 'ato super owing'.

The mapping from the user-facing 'ato super owing' metric name to the 'tax' widget ID is correctly implemented, while maintaining the existing mapping for 'currently owing to ato and super'.

packages/frontend/lib/api.ts (3)

12-13: LGTM! API schema correctly updated for new metrics.

The MetricsResponse schema has been updated to include the new 'atoSuperOwing' and 'totalReceivables' fields as nullable, optional numbers, which ensures proper type safety throughout the application.


142-142: LGTM! Mock data properly updated for ATO super owing.

The mock dashboard metrics now use 'atoSuperOwing' instead of the previous 'taxAndSuper' field, maintaining consistency with the schema changes.


139-139:

Details

✅ Verification successful

Verify totalReceivables in mock data.

I notice that line 139 includes 'totalReceivables' without being marked as a new line. Please verify that this field was either previously present but unused, or that it should have been marked as a new addition.


🏁 Script executed:

#!/bin/bash
# Check if totalReceivables existed in the previous version of this file
git show HEAD^:packages/frontend/lib/api.ts | grep -n "totalReceivables"

Length of output: 110


totalReceivables field is not new
Verified via git show HEAD^:packages/frontend/lib/api.ts that totalReceivables was already present at line 139 in the previous commit. No change needed.

packages/backend/src/services/metricsAggregation.receivables.test.ts (2)

1-7: Well-structured test file with appropriate imports.

The test file is properly set up with the necessary imports for testing and database interaction.


8-25: Good database setup function

The setupDb function creates an in-memory SQLite database with a schema that matches the production xero_invoices table. This is a good practice for unit testing as it isolates the tests from the actual database.

packages/frontend/components/dashboard-metrics.tsx (2)

137-140: Good explicit metric name mapping

Adding explicit mappings for the new metrics ensures consistent naming throughout the application. This approach is better than relying solely on the generic camelCase-to-Title Case conversion logic.


178-178: Correct description added for atoSuperOwing

The description matches the existing atoAndSuper metric description, ensuring consistency across the application.

packages/backend/src/services/metricsAggregation.ts (5)

107-120: Properly implemented metric aggregation for new metrics

The implementation of totalReceivables and atoSuperOwing follows the established patterns in the file. The SQL queries correctly filter for:

  1. totalReceivables: AUTHORISED invoices with amountDue > 0 within the date range
  2. atoSuperOwing: Latest recorded value from the 'xero_ato_super_owing' metric

The code properly handles null/undefined results by defaulting to 0.


305-306: Good inclusion of new metrics in result object

The new metrics are properly added to the result object to be returned from the dashboard aggregation function.


328-328: Updated expected keys list to include new metrics

The expectedKeys array is updated to include the new metrics, ensuring they are properly validated and defaulted to 0 if missing.


515-532: Well-implemented standalone metric handlers

The standalone metric handlers for totalReceivables and atoSuperOwing follow the established pattern in the file and reuse the same SQL query logic as in the dashboard aggregation, which is good for consistency and maintainability.


535-536: Updated TODO comment to remove implemented metrics

The TODO comment has been updated to remove the now-implemented metrics, which is a good practice to keep documentation accurate.

packages/frontend/components/today-widgets/TotalReceivablesWidget.tsx (2)

1-12: Well-defined component interface

The component imports the necessary dependencies and defines a clear props interface. The props follow the pattern used by other widget components in the application.


13-50: Well-structured component implementation

The component is implemented as a functional component using React best practices. It properly extends the BaseWidget component and includes appropriate styling, hover effects, and an accessible tooltip.

The "Unpaid Invoices" label is consistent with the mapping defined in dashboard-metrics.tsx.

packages/frontend/components/today-overview.tsx (6)

26-26: Looks good - appropriate TotalReceivablesWidget import added

The import of the new TotalReceivablesWidget component has been correctly added to support displaying the new receivables metric in the dashboard.


66-66: Proper type interface updates for the new metrics

The MetricsState interface has been appropriately updated to replace taxAndSuper with atoSuperOwing and add totalReceivables, maintaining proper type safety for the new financial metrics.

Also applies to: 86-86


113-113: Good addition to component map

The TotalReceivablesWidget has been properly registered in the COMPONENT_MAP, allowing it to be used in the dashboard.


130-130: State initialization correctly updated

The initial state values are properly initialized for both the renamed atoSuperOwing and the new totalReceivables metrics, ensuring the component state matches the updated interface.

Also applies to: 150-150


200-200: Properly retrieving new metrics from API response

The data fetching logic correctly sets the state with the new metrics values from the API response, using proper null-checking with the nullish coalescing operator.

Also applies to: 220-220


520-520: Correctly updated TaxAndSuperWidget prop

The TaxAndSuperWidget component now correctly receives the renamed atoSuperOwing metric instead of the previous taxAndSuper value, ensuring proper data flow.

Comment on lines +483 to +495
<WidgetContainer
id="receivables"
visible={visibleWidgets.receivables}
gridPosition={{ x: 0, y: 2, w: 1, h: 1 }}
gridItemStyle={gridStackStyles.gridItem}
>
<TotalReceivablesWidget
value={metrics.totalReceivables}
hovered={hoveredCard === "receivables"}
onHover={(v: boolean) => setHoveredCard(v ? "receivables" : null)}
getDescription={getMetricDescription}
/>
</WidgetContainer>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Check for widget position conflict

The new receivables widget has been added with position coordinates (0, 2), which appears to conflict with the survival time widget that also uses position (0, 2) at lines 425-437.

Verify that this positioning is intentional or that widget layouts are dynamically adjusted based on visibility. If both widgets are visible simultaneously, they would overlap in the current configuration.


🏁 Script executed:

#!/bin/bash
# Check for other widgets at the same grid position

echo "Checking for widgets with position (0, 2)..."
grep -A 5 "gridPosition.*x: 0.*y: 2" packages/frontend/components/today-overview.tsx

echo "Checking for position handling in GridStackWrapper..."
grep -A 10 "GridStackWrapper" packages/frontend/components/today-widgets/GridStackWrapper.tsx || echo "File not found or no matching pattern"

Length of output: 1409


Resolve overlapping widget positions

Both the SurvivalTimeWidget (lines 425–437) and the TotalReceivablesWidget (lines 483–495) are defined with the same gridPosition { x: 0, y: 2, w: 1, h: 1 }, which will cause them to render on top of each other when both are visible.

Please address this by either:

  • Assigning a unique gridPosition to one of the widgets, or
  • Enhancing the layout logic in GridStackWrapper to detect and resolve overlapping placements dynamically.

• packages/frontend/components/today-overview.tsx
– SurvivalTimeWidget: gridPosition={{ x: 0, y: 2, w: 1, h: 1 }}
– TotalReceivablesWidget: gridPosition={{ x: 0, y: 2, w: 1, h: 1 }}

🤖 Prompt for AI Agents
In packages/frontend/components/today-overview.tsx around lines 425-437 and
483-495, the SurvivalTimeWidget and TotalReceivablesWidget share the same
gridPosition { x: 0, y: 2, w: 1, h: 1 }, causing them to overlap when both are
visible. To fix this, assign a unique gridPosition to one of these widgets by
changing either the x or y coordinate to a non-conflicting value, or
alternatively, update the GridStackWrapper layout logic to detect and resolve
overlapping widget positions dynamically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant