"""Logic for Ledger Reserve Taken.""" from typing import Dict, List, Type from abacus_common_logic.connectors.database import db from owsresponse import response from ledger.constants.constants import ( DEFAULT_PAGE_LIMIT, DEFAULT_PAGE_OFFSET, LEDGER_ACCOUNT_ENTRIES_SUCCESS_MSG, ) from ledger.constants.error import ( ERROR_POST_REQUEST_DUPLICATE_RECORDS, ERROR_RECORD_ALREADY_EXISTS, ERROR_UNKNOWN_CURRENCY, ) from ledger.logic.bulk_ledgers import build_account_ledger_entries from ledger.models.ledger_reserve_taken import LedgerReserveTaken from ledger.schemas.ledger_reserve_taken import ( LedgerReserveTakenDetailSchema, LedgerReserveTakenWithAccountContractSchema, ) from ledger.utils.currency import currency_exists from ledger.utils.format_error import validation_error from ledger.utils.request import validate_pagination_params def bulk_create(request_body: List[Dict]) -> Type[response.Response]: """Bulk insertion for ledger_reserve_taken. Args: request_body(list): POST request body of ledger_reserve_taken entries Returns: List of newly created ledger_reserve_taken records """ ledger_reserve_taken_records = list() try: _validate_post_request_body(request_body) abacus_event_ids = set(record.get('abacus_event_id') for record in request_body) accounting_run_ids = set( record.get('accounting_run_id') for record in request_body ) contract_reserve_ids = set( record.get('contract_reserve_id') for record in request_body ) existing_records = _get_existing_ledger_records( abacus_event_ids, accounting_run_ids, contract_reserve_ids ) for record in request_body: _validate_entry(existing_records, record) ledger_reserve_taken = LedgerReserveTaken(**record) ledger_reserve_taken_records.append(ledger_reserve_taken) if len(ledger_reserve_taken_records) > 0: db.session.bulk_save_objects(ledger_reserve_taken_records) db.session.commit() new_records = ( LedgerReserveTaken.get_by_abacus_events_accounting_runs_contract_reserves( abacus_event_ids, accounting_run_ids, contract_reserve_ids ) ) result = response.Response( message=LedgerReserveTakenDetailSchema(many=True).dump(new_records), status=201, ) except Exception as e: db.session.rollback() result = validation_error(str(e)) finally: db.session.close() return result def get_ledger_reserve_taken_by_accounting_run_id( accounting_run_id: int, request_params: dict ) -> Type[response.Response]: """Get a list of ledger_reserve_taken by accounting_run_id. Args: accounting_run_id (int): id of an accounting run request_params (dict)(Optional): dict of query string passed to the url - limit(int): the size of page - offset(int): the page number """ try: limit = request_params.get('limit', DEFAULT_PAGE_LIMIT) offset = request_params.get('offset', DEFAULT_PAGE_OFFSET) pagination_params = validate_pagination_params(limit, offset) items, total_count = LedgerReserveTaken.get_by_accounting_run( accounting_run_id, **pagination_params ) message = { 'items': LedgerReserveTakenWithAccountContractSchema(many=True).dump(items), 'total_count': total_count, } except Exception as e: return validation_error(str(e)) return response.Response(message=message, status=200) def debit_reserves_from_ledger_account_by_run( accounting_run_id: int, ) -> Type[response.Response]: """Debit reserves from ledger_account_contract for ledger_reserve_taken entries by accounting_run. Args: accounting_run_id (int): id of an accounting run Returns: an ows response """ try: limit = 100000 offset = DEFAULT_PAGE_OFFSET items, total_count = LedgerReserveTaken.get_by_accounting_run( accounting_run_id, limit, offset ) formatted_ledger_entries = list() for item in items: formatted_ledger_entries.append( { 'abacus_event_id': item['abacus_event_id'], 'account_id': item['account_id'], 'contract_id': item['contract_id'], 'currency_code': item['currency_code'], 'currency_amount': item['reserve_amount'], } ) ledger_account_entries, ledger_account_contract_entries = ( build_account_ledger_entries(formatted_ledger_entries) ) db.session.bulk_save_objects(ledger_account_entries) db.session.bulk_save_objects(ledger_account_contract_entries) db.session.commit() result = response.Response( message={'message': LEDGER_ACCOUNT_ENTRIES_SUCCESS_MSG.format(total_count)}, status=201, ) except Exception as e: db.session.rollback() result = validation_error(str(e)) finally: db.session.close() return result def _get_existing_ledger_records( abacus_event_ids: list, accounting_run_ids: list, contract_reserve_ids: list ) -> list: """Get existing ledger_reserve_taken records by the specified parameters. Args: abacus_event_ids (list): list of abacus_event_ids accounting_run_ids (list): list of accounting_run_ids contract_reserve_ids (list): list of contract_reserve_ids """ existing_records = ( LedgerReserveTaken.get_by_abacus_events_accounting_runs_contract_reserves( abacus_event_ids, accounting_run_ids, contract_reserve_ids ) ) return LedgerReserveTakenDetailSchema( many=True, only=( 'abacus_event_id', 'accounting_run_id', 'contract_reserve_id', ), ).dump(existing_records) def _validate_entry(existing_records: list, new_record: dict) -> bool: """Validate ledger_reserve_taken record. Args: existing_records (list): list of existing ledger_reserve_taken entries new_record (dict): arguments of an entry in create's POST body """ currency_code = new_record.get('currency_code') if not currency_exists(currency_code): raise Exception(ERROR_UNKNOWN_CURRENCY.format(code=currency_code)) new_record_dict = { 'abacus_event_id': new_record.get('abacus_event_id'), 'accounting_run_id': new_record.get('accounting_run_id'), 'contract_reserve_id': new_record.get('contract_reserve_id'), } if new_record_dict in existing_records: raise Exception(ERROR_RECORD_ALREADY_EXISTS.format(new_record_dict)) return True def _validate_post_request_body(request_body: list) -> None: """Validate that there are no duplicates in POST request body. A "duplicate" is any record that has the same abacus_event_id, accounting_run_id, and contract_reserve_id as another record. Args: request_body (list): POST request payload """ reduced_request_body = LedgerReserveTakenDetailSchema( many=True, only=( 'abacus_event_id', 'accounting_run_id', 'contract_reserve_id', ), ).dump(request_body) unique_items = list() for item in reduced_request_body: if item in unique_items: raise Exception(ERROR_POST_REQUEST_DUPLICATE_RECORDS.format(item)) unique_items.append(item)