diff --git a/finary_uapi/__main__.py b/finary_uapi/__main__.py index 4ca71c6..e7f4fe2 100644 --- a/finary_uapi/__main__.py +++ b/finary_uapi/__main__.py @@ -7,16 +7,16 @@ finary_uapi organizations finary_uapi timeseries finary_uapi checking_accounts transactions [--page=] [--perpage=] [--account=] [--institution=] [--query=] [--start-date=] [--end-date=] [--marked=] - finary_uapi fonds_euro + finary_uapi fonds_euro [--org-id=] finary_uapi startups - finary_uapi investments + finary_uapi investments [--org-id=] finary_uapi investments dividends finary_uapi investments transactions [--page=] [--perpage=] [--account=] [--institution=] [--query=] [--start-date=] [--end-date=] [--marked=] finary_uapi crowdlendings finary_uapi crowdlendings distribution finary_uapi crowdlendings add finary_uapi crowdlendings delete - finary_uapi cryptos + finary_uapi cryptos [--org-id=] finary_uapi cryptos distribution finary_uapi cryptos add finary_uapi cryptos update @@ -25,7 +25,7 @@ finary_uapi precious_metals finary_uapi precious_metals add finary_uapi precious_metals delete - finary_uapi holdings_accounts [crypto | stocks | crowdlending | ] + finary_uapi holdings_accounts [crypto | stocks | crowdlending | ] [--org-id=] finary_uapi holdings_accounts add (crypto | stocks | crowdlending) finary_uapi holdings_accounts add (checking | saving) finary_uapi holdings_accounts delete @@ -40,18 +40,18 @@ finary_uapi fiat_currency search QUERY finary_uapi institutions search QUERY finary_uapi securities search QUERY - finary_uapi securities + finary_uapi securities [--org-id=] finary_uapi securities add finary_uapi securities delete finary_uapi credit_accounts transactions [--page=] [--perpage=] [--account=] [--institution=] [--query=] [--start-date=] [--end-date=] [--marked=] - finary_uapi real_estates + finary_uapi real_estates [--org-id=] finary_uapi real_estates add rent
[] finary_uapi real_estates add
[] finary_uapi real_estates update rent finary_uapi real_estates update finary_uapi real_estates delete finary_uapi scpis search QUERY - finary_uapi scpis + finary_uapi scpis [--org-id=] finary_uapi watches search QUERY finary_uapi import crowdlending_csv FILENAME [-d] [-f] finary_uapi import cryptocom FILENAME [(--new=NAME | --edit=account_id | --add=account_id)] @@ -73,6 +73,7 @@ --start-date= Start date for transactions (format: YYYY-MM-DD) --end-date= End date for transactions (format: YYYY-MM-DD) --marked= Filter marked transactions (true or false) + --org-id= Organisation ID (UUID or 'family' to auto-resolve); queries org-level endpoint instead of /users/me/ """ # noqa @@ -142,7 +143,16 @@ update_user_crypto_by_code, ) from .user_fonds_euro import get_user_fonds_euro -from .user_me import get_user_me, get_user_me_institution_connections +from .user_me import get_family_org_id, get_user_me, get_user_me_institution_connections +from .user_organizations import ( + get_organization_cryptos, + get_organization_fonds_euro, + get_organization_holdings_accounts, + get_organization_investments, + get_organization_real_estates, + get_organization_scpis, + get_organization_securities, +) from .user_precious_metals import ( add_user_precious_metals_by_name, delete_user_precious_metals, @@ -170,6 +180,12 @@ def main() -> int: # pragma: nocover result = signin(args["MFA_CODE"]) else: session = prepare_session() + org_id = args["--org-id"] + if org_id == "family": + org_id = get_family_org_id(session) + if org_id is None: + print("Error: no family organisation found for this account", file=sys.stderr) + return 1 if args["me"]: result = get_user_me(session) elif args["institution_connections"]: @@ -188,7 +204,10 @@ def main() -> int: # pragma: nocover marked=args["--marked"], ) elif args["fonds_euro"]: - result = get_user_fonds_euro(session) + if org_id: + result = get_organization_fonds_euro(session, org_id) + else: + result = get_user_fonds_euro(session) elif args["startups"]: result = get_user_startups(session) elif args["search"]: @@ -352,6 +371,8 @@ def main() -> int: # pragma: nocover elif args["cryptos"]: if args["distribution"]: result = get_portfolio_cryptos_distribution(session) + elif org_id: + result = get_organization_cryptos(session, org_id) else: result = get_user_cryptos(session) elif args["investments"]: @@ -369,6 +390,8 @@ def main() -> int: # pragma: nocover end_date=args["--end-date"], marked=args["--marked"], ) + elif org_id: + result = get_organization_investments(session, org_id) else: result = get_portfolio_investments(session) elif args["timeseries"]: @@ -378,6 +401,8 @@ def main() -> int: # pragma: nocover result = get_holdings_account_per_name_or_id( session, args[""] ) + elif org_id: + result = get_organization_holdings_accounts(session, org_id) else: holdings_account_types = ["crypto", "stocks", "crowdlending"] hats = [i for i in holdings_account_types if args[i]] @@ -390,7 +415,10 @@ def main() -> int: # pragma: nocover elif args["precious_metals"]: result = get_user_precious_metals(session) elif args["securities"]: - result = get_user_securities(session) + if org_id: + result = get_organization_securities(session, org_id) + else: + result = get_user_securities(session) elif args["credit_accounts"]: if args["transactions"]: result = get_portfolio_credit_accounts_transactions( @@ -405,9 +433,15 @@ def main() -> int: # pragma: nocover marked=args["--marked"], ) elif args["real_estates"]: - result = get_user_real_estates(session) + if org_id: + result = get_organization_real_estates(session, org_id) + else: + result = get_user_real_estates(session) elif args["scpis"]: - result = get_user_scpis(session) + if org_id: + result = get_organization_scpis(session, org_id) + else: + result = get_user_scpis(session) elif args["import"]: to_be_imported = [] if args["crowdlending_csv"]: diff --git a/finary_uapi/user_me.py b/finary_uapi/user_me.py index 1131467..2054f48 100644 --- a/finary_uapi/user_me.py +++ b/finary_uapi/user_me.py @@ -32,6 +32,15 @@ def get_user_me_subscription_details(session: requests.Session) -> Any: return get_and_print(session, url) +def get_family_org_id(session: requests.Session) -> str | None: + """Return the ID of the user's family organisation, or None.""" + data = get_user_me_organizations(session) + for org in data.get("result", []): + if org.get("organization_type") == "family": + return org["id"] + return None + + # convenience functions diff --git a/finary_uapi/user_organizations.py b/finary_uapi/user_organizations.py new file mode 100644 index 0000000..7325164 --- /dev/null +++ b/finary_uapi/user_organizations.py @@ -0,0 +1,41 @@ +from curl_cffi import requests +from typing import Any + +from .constants import API_ROOT +from .utils import get_and_print + + +def get_organization_investments(session: requests.Session, org_id: str) -> Any: + """Aggregated portfolio view — result is a dict, not a list.""" + url = f"{API_ROOT}/organizations/{org_id}/portfolio/investments" + return get_and_print(session, url) + + +def get_organization_securities(session: requests.Session, org_id: str) -> Any: + url = f"{API_ROOT}/organizations/{org_id}/securities" + return get_and_print(session, url) + + +def get_organization_cryptos(session: requests.Session, org_id: str) -> Any: + url = f"{API_ROOT}/organizations/{org_id}/cryptos" + return get_and_print(session, url) + + +def get_organization_fonds_euro(session: requests.Session, org_id: str) -> Any: + url = f"{API_ROOT}/organizations/{org_id}/fonds_euro" + return get_and_print(session, url) + + +def get_organization_real_estates(session: requests.Session, org_id: str) -> Any: + url = f"{API_ROOT}/organizations/{org_id}/real_estates" + return get_and_print(session, url) + + +def get_organization_scpis(session: requests.Session, org_id: str) -> Any: + url = f"{API_ROOT}/organizations/{org_id}/scpis" + return get_and_print(session, url) + + +def get_organization_holdings_accounts(session: requests.Session, org_id: str) -> Any: + url = f"{API_ROOT}/organizations/{org_id}/holdings_accounts" + return get_and_print(session, url) diff --git a/tests/test_get.py b/tests/test_get.py index 7f5ed60..b78180e 100644 --- a/tests/test_get.py +++ b/tests/test_get.py @@ -81,6 +81,45 @@ def test_generic_test( assert len(result["result"]) > 0 +def test_organizations(session: requests.Session) -> None: + from finary_uapi.user_me import get_family_org_id + from finary_uapi.user_organizations import ( + get_organization_investments, + get_organization_securities, + get_organization_cryptos, + get_organization_fonds_euro, + get_organization_real_estates, + get_organization_scpis, + get_organization_holdings_accounts, + ) + + org_id = get_family_org_id(session) + if org_id is None: + pytest.skip("No family organisation on this account") + + # portfolio-level aggregated view (result is a dict) + result = get_organization_investments(session, org_id) + assert result + assert result.get("message") == "OK" + assert result.get("error") is None + assert isinstance(result.get("result"), dict) + + # flat-list endpoints (result is a list) + for fn in ( + get_organization_securities, + get_organization_cryptos, + get_organization_fonds_euro, + get_organization_real_estates, + get_organization_scpis, + get_organization_holdings_accounts, + ): + result = fn(session, org_id) + assert result + assert result.get("message") == "OK" + assert result.get("error") is None + assert isinstance(result.get("result"), list) + + def test_get_security_error(session: requests.Session) -> None: securities = get_securities(session, "US5949181045XDE") assert securities