--- 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`