"""Ledger adjustment logic.""" from decimal import Decimal from moneyhub import models from moneyhub.constants.constants import FLOWTHROUGH_ADJUSTMENT_TYPE_ID from moneyhub.constants.constants import GroupBy from moneyhub.models.expenses import Expenses from moneyhub.schemas.ledger_adjustment import ExpenseSchema from moneyhub.schemas.ledger_adjustment import LedgerAdjustmentAppliedSchema from moneyhub.schemas.ledger_adjustment import LedgerAdjustmentExpenseSchema from moneyhub.schemas.ledger_adjustment import PaginatedExpenseSchema from moneyhub.schemas.reference_adjustment_type import ReferenceAdjustmentType from moneyhub.utils.request import create_paginated_response def get_by_account_id( account_id: int, contract_id: int | None, statement_period_id_start: int | None, statement_period_id_end: int | None, adjustment_type_id: int | None ) -> list: """Get ledger adjustments by an account_id. Args: account_id (int): The id of an account contract_id (int): Optional id of the contract statement_period_id_start (int): Optional id of the statement period to range from statement_period_id_end (int): Optional id of the statement period to range to adjustment_type_id (int): Optional id of adjustment type to filter on Returns: list: list of ledger adjustments """ visible_periods = models.StatementPeriodPaymentEntity.get_visible_statement_period_ids( account_id) adjustments = models.CombinedAdjustments.get_by_account_id( account_id, contract_id, statement_period_id_start, statement_period_id_end, adjustment_type_id ) return [ adjustment for adjustment in adjustments if adjustment.apply_to_statement_period_id in visible_periods ] def get_ledger_adjustments_types_by_account_id(account_id: int) -> list[ReferenceAdjustmentType]: """Get associated adjustments types for adjustments for a given account. Args: account_id (int): ID of an account Returns: list: list of adjustments types """ return models.CombinedAdjustments.get_adjustments_types_by_account_id(account_id) def get_adjustments_by_account_and_statement_periods( account_id: int, contract_id: int | None, statement_period_ids: list[int] ) -> list[LedgerAdjustmentAppliedSchema]: """Get applied ledger adjustments by an account_id and statement period id. Args: account_id (int): The id of an account contract_id (int): Optional id of the contract statement_period_ids (list): List of statement periods adjustments were applied to Returns: list: List of aggregate dicts of ledger adjustments """ visible_periods = models.StatementPeriodPaymentEntity.get_visible_statement_period_ids( account_id) items = models.AdjustmentsByType.get_by_account_id(account_id, contract_id, statement_period_ids) # noqa: E501 if len(items) == 0: return [] results = {} for item in items: item = item.to_dict() type_id = item['reference_adjustment_type_id'] contract_id = None statement_period_id = item['statement_period_id'] # For now, flowthrough adjustments do not get aggregated # Instead, these items have the contract ID avalaible for disambiguation if type_id == FLOWTHROUGH_ADJUSTMENT_TYPE_ID: contract_id = item['contract_id'] type_id = str(type_id) + '_' + str(contract_id) if statement_period_id not in visible_periods: # Skip statement periods that aren't visible for this contract continue if statement_period_id not in results: results[statement_period_id] = { 'adjustment_total_payee_currency': Decimal(0), 'adjustment_payee_currency_code': None, 'statement_period_id': statement_period_id, 'breakdown_items': {} } result = results[statement_period_id] if type_id not in result['breakdown_items']: result['breakdown_items'][type_id] = { 'adjustment_total_payee_currency': Decimal(0), 'adjustment_payee_currency_code': item['adjustment_payee_currency_code'], 'reference_adjustment_type_id': item['reference_adjustment_type_id'], 'reference_adjustment_type_name': item['reference_adjustment_type_name'], 'contract_id': contract_id } result['breakdown_items'][type_id]['adjustment_total_payee_currency'] \ += item['adjustment_amount_payee_currency'] result['adjustment_payee_currency_code'] = item['adjustment_payee_currency_code'] result['adjustment_total_payee_currency'] += item['adjustment_amount_payee_currency'] for key, result in results.items(): results[key]['breakdown_items'] = list(result['breakdown_items'].values()) return list(LedgerAdjustmentAppliedSchema(**result) for result in results.values()) def get_expenses_by_account_id( account_id: int, limit: int | None, offset: int | None, contract_id: int | None = None, statement_period_id_start: int | None = None, statement_period_id_end: int | None = None, upc: str | None = None, expense_type_id: int | None = None, distribution_type: str | None = None, artist_id: int | None = None, subaccount_id: int | None = None, group_by: GroupBy | None = None ) -> PaginatedExpenseSchema | list[ExpenseSchema]: """Get ledger adjustments expenses by an account_id. Args: account_id (int): The id of an account limit (int): how many entities to retrieve. offset (int): the offset (for pagination). contract_id (int): Optional id of the contract statement_period_id_start (int): Optional id of the statement period to range from statement_period_id_end (int): Optional id of the statement period to range to upc (str): Optional UPC to filter the results by expense_type_id (int): Optional expense type id to filter by distribution_type (str): Optional distribution type to filter by artist_id (int): Optional artist id to filter by subaccount_id (int): Optional subaccount id to filter by group_by (str): Optional dimension to group expenses by Returns: list: list of ledger adjustments """ match group_by: case GroupBy.IMPRINT_ID: ( records_list, total_records ) = models.ExpensesByImprint.get_by_imprint_account_id( account_id, limit, offset, contract_id, statement_period_id_start, statement_period_id_end, upc, expense_type_id, artist_id, subaccount_id ) case GroupBy.ARTIST_ID: ( records_list, total_records ) = models.ExpensesByArtist.get_by_artist_by_account_id( account_id, limit, offset, contract_id, statement_period_id_start, statement_period_id_end, upc, expense_type_id, artist_id, subaccount_id ) case GroupBy.SUBACCOUNT_ID: (records_list, total_records) = models.ExpensesBySubaccount.get_by_subaccount( account_id, limit, offset, contract_id, statement_period_id_start, statement_period_id_end, upc, expense_type_id, artist_id, subaccount_id ) case _: (records_list, total_records) = models.Expenses.get_by_account_id( account_id, limit, offset, contract_id, statement_period_id_start, statement_period_id_end, upc, distribution_type, expense_type_id, artist_id, subaccount_id, group_by ) payload = create_paginated_response(records_list, total_records) return PaginatedExpenseSchema(**payload) def get_expenses_types_by_account_id(account_id: int) -> list: """Get associated expenses types for expenses for a given account. Args: account_id (int): ID of an account Returns: list: list of expenses types """ return models.Expenses.get_expenses_types_by_account_id( account_id) def get_expenses_subaccounts_by_account_id(account_id: int) -> list: """Get associated subaccounts for expenses for a given account. Args: account_id (int): ID of an account Returns: list: list of expenses subaccounts """ return models.Expenses.get_expenses_subaccounts_by_account_id( account_id) def get_expenses_by_account_and_statement_periods( account_id: int, statement_period_ids: list[int], contract_id: int | None ) -> list[LedgerAdjustmentExpenseSchema]: """Get aggregated expenses by types by an account_id and statement period id. Args: account_id (int): The id of an account contract_id (int): Optional id of the contract statement_period_ids (list): List of statement periods adjustments were applied to Returns: dict: Aggregate dict of expenses """ items = models.Expenses.get_expenses_by_account_and_statement_periods( account_id, statement_period_ids, contract_id ) if len(items) == 0: return [] results = {} def _create_expense_entries(item: Expenses): item = item._asdict() type_id = item['reference_adjustment_type_id'] statement_period_id = item['statement_period_id'] if statement_period_id not in results: results[statement_period_id] = { 'amount': Decimal(0), 'currency_code': None, 'statement_period_id': statement_period_id, 'breakdown_items': {} } result = results[statement_period_id] if type_id not in result['breakdown_items']: result['breakdown_items'][type_id] = { 'adjustment_total_payee_currency': item['adjustment_amount_payee_currency'], 'adjustment_payee_currency_code': item['adjustment_payee_currency_code'], 'reference_adjustment_type_id': type_id, 'reference_adjustment_type_name': item['reference_adjustment_type_name'] } result['currency_code'] = item['adjustment_payee_currency_code'] result['amount'] += item['adjustment_amount_payee_currency'] list(map(_create_expense_entries, items)) for key, result in results.items(): results[key]['breakdown_items'] = list(result['breakdown_items'].values()) return list(LedgerAdjustmentExpenseSchema(**result) for result in results.values()) def get_expenses_artists_by_account_id(account_id: int) -> list: """Get all available artists associated with expenses for an account ID. Args: account_id (int): ID of an account Returns: list: list of artists """ return models.Expenses.get_artists_by_account(account_id) def get_expenses_upcs_by_account_id(account_id: int) -> list: """Get all available UPCs associated with expenses for an account ID. Args: account_id (int): ID of an account Returns: list: list of UPCs """ return models.Expenses.get_upcs_by_account(account_id)