Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions finary_uapi/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
finary_uapi organizations
finary_uapi timeseries <period> <type>
finary_uapi checking_accounts transactions [--page=<page>] [--perpage=<perpage>] [--account=<account_ids>] [--institution=<institution_ids>] [--query=<query>] [--start-date=<start_date>] [--end-date=<end_date>] [--marked=<marked>]
finary_uapi fonds_euro
finary_uapi fonds_euro [--org-id=<org_id>]
finary_uapi startups
finary_uapi investments
finary_uapi investments [--org-id=<org_id>]
finary_uapi investments dividends
finary_uapi investments transactions [--page=<page>] [--perpage=<perpage>] [--account=<account_ids>] [--institution=<institution_ids>] [--query=<query>] [--start-date=<start_date>] [--end-date=<end_date>] [--marked=<marked>]
finary_uapi crowdlendings
finary_uapi crowdlendings distribution
finary_uapi crowdlendings add <account_name> <name> <annual_yield> <month_duration> <initial_investment> <current_price> <currency_code> <start_date>
finary_uapi crowdlendings delete <crowdlending_id>
finary_uapi cryptos
finary_uapi cryptos [--org-id=<org_id>]
finary_uapi cryptos distribution
finary_uapi cryptos add <code> <quantity> <price> <account_id>
finary_uapi cryptos update <code> <quantity> <price> <account_id>
Expand All @@ -25,7 +25,7 @@
finary_uapi precious_metals
finary_uapi precious_metals add <name> <quantity> <price>
finary_uapi precious_metals delete <commodity_id>
finary_uapi holdings_accounts [crypto | stocks | crowdlending | <account_name>]
finary_uapi holdings_accounts [crypto | stocks | crowdlending | <account_name>] [--org-id=<org_id>]
finary_uapi holdings_accounts add (crypto | stocks | crowdlending) <account_name>
finary_uapi holdings_accounts add (checking | saving) <account_name> <bank_name> <account_type> <balance>
finary_uapi holdings_accounts delete <account_id>
Expand All @@ -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=<org_id>]
finary_uapi securities add <code> <quantity> <price> <account_id>
finary_uapi securities delete <security_id>
finary_uapi credit_accounts transactions [--page=<page>] [--perpage=<perpage>] [--account=<account_ids>] [--institution=<institution_ids>] [--query=<query>] [--start-date=<start_date>] [--end-date=<end_date>] [--marked=<marked>]
finary_uapi real_estates
finary_uapi real_estates [--org-id=<org_id>]
finary_uapi real_estates add rent <address> <user_estimated_value> <description> <surface> <buying_price> <building_type> <ownership_percentage> <monthly_charges> <monthly_rent> <yearly_taxes> <rental_period> <rental_type> [<currency_code>]
finary_uapi real_estates add <category> <address> <user_estimated_value> <description> <surface> <buying_price> <building_type> <ownership_percentage> [<currency_code>]
finary_uapi real_estates update rent <asset_id> <user_estimated_value> <description> <buying_price> <ownership_percentage> <monthly_rent>
finary_uapi real_estates update <category> <asset_id> <user_estimated_value> <description> <buying_price> <ownership_percentage>
finary_uapi real_estates delete <asset_id>
finary_uapi scpis search QUERY
finary_uapi scpis
finary_uapi scpis [--org-id=<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)]
Expand All @@ -73,6 +73,7 @@
--start-date=<start_date> Start date for transactions (format: YYYY-MM-DD)
--end-date=<end_date> End date for transactions (format: YYYY-MM-DD)
--marked=<marked> Filter marked transactions (true or false)
--org-id=<org_id> Organisation ID (UUID or 'family' to auto-resolve); queries org-level endpoint instead of /users/me/


""" # noqa
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"]:
Expand All @@ -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"]:
Expand Down Expand Up @@ -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"]:
Expand All @@ -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"]:
Expand All @@ -378,6 +401,8 @@ def main() -> int: # pragma: nocover
result = get_holdings_account_per_name_or_id(
session, args["<account_name>"]
)
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]]
Expand All @@ -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(
Expand All @@ -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"]:
Expand Down
9 changes: 9 additions & 0 deletions finary_uapi/user_me.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
41 changes: 41 additions & 0 deletions finary_uapi/user_organizations.py
Original file line number Diff line number Diff line change
@@ -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)
39 changes: 39 additions & 0 deletions tests/test_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down