"""Statement Attachment Handler.""" # pylint: disable=C0301 from typing import Annotated from fastapi import APIRouter from fastapi import Depends from fastapi import Header from fastapi import HTTPException from fastapi import Query from moneyhub.constants.constants import JWTKeys from moneyhub.constants.constants import NumberFormat from moneyhub.constants.constants import StatementAttachmentFileType from moneyhub.constants.constants import StatementAttachmentType from moneyhub.constants.error import MISSING_PROFILE_HEADERS from moneyhub.logic import statement_attachment as logic from moneyhub.schemas.statement_attachment import InternalAttachmentPostSchema from moneyhub.schemas.statement_attachment import StatementAttachmentDetailSchema from moneyhub.schemas.statement_attachment import StatementAttachmentFiltersSchema from moneyhub.schemas.statement_attachment import StatementAttachmentLegacyReportSchema from moneyhub.schemas.statement_attachment import StatementAttachmentPutSchema from moneyhub.schemas.statement_attachment import StatementAttachmentRegenerateSchema from moneyhub.utils.request import extract_json_body from moneyhub.utils.request import extract_token statement_attachment_router = APIRouter( prefix='/statement-attachment', tags=['statement_attachments']) @statement_attachment_router.get( '/{statement_attachment_id}', status_code=200 ) def get_statement_attachment( statement_attachment_id: int, profile_dict: dict = Depends(extract_token), ) -> StatementAttachmentDetailSchema | StatementAttachmentLegacyReportSchema: """Get a statement attachment by ID. Args: statement_attachment_id (int): ID of the attachment Returns: StatementAttachmentDetailSchema: the statement attachment """ return logic.get_statement_attachment_by_id( statement_attachment_id, profile_dict[JWTKeys.PROFILE_TYPE], profile_dict[JWTKeys.PROFILE_ID], ) @statement_attachment_router.get( '/account/{account_id}/statement-period/{statement_period_id}', status_code=200 ) def get_statement_attachments( account_id: int, statement_period_id: int, contract_id: int | None = None, subaccount_id: int | None = None, ) -> list[StatementAttachmentDetailSchema | StatementAttachmentLegacyReportSchema]: """Get statement attachments by an account_id and statement_period_id. Args: account_id (int): The id of an account statement_period_id (int): The id of statement period contract_id (int): the id of a contract subaccount_id (int): Optional id of the subaccount Returns: list: list of statement attachments """ return logic.get_statement_attachments_by_account_and_statement_periods( account_id, [statement_period_id], contract_id, subaccount_id) @statement_attachment_router.get( '/account/{account_id}', status_code=200 ) def get_statement_attachments_by_account( account_id: int, contract_id: int | None = None, subaccount_id: int | None = None, ) -> list[StatementAttachmentDetailSchema | StatementAttachmentLegacyReportSchema]: """Get statement attachments by an account_id. Args: account_id (int): The id of an account contract_id (int): the id of a contract subaccount_id (int): Optional id of the subaccount Returns: list: list of statement attachments """ return logic.get_statement_attachments_by_account_and_statement_periods( account_id, None, contract_id, subaccount_id) @statement_attachment_router.post( '/account/{account_id}/statement-period/{statement_period_id}/revenue-detail', status_code=200 ) def create_revenue_detail_reports( account_id: int, statement_period_id: int, contract_id: int | None = None, profile_dict: dict = Depends(extract_token), correlation_id: str | None = Header(None), subaccount_id: int | None = None, ) -> list[StatementAttachmentDetailSchema]: """Create revenue detail reports for an account and statement period. Args: account_id (int): Account to make the report for statement_period_id (int): Period to make the report for profile_dict (dict): Profile details correlation_id (str): Request correlation ID subaccount_id (int): Optional id of the subaccount Returns: list: List of created statement attachments """ orchard_identity_id = profile_dict[JWTKeys.ORCHARD_IDENTITY_ID] return logic.create_revenue_detail_reports( account_id, statement_period_id, orchard_identity_id, correlation_id, contract_id, subaccount_id) @statement_attachment_router.post( '/account/{account_id}/statement-period/{statement_period_id}/collection-summary', status_code=201 ) def create_statement_period_collection_summary_documents( account_id: int, statement_period_id: int, profile_dict: dict = Depends(extract_token), correlation_id: str | None = Header(None) ) -> list[StatementAttachmentDetailSchema]: """Create collection summary documents for an account and statement period. Args: account_id (int): Account to make the report for statement_period_id (int): Period to make the report for profile_dict (dict): Profile details correlation_id (str): Request correlation ID Returns: list: List of created statement attachments """ orchard_identity_id = profile_dict[JWTKeys.ORCHARD_IDENTITY_ID] return logic.create_collection_summary_documents( account_id, statement_period_id, orchard_identity_id, correlation_id ) @statement_attachment_router.post( '/account/{account_id}/statement-period/{statement_period_id}/legacy-revenue-report', status_code=201 ) def create_legacy_revenue_report( account_id: int, statement_period_id: int, report_type: StatementAttachmentType, file_type: StatementAttachmentFileType, number_format: NumberFormat, contract_id: int | None = None, profile_dict: dict = Depends(extract_token), correlation_id: str | None = Header(None), subaccount_id: int | None = None, filters: StatementAttachmentFiltersSchema | None = None, statement_period_ids: Annotated[list[int] | None, Query()] = None, ) -> list[StatementAttachmentLegacyReportSchema]: """Create legacy revenue reports for an account, contract and statement period. Args: account_id (int): Account to make the report for contract_id (int): Contract id to make the report for statement_period_id (int): Period to make the report for report_type (StatementAttachmentType): the type of legacy revenue report file_type (StatementAttachmentFileType): the requested file type number_format (NumberFormat): Format for numbers ('us' or 'eu') profile_dict (dict): Profile details correlation_id (str): Request correlation ID subaccount_id (int): Optional id of the subaccount statement_period_ids (list): Optional list of statement period ids Returns: StatementAttachmentDetailSchema: the created legacy revenue report """ orchard_identity_id = profile_dict[JWTKeys.ORCHARD_IDENTITY_ID] filters = filters.model_dump() if filters else None return logic.create_legacy_revenue_report( account_id, contract_id, statement_period_id, orchard_identity_id, report_type, file_type, number_format, correlation_id, subaccount_id, filters, statement_period_ids ) @statement_attachment_router.post( '/statement-period/{statement_period_id}', status_code=201 ) def create_statement_attachments( statement_period_id: int, profile_dict: dict = Depends(extract_token) ) -> list[StatementAttachmentDetailSchema | StatementAttachmentLegacyReportSchema]: """Create statement attachments for a specified statement period. Args: statement_period_id (int): The id of statement period profile_dict (dict): Profile details Returns: list: list of statement attachments """ orchard_identity_id = profile_dict[JWTKeys.ORCHARD_IDENTITY_ID] return logic.create_statement_attachments(statement_period_id, orchard_identity_id) @statement_attachment_router.post( '/account/{account_id}/statement-period/{statement_period_id}/internal-attachment', status_code=201 ) def create_internal_attachment( account_id: int, statement_period_id: int, body: InternalAttachmentPostSchema, profile_dict: dict = Depends(extract_token) ) -> StatementAttachmentDetailSchema | None: """Create internal file statement attachments. Args: account_id(int): The id of the account. statement_period_id (int): The id of statement period. body: includes the file_type, file_location, upload_date profile_dict (dict): Profile details Returns: list: list of statement attachments uploaded. """ orchard_identity_id = profile_dict[JWTKeys.ORCHARD_IDENTITY_ID] return logic.create_internal_attachment( account_id, statement_period_id, body.file_type, body.file_location, orchard_identity_id, body.upload_date, body.contract_id, ) @statement_attachment_router.post( '/statement-period/{statement_period_id}/regenerate', status_code=200 ) def regenerate_statement_attachments( statement_period_id: int, payload: StatementAttachmentRegenerateSchema, correlation_id: str | None = Header(None) ) -> list[StatementAttachmentDetailSchema]: """Regenerate statement attachments for a specified statement period. Args: statement_period_id (int): The ID of statement period payload (StatementAttachmentRegenerateSchema): Payload for filtering attachments correlation_id (str): Optional request correlation ID Returns: list: list of statement attachments """ return logic.regenerate_statement_attachments( statement_period_id, payload.account_id, payload.statement_attachment_type, correlation_id ) @statement_attachment_router.delete( '/{statement_attachment_id}', status_code=204 ) def delete_statement_attachment(statement_attachment_id: int) -> None: """Delete a statement attachment. Args: statement_attachment_id (int): ID of the statement attachment to delete. """ logic.delete_statement_attachment(statement_attachment_id) @statement_attachment_router.put( '/{statement_attachment_id}', status_code=200 ) def update_statement_attachment( statement_attachment_id: int, payload: StatementAttachmentPutSchema ) -> StatementAttachmentDetailSchema: """Update statement attachment by statement_attachment_id. Args: statement_attachment_id (int): The id of statement attachment payload (StatementAttachmentPutSchema): PUT parameters Returns: StatementAttachmentDetailSchema: Updated statement attachment details """ put_request_body = extract_json_body(payload) return logic.update_statement_attachment(statement_attachment_id, **put_request_body) @statement_attachment_router.post( '/statement-period/{statement_period_id}/generate', status_code=201 ) def generate_statement_attachments( statement_period_id: int, account_id: int | None = None, correlation_id: str | None = Header(None) ) -> dict: """Generate statement attachments by statement_period_id. Args: statement_period_id (int): Statement period ID to generate attachments for account_id (int): Optional account to generate attachments for correlation_id (str): Correlation Id from the header Returns: Response: 'OK' with status code 201 if success """ return logic.trigger_statement_attachment_generation( statement_period_id, account_id, correlation_id) @statement_attachment_router.get( '/{statement_attachment_id}/download', status_code=200 ) def get_invoice_presigned_url( statement_attachment_id: int, orchard_profile_id: str | None = Header(None), orchard_profile_type: str | None = Header(None) ) -> dict: """Get presigned S3 URL for downloading invoice. Args: statement_attachment_id (int): The id of statement attachment orchard_profile_id (str): Profile Identifier orchard_profile_type (str): Type of profile Returns: dict: presigned url of invoice file location """ if orchard_profile_type is None or orchard_profile_id is None: raise HTTPException(status_code=400, detail=MISSING_PROFILE_HEADERS) return {'url': logic.get_invoice_presigned_url( statement_attachment_id, orchard_profile_type, int(orchard_profile_id))}