Skip to main content

SPEC 014 — Reports

FieldValue
StatusDRAFT
PriorityP1 — Core Platform
BackendNone dedicated — data sourced from cap table, ESOP, and holdings entities via shared loaders
Frontendequa-web/src/modules/reports/

1. Feature Purpose

Reports provide organization administrators and stakeholders with tabular views of cap table data. Three report types are available: Holder Report (per-member ownership summary), Pools Report (option pool and convertible instrument utilization), and Holdings Report (individual certificate-level detail). All reports support dynamic filtering, sortable columns, and footer totals. Reports are read-only views over existing cap table data — no dedicated backend module exists.

2. Current State (Verified)

2.1 Holder Report

DetailValue
Fileequa-web/src/modules/reports/pages/holder-report.tsx
ComponentHolderReportsPage
Data loaderloadCaptableData('false')
PermissionBuiltInPermission.viewCapTable OR BuiltInPermission.viewSelf
HOC chainwithPermissionswithLoadingCachedMultiple
ColumnsHolder (avatar + name link), Investment (USD), Outstanding (shares), Ownership (%), Fully Diluted (shares), Ownership (%)
FooterRow totals across all columns
Row filteringExcludes members with no holdings and no voided records
Page size100,000 (effectively unbounded)
Member linkOpens in new tab via organizationMemberPath

2.2 Pools Report

DetailValue
Fileequa-web/src/modules/reports/pages/pools-report.tsx
ComponentPoolsReportsPage
Data loaderloadCaptablePools
PermissionBuiltInPermission.viewCapTable OR BuiltInPermission.viewSelf
ColumnsPool (name link), Equity (link), Type, Authorized, Treasury, Investment (USD), Fully Diluted, Ownership (%)
FiltersEquity (dynamic from pool data), Type (Incentive / Convertible)
Filter componentdynamicFilter shared component
FooterRow totals for Authorized, Treasury, Investment, Fully Diluted, Ownership
Pool linkLinks to poolPath for incentive pools, viewConvertibleInstrumentPath for convertibles

2.3 Holdings Report

DetailValue
Fileequa-web/src/modules/reports/pages/holdings-report.tsx
ComponentHoldingsReportsPage
Data loaderloadQueryData
PermissionBuiltInPermission.viewCapTable OR BuiltInPermission.viewSelf
ColumnsCert ID (certificate image + link), Holder (link), Class, # (index), Type, Issue Date, Investment (USD), Outstanding, Ownership (%), Fully Diluted, Ownership (%)
FiltersHolder (dynamic), Class (dynamic), Type (dynamic)
ExclusionsConvertible instruments, pools, and org assets are filtered out of the holdings list
FooterRow totals for Investment, Outstanding, Ownership, Fully Diluted, Ownership

2.4 Module Exports

FileExports
pages/index.tsHolderReportsPage, PoolsReportsPage, HoldingsReportsPage (re-exported from page files)
index.tsRe-exports all pages

3. Data Model

Reports do not maintain their own data model. They consume data from:

Source: Cap Table Dashboard (GetCaptableDashboardResponse)

FieldTypeUsed in
membersArray of member records with holdings, voided, memberInvestment, memberOutstanding, memberOutstandingPer, memberFullyDiluted, memberFullyDilutedPerHolder Report

Source: Pools Report (GetPoolsReportResponse)

FieldTypeUsed in
poolsArray with name, equity, equityName, internalType, authorized, treasury, investment, fullyDiluted, ownership, plan, hashPools Report

Source: Holdings Query (QueryResponse)

FieldTypeUsed in
holdingArray with name, holderName, class, internalType, issueDate, capitalContribution, outstanding, outstandingPercentage, fullyDiluted, fullyDilutedPercentage, holdingType, ownerHoldings Report

Enums Referenced

EnumValuesUsed in
HoldingRecordTypeconvertibleInstrument, pool, orgAsset (excluded from holdings report)Holdings Report filter
BuiltInPermissionviewCapTable, viewSelfAll reports

4. API Endpoints

Reports consume existing cap table endpoints — no dedicated report endpoints exist.
MethodPathAuthDescriptionUsed by
GET/api/v1/organization/:organization/captable/dashboardSession + permissionCap table member summaryHolder Report
GET/api/v1/organization/:organization/captable/poolsSession + permissionPool summary dataPools Report
GET/api/v1/organization/:organization/captable/querySession + permissionIndividual holdings with certificate dataHoldings Report

5. Frontend Components

Module: equa-web/src/modules/reports/

ComponentFilePurpose
HolderReportsPagepages/holder-report.tsxTable of holders with investment, outstanding, ownership, fully diluted
PoolsReportsPagepages/pools-report.tsxTable of option pools and convertible instruments with utilization metrics
HoldingsReportsPagepages/holdings-report.tsxTable of individual holdings/certificates with filtering by holder, class, type

Shared Dependencies

ComponentSourcePurpose
Table@components/tables/tableBase table component with sorting, pagination, borders
dynamicFilter@shared/components/dynamic-filterCreates filter dropdowns from data
PathLink@components/navigationNavigation links to member/security/pool pages
Avatar@components/avatarMember avatar in holder report rows
CertificateLink@modules/captable/componentsCertificate image + link in holdings report
withPermissions@shared/hocs/with-permissionsPermission guard HOC
withLoadingCachedMultiple@components/loadingData loading with caching
PageContent / PageContentHeader@components/pagesPage layout wrappers

6. Business Rules

  1. Permission gating: All three reports require either viewCapTable or viewSelf permission. Users with viewSelf see only their own holdings in the data.
  2. Holder Report excludes members who have no holdings AND no voided records (i.e., members with zero equity involvement are hidden).
  3. Holdings Report excludes records of type convertibleInstrument, pool, and orgAsset — these appear in their own dedicated views.
  4. Pools Report classifies pools as either Incentive (ESOP plans) or Convertible (convertible instruments).
  5. Ownership percentages are displayed to 2 decimal places (.toFixed(2)).
  6. Investment values are formatted as USD using optionalUsdString.
  7. Share counts use toCommaFloat for comma-separated formatting.
  8. Footer totals aggregate across all visible rows (post-filter).
  9. All links from reports open in a new browser tab (target='_blank').
  10. Page size is set to 100,000 across all reports — effectively no client-side pagination.

7. Acceptance Criteria

  • Holder Report displays all members with equity involvement, with correct investment, outstanding, ownership, and fully diluted values
  • Holder Report footer shows accurate totals for all numeric columns
  • Pools Report displays incentive pools and convertible instruments with correct metrics
  • Pools Report filter by Equity and Type works correctly
  • Holdings Report displays individual certificates with all metadata fields
  • Holdings Report filter by Holder, Class, and Type works correctly
  • All percentage columns display 2 decimal places
  • All USD columns are properly formatted
  • Links to members, securities, pools, and certificates open in new tabs
  • Users without viewCapTable or viewSelf permission cannot access reports
  • Reports load successfully for organizations with large cap tables (1000+ holdings)

8. Risks

RiskImpactMitigation
No server-side pagination (page size 100,000)Browser performance with very large cap tablesImplement server-side pagination or virtual scrolling for 1000+ rows
No CSV/PDF exportUsers must screenshot or copy data manuallyAdd export functionality (referenced in platform feature list but not yet implemented in reports module)
Permission check uses OR logic (viewCapTable OR viewSelf)viewSelf users may see aggregate totals that reveal other holders’ dataEnsure server filters response data to only the requesting user’s holdings when viewSelf is the sole permission
Reports pull full cap table data on each page loadRedundant API calls when switching between report typesShared data cache across report pages within same session
No date range filteringCannot view historical snapshotsFuture enhancement: add as-of-date parameter for point-in-time reporting