"""Logic for Worksheet Account Contract Closing Balance.""" from decimal import Decimal from typing import List from abacus_common_logic.connectors.database import db from sqlalchemy import exc from payment.constants import error from payment.logic.exceptions import LogicError from payment.models import Items from payment.models.worksheet_account_contract_payable_details import ( WorksheetAccountContractPayableDetails, ) from payment.models.worksheet_payable_balance_after_tax import ( WorksheetPayableBalanceAfterTax, ) from payment.repository import worksheet_payable_balance_after_tax as repository from payment.utils.format_response import prepare_generic_success_response def bulk_create( event_id: int, statement_period_id: int, create_params: tuple, ) -> dict: """Create one or more worksheet_payable_balance_after_tax records. Args: event_id (int): id of the event statement_period_id (int): id of the statement period create_params (tuple): POST parameters per record """ contract_ids = [record['contract_id'] for record in create_params] if len(contract_ids) != len(set(contract_ids)): raise LogicError(error.ERROR_CONTRACT_DUPLICATE) if WorksheetPayableBalanceAfterTax.exists_for_contracts_statement_period_event_id( contract_ids=contract_ids, statement_period_id=statement_period_id, event_id=event_id, ): raise LogicError(error.ERROR_WORKSHEET_ALREADY_EXISTS) base_params = { 'abacus_event_id': event_id, 'statement_period_id': statement_period_id, } if not create_params: raise LogicError(error.ERROR_NO_INSTANCES_TO_CREATE) worksheet_payable_balance_after_tax = [ {**params, **base_params} for params in create_params ] try: entries = build_worksheet_payable_balance_after_tax_entries( worksheet_payable_balance_after_tax ) WorksheetPayableBalanceAfterTax.bulk_create(entries) except exc.IntegrityError: db.session.rollback() raise LogicError(error.ERROR_INTEGRITY) return prepare_generic_success_response() def bulk_delete_worksheet_payable_balance_after_tax(event_id: int) -> None: """Bulk delete items by event_id.""" WorksheetPayableBalanceAfterTax.soft_delete_by_event_id(event_id) def get_worksheet_payable_balance_after_tax( event_id: int, statement_period_id: int, limit: int, offset: int, contract_ids: List[int] | None = None, search_term: str | None = None, sort_by: str | None = None, sort_order: str | None = None, ) -> Items[WorksheetPayableBalanceAfterTax]: """Get worksheet_payable_balance_after_tax records.""" items, total_count = repository.get_filtered_active_records( statement_period_id=statement_period_id, event_id=event_id, contract_ids=contract_ids, limit=limit, offset=offset, search_term=search_term, sort_by=sort_by, sort_order=sort_order, ) return Items(items, total_count) def build_worksheet_payable_balance_after_tax_entries( records: List[dict], ) -> List[WorksheetPayableBalanceAfterTax]: """Build a list of WorksheetPayableBalanceAfterTax objects from the given records.""" return [WorksheetPayableBalanceAfterTax(**record) for record in records] def get_worksheet_payable_balance_after_tax_overview( event_id: int, ) -> dict[str, Decimal]: """Get aggregated overview of worksheet payable balance after tax for a given event_id.""" return WorksheetPayableBalanceAfterTax.get_overview_by_event_id(event_id) def delete_worksheet_payable_balance_after_tax( worksheet_account_contract_payable_after_tax_id, ): """Delete a worksheet_payable_balance_after_tax record and details records by id.""" try: WorksheetPayableBalanceAfterTax.soft_delete_by_id( worksheet_account_contract_payable_after_tax_id, False ) WorksheetAccountContractPayableDetails.soft_delete_by_worksheet_payable_after_tax_id( worksheet_account_contract_payable_after_tax_id, False ) db.session.commit() except Exception as e: db.session.rollback() raise e def get_worksheet_payable_balance_after_tax_detail( worksheet_account_contract_payable_after_tax_id: int, ) -> dict[str, Decimal]: """Get detail item.""" return WorksheetPayableBalanceAfterTax.get_by_id_or_error( worksheet_account_contract_payable_after_tax_id ) def bulk_update_worksheet_payable_balance_after_tax( update_params: List[dict], ) -> List[WorksheetPayableBalanceAfterTax]: """Bulk update worksheet payable balance after tax records. Args: update_params: List of dictionaries containing worksheet_account_contract_payable_after_tax_id and fields to update (tax_withholding_amount, vat_amount, payable_amount_post_tax) Returns: List of updated WorksheetPayableBalanceAfterTax instances Raises: LogicError: If worksheet records are not found, duplicate IDs provided, or update fails """ if not update_params: return [] worksheet_ids = [ params['worksheet_account_contract_payable_after_tax_id'] for params in update_params ] if len(worksheet_ids) != len(set(worksheet_ids)): raise LogicError('Duplicate worksheet IDs provided in request') existing_worksheets = WorksheetPayableBalanceAfterTax.get_by_ids_list(worksheet_ids) existing_ids = { w.worksheet_account_contract_payable_after_tax_id for w in existing_worksheets } missing_ids = set(worksheet_ids) - existing_ids if missing_ids: raise LogicError(f'Worksheet records not found: {sorted(missing_ids)}') try: WorksheetPayableBalanceAfterTax.bulk_update(update_params, commit=True) except exc.SQLAlchemyError as e: raise LogicError(f'Failed to update worksheets: {str(e)}') return WorksheetPayableBalanceAfterTax.get_by_ids_list(worksheet_ids)