"""Abacus GraphQL client for entity lookups.""" import logging from connectors.errors import ApiError from connectors.graphql import GraphQLClient, GraphQLError from schemas import ( AbacusContract, AbacusContractWithLifecyclesInput, GraphQLResult, ProcessingStatus, ReferenceSigningEntity, RunController, RunControllerContract, ) logger = logging.getLogger(__name__) _GET_SIGNING_ENTITIES = """ query AbacusReferenceSigningEntities { abacusReferenceSigningEntities { items { referenceSigningEntityId legalName companyCode address companyRegistrationNumber vatNumber } } } """ _GET_RUN_CONTROLLERS = """ query AbacusRunControllers($limit: Int!, $offset: Int!) { abacusRunControllers(limit: $limit, offset: $offset) { items { runControllerId runControllerName contractType } } } """ _ATTACH_RUN_CONTROLLER = """ mutation AbacusUpdateRunControllerContracts( $input: AbacusRunControllerContractUpdate! ) { abacusUpdateRunControllerContracts(input: $input) { contractId runControllerContractId runControllerId } } """ _CREATE_CONTRACT = """ mutation AbacusCreateContractWithLifecycles( $input: AbacusContractWithLifecyclesInput ) { abacusCreateContractWithLifecycles(input: $input) { contractId contractName contractType contractStatus runControllerId isExcludedFromAccountingRun isPrimaryContract executionDate } } """ _RUN_CONTROLLER_PAGE_SIZE = 100 class AbacusClient: """Domain client for entity lookups via the Abacus GraphQL gateway.""" def __init__(self, graphql: GraphQLClient): self._graphql = graphql def get_reference_signing_entities( self, ) -> list[ReferenceSigningEntity]: """Load all reference signing entities. Raises ApiError on transport failures. Raises GraphQLError on query errors. """ data = self._graphql.query(_GET_SIGNING_ENTITIES) items = data.get('abacusReferenceSigningEntities', {}).get('items', []) entities = [ReferenceSigningEntity(**item) for item in items] logger.info(f'[GraphQL] Loaded {len(entities)} signing entities') return entities def get_run_controllers(self) -> list[RunController]: """Load all run controllers (paginated). Raises ApiError on transport failures. Raises GraphQLError on query errors. """ all_items: list[dict] = [] offset = 0 while True: data = self._graphql.query( _GET_RUN_CONTROLLERS, variables={ 'limit': _RUN_CONTROLLER_PAGE_SIZE, 'offset': offset, }, ) items = data.get('abacusRunControllers', {}).get('items', []) if not items: break all_items.extend(items) if len(items) < _RUN_CONTROLLER_PAGE_SIZE: break offset += _RUN_CONTROLLER_PAGE_SIZE controllers = [RunController(**item) for item in all_items] logger.info(f'[GraphQL] Loaded {len(controllers)} run controllers') return controllers def attach_run_controller( self, contract_id: int, run_controller_id: int, ) -> RunControllerContract: """Attach a run controller to a contract via GraphQL mutation. Raises GraphQLError on mutation errors. Raises ApiError on transport failures. """ data = self._graphql.query( _ATTACH_RUN_CONTROLLER, variables={ 'input': { 'contractId': contract_id, 'runControllerId': run_controller_id, } }, ) result = data['abacusUpdateRunControllerContracts'] return RunControllerContract(**result) def create_contract_with_lifecycles( self, gql_input: AbacusContractWithLifecyclesInput, ) -> GraphQLResult[AbacusContract]: """Create a contract with lifecycle via GraphQL mutation.""" contract_name = gql_input.contract.contract_name try: data = self._graphql.query( _CREATE_CONTRACT, variables={'input': gql_input.model_dump(by_alias=True)}, ) except (ApiError, GraphQLError) as e: logger.error(f'Failed to create contract {contract_name}: {e}') return GraphQLResult(status=ProcessingStatus.ERROR, error=str(e)) raw = data.get('abacusCreateContractWithLifecycles', {}) created = AbacusContract(**raw) logger.info(f' Created contract: {contract_name} (ID: {created.contract_id})') return GraphQLResult(status=ProcessingStatus.SUCCESS, data=created)