Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
17 KiB
17 KiB
Scratchpad — Grouped Automatic Invoices Selection
- Plan slug:
grouped-automatic-invoices-selection - Created:
2026-03-20
What This Is
Working notes for the grouped automatic-invoices selection redesign. This plan intentionally spans both UI and backend invoice-scope behavior.
Decisions
- (2026-03-20) Group recurring due work visually by
client + invoice window. Parent grouping is for user comprehension first, not an automatic promise that the result is one invoice. - (2026-03-20) Parent checkbox semantics are strict: if enabled and selected, it means “generate one combined invoice.” If the group cannot combine, the parent checkbox is disabled rather than overloaded.
- (2026-03-20)
Select Allmust be smart: select the parent for combinable groups and select child rows individually for non-combinable groups. - (2026-03-20) Combined execution is allowed only when the selected children share compatible invoice-level financial scope: client, currency, PO scope, tax source, and export shape.
- (2026-03-20) Multi-assignment combined invoices are explicitly in scope for this plan. The backend must stop requiring a fake single invoice owner for valid combined selections.
Discoveries / Constraints
- (2026-03-20)
AutomaticInvoicesalready receives parent candidate rows withmembers, but the UI still behaves as a flat table and selection is onlySet<candidateKey>. File:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Preview is currently gated to a single selected candidate with exactly one child member. Grouped preview behavior does not exist today.
- (2026-03-20) The shared grouping logic already computes split reasons
single_contract,purchase_order_scope, andfinancial_constraintper invoice window. Files:shared/billingClients/recurringTiming.ts,packages/billing/src/actions/billingAndTax.ts. - (2026-03-20) The current preview/generate APIs are strictly single-selector.
IRecurringDueSelectionInputrepresents one selector only; generation scopes to one canonical line/window before billing. Files:packages/types/src/interfaces/recurringTiming.interfaces.ts,packages/billing/src/actions/invoiceGeneration.ts. - (2026-03-20) Grouped preview now supports exact parent/child selection bundles through
previewGroupedInvoicesForSelectionInputs, while direct "Generate from preview" remains intentionally single-selector until grouped generation semantics (F015/F016) land. Files:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx,packages/billing/src/actions/invoiceGeneration.ts. - (2026-03-20) Grouped generation request semantics now exist via
generateGroupedInvoicesAsRecurringBillingRun, andAutomaticInvoicesnow submits grouped targets (groupKey + selectorInputs) for bulk generation paths instead of only flattened single-selector targets. Files:packages/billing/src/actions/recurringBillingRunActions.ts,packages/billing/src/actions/recurringBillingRunActions.shared.ts,packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Combined grouped execution now routes through a multi-selector generation path (
generateInvoiceForSelectionInputs) so one selected parent group can execute as one invoice request; incompatible child-level selections still fan out via one request per child group. File:packages/billing/src/actions/invoiceGeneration.ts,packages/billing/src/actions/recurringBillingRunActions.ts. - (2026-03-20) Multi-assignment combined generation now persists invoice headers with
client_contract_id: nullwhen charges span multiple assignments, while preserving per-charge assignment attribution inpersistInvoiceCharges. Files:packages/billing/src/actions/invoiceGeneration.ts,server/src/test/unit/billing/invoiceGeneration.selectorInputGenerate.test.ts. - (2026-03-20) Recurring generation no longer throws on multi-assignment charge sets; combined invoices persist with header
client_contract_id: nullwhile per-charge assignment IDs remain authoritative. File:packages/billing/src/actions/invoiceGeneration.ts. - (2026-03-20) PO enforcement and consumption are currently header-assignment scoped through
invoices.client_contract_id, so grouped combination must not bypass PO compatibility constraints. Files:packages/billing/src/services/purchaseOrderService.ts,packages/billing/src/actions/invoiceQueries.ts. - (2026-03-20) Charge-level assignment attribution exists on
invoice_charges, but invoice read models do not currently expose enough of that provenance for a combined invoice UX. Files:packages/billing/src/services/invoiceService.ts,packages/billing/src/models/invoice.ts.
Commands / Runbooks
- (2026-03-20) Scaffolded this plan with:
python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "Grouped Automatic Invoices Selection" --slug grouped-automatic-invoices-selection
- (2026-03-20) Validate the plan with:
python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/validate_plan.py ee/docs/plans/2026-03-20-grouped-automatic-invoices-selection
- (2026-03-20) JSON sanity:
jq empty ee/docs/plans/2026-03-20-grouped-automatic-invoices-selection/features.jsonjq empty ee/docs/plans/2026-03-20-grouped-automatic-invoices-selection/tests.json
Links / References
- Related plan:
ee/docs/plans/2026-03-20-multi-active-contracts-per-client/ - UI entry point:
packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx - Grouping logic:
shared/billingClients/recurringTiming.ts - Due-work shaping:
packages/billing/src/actions/billingAndTax.ts - Execution path:
packages/billing/src/actions/invoiceGeneration.ts - Recurring run actions:
packages/billing/src/actions/recurringBillingRunActions.ts - Invoice persistence:
packages/billing/src/services/invoiceService.ts - PO behavior:
packages/billing/src/services/purchaseOrderService.ts
Progress Log
- (2026-03-20) Codified parent/child selection semantics directly in
AutomaticInvoiceswithRecurringInvoiceParentGroupandbuildRecurringInvoiceParentGroups, so parent rows are explicit model objects and child members are explicitly treated as execution units. File:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Updated ready-work explanatory copy to align with PRD semantics: parent rows group by
client + invoice window; child obligations remain atomic execution units. File:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Added a jsdom UI behavior test that mocks due-work responses and verifies one top-level automatic-invoices row renders for a multi-member shared
client + invoice windowcandidate. File:packages/billing/tests/automaticInvoices.groupedParentRows.test.tsx. - (2026-03-20) Targeted validation for the new grouped parent-row behavior test:
cd packages/billing && npx vitest run tests/automaticInvoices.groupedParentRows.test.tsx
- (2026-03-20) Completed
F002by refactoring the ready-work UI model into explicit parent summaries (parentSummary) and child execution rows (childExecutionRows), while keeping generation/preview wired to selected parent-group candidates. File:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Completed
F003by introducing explicit parent-group identity (parentGroupKey) and parent-selection identity (parentSelectionKey) in the ready-work view model, and routing group selection state through those keys instead of child selector identities. File:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Added optional parent aggregate amount summary (
aggregateAmountCents) derived from child rows when amount data is present, with an explicit fallback when unavailable. File:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-20) Completed
T002by asserting grouped parent summary output (child count, invoice window label, aggregate amount) in the automatic invoices jsdom behavior test. File:packages/billing/tests/automaticInvoices.groupedParentRows.test.tsx. - (2026-03-20) Completed
F004with explicit parent-row expand/collapse controls and inline child-row rendering in the automatic invoices grouped table. - (2026-03-20) Completed
F006by rendering child details in the expanded group view, including assignment/contract context, cadence source, billing timing, service period, and amount. - (2026-03-20) Completed
F005by rendering parent summary combinability state alongside client, window, child-count, and aggregate-amount summaries. - (2026-03-20) Completed
T003by testing parent expansion and verifying child detail rendering in the automatic invoices grouped UI behavior test. - (2026-03-20) Added financial-scope fields (
purchaseOrderScopeKey,currencyCode,taxSource,exportShapeKey) to recurring due-work rows/candidates and wired those fields through due-work shaping so UI combinability logic can evaluate true invoice-scope compatibility. - (2026-03-20) Completed
F007by computing parent combinability from effective child scope values (client, currency, PO scope, tax source, export shape) and completedF008by enabling parent selection only when combinable. - (2026-03-20) Completed
F012by surfacing explicit non-combinable reason text on parent rows (PO scope differs,Currency differs,Tax treatment differs,Export shape differs). - (2026-03-20) Completed
T004-T008with scenario tests that verify combinability gating and each incompatibility reason. - (2026-03-20) Validation run:
cd packages/billing && npx vitest run tests/automaticInvoices.groupedParentRows.test.tsxnpm -w packages/billing run typecheck(fails due existing unrelated package-level TS errors; seebillingCycleActions.ts,recurringBillingRunActions.shared.ts,billingEngine.ts,invoiceService.ts, plus recurring due-window typing drift aroundduePosition).
- (2026-03-20) Completed
F009by introducing dual parent/child selection targets, child-level checkbox selection in expanded groups, and parent tri-state behavior when child selections are partial. - (2026-03-20) Completed
F010by implementing smartSelect All: combinable groups select parent targets, non-combinable groups select child targets. - (2026-03-20) Completed
F011by keeping blocked child rows visible while preventing blocked child selection in both direct child selection andSelect All. - (2026-03-20) Completed
T009-T014with UI behavior tests covering parent selection semantics, child selection availability, parent tri-state, smartSelect All, and blocked child visibility/selection guardrails. - (2026-03-20) Completed
F013by adding grouped preview request semantics end-to-end:AutomaticInvoicesnow emits grouped preview payloads from parent/child selection state andinvoiceGenerationnow exposespreviewGroupedInvoicesForSelectionInputswith exact selector-scope support. - (2026-03-20) Completed
F014by updating preview rendering to show explicit invoice-count messaging (This selection will generate N invoice(s).) and render grouped preview cards for one-or-many preview outputs. - (2026-03-20) Completed
T015-T017with jsdom behavior tests that validate grouped parent preview count, split child preview count, and exact-scope preview payloads (no unselected siblings). - (2026-03-20) Completed
F015by introducing grouped generation payload semantics and wiring automatic-invoice generation calls to submit explicit grouped targets derived from parent/child selection state. - (2026-03-20) Completed
F016by preserving exact selector scope through grouped generation payloads and preventing sibling re-expansion in execution request construction. - (2026-03-20) Completed
F017by adding a grouped run execution loop that calls multi-selector invoice generation once per selected combinable parent group. - (2026-03-20) Completed
F018by preserving fan-out execution for incompatible selections via grouped run targets where each child selection executes independently. - (2026-03-20) Completed
F019by ensuring grouped recurring run execution skips duplicate selections (idempotent duplicate code path) while continuing unrelated selected groups in the same run. - (2026-03-20) Completed
F020by removing the single-assignment hard-fail at invoice-header persistence and allowing combined multi-assignment invoices to persist without a fake header owner. - (2026-03-20) Completed
F021by preserving per-chargeclient_contract_idattribution for combined multi-assignment execution paths. - (2026-03-20) Completed
T020with a jsdom behavior test that asserts generation sends only explicitly selected child selectors even when the group contains additional siblings. - (2026-03-20) Completed
T018andT019with server unit tests that verify grouped run execution creates one invoice for a combinable parent group and multiple invoices for split child groups. - (2026-03-20) Completed
T021with server unit coverage that verifies duplicate grouped/member selections are skipped without blocking unrelated sibling groups. - (2026-03-20) Completed
T022andT023by converting selector-input generation coverage to assert multi-assignment persistence semantics: header assignment ownership is optional and per-charge assignment IDs remain intact. - (2026-03-21) Completed
F022by extending recurring invoice history reads with assignment provenance (assignment_contract_ids) and rendering that provenance in history UI via assignment summary + multi-contract badge. Files:packages/billing/src/actions/billingCycleActions.ts,packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx. - (2026-03-21) Completed
F023by shifting due-work grouping to parent scope (client + invoice window) while preserving PO compatibility semantics through explicitpo_required-awarepurchaseOrderScopeKeymetadata. Files:shared/billingClients/recurringTiming.ts,packages/billing/src/actions/billingAndTax.ts. - (2026-03-21) Completed
F024with regression-guarded legacy behavior for single-assignment/single-child parent groups across preview and generation paths. File:packages/billing/tests/automaticInvoices.groupedParentRows.test.tsx. - (2026-03-21) Completed
F025by adding explicit in-product copy for smartSelect Allsemantics and introducing a dedicated grouped-automatic-invoices runbook contract. Files:packages/billing/src/components/billing-dashboard/AutomaticInvoices.tsx,ee/docs/plans/2026-03-20-grouped-automatic-invoices-selection/RUNBOOK.md. - (2026-03-21) Completed
T024andT025with UI behavior coverage for recurring history assignment-scope rendering (assignmentSummary,Multi-contract invoicebadge). File:packages/billing/tests/automaticInvoices.groupedParentRows.test.tsx. - (2026-03-21) Completed
T026andT027by validating PO-scope non-combinability in grouped UI and no-PO combined generation behavior in selector-input generation tests. Files:packages/billing/tests/automaticInvoices.groupedParentRows.test.tsx,server/src/test/unit/billing/invoiceGeneration.selectorInputGenerate.test.ts. - (2026-03-21) Completed
T028andT029with legacy-flow coverage for single-child/single-assignment grouped parents. - (2026-03-21) Completed
T030andT031with recurring due-work integration coverage proving same-client same-window rows remain one parent group for no-PO combinations and carry PO split reasons when scopes conflict. File:server/src/test/unit/billing/recurringDueWorkReader.integration.test.ts. - (2026-03-21) Completed
T032andT033with grouped recurring run execution coverage for mixed parent+child fan-out semantics and duplicate-protection isolation across sibling targets. File:server/src/test/unit/billing/recurringBillingRunActions.test.ts. - (2026-03-21) Completed
T034with docs/wiring coverage that locks runbook text and in-product explanatory copy for parent grouping + smartSelect Allsemantics. File:server/src/test/unit/docs/groupedAutomaticInvoices.runbook.test.ts. - (2026-03-20) Validation run:
cd packages/billing && npx vitest run tests/automaticInvoices.groupedParentRows.test.tsxcd server && npx vitest run src/test/unit/billing/recurringBillingRunActions.test.ts
- (2026-03-21) Validation run:
cd packages/billing && npx vitest run tests/automaticInvoices.groupedParentRows.test.tsxcd server && npx vitest run src/test/unit/billing/recurringDueWorkReader.integration.test.ts src/test/unit/billing/recurringBillingRunActions.test.ts src/test/unit/billing/invoiceGeneration.selectorInputGenerate.test.ts src/test/unit/docs/groupedAutomaticInvoices.runbook.test.ts --coverage.enabled=false
Open Questions
- Should multi-invoice preview render several full invoice previews or a summary-first experience with drill-down?
- Should combined multi-assignment invoices expose a dedicated badge or invoice-scope type in history/detail screens?
- Should parent totals show only ready children or both ready and blocked totals when a group contains a mix?