"""Logic for the ReferenceSapProfitCenter admin endpoints.""" from abacus_common_logic.connectors.database import db from marshmallow import ValidationError from owsresponse import response from sqlalchemy.exc import IntegrityError, SQLAlchemyError from abacus_contract import models from abacus_contract.schemas.reference_sap_profit_center import ( ReferenceSapProfitCenterFilterSchema, ReferenceSapProfitCenterSchema, ) from abacus_contract.schemas.reference_signing_entity import ( ReferenceSigningEntitySchema, ) from abacus_contract.schemas.signing_entity_sap_profit_center import ( SigningEntitySapProfitCenterWithSigningEntitySchema, ) from abacus_contract.utils.format_error import validation_error from abacus_contract.utils.format_response import ( prepare_dataload_response, prepare_dataload_with_data_as_list_response, ) from abacus_contract.utils.request import validate_pagination_params def update_reference_sap_profit_center( reference_sap_profit_center: models.ReferenceSapProfitCenter, **params, ) -> response.Response: """Update mutable fields on a SAP profit center (currently just display_name).""" reference_sap_profit_center.update_attributes(**params) models.ReferenceSapProfitCenter.commit_changes() return response.Response( message=ReferenceSapProfitCenterSchema().dump(reference_sap_profit_center), status=200, ) def list_signing_entities_for_sap_profit_center( reference_sap_profit_center_id: int, search_term: str | None = None, limit: int | None = None, offset: int | None = None, ) -> response.Response: """List signing entities mapped to a given profit center (live mappings only). Powers the "Manage SEs" drawer on the SAP profit center admin page. """ models.ReferenceSapProfitCenter.get_by_id_or_error( reference_sap_profit_center_id, error_status=404 ) try: pagination = validate_pagination_params( limit if limit is not None else 50, offset if offset is not None else 0, ) except Exception as exc: return validation_error(str(exc)) items, total_count = ( models.ReferenceSigningEntity.get_authorized_for_sap_profit_centers( [reference_sap_profit_center_id], limit=pagination['limit'], offset=pagination['offset'], search_term=search_term, ) ) return response.Response( message={ 'items': SigningEntitySapProfitCenterWithSigningEntitySchema( many=True ).dump(items), 'total_count': total_count, }, status=200, ) def get_reference_sap_profit_centers(request_params: dict) -> response.Response: """Get the list of reference-sap-profit-centers. Args: request_params (dict, optional): Query string parameters for pagination, sorting, and filtering. - limit (int): The maximum number of records to return per page. - offset (int): The number of records to skip before starting to return results. - sort_by (str): The column name used to sort the results. - sort_order (str): The sort direction, either "asc" or "desc". - search_term (str): Free-text search filter matched against `display_name` and `profit_center`. - orphan (bool): If True, filters the results to only return records that lack an active junction/mapping entry. - signing_entity_ids (str): Comma separated signing entity ids Returns: a list of reference-sap-profit-centers. """ try: params = ReferenceSapProfitCenterFilterSchema().load(request_params) items, total_count = ( models.ReferenceSapProfitCenter.get_reference_sap_profit_centers(**params) ) message = dict( items=ReferenceSapProfitCenterSchema().dump(items, many=True), total_count=total_count, ) except ValidationError as exc: return validation_error(str(exc)) except Exception as e: raise e return response.Response(message=message, status=200) def get_signing_entities_by_profit_centers( sap_profit_center_ids: list[int], ) -> response.Response: """Retrieve and structures active signing entities authorized for specific SAP profit centers. Args: sap_profit_center_ids (list[int]): A list of SAP profit center IDs to filter by. Returns: response.Response: A response object containing fields from the SigningEntitySapProfitCenter alongside authorized signing entity mapping details. """ unique_ids = list(dict.fromkeys(sap_profit_center_ids)) items, _ = models.ReferenceSigningEntity.get_authorized_for_sap_profit_centers( unique_ids, 100, 0 ) signing_entities = SigningEntitySapProfitCenterWithSigningEntitySchema( many=True ).dump(items) message = prepare_dataload_with_data_as_list_response( unique_ids, signing_entities, 'reference_sap_profit_center_id', ) return response.Response( message=message, status=200, ) def get_sap_profit_centers_by_ids( sap_profit_center_ids: list[int], ) -> response.Response: """Retrieve and structures SAP profit centers. Args: sap_profit_center_ids (list[int]): A list of SAP profit center IDs Returns: response.Response: A response object containing fields for SAP profit centers. """ unique_ids = list(dict.fromkeys(sap_profit_center_ids)) items = models.ReferenceSapProfitCenter.get_by_ids(unique_ids) sap_profit_centers = ReferenceSapProfitCenterSchema(many=True).dump(items) message = prepare_dataload_response( unique_ids, sap_profit_centers, 'reference_sap_profit_center_id', ) return response.Response( message=message, status=200, ) def create_reference_sap_profit_center( profit_center: str, company_code: str, business_group: str, display_name: str, reference_signing_entity_ids: list[str] = None, ) -> response.Response: """Create an SAP Profit Center and map its associated Signing Entities. Args: profit_center (str): Alpha-numeric identity of the profit center. company_code (str): The profit center's company code. business_group (str): The profit center's business group. display_name (str): Name of the profit center. reference_signing_entity_ids (list[str]): Ids of the signing entities. Returns: response.Response: A response object containing the created SAP profit center record. """ unique_signing_entity_ids = list(set(reference_signing_entity_ids or [])) try: _check_whether_signing_entities_exist(unique_signing_entity_ids) sap_profit_center = models.ReferenceSapProfitCenter.build( profit_center=profit_center, company_code=company_code, business_group=business_group, display_name=display_name, ) db.session.flush() if unique_signing_entity_ids: for sid in unique_signing_entity_ids: models.SigningEntitySapProfitCenter.build( reference_signing_entity_id=sid, reference_sap_profit_center_id=sap_profit_center.reference_sap_profit_center_id, ) db.session.commit() except ValidationError as e: return validation_error(str(e)) except (IntegrityError, SQLAlchemyError) as e: db.session.rollback() raise e return response.Response( message=ReferenceSapProfitCenterSchema().dump(sap_profit_center), status=201 ) def _check_whether_signing_entities_exist(reference_signing_entity_ids: list) -> bool: """Check if signing entities already exist. Args: reference_signing_entity_ids (list): list of ids of signing entities Returns: bool: True if all signing entities exist, otherwise raises ValidationError """ if not reference_signing_entity_ids: return True input_ids_set = set(reference_signing_entity_ids) signing_entities = models.ReferenceSigningEntity.get_by_ids( reference_signing_entity_ids ) existing_ids = set( [ signing_entity.reference_signing_entity_id for signing_entity in signing_entities ] ) missing_ids = list(input_ids_set - existing_ids) if missing_ids: raise ValidationError(f'Signing entities {missing_ids} do not exist') return True