"""Statement Period handlers.""" from http import HTTPStatus from typing import List, Optional from flask import Response from flask.typing import ResponseReturnValue from flask_pydantic import validate from pydantic import Field, field_validator, model_validator from collaborator.api import app from collaborator.logic import statement_period from collaborator.schemas import ( BaseSchema, CommaSeparatedList, SortableRequestMixin, ) from collaborator.utils import handlers as utils from collaborator.utils.helpers import check_vendors_authorization class GetStatementPeriodsQuery(BaseSchema, SortableRequestMixin): """Query parameters for GET /statement-periods.""" vendor_id: int status: CommaSeparatedList[str] = [] limit: int = 0 offset: int = 0 term: Optional[str] = None @app.route("/statement-periods", methods=["GET"]) @validate() @utils.fetch_authorized_resources def get_statement_periods( authorized_resources, user, query: GetStatementPeriodsQuery ) -> ResponseReturnValue: """Get statement periods for a given vendor. Args: authorized_resources (List[Dict]): Resources to which the requestor has access Returns: flask.Response: list of statement periods. """ check_vendors_authorization(authorized_resources, [query.vendor_id]) result = statement_period.get_statement_periods( query.vendor_id, query.status, query.limit, query.offset, query.sort_key, query.sort_direction, query.term, ) return result.message class StatementPeriodIdsBody(BaseSchema): """Request body carrying a list of statement period IDs.""" statement_period_ids: List[int] @app.route("/statement-period/dataloader", methods=["POST"]) @validate() @utils.fetch_authorized_resources def statement_periods_dataloader( authorized_resources, user, body: StatementPeriodIdsBody ) -> ResponseReturnValue: """Statement period dataloader enpoint.""" result = statement_period.statement_periods_dataloader( body.statement_period_ids, authorized_resources ) return result class CloseStatementPeriodBody(BaseSchema): """Request body for closing a statement period.""" vendor_id: int name: str = Field(min_length=1) @app.route("/statement-period/close", methods=["PUT"]) @validate() @utils.fetch_authorized_resources def close_statement_period( authorized_resources, user, body: CloseStatementPeriodBody ) -> ResponseReturnValue: """Close a specified statement period. Args: account (Account): Account which made the request. Returns: flask.Response: The updated statement period. """ statement_period.check_can_access_statement_period_data( authorized_resources, vendor_id=body.vendor_id ) result = statement_period.close_statement_period(body.vendor_id, body.name) return result class BulkCloseStatementPeriodsBody(BaseSchema): """Schema for bulk close statement periods request body.""" direct_payments_enabled: bool period_name: str = Field(min_length=1, max_length=255) new_abacus_statement_period_id: int @field_validator("direct_payments_enabled") @classmethod def validate_direct_payments_enabled(cls, v): """Validate direct_payments_enabled is true. Currently we only support closing statement periods for all dp enabled vendors """ if not v: raise ValueError("direct_payments_enabled must be true") return v @app.route("/statement-period/bulk-close", methods=["PUT"]) @validate() def bulk_close_statement_periods( body: BulkCloseStatementPeriodsBody, ) -> ResponseReturnValue: """Bulk close statement periods. Returns: flask.Response: Response with no content. """ statement_period.bulk_close_statement_periods( period_name=body.period_name, new_abacus_statement_period_id=body.new_abacus_statement_period_id, ) return Response(status=HTTPStatus.NO_CONTENT) class GetTransactionsForStatementPeriodQuery(BaseSchema): """Query parameters for GET /statement-period//transactions.""" limit: int = 0 offset: int = 0 @app.route("/statement-period//transactions", methods=["GET"]) @validate() @utils.fetch_authorized_resources def get_transactions_for_statement_period( statement_period_id: int, authorized_resources, user, query: GetTransactionsForStatementPeriodQuery, ) -> ResponseReturnValue: """Get transactions for a statement period. Args: statement_period_id (int): the statement's period unique identifier. Returns: flask.Response: The updated statement period. """ statement_period.check_can_access_statement_period_data( authorized_resources, statement_period_id=statement_period_id ) result = statement_period.get_transactions_for_statement_period( statement_period_id, query.limit, query.offset ) return result.message @app.route("/statement-period/totals-dataloader", methods=["POST"]) @validate() @utils.fetch_authorized_resources def statement_period_vendor_totals_dataloader( authorized_resources, user, body: StatementPeriodIdsBody ) -> ResponseReturnValue: """Get vendor-level totals for statement periods by ID. Args: authorized_resources (List[Dict]): Resources to which the requestor has access user: Unused Returns: flask.Response: List of responses corresponding to IDs """ result = statement_period.statement_period_vendor_totals_dataloader( body.statement_period_ids, authorized_resources ) return result class GetStatementPeriodParticipationsQuery(BaseSchema): """Query parameters for GET /statement-period-participations.""" statement_period_id: Optional[int] = None collaborator_id: Optional[int] = None status: Optional[str] = None limit: Optional[int] = None offset: Optional[int] = None term: Optional[str] = None @model_validator(mode="after") def require_statement_period_or_collaborator(self): """Require at least one of statement_period_id or collaborator_id.""" if not self.statement_period_id and not self.collaborator_id: raise ValueError( "one of statement_period_id or collaborator_id is required" ) return self @app.route("/statement-period-participations", methods=["GET"]) @validate() @utils.fetch_authorized_resources @utils.fetch_profile_type def get_statement_period_participations( authorized_resources, profile_type, user, query: GetStatementPeriodParticipationsQuery, ) -> ResponseReturnValue: """Get statement period participations. Args: authorized_resources (List[Dict]): Resources to which the requestor has access user: Unused Returns: flask.Response: Paginated statement period participations response """ result = statement_period.get_statement_period_participations( authorized_resources, profile_type, query.statement_period_id, query.collaborator_id, query.status, query.limit, query.offset, query.term, ) return result.message