Skip to content

feat: add organisation-level portfolio API support#259

Open
patjlm wants to merge 3 commits intolasconic:mainfrom
patjlm:feat/organization-api
Open

feat: add organisation-level portfolio API support#259
patjlm wants to merge 3 commits intolasconic:mainfrom
patjlm:feat/organization-api

Conversation

@patjlm
Copy link

@patjlm patjlm commented Mar 2, 2026

Summary

Relates to #132.

Finary's backend exposes /organizations/{org_id}/… endpoints for household/family accounts. Without this, a user in a couple sees only their own accounts via /users/me/…, missing their partner's PEA, AV, PER, livrets, etc.

This PR adds minimal, additive support for the organisation-level endpoints.


New file: finary_uapi/user_organizations.py

Seven functions covering all organisation-level data endpoints:

Function Endpoint Result shape
get_organization_investments /organizations/{id}/portfolio/investments dict (aggregated, mirrors get_portfolio_investments)
get_organization_securities /organizations/{id}/securities list
get_organization_cryptos /organizations/{id}/cryptos list
get_organization_fonds_euro /organizations/{id}/fonds_euro list
get_organization_real_estates /organizations/{id}/real_estates list
get_organization_scpis /organizations/{id}/scpis list
get_organization_holdings_accounts /organizations/{id}/holdings_accounts list

Note: get_organization_investments returns a dict (same shape as get_portfolio_investments in user_portfolio.py) because /portfolio/investments is the only available investments endpoint at the org level. All other functions return flat lists.

New helper: get_family_org_id() in user_me.py

Calls the already-existing get_user_me_organizations() and returns the ID of the first organisation with organization_type == "family", or None for single-user accounts.

CLI: --org-id option on read commands

All read commands that have an org-level equivalent now accept --org-id:

finary_uapi fonds_euro [--org-id=<org_id>]
finary_uapi investments [--org-id=<org_id>]
finary_uapi cryptos [--org-id=<org_id>]
finary_uapi securities [--org-id=<org_id>]
finary_uapi holdings_accounts [...] [--org-id=<org_id>]
finary_uapi real_estates [--org-id=<org_id>]
finary_uapi scpis [--org-id=<org_id>]

--org-id accepts either a UUID or the keyword family, which auto-resolves via get_family_org_id(). Write operations (add, update, delete) are unaffected.

New integration test: test_organizations in tests/test_get.py

  • Skips cleanly (pytest.skip) when the account has no family organisation — single-user accounts are unaffected
  • Asserts isinstance(result["result"], list) for all flat-list endpoints, and dict for get_organization_investments, so wrong endpoint shapes are caught immediately

Remaining gaps (out of scope, for follow-up)

The following org-level endpoints exist and return data, but are not included here because they require pagination and filtering parameters (mirroring the complexity of their counterparts in user_portfolio.py):

Endpoint Notes
portfolio/investments/dividends works, returns dict
portfolio/investments/transactions works, paginated list
portfolio/checking_accounts/transactions works, paginated list
portfolio/credit_accounts/transactions works

The following endpoints do not exist at the org level (404):

  • portfolio/cryptos/distribution
  • portfolio/crowdlendings/distribution

Design notes

  • All changes are purely additive — no existing function signatures or behaviours are modified
  • The approach matches the design option discussed in Add support for Organisation #132: expose org-level functions explicitly rather than auto-switching inside existing functions. Callers opt in by using get_family_org_id() + the new get_organization_* functions, or via --org-id family on the CLI
  • Single-user accounts are unaffected: get_family_org_id returns None and the new module is never imported unless explicitly used
  • Validated against a real family account: get_organization_holdings_accounts returns 40 accounts, get_organization_real_estates returns 2 properties, get_organization_fonds_euro returns 6 funds, get_organization_securities returns 26 securities

Test plan

  • Run existing test suite — all existing tests pass unchanged
  • On an account with a family organisation: test_organizations passes with correct list/dict shapes verified
  • On a single-user account: test_organizations skips cleanly
  • CLI: finary_uapi holdings_accounts --org-id family returns combined household accounts

patjlm and others added 3 commits March 2, 2026 11:41
Add `user_organizations.py` with 6 functions mirroring the
`/organizations/{org_id}/…` endpoints (investments, cryptos,
fonds_euro, real_estates, scpis, holdings_accounts).

Add `get_family_org_id()` convenience helper to `user_me.py` that
resolves the family organisation ID from the authenticated user's
organisations list.

Add `test_organizations` integration test that skips cleanly when
the account has no family organisation.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- get_organization_cryptos: use /cryptos (flat list) instead of
  /portfolio/cryptos (aggregated dict) — consistent with all other
  flat-list org functions
- get_organization_investments: keep as /portfolio/investments (dict),
  mirrors get_portfolio_investments in user_portfolio.py; add docstring
  to make the return shape explicit
- get_organization_securities: new function for /securities (flat list),
  the actual flat-list counterpart to investments

Test: assert isinstance(result["result"], list) for all flat-list
functions and dict for get_organization_investments, so wrong endpoint
shapes are caught immediately.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Adds an optional --org-id flag to all commands that have an
organisation-level equivalent:

  finary_uapi fonds_euro [--org-id=<org_id>]
  finary_uapi investments [--org-id=<org_id>]
  finary_uapi cryptos [--org-id=<org_id>]
  finary_uapi securities [--org-id=<org_id>]
  finary_uapi holdings_accounts [...] [--org-id=<org_id>]
  finary_uapi real_estates [--org-id=<org_id>]
  finary_uapi scpis [--org-id=<org_id>]

Accepts either a UUID or the keyword 'family', which auto-resolves
via get_family_org_id(). When --org-id is provided the command routes
to the corresponding get_organization_*() function; otherwise falls
through to the existing /users/me/ behaviour. Write operations
(add, update, delete) are unaffected.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant