Objective
- Make business segments easy to see and filter in Reports Hub exports (job-level and line-item), so finance and ops can segment revenue the same way they do in scheduling and admin tooling.
- Expand collection linking so report builders can combine the collections they need (e.g. inspections + contacts + payments + charges) without hitting dead ends in the link picker — aligned with revenue-reconciliation and pricing-analysis workflows from product feedback.
Background
- Product feedback from the Andy/Erin Reports Hub session (see related issues ATT-1856, ATT-1857, ATT-1859) identified revenue breakdown, categories, fees, and client identity as gaps. These two items were called out as additional needs on top of that list.
- Business segments today: Segment data exists on services (
_segmentId→ business segment name) and is partially wired into exports, but it is not surfaced well for end users: - On inspections,
businessSegmentIdinattik-backend/src/config/exportFieldDefinitions.tsispickerHidden: trueand resolves from the primary service’s segment via charges (aggregationPath: '__primarySegmentId'inaggregationHelpers.ts) — usable for aggregation group-by, not obvious as a column. - There is no inspection-level “Business Segment” name column in the picker (unlike services
segmentand chargescatalogSegment). - Charge rows expose
catalogSegment(name) and a filter-onlycatalogBusinessSegmentId; segment appears reliably only when charges are the expanded linked collection. BusinessSegmentmodel is referenced inattik-backend/src/util/functions/dataExports/metadata.ts(getModelForEntity('businessSegments')) but business segments are not an allowed base/linked entity inALLOWED_ENTITIES/entityFieldDefinitions.- Collection linking today: Joins are governed by
referenceableFromBaseinexportFieldDefinitions.ts, with resolvers ingetRelatedIdsandreverseLookupConfig(joinedReport.ts,metadata.ts). The matrix is intentionally sparse, not N×N: contactsbase →[]— no linked collections from a contact-base report.- Inspections link people, payments, charges, etc., but not contacts directly (contacts are reached via people or reverse lookup from contacts → inspections).
- Payments link only inspections + contacts; not charges, fees, or people.
- People base omits services and reports “until reverse lookups support mixed job ids without ambiguity” (comment in
referenceableFromBase). - Prior ticket ATT-1489 (Done) flagged that inspections + contacts could not be re-associated when editing an older revenue reconciliation report — evidence that the link matrix blocks real user workflows.
- Full relationship map and maintenance rules:
attik-backend/src/util/functions/dataExports/RELATIONSHIPS.md(must stay in sync withreferenceableFromBase,getRelatedIds,reverseLookupConfig).
Scope
Frontend (attik-frontend/src/app/tools/data-exports/)
- Link collections UI (
CreateReportForm.tsx,ReportColumnsSection.tsx,useDataExportsEntities.ts) readsreferenceableFromfromGET /data-exports/entities?include=referenceable— any backend matrix expansion must be reflected here automatically; verify multi-link + column picker UX when many collections are linkable. - Filters (
ReportFiltersSection.tsx) already has abusiness-segmentAPI endpoint branch — ensure new segment columns/filters use consistent labels with admin business-segment settings.
Backend — Business segments
attik-backend/src/config/exportFieldDefinitions.ts— add or unhide user-facing fields, e.g. Primary Business Segment (name) on inspections and/or quotes, resolved from primary charge → service →_segmentId→ segment name (mirror__primarySegmentIdpipeline inaggregationHelpers.tsand chargecatalogSegmentinjoinedReport.ts).- Consider exposing segment on payments-linked job context if product wants fee reports segmented by line of business.
- Decision needed: Job-level primary segment only vs. segment per charge line (already on charges) vs. both.
- Decision needed: Whether to add business segments as a first-class export entity (segment catalog report) or only resolved names on jobs/charges.
Backend — Collection linking (“all collections ↔ all collections”)
- Treat “all mapped with all” as product intent — implement as expanding
referenceableFromBaseplus resolvers, not a single generic join engine (unless dev chooses otherwise). - Key gaps to close (non-exhaustive; dev to produce full matrix diff):
- Contacts base: allow links to inspections, quotes, people, payments (via reverse paths already partially in
reverseLookupConfig.contacts). - Inspections base: allow direct contacts link (today: people only) if product prefers contact fields without people row expansion.
- Payments ↔ charges/fees, reports ↔ quotes, services ↔ payments, etc. — audit each
ALLOWED_ENTITIESpair against user stories. - Each new pair requires: entry in
referenceableFromBase, resolver ingetRelatedIdsorreverseLookupConfig, row-expansion rule inexpandRowsByRelatedif one-to-many, filter path infilterBuilders.ts, and RELATIONSHIPS.md update. - Decision needed: True full N×N vs. “complete the matrix for the 12 export entities” with documented exclusions (e.g. circular multi-hop).
- Decision needed: Behavior when linking multiple expandable collections (charges + people + payments) — row cartesian product limits / performance caps.
Product / architecture decisions (for the implementer)
- Decision needed: Priority order — segment surfacing (likely smaller) vs. link matrix (larger) vs. bundling with ATT-1856/1857/1859.
- Decision needed: Backward compatibility for saved reports that reference link pairs removed or renamed.
References
- Related: ATT-1856 — Break out base price and add-ons in Reports Hub
- Related: ATT-1857 — Add category columns for core and standalone reporting
- Related: ATT-1859 — Expose convenience and PAC fees in Reports Hub
- Related: ATT-1489 — Reports Hub collection associations (done; context for inspections ↔ contacts pain)
- Field defs:
attik-backend/src/config/exportFieldDefinitions.ts - Join config:
attik-backend/src/util/functions/dataExports/joinedReport.ts,metadata.ts - Relationship doc:
attik-backend/src/util/functions/dataExports/RELATIONSHIPS.md