16 KiB
Tasks: Portfolio Performance Views
Input: Design documents from /specs/003-portfolio-performance-views/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/api-contracts.md, quickstart.md
Tests: Not explicitly requested in the feature specification. Test tasks are omitted.
Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Phase 1: Setup (Shared Infrastructure)
Purpose: Shared interfaces, types, pipes, and route scaffolding that all views depend on
- T001 [P] Add IPerformanceRow, IEntityPerformanceRow, IPortfolioSummary interfaces in libs/common/src/lib/interfaces/family-office.interface.ts
- T002 [P] Add IAssetClassPerformanceRow, IAssetClassSummary interfaces in libs/common/src/lib/interfaces/family-office.interface.ts
- T003 [P] Add IActivityRow, IActivityDetail interfaces in libs/common/src/lib/interfaces/family-office.interface.ts
- T004 [P] Extend K1Data interface with beginningTaxBasis, endingTaxBasis, endingGLBalance, k1CapitalAccount, otherAdjustments, activityNotes fields in libs/common/src/lib/interfaces/k-document.interface.ts
- T005 [P] Add FamilyOfficeAssetType-to-display-label mapping utility in libs/common/src/lib/utils/ or libs/common/src/lib/helper.ts
- T006 [P] Create accounting number format pipe (comma separators, parentheses for negatives, dash for zero) in apps/client/src/app/pipes/accounting-number.pipe.ts
- T007 Create portfolio-views page scaffold with route config in apps/client/src/app/pages/portfolio-views/portfolio-views-page.routes.ts
- T008 Register portfolio-views lazy route in apps/client/src/app/app.routes.ts
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Backend service helper methods reused across all three endpoints
⚠️ CRITICAL: No user story endpoint work can begin until this phase is complete
- T009 Implement helper method to determine partnership asset class from majority PartnershipAsset.assetType in apps/api/src/app/family-office/family-office.service.ts
- T010 Implement helper method to build entity-level aggregated cash flows (contributions, distributions, terminal NAV) for XIRR in apps/api/src/app/family-office/family-office.service.ts
- T011 Implement helper method to compute IPerformanceRow (Original Commitment, Paid-In, Unfunded, Distributions, Residual, DPI, RVPI, TVPI, IRR) from aggregated membership/distribution/valuation data in apps/api/src/app/family-office/family-office.service.ts
- T012 [P] Implement helper method to map K1Data fields to Activity row income components (capitalGains, remainingK1IncomeDed, totalIncome) in apps/api/src/app/family-office/family-office.service.ts
- T013 [P] Implement helper method to compute Activity row derived fields (bookToTaxAdj, k1CapitalVsTaxBasisDiff, excessDistribution, negativeBasis, deltaEndingBasis) in apps/api/src/app/family-office/family-office.service.ts
Checkpoint: Foundation ready — user story implementation can now begin in parallel
Phase 3: User Story 1 — Portfolio Summary View (Priority: P1) 🎯 MVP
Goal: Entity-level rollup table showing Original Commitment, % Called, Unfunded, Paid-In, Distributions, Residual, DPI, RVPI, TVPI, IRR per entity with "All Entities" totals row
Independent Test: Navigate to /portfolio-views, verify Portfolio Summary tab shows one row per entity with correct metrics and a totals row
Implementation for User Story 1
- T014 [US1] Add GET /family-office/portfolio-summary endpoint with valuationYear query param and JWT + readEntity guard in apps/api/src/app/family-office/family-office.controller.ts
- T015 [US1] Implement getPortfolioSummary(userId, valuationYear) method in apps/api/src/app/family-office/family-office.service.ts — load entities, active memberships, distributions ≤ year-end, latest valuations ≤ year-end; compute IPerformanceRow per entity; compute totals row; return IPortfolioSummary
- T016 [US1] Add fetchPortfolioSummary(params?) method in apps/client/src/app/services/family-office-data.service.ts
- T017 [US1] Build Portfolio Summary tab in portfolio-views page — mat-table with columns: Entity, Original Commitment, % Called, Unfunded Commitment, Paid-In, Distributions, Residual Used, DPI, RVPI, TVPI, IRR in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T018 [US1] Add valuation year dropdown filter at page level (default: current year) that triggers data reload in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T019 [US1] Add "All Entities" sticky totals row at table bottom with bold styling in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T020 [US1] Apply accounting number pipe to all monetary columns, percent pipe to % Called, decimal pipe to DPI/RVPI/TVPI, percent pipe or "N/A" to IRR in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T021 [US1] Add row click handler to navigate to /entities/:id detail page in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T022 [US1] Style Portfolio Summary table — zero/dash display for empty entities, loading spinner during fetch in apps/client/src/app/pages/portfolio-views/portfolio-views-page.scss
Checkpoint: Portfolio Summary tab is fully functional — entity rows display correct metrics, totals row aggregates, entity click navigates to detail page, valuation year filter recalculates metrics
Phase 4: User Story 2 — Asset Class Summary View (Priority: P2)
Goal: Asset-class-level rollup table showing the same financial metrics grouped by FamilyOfficeAssetType with "All Asset Classes" totals row
Independent Test: Navigate to /portfolio-views, switch to Asset Class Summary tab, verify one row per asset class with correct metrics and totals row
Implementation for User Story 2
- T023 [US2] Add GET /family-office/asset-class-summary endpoint with valuationYear query param and JWT + readEntity guard in apps/api/src/app/family-office/family-office.controller.ts
- T024 [US2] Implement getAssetClassSummary(userId, valuationYear) method in apps/api/src/app/family-office/family-office.service.ts — load partnerships with assets + memberships + distributions + valuations; determine asset class per partnership via helper (T009); group by asset class; compute IPerformanceRow per class; compute totals row; return IAssetClassSummary
- T025 [US2] Add fetchAssetClassSummary(params?) method in apps/client/src/app/services/family-office-data.service.ts
- T026 [US2] Build Asset Class Summary tab in portfolio-views page — mat-table with columns: Asset Class, Original Commitment, % Called, Unfunded Commitment, Paid-In, Distributions, Residual Used, DPI, RVPI, TVPI, IRR in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T027 [US2] Add "All Asset Classes" sticky totals row with bold styling in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T028 [US2] Apply accounting number pipe and display label mapping (FamilyOfficeAssetType → display name) to all columns in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T029 [US2] Display zeros/dashes for asset classes with no investments in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T030 [US2] Add row click handler to expand or drill down into partnerships within the selected asset class in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
Checkpoint: Asset Class Summary tab is fully functional — asset class rows display correct metrics, unassigned partnerships fall to "Other", totals row aggregates, drill-down shows partnerships
Phase 5: User Story 3 — Activity Detail View (Priority: P3)
Goal: Full transaction-level ledger showing per year/entity/partnership K-1 income components, tax basis, distributions, derived flags (negative basis, excess distributions), with entity/partnership/year filtering and pagination
Independent Test: Navigate to /portfolio-views, switch to Activity tab, verify rows show all financial columns, filter by entity/partnership/year, check negative basis rows are highlighted
Implementation for User Story 3
- T031 [US3] Add GET /family-office/activity endpoint with entityId, partnershipId, year, skip, take query params and JWT + readEntity guard in apps/api/src/app/family-office/family-office.controller.ts
- T032 [US3] Implement getActivity(userId, filters) method in apps/api/src/app/family-office/family-office.service.ts — join PartnershipMembership + KDocument + Distribution per (entity, partnership, year); map K1Data to income columns using helper (T012); compute derived fields using helper (T013); apply filters; paginate; return IActivityDetail with filter option lists
- T033 [US3] Add fetchActivity(params?) method in apps/client/src/app/services/family-office-data.service.ts
- T034 [US3] Build Activity Detail tab in portfolio-views page — mat-table with columns: Year, Entity, Partnership, Beg Basis, Contributions, Interest, Dividends, Cap Gains, Remaining K-1 Income/Ded, Total Income, Distributions, Other Adj, Ending Tax Basis, Ending GL Balance, Book-to-Tax Adj, Ending K-1 Capital Account, K-1 Capital vs Tax Basis Diff, Excess Distribution, Negative Basis?, Δ Ending Basis, Notes in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T035 [US3] Add entity dropdown filter populated from IActivityDetail.filters.entities in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T036 [US3] Add partnership dropdown filter populated from IActivityDetail.filters.partnerships in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T037 [US3] Add year dropdown filter populated from IActivityDetail.filters.years in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T038 [US3] Implement pagination (mat-paginator) with skip/take params synced to API in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T039 [US3] Add conditional row highlighting — red/warning background for rows where negativeBasis is true in apps/client/src/app/pages/portfolio-views/portfolio-views-page.scss
- T040 [US3] Add visual flag for excess distribution values > 0 (bold text or icon indicator) in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T041 [US3] Apply accounting number pipe to all monetary columns, display "YES"/"" for negativeBasis boolean in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T042 [US3] Enable horizontal scroll for wide table on smaller viewports in apps/client/src/app/pages/portfolio-views/portfolio-views-page.scss
Checkpoint: Activity Detail tab is fully functional — all K-1 columns display, filters work, pagination works, negative basis and excess distribution rows are visually flagged
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Improvements affecting multiple user stories
- T043 [P] Add navigation link to /portfolio-views in the application sidebar/navigation menu
- T044 [P] Add loading spinners for all three tabs during data fetch in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T045 [P] Add empty state messaging when valuation year has no data in apps/client/src/app/pages/portfolio-views/portfolio-views-page.html
- T046 Verify all three tabs share the page-level valuation year filter correctly in apps/client/src/app/pages/portfolio-views/portfolio-views-page.component.ts
- T047 Run quickstart.md validation — verify all data flows work end-to-end
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies — can start immediately
- Foundational (Phase 2): Depends on T001–T004 (interfaces); T009–T013 BLOCK all user stories
- User Stories (Phase 3–5): All depend on Foundational phase completion
- User stories can proceed in parallel (if staffed) or sequentially in priority order (P1 → P2 → P3)
- Polish (Phase 6): Depends on all desired user stories being complete
User Story Dependencies
- User Story 1 (P1): Can start after Phase 2 — no dependencies on other stories
- User Story 2 (P2): Can start after Phase 2 — no dependencies on US1 (shares same page component file but different tab section)
- User Story 3 (P3): Can start after Phase 2 — no dependencies on US1/US2 (different tab, different API endpoint)
Within Each User Story
- Backend endpoint + service (T014–T015, T023–T024, T031–T032) before client data service method (T016, T025, T033)
- Client data service method before UI implementation (T017+, T026+, T034+)
- Core table rendering before styling, formatting, and interaction (click handlers, filters, pagination)
Parallel Opportunities
- T001–T006 (Setup) — all marked [P], can run in parallel
- T009–T013 (Foundational) — T012 and T013 can run in parallel with each other; T009–T011 are sequential
- T014 + T023 + T031 (controller endpoints) — in different sections of same file, can be parallelized carefully
- T016 + T025 + T033 (client service methods) — in different sections of same file, can be parallelized carefully
Parallel Example: User Story 1
# Launch interface + pipe work in parallel:
Task T001: "Add IPerformanceRow, IEntityPerformanceRow, IPortfolioSummary interfaces"
Task T006: "Create accounting number format pipe"
# Once interfaces exist, launch backend + client service in parallel:
Task T014: "Add GET /portfolio-summary endpoint" (depends on T001)
Task T015: "Implement getPortfolioSummary service" (depends on T001, T010, T011)
Task T016: "Add fetchPortfolioSummary client method" (depends on T001)
# Once service methods exist, build UI:
Task T017–T022: Sequential UI implementation
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1: Setup (T001–T008)
- Complete Phase 2: Foundational (T009–T013)
- Complete Phase 3: User Story 1 (T014–T022)
- STOP and VALIDATE: Navigate to /portfolio-views, verify entity rollup table, totals row, year filter, entity click navigation
- Deploy/demo if ready — user has the Portfolio Summary view working
Incremental Delivery
- Complete Setup + Foundational → Foundation ready
- Add User Story 1 → Test independently → Deploy (MVP: Portfolio Summary)
- Add User Story 2 → Test independently → Deploy (+ Asset Class Summary)
- Add User Story 3 → Test independently → Deploy (+ Activity Detail)
- Polish → Navigation, loading states, empty states
- Each story adds value without breaking previous stories
Sequential Single-Developer Strategy
- T001–T008 (Setup) → commit
- T009–T013 (Foundational) → commit
- T014–T022 (Portfolio Summary) → commit + validate
- T023–T030 (Asset Class Summary) → commit + validate
- T031–T042 (Activity Detail) → commit + validate
- T043–T047 (Polish) → commit + validate
Notes
- [P] tasks = different files, no dependencies on concurrent tasks
- [US1/US2/US3] label maps task to specific user story for traceability
- All three tabs live on the same page component — coordinate tab index and shared state (valuation year)
- The accounting number pipe is critical infrastructure — must be correct before UI work begins
- Existing
FamilyOfficePerformanceCalculatorhandles XIRR/TVPI/DPI/RVPI — no changes needed - K1Data JSON extension is backward-compatible (all new fields optional)
- Commit after each task or logical group
- Stop at any checkpoint to validate story independently