Skip to content

AR Aging Workflow

1. Executive Summary

Purpose

The AR Aging workflow provides accounts receivable aging reports that show how long outstanding balances have been owed across the billing item portfolio. Users search and filter the receivable population, then view results in either a Summary view (one row per billing item, combining REV and PAY) or a Detail view (separate rows for each REV and PAY detail). Each row classifies the outstanding balance into one of five time buckets — Current, 1–30 days, 31–60 days, 61–90 days, and 90+ days — based on billing_item.billing_item_due_dt. From within the report, users can also open a deduction management dialog to update deductions on a billing item without leaving the report context.

Scope

Covered:

  • Filtering the billing item population by client, buyer, deal, department, currency, collection style, open/closed status, and write-off inclusion
  • Summary view: one row per billing item with combined REV + PAY totals, aging bucket classification, and write-off status
  • Detail view: separate rows per billing_item_detail type (REV and PAY) with per-detail balances and aging bucket classification
  • Deduction management: inline dialog to view and update billing_item_deduction records for a selected billing item
  • Export of report data to file

Not covered (documented separately):

Key Objectives

  • Give finance teams a real-time view of the outstanding receivable portfolio, organized by aging bucket
  • Identify overdue items by client, buyer, deal, or department for collections follow-up
  • Surface write-off status on individual receivables so users can navigate to the write-off packet
  • Allow targeted deduction adjustments directly from the aging report without navigating away

2. Process Overview

mermaid
flowchart TD
    A[User opens AR Aging Report] --> B[Enter search criteria]
    B --> C{Run Search}
    C --> D[Summary View loaded\none row per billing item]
    C --> E[Detail View loaded\none row per detail line]
    D --> F{Select view tab}
    E --> F
    F -->|Summary tab| G[Review combined REV+PAY balances\nand aging buckets]
    F -->|Detail tab| H[Review per-detail REV / PAY balances\nand aging buckets]
    G --> I{Row has deductions?}
    H --> I
    I -->|Yes| J[Click row to open\nDeduction Management dialog]
    I -->|No| K[Toast: no deductions]
    J --> L[Edit deduction amounts\nand types]
    L --> M[Save Deductions]
    M --> N[Deductions updated\nin-place on billing item]
    G --> O[Export to file]
    H --> O

Walkthrough

  1. Enter search criteria — The user sets any combination of filters: client, buyer, deal, department, currency, collection style, date range, free-text term, open-items-only flag, and include-written-off flag. The default state is open items only, written-off excluded.
  2. Run search — Executing the search fires two parallel queries: getArAgingSummary and getArAgingDetail. Both return immediately after the server action completes.
  3. Review Summary tab — The default active tab shows one row per billing_item. Balances shown are combined REV + PAY; the balance is distributed into exactly one aging bucket per row based on billing_item.billing_item_due_dt compared to the current date.
  4. Switch to Detail tab — The user can switch to the Detail tab to see separate rows for the REV and PAY billing_item_detail records, each with their own per-detail balance and aging bucket.
  5. Manage deductions — Clicking any row that has deductions opens the Deduction Management dialog. The user can view, add, edit, or delete deduction rows for both the REV and PAY details, then save. Deductions are saved directly on the existing billing item without triggering a rebill.
  6. Export — The user can export the currently visible grid to a file using the built-in export control on either tab.

3. Business Rules

3.1 Aging Date Source

Business rule: Aging is calculated from billing_item.billing_item_due_dt. If no due date is set on the billing item, the balance is classified as Current.

Foundation reference: AR Aging Summary query — Computed Values

Workflow context: When billing_item.billing_item_due_dt is NULL, daysOverdue resolves to NULL and the aging bucket calculation places the full balance in the agingCurrent bucket. The due date column in the grid displays a dash for such rows.

**PoC Artifact:**

The data model defines a separate billing_item.billing_item_aging_dt field intended as the aging date, which defaults to billing_item_due_dt but can be manually adjusted by a user to override aging bucket placement. The PoC implementation drives all aging bucket calculations directly from billing_item_due_dt. A production implementation should use billing_item_aging_dt as the aging date source so that user overrides are respected.

3.2 Aging Bucket Classification

Business rule: Each billing item's outstanding balance is placed in exactly one of five aging buckets: Current (not yet due), 1–30 days, 31–60 days, 61–90 days, or 90+ days past due. A balance cannot appear in more than one bucket simultaneously.

Foundation reference: Aging Bucket Classification (3.9)

3.3 Balance Calculation Scope

Business rule: Outstanding balance is computed as the total billed amount minus cash applied and deductions applied, but only from worksheets that are current and in Approved ('A') or Submitted ('S') status. Draft, returned, and superseded worksheets do not affect the displayed balance.

Foundation reference: AR Aging Summary query — totalBalance computed value

IMPORTANT

The status filter IN ('A', 'S') means that cash applied on a Draft worksheet ('D') is not subtracted from the aging balance until that worksheet is approved. This is intentional: unapproved cash has not yet been committed.

3.4 Net After Deductions Computation

Business rule: "Net After Deductions" subtracts only deductions flagged as net-impacting (billing_item_deduction.billing_item_deduction_update_net_ind = true) from the gross amount. Informational-only deductions do not reduce the displayed net.

Foundation reference: Net After Deductions (3.8)

3.5 Write-Off Visibility Filter

Business rule: By default, billing items whose REV detail has write_off_status_cd = 'WRITTEN_OFF' are excluded from the aging report. Users can include written-off items by checking the "Include Written Off" filter before running the search.

Foundation reference: AR Aging Summary — Filters

Workflow context: When a row has write-off status WRITTEN_OFF or RECOVERED, the report displays a badge in the Write-Off column. If a write-off packet ID is present, the badge is clickable and opens the write-off packet in a new browser tab.

3.6 Open Items Filter Default

Business rule: The search panel defaults to showing only open items (billing_item.open_item_ind = true). Users can uncheck "Open Items Only" to include fully-collected receivables in the results.

Foundation reference: Billing Items Data Model — open_item_ind

Workflow context: The default is intentional for collections workflows — users typically want to see only what remains outstanding.

3.7 Deduction Management Saves In-Place

Business rule: Saving deductions from within the aging report updates deduction records directly on the existing billing item. No rebill, reversal, or new billing item is created.

Foundation reference: Manage Billing Item Deductions (2.7)

Workflow context: After a successful deduction save, the aging grid is not automatically refreshed. The user sees a success toast but must re-run the search to see the updated net amounts. This is intentional — deductions do not affect billing_item_detail_amt or billing_item_detail_total_amt directly; they affect the net display at the query level.

**PoC Artifact:**

The comment in the client code explicitly notes that refreshing the AR aging data after deduction save is not implemented because "deductions don't affect the AR aging calculations (only cash receipts do)." However, deductions do affect the deductionTotal and netAfterDeductions columns displayed in the grid, so a production implementation should refresh the relevant rows after save.

3.8 Summary vs. Detail Balance Scope

Business rule: In the Summary view, totalBalance is the combined outstanding balance across both the REV and PAY details. In the Detail view, detailBalance is the outstanding balance for that specific REV or PAY detail only.

Foundation reference: AR Aging Detail query (2.6.2)

3.9 Only REV Details Carry Write-Off Status

Business rule: Only billing_item_detail rows with billing_item_detail_type_cd = 'REV' can have a non-null write_off_status_cd. The Detail view suppresses the Write-Off badge for PAY rows regardless of the underlying value.

Foundation reference: Billing Items Data Model — write_off_status_cd


4. Data Access & Operations References

4.1 Queries Used

OperationFoundation DocPurpose in This Workflow
getArAgingSummaryAR Aging Summary (2.6.1)Loads Summary tab data — one row per billing item with combined REV+PAY balance and aging buckets
getArAgingDetailAR Aging Detail (2.6.2)Loads Detail tab data — separate rows for each REV and PAY detail with per-detail balance and aging buckets
getBillingItemByIdGet Billing Item by ID (2.1.1)Loads billing item header for the Deduction Management dialog header display
searchClientsParty QueriesAutocomplete lookup for Client filter field
searchBuyersParty QueriesAutocomplete lookup for Buyer filter field
searchDealsDeals QueriesAutocomplete lookup for Deal filter field
searchDepartmentsTODO: Document in foundation/queries/parties.mdAutocomplete lookup for Department filter field
getBillingItemDeductionDataGet Flattened Billing Item DisplayLoads billing item, details, and existing deductions into the Deduction Management dialog
getBillingItemDeductionTypeOptionsTODO: Document in foundation/queries/billing-items.mdLoads code master values for deduction type dropdown in the Deduction Management dialog

4.2 Procedures Used

OperationFoundation DocTrigger in This Workflow
manageBillingItemDeductionsManage Billing Item Deductions (2.7)User saves changes in the Deduction Management dialog

5. Key User Actions

Preconditions:

  • User has navigated to the AR Aging Report page at /reports/ar-aging
  • No role restriction applies — the page is accessible to all authenticated users

Procedure reference: AR Aging Summary (2.6.1) and AR Aging Detail (2.6.2)

Steps:

  1. User optionally sets filter values: client, buyer, deal, department, currency, collection style, due date range, or free-text search term
  2. User adjusts the Open Items Only and Include Written Off checkboxes as needed
  3. User clicks the Search button
  4. The system fires getArAgingSummary and getArAgingDetail in parallel; both tab result sets are populated simultaneously
  5. Results appear; the Summary tab is active by default and shows the count of returned rows

Postconditions:

  • summaryData populated with rows where each row maps to one billing_item record and includes five aging bucket amounts
  • detailData populated with rows where each row maps to one billing_item_detail record

UI trigger: Search button in the Search Criteria panel. Visible always. Enabled always (no required fields enforce a minimum filter).


5.2 Switch Between Summary and Detail Views

Preconditions:

  • A search has been executed (results are present)

Procedure reference: No data mutation — display-only tab switch.

Steps:

  1. User clicks the Summary tab or the Detail tab
  2. The corresponding data grid becomes visible; the inactive tab's grid is hidden

Postconditions:

  • Active tab label shows the row count for the selected view

UI trigger: Summary tab trigger (labeled "Summary (N)") or Detail tab trigger (labeled "Detail (N)"). Visible after at least one search has been executed. Enabled always once results exist.


5.3 Open Deduction Management Dialog

Preconditions:

  • A search has been executed and results are present
  • The selected row has hasDeductions = true

Procedure reference: Get Flattened Billing Item Display (2.3) — used to load dialog data.

Steps:

  1. User clicks a row in either the Summary or Detail grid
  2. If row.hasDeductions is true, the Deduction Management dialog opens and loads the billing item's details and existing deductions
  3. If row.hasDeductions is false, a toast notification informs the user that the billing item has no deductions; the dialog does not open

Postconditions:

  • Deduction Management dialog displays with billing item header context and editable deduction rows grouped by REV and PAY detail

UI trigger: Row click on any row in the Summary or Detail grid. Visible always. When hasDeductions = false, the click triggers a toast notification instead of opening the dialog.


5.4 Save Deductions

Preconditions:

  • Deduction Management dialog is open with a valid billing_item_id
  • Both REV and PAY details must be present on the billing item

Procedure reference: Manage Billing Item Deductions (2.7)

Steps:

  1. User adds, edits, or removes deduction rows in the REV section or PAY section of the dialog
  2. For each deduction row the user sets: billing_item_deduction_type_cd, billing_item_deduction_amt, billing_item_deduction_update_net_ind, and optionally a comment
  3. User clicks Save Changes
  4. The system compares the new deduction set against the existing set; if no changes are detected, the procedure returns without mutating the database
  5. If changes are detected: a reversal is created, a replacement billing item is created with new detail IDs, new billing_item_deduction records are inserted, cash applications are migrated, and open_item_ind is recalculated
  6. Success toast is displayed; the dialog closes

Postconditions:

  • Original billing_item has current_item_ind = false
  • New replacement billing_item exists with current_item_ind = true and new billing_item_detail and billing_item_deduction records
  • cash_receipt_application records point to the new billing_item_detail_id values
  • billing_item.open_item_ind recalculated on the new record

UI trigger: Save Changes button in the Deduction Management dialog. Visible always when the dialog is open. Disabled while the save operation is in progress.


5.5 Export Report Data

Preconditions:

  • A search has been executed and results are present on the active tab

Procedure reference: No server-side procedure — client-side export of in-memory data.

Steps:

  1. User clicks the Export button on the Summary or Detail grid toolbar
  2. The system exports the currently visible grid data to a file named ar-aging-summary or ar-aging-detail respectively

Postconditions:

  • File downloaded to the user's browser; no server-side mutation occurs

UI trigger: Export button in the DataTable toolbar. Visible when the grid has data. Enabled always when visible.


6. Permissions & Role-Based Access

ActionCASH_MANAGERCASH_PROCESSORSETTLEMENT_APPROVERIT
View AR Aging ReportYesYesYesYes
Run AR Aging SearchYesYesYesYes
Open Deduction Management dialogYesYesYesYes
Save DeductionsYesYesYesYes
Export report dataYesYesYesYes

Field-level restrictions:

  • No field-level restrictions apply to the AR Aging report itself. All columns are read-only display fields.
  • The Deduction Management dialog allows all authenticated users to modify deductions. Production implementations may wish to restrict deduction editing to users with a specific role such as CASH_MANAGER or IT, as deduction changes affect downstream balance calculations.

**PoC Artifact:**

Role-based access control is not enforced at the server action level in the PoC. The searchArAging server action does not perform any role check before executing the query. A production implementation should enforce at minimum a read permission check.


7. Integration Points

7.1 Upstream

SourceData ProvidedMechanism
Billing items and revenue itemsbilling_item, billing_item_detail, billing_item_deduction records that form the receivable populationFK lookup at query time
Deal enginedeal.deal_name, billing_item.deal_id — deal context displayed in report columnsFK join on billing_item.deal_id
Party registryparty.display_name for client and buyer names displayed in report columnsFK join on billing_item.client_id and billing_item.buyer_id
Department registrydepartment.department_name for grouping and filteringFK join on billing_item.department_id
Cash worksheetscash_receipt_application and cash_receipt_application_deduction records that reduce the aging balanceCorrelated subqueries in aging balance calculation
Write-off packetsbilling_item_detail.write_off_status_cd and write_off_packet_id for write-off badge displayCarried on the billing_item_detail record

7.2 Downstream

ConsumerData ConsumedMechanism
Write-Offs WorkflowUser clicks write-off badge to navigate to the packetBrowser navigation to /write-offs/packets/:packetId opened in a new tab
Billing Items (deductions)Deduction Management dialog saves create, update, and delete billing_item_deduction records on the existing billing itemServer action → manageBillingItemDeductions procedure

7.3 External Integrations

No external integrations for this workflow. The AR Aging report is an internal read-only analytical view of data that already exists within the Client Processing database.


8. Functional Screen Requirements

8.1 AR Aging Report Page

Route: /reports/ar-aging

Data loading:

  • No data is loaded on page entry. All data is loaded in response to an explicit user-initiated search action.

Search Criteria Region

Collapsible panel that accepts filter inputs. Expanded by default.

Field / ColumnSourceEditable?Condition
Search (free text)User input; matched against billing_item.billing_item_name, party.display_name (client), party.display_name (buyer), deal.deal_nameYesAlways visible
ClientAutocomplete against party table; stored as ArAgingSearchCriteria.clientId mapped to billing_item.client_idYesAlways visible
BuyerAutocomplete against party table; stored as ArAgingSearchCriteria.buyerId mapped to billing_item.buyer_idYesAlways visible
DealAutocomplete against deal table; stored as ArAgingSearchCriteria.dealId mapped to billing_item.deal_idYesAlways visible
DepartmentAutocomplete against department table; stored as ArAgingSearchCriteria.departmentId mapped to billing_item.department_idYesAlways visible
CurrencyDropdown: USD, EUR, GBP; mapped to billing_item.currency_cdYesAlways visible
Collection StyleDropdown: All / Client / Buyer; mapped to billing_item.collection_style_cdYesAlways visible
Open Items OnlyCheckbox; when checked maps to billing_item.open_item_ind = true filterYesAlways visible; default: checked
Include Written OffCheckbox; when unchecked excludes revDetail.write_off_status_cd = 'WRITTEN_OFF'YesAlways visible; default: unchecked

Grid features:

  • No grid in this region; inputs only
  • Search button triggers the data fetch
  • Clear button resets all criteria to defaults (openItemOnly = true, includeWrittenOff = false, all others empty)

Conditional display:

  • Search button displays a loading state while the search is in progress
  • The results region below is hidden until the first search is executed

Summary Tab Region

Displays one row per billing_item. Shown when the Summary tab is active.

Field / ColumnSourceEditable?Condition
Departmentdepartment.department_name via billing_item.department_idNoAlways visible
Dealdeal.deal_name via billing_item.deal_idNoAlways visible
Clientparty.display_name via billing_item.client_idNoAlways visible
Buyerparty.display_name via billing_item.buyer_idNoAlways visible
Collectionbilling_item.collection_style_cd; displayed as "Client" or "Buyer"NoAlways visible
Billing Itembilling_item.billing_item_nameNoAlways visible
Openbilling_item.open_item_ind; displayed as "Yes" or "No"NoAlways visible
Write-Offbilling_item_detail.write_off_status_cd (from REV detail); displayed as badge when WRITTEN_OFF or RECOVEREDNoBadge visible only when status is not NOT_WRITTEN_OFF
DeductionsComputed: hasDeductions boolean; displayed as "Yes" or "No"NoAlways visible
Gross AmountComputed: payDetail.billing_item_detail_gross_amtNoAlways visible
Deduction TotalComputed: sum of billing_item_deduction.billing_item_deduction_amt where billing_item_deduction_update_net_ind = true for both REV and PAY detailsNoAlways visible
Net After DedComputed: billingGrossAmt - deductionTotalNoAlways visible
Comm %revDetail.billing_item_detail_percent formatted as percentageNoAlways visible
Total BalanceComputed: combined REV + PAY outstanding balance after cash applied and deductions applied (Approved/Submitted worksheets only)NoAlways visible
RevenuerevDetail.billing_item_detail_total_amtNoAlways visible
Cash CollectedComputed: sum of cash_receipt_application.cash_receipt_amt_applied for both details on Approved/Submitted worksheetsNoAlways visible
Currencybilling_item.currency_cdNoAlways visible
Due Datebilling_item.billing_item_due_dtNoAlways visible
CurrentComputed: totalBalance when not yet due or no due date, else 0NoAlways visible
1-30 DaysComputed: totalBalance when 1–30 days past due, else 0NoAlways visible
31-60 DaysComputed: totalBalance when 31–60 days past due, else 0NoAlways visible
61-90 DaysComputed: totalBalance when 61–90 days past due, else 0NoAlways visible
90+ DaysComputed: totalBalance when more than 90 days past due, else 0NoAlways visible

Grid features:

  • Sortable columns: all columns
  • Filters: global text filter across all visible columns
  • Row selection: single-row click — opens Deduction Management dialog if hasDeductions = true
  • Pagination: yes
  • Export: yes; filename ar-aging-summary

Conditional display:

  • Empty state ("No results found. Try adjusting your search criteria") visible when search returned zero rows
  • Write-Off badge clickable (opens write-off packet in new tab) when writeOffPacketId is not null
  • Summary tab label shows count: "Summary (N)"

Detail Tab Region

Displays one row per billing_item_detail (separate rows for REV and PAY). Shown when the Detail tab is active.

Field / ColumnSourceEditable?Condition
Departmentdepartment.department_name via billing_item.department_idNoAlways visible
Dealdeal.deal_name via billing_item.deal_idNoAlways visible
Clientparty.display_name via billing_item.client_idNoAlways visible
Buyerparty.display_name via billing_item.buyer_idNoAlways visible
Billing Itembilling_item.billing_item_nameNoAlways visible
Typebilling_item_detail.billing_item_detail_type_cd; displayed as "Revenue" (REV) or "Payment" (PAY)NoAlways visible
Openbilling_item.open_item_ind; displayed as "Yes" or "No"NoAlways visible
Write-Offbilling_item_detail.write_off_status_cd; badge only for REV rowsNoBadge visible only for REV rows with status WRITTEN_OFF or RECOVERED
DeductionsComputed: hasDeductions across both details of the parent billing item; displayed as "Yes" or "No"NoAlways visible
Gross Amountbilling_item_detail.billing_item_detail_gross_amtNoAlways visible
Deduction TotalComputed: sum of billing_item_deduction.billing_item_deduction_amt where billing_item_deduction_update_net_ind = true for this specific detailNoAlways visible
Net After DedComputed: billing_item_detail_gross_amt - deductionTotal for this detailNoAlways visible
Comm %billing_item_detail.billing_item_detail_percent; displayed as percentage; shown as dash for PAY rowsNoAlways visible
Detail BalanceComputed: billing_item_detail_total_amt - deductionsApplied(A/S) - cashApplied(A/S) for this detailNoAlways visible
Total Amountbilling_item_detail.billing_item_detail_total_amtNoAlways visible
Cash AppliedComputed: sum of cash_receipt_application.cash_receipt_amt_applied for this detail on Approved worksheetsNoAlways visible
Due Datebilling_item.billing_item_due_dtNoAlways visible
CurrentComputed: detailBalance when not yet due or no due date, else 0NoAlways visible
1-30 DaysComputed: detailBalance when 1–30 days past due, else 0NoAlways visible
31-60 DaysComputed: detailBalance when 31–60 days past due, else 0NoAlways visible
61-90 DaysComputed: detailBalance when 61–90 days past due, else 0NoAlways visible
90+ DaysComputed: detailBalance when more than 90 days past due, else 0NoAlways visible

Grid features:

  • Sortable columns: all columns
  • Filters: global text filter across all visible columns
  • Row selection: single-row click — opens Deduction Management dialog if hasDeductions = true
  • Pagination: yes
  • Export: yes; filename ar-aging-detail

Conditional display:

  • Empty state visible when search returned zero detail rows
  • Write-Off badge suppressed on PAY rows (type check: only shown when billingItemDetailTypeCd = 'REV')
  • Detail tab label shows count: "Detail (N)"

**PoC Artifact:**

The PoC shows an empty state message "Detail view is coming soon. Use the Summary tab for now." for the Detail tab when detailData.length === 0. In practice, the getArAgingDetail query runs in parallel with the Summary query and will return data when billing items are found. The empty state message wording is a PoC artifact and should be updated in production.


8.2 Deduction Management Dialog

Route: Modal dialog overlaying /reports/ar-aging (no dedicated route)

Data loading:

  • getBillingItemDeductionData — loads billing_item, both billing_item_detail records, and all billing_item_deduction records for the selected billing_item_id
  • getBillingItemDeductionTypeOptions — loads available deduction type codes for the type dropdown

Dialog Header Region

Summary of the billing item being edited.

Field / ColumnSourceEditable?Condition
Billing Item namebilling_item.billing_item_nameNoAlways visible
Gross AmountrevDetail.billing_item_detail_gross_amtNoAlways visible
Total NetComputed: (revDetail.gross * revDetail.percent) + (payDetail.gross * payDetail.percent)NoAlways visible
Total DeductionComputed: sum of all net-impacting deductions across REV and PAYNoAlways visible; live-updated as user edits
Total BillingComputed: Total Net - Total DeductionNoAlways visible; live-updated as user edits
Currencybilling_item.currency_cdNoAlways visible

REV Deductions Region

Editable deduction rows for the REV (billing_item_detail_type_cd = 'REV') detail.

Field / ColumnSourceEditable?Condition
REV section header (Percent, Net Amt, Total Ded, Billing Amt)Computed from revDetail fields and deduction rowsNoAlways visible when REV detail exists
Deduction Typebilling_item_deduction.billing_item_deduction_type_cd; dropdown from code masterYesPer deduction row
Amountbilling_item_deduction.billing_item_deduction_amtYesPer deduction row
Affects Net (checkbox)billing_item_deduction.billing_item_deduction_update_net_indYesPer deduction row
Commentbilling_item_deduction.commentYesPer deduction row
Delete buttonRemoves deduction row from edit stateN/APer deduction row
Add row button ("+")Adds a blank deduction row for the REV detailN/AVisible on last row only

PAY Deductions Region

Editable deduction rows for the PAY (billing_item_detail_type_cd = 'PAY') detail. Same structure as REV Deductions Region.

Field / ColumnSourceEditable?Condition
PAY section header (Percent, Net Amt, Total Ded, Billing Amt)Computed from payDetail fields and deduction rowsNoAlways visible when PAY detail exists
Deduction Typebilling_item_deduction.billing_item_deduction_type_cdYesPer deduction row
Amountbilling_item_deduction.billing_item_deduction_amtYesPer deduction row
Affects Net (checkbox)billing_item_deduction.billing_item_deduction_update_net_indYesPer deduction row
Commentbilling_item_deduction.commentYesPer deduction row
Delete buttonRemoves deduction row from edit stateN/APer deduction row
Add row button ("+")Adds a blank deduction row for the PAY detailN/AVisible on last row only

Grid features:

  • No pagination — all deduction rows for the billing item shown at once
  • No export

Conditional display:

  • Each detail section always shows at least one row (a blank empty row is pre-populated if no deductions exist for that detail)
  • Save Changes button disabled while a save operation is in progress
  • Loading indicator shown while initial data loads; error message shown if load fails

9. Additional Diagrams

Aging Bucket Calculation Logic

mermaid
flowchart TD
    A[billing_item.billing_item_due_dt] --> B{Due date null?}
    B -->|Yes| C[agingCurrent = totalBalance\nAll other buckets = 0]
    B -->|No| D[daysOverdue = CURRENT_DATE - billing_item_due_dt]
    D --> E{daysOverdue <= 0?}
    E -->|Yes - not yet due| F[agingCurrent = totalBalance]
    E -->|No - overdue| G{1 to 30?}
    G -->|Yes| H[aging1to30 = totalBalance]
    G -->|No| I{31 to 60?}
    I -->|Yes| J[aging31to60 = totalBalance]
    I -->|No| K{61 to 90?}
    K -->|Yes| L[aging61to90 = totalBalance]
    K -->|No| M[aging90Plus = totalBalance]

Balance Exclusion Scope

mermaid
flowchart LR
    subgraph "Included in Balance"
        A[Approved worksheets\nstatus = 'A'\ncurrent_item_ind = true]
        B[Submitted worksheets\nstatus = 'S'\ncurrent_item_ind = true]
    end
    subgraph "Excluded from Balance"
        C[Draft worksheets\nstatus = 'D']
        D[Applied worksheets\nstatus = 'P']
        E[Settled worksheets\nstatus = 'T']
        F[Returned worksheets\nstatus = 'R']
        G[Superseded worksheets\ncurrent_item_ind = false]
    end
    A --> H[Cash applied reduces\naging balance]
    B --> H

10. Cross-References

DocumentRelationship
Billing Items Data ModelDefines billing_item, billing_item_detail, billing_item_deduction, and key fields billing_item.open_item_ind, billing_item.billing_item_due_dt, and billing_item.billing_item_aging_dt (production aging date override) used throughout this workflow
Billing Items QueriesSpecifies getArAgingSummary (2.6.1), getArAgingDetail (2.6.2), aging bucket classification (3.9), and net-after-deductions (3.8)
Billing Items ProceduresSpecifies manageBillingItemDeductions (2.7) triggered by the Deduction Management dialog save
Worksheets WorkflowUpstream: approved worksheet cash applications are what reduce the aging balance displayed here
Write-Offs WorkflowThe AR Aging report surfaces write_off_status_cd and links to write-off packets; write-offs are created and managed in that workflow
Billing Items WorkflowThe billing items visible in the aging report are created and managed in this workflow

11. Gherkin Scenarios

gherkin
Feature: AR Aging Report - Search and Filtering

  Scenario: Default search shows only open items, excluding written-off
    Given the user navigates to /reports/ar-aging
    And the search panel shows "Open Items Only" checked and "Include Written Off" unchecked
    When the user clicks Search without changing any criteria
    Then the Summary tab displays rows only where billing_item.open_item_ind = true
    And no rows are displayed where billing_item_detail.write_off_status_cd = 'WRITTEN_OFF' on the REV detail

  Scenario: Filtering by client narrows results to that client's receivables
    Given the user is on the AR Aging Report page
    And there exist billing items for client "Taylor Swift" (party.party_id = 101) and "Adele" (party.party_id = 202)
    When the user selects "Taylor Swift" in the Client autocomplete filter
    And the user clicks Search
    Then all rows in the Summary tab have clientName = "Taylor Swift"
    And no rows appear for "Adele"
    And the filter applied is billing_item.client_id = 101

  Scenario: Currency filter limits results to single currency
    Given multiple billing items exist in both USD and GBP
    When the user selects "GBP" from the Currency dropdown
    And the user clicks Search
    Then all Summary rows have billing_item.currency_cd = 'GBP'
    And no USD rows appear in the results

  Scenario: Including written-off items reveals WRITTEN_OFF receivables
    Given billing item 500 has its REV detail with write_off_status_cd = 'WRITTEN_OFF'
    And the user has left "Include Written Off" unchecked
    When the user runs a search without the written-off item appearing
    And the user checks "Include Written Off"
    And the user clicks Search again
    Then billing item 500 appears in the Summary results
    And the Write-Off column for that row shows a "Written Off" badge

Feature: AR Aging Report - Aging Bucket Classification

  Scenario: Not-yet-due item appears in Current bucket
    Given billing item 200 has billing_item.billing_item_due_dt = '2026-03-15' (today is 2026-03-02)
    And the outstanding totalBalance for billing item 200 is $10,000.00
    When the user runs a search that includes billing item 200
    Then the Summary row for billing item 200 shows agingCurrent = $10,000.00
    And aging1to30 = $0.00
    And aging31to60 = $0.00
    And aging61to90 = $0.00
    And aging90Plus = $0.00

  Scenario: Item overdue by 45 days appears in 31-60 bucket
    Given billing item 300 has billing_item.billing_item_due_dt = '2026-01-16' (45 days before 2026-03-02)
    And the totalBalance for billing item 300 is $5,000.00
    When the user runs a search that includes billing item 300
    Then the Summary row for billing item 300 shows aging31to60 = $5,000.00
    And agingCurrent = $0.00
    And aging1to30 = $0.00
    And aging61to90 = $0.00
    And aging90Plus = $0.00

  Scenario: Item with no due date defaults to Current bucket
    Given billing item 400 has billing_item.billing_item_due_dt = NULL
    And the totalBalance for billing item 400 is $2,500.00
    When the user runs a search that includes billing item 400
    Then the Summary row for billing item 400 shows agingCurrent = $2,500.00
    And all other aging buckets for that row show $0.00

  Scenario: Item overdue more than 90 days appears in 90+ bucket
    Given billing item 600 has billing_item.billing_item_due_dt = '2025-11-01' (122 days before 2026-03-02)
    And the totalBalance for billing item 600 is $75,000.00
    When the user runs a search that includes billing item 600
    Then the Summary row for billing item 600 shows aging90Plus = $75,000.00
    And agingCurrent = $0.00
    And aging1to30 = $0.00
    And aging31to60 = $0.00
    And aging61to90 = $0.00

Feature: AR Aging Report - Detail View

  Scenario: Detail tab shows separate rows for REV and PAY
    Given billing item 250 has a REV detail (billing_item_detail_id = 501) and a PAY detail (billing_item_detail_id = 502)
    When the user runs a search that includes billing item 250
    And the user clicks the Detail tab
    Then two rows appear for billing item 250: one with billingItemDetailTypeCd = 'REV' and one with billingItemDetailTypeCd = 'PAY'
    And each row shows its own detailBalance computed from billing_item_detail_id-specific cash applications

  Scenario: Write-Off badge suppressed on PAY detail rows
    Given billing item 350 has its REV detail (billing_item_detail_id = 601) with write_off_status_cd = 'WRITTEN_OFF'
    And "Include Written Off" is checked in the search criteria
    When the user runs a search and switches to the Detail tab
    Then the REV row for billing item 350 shows the "Written Off" badge
    And the PAY row for billing item 350 shows no Write-Off badge

Feature: AR Aging Report - Deduction Management

  Scenario: Clicking a row with deductions opens the Deduction Management dialog
    Given billing item 700 has billing_item_deduction records on its REV detail
    And the user has run a search that returns billing item 700
    When the user clicks the row for billing item 700 in the Summary grid
    Then the Deduction Management dialog opens
    And the dialog loads billing_item.billing_item_name for item 700
    And the existing billing_item_deduction records appear in the REV section

  Scenario: Clicking a row without deductions shows a toast
    Given billing item 800 has no billing_item_deduction records on either detail
    And the user has run a search that returns billing item 800 (row shows hasDeductions = false)
    When the user clicks the row for billing item 800 in the Summary grid
    Then the Deduction Management dialog does not open
    And a toast notification appears: "This billing item has no deductions"

  Scenario: Adding a new deduction and saving updates deductions in-place
    Given billing item 900 exists with current_item_ind = true and no existing billing_item_deduction records
    And the user opens the Deduction Management dialog for billing item 900
    When the user enters deduction type 'B' (Bank Charge), amount $250.00, Affects Net checked
    And the user clicks Save Changes
    Then manageBillingItemDeductions is invoked for billing_item_id = 900
    And billing_item.current_item_ind for item 900 remains true
    And no new billing_item is created
    And the REV detail has a billing_item_deduction with billing_item_deduction_type_cd = 'B' and billing_item_deduction_amt = 250.00
    And a success toast appears: "Deductions saved successfully"

  Scenario: Saving with no changes does not mutate the database
    Given billing item 950 has one existing billing_item_deduction on its REV detail with type 'D' and amount $100.00
    And the user opens the Deduction Management dialog for billing item 950
    And the user makes no changes to the deduction rows
    When the user clicks Save Changes
    Then manageBillingItemDeductions is invoked with the same deduction set
    And no deduction records are changed
    And billing_item.current_item_ind for item 950 remains true
    And a success toast appears

Feature: AR Aging Report - Balance Accuracy

  Scenario: Draft worksheet cash is not subtracted from aging balance
    Given billing item 1000 has a REV detail with billing_item_detail_total_amt = $5,000.00
    And there is a cash_receipt_application for $2,000.00 against that REV detail on a worksheet with cash_receipt_worksheet_status_cd = 'D'
    When the user runs a search that includes billing item 1000
    Then the Total Balance for billing item 1000 reflects the full $5,000.00 (plus PAY detail balance)
    And the $2,000.00 Draft application is not subtracted

  Scenario: Approved worksheet cash reduces the aging balance
    Given billing item 1000 has a REV detail with billing_item_detail_total_amt = $5,000.00
    And there is a cash_receipt_application for $2,000.00 against that REV detail on a worksheet with cash_receipt_worksheet_status_cd = 'A' and current_item_ind = true
    When the user runs a search that includes billing item 1000
    Then the REV portion of the Total Balance for billing item 1000 is $3,000.00

Confidential. For internal use only.