feat: complete automatic sales summary calculations and ledger posting

This commit is contained in:
2026-04-25 08:43:16 -07:00
parent 297464c188
commit 2bcebc4424
8 changed files with 529 additions and 105 deletions

View File

@@ -0,0 +1,219 @@
---
title: Complete Automatic Sales Summary Calculations and Ledger Posting
type: feat
status: completed
date: 2026-04-24
---
# Complete Automatic Sales Summary Calculations and Ledger Posting
## What's Incomplete
- **Automatic Totals**: Aggregate attributes (e.g., `:sales-summary/total-card-payments`) are not calculated/stored by the job.
- **Data Persistence**: Automatic recalculations risk overwriting manual user adjustments.
- **Automation Gap**: Ledger entries are currently imported from external Excel files rather than generated automatically from the summaries.
- **UI Polish**: "Clientization" and HTMX context (`client-id`) TODOs remain in the admin interface.
---
## Overview
...
This plan completes the implementation of automatic sales summary calculations and ensures they are correctly posted to the ledger. Currently, the `sales-summaries-v2` job calculates detailed daily summaries, but it doesn't store aggregate totals, preserve manual adjustments, or trigger the creation of actual ledger entries. Additionally, the admin UI has several unresolved TODOs.
---
## Problem Frame
The system currently aggregates raw sales data into a `sales-summary` entity, but the final step—creating balanced journal entries for the general ledger—is a manual process involving external Excel calculations and subsequent imports. This creates a dependency on external tools and increases the risk of data entry errors. The goal is to automate this pipeline entirely within the product.
---
## Requirements Trace
- R1. Calculate and store aggregate totals (e.g., `:sales-summary/total-card-payments`) on the `sales-summary` entity.
- R2. Preserve user-made manual adjustments (`:sales-summary-item/manual? true`) during automatic recalculations.
- R3. Aggregate detailed `sales-summary-item`s into balanced `journal-entry` lines by account and location.
- R4. Automate the posting of these aggregated totals to the ledger.
- R5. Resolve UI TODOs in the Sales Summaries admin page, specifically regarding client-scoping ("clientize") and HTMX context (`client-id`).
---
## Scope Boundaries
- **In-Scope**:
- Enhancements to the `sales-summaries-v2` job.
- Implementation of the summary-to-ledger aggregation and posting logic.
- Cleanup of the Sales Summaries admin UI.
- **Out-of-Scope**:
- Changing the fundamental calculation logic for sales orders/refunds.
- Creating new ledger accounts (assume existing account mapping is sufficient).
- Changing the naming of refunds/returns (user requested to keep as is).
---
## Context & Research
### Relevant Code and Patterns
- **Jobs**: `src/clj/auto_ap/jobs/sales_summaries.clj` contains the main calculation logic.
- **UI**: `src/clj/auto_ap/ssr/admin/sales_summaries.clj` implements the admin interface.
- **Ledger Posting**: `src/clj/auto_ap/ledger.clj` and `iol_ion/src/iol_ion/tx/upsert_ledger.clj` handle journal entry creation.
- **Reconciliation Pattern**: `reconcile-ledger` in `src/clj/auto_ap/ledger.clj` shows how to find missing ledger entries and trigger their creation.
### Institutional Learnings
- No existing documented patterns for sales summary posting were found in `docs/solutions/`. This implementation will establish the pattern.
---
## Key Technical Decisions
- **Detailed Summary $\to$ Aggregated Ledger**: The `sales-summary` will maintain granular detail (line items, specific fee types), but the ledger posting will aggregate these items by account and location to create balanced `journal-entry` lines.
- **Automatic Posting**: Posting to the ledger will be integrated into the reconciliation process, similar to how invoices and transactions are handled in `reconcile-ledger`.
- **Location Handling**: Since `sales-summary-item`s don't have a location, a default location for the client will be used for ledger posting.
---
## Open Questions
### Resolved During Planning
- **Architectural Decision**: Use a detailed summary that aggregates into the ledger.
- **Renaming**: Keep "Refunds/Returns" as is.
### Deferred to Implementation
- **Default Location Logic**: Exactly how the "default location" for a client is retrieved or defined.
---
## Implementation Units
- U1. **Enhance `sales-summaries-v2` Job**
**Goal:** Ensure the job stores aggregate totals and preserves manual adjustments.
**Requirements:** R1, R2
**Dependencies:** None
**Files:**
- Modify: `src/clj/auto_ap/jobs/sales_summaries.clj`
**Approach:**
- Update `sales-summaries-v2` to calculate totals for attributes like `:sales-summary/total-card-payments`, `:sales-summary/total-cash-payments`, etc., based on the generated items.
- Implement a merge strategy: when updating a summary, keep any items where `:sales-summary-item/manual?` is true, and only replace the automatically calculated items.
**Test scenarios:**
- Happy path: Running the job for a client with sales and refunds results in a `sales-summary` with correct `:sales-summary/total-*` attributes.
- Edge case: Running the job on a summary that already has a manual item ensures the manual item is not overwritten.
**Verification:**
- Datomic query shows `sales-summary` entities have populated total attributes and preserved manual items.
---
- U2. **Implement Summary-to-Ledger Aggregation**
**Goal:** Create a function to transform detailed summary items into balanced ledger lines.
**Requirements:** R3
**Dependencies:** U1
**Files:**
- Create: `src/clj/auto_ap/ledger/sales_summaries.clj` (or add to `src/clj/auto_ap/ledger.clj`)
- Test: `test/clj/auto_ap/ledger_test.clj`
**Approach:**
- Create a function `aggregate-summary-items` that:
1. Groups `sales-summary-item`s by `:ledger-mapped/account`.
2. Sums the `:ledger-mapped/amount` based on `:ledger-mapped/ledger-side` (debit vs credit).
3. Assigns a location (default client location).
4. Returns a list of `journal-entry-line` maps.
**Test scenarios:**
- Happy path: A set of items with mixed accounts and sides aggregates into the correct number of ledger lines with summed amounts.
- Edge case: Items with `nil` accounts are handled gracefully (e.g., mapped to an "Unknown" account or logged as error).
**Verification:**
- Unit tests verify that a list of `sales-summary-item`s is correctly transformed into `journal-entry-line`s.
---
- U3. **Implement Automatic Ledger Posting for Summaries**
**Goal:** Ensure sales summaries trigger the creation of ledger entries.
**Requirements:** R4
**Dependencies:** U2
**Files:**
- Modify: `src/clj/auto_ap/ledger.clj`
- Modify: `src/clj/auto_ap/jobs/sales_summaries.clj`
**Approach:**
- Implement a `:upsert-sales-summary-ledger` transaction or function that takes a `sales-summary` and uses the aggregation logic from U2 to post to the ledger.
- Integrate this into the `reconcile-ledger` function in `src/clj/auto_ap/ledger.clj` to find summaries missing ledger entries and post them.
**Test scenarios:**
- Integration: Running `reconcile-ledger` identifies a `sales-summary` missing a `journal-entry` and creates a balanced `journal-entry` for it.
- Happy path: The created `journal-entry` has the correct total amount and matches the summary totals.
**Verification:**
- A `sales-summary` entity is linked to a `journal-entry` via `:journal-entry/original-entity`.
---
- U4. **Resolve UI TODOs in Sales Summaries Admin**
**Goal:** Fix client-scoping and HTMX context in the admin UI.
**Requirements:** R5
**Dependencies:** None
**Files:**
- Modify: `src/clj/auto_ap/ssr/admin/sales_summaries.clj`
**Approach:**
- Resolve "clientize" TODOs: Ensure the data pulled for the table and edit wizard is correctly scoped and transformed using client-specific context.
- Fix HTMX `client-id` passing: Update the `new-summary-item` trigger to correctly pass the `client-id` via `hx-vals` from the form state.
- Clean up any remaining schema TODOs in the SSR file.
**Test scenarios:**
- Integration: Adding a new summary item in the UI correctly sends the `client-id` and the item is created for the correct client.
- Happy path: The summary table displays correctly and "missing account" warnings appear only for items without a mapped account.
**Verification:**
- Manual verification in the browser: New items are added correctly, and the UI is free of "missing account" red pills for mapped items.
---
## System-Wide Impact
- **Interaction graph**: The `sales-summaries-v2` job now feeds into the ledger system via `reconcile-ledger`.
- **Error propagation**: Failures in the aggregation logic will prevent the `journal-entry` from being created, which will be surfaced by `reconcile-ledger` as a missing entry.
- **State lifecycle risks**: Ensuring that `manual?` items are not overwritten during automatic recalculation is critical to avoid losing user adjustments.
- **Integration coverage**: Integration tests must cover the full flow: `sales-orders` $\to$ `sales-summary` $\to$ `journal-entry`.
---
## Risks & Dependencies
| Risk | Mitigation |
|------|------------|
| Overwriting manual adjustments | Implement explicit merge logic based on the `:sales-summary-item/manual?` flag. |
| Unbalanced ledger entries | Use a strict aggregation function that ensures debits = credits for every posted summary. |
| Missing location data | Implement a robust fallback to a default client location. |
---
## Sources & References
- Related code: `src/clj/auto_ap/jobs/sales_summaries.clj`
- Related code: `src/clj/auto_ap/ssr/admin/sales_summaries.clj`
- Related code: `src/clj/auto_ap/ledger.clj`
- Related code: `iol_ion/src/iol_ion/tx/upsert_ledger.clj`