"""Worksheet payment custom.""" import typing from abacus_common_logic.connectors.database import db from abacus_common_logic.models.base import BaseSoftDeleteModel from flask import abort from sqlalchemy import and_, literal_column, or_, select, table from payment.constants import constants class WorksheetPaymentCustom(BaseSoftDeleteModel): """Worksheet Payment Custom Model.""" __tablename__ = 'worksheet_payment_custom' worksheet_payment_custom_id = db.Column(db.Integer, primary_key=True) account_id = db.Column(db.Integer, nullable=False) contract_id = db.Column(db.Integer, nullable=False) currency_code = db.Column(db.String(3), nullable=False) amount = db.Column(db.Numeric(20, 2), nullable=False) withholding_tax_amount = db.Column(db.Numeric(20, 2), nullable=False) withholding_tax_rate = db.Column(db.Numeric(5, 2), nullable=False) vat_amount = db.Column(db.Numeric(20, 2), nullable=False) vat_rate = db.Column(db.Numeric(5, 2), nullable=False) amount_after_withholding_and_vat = db.Column(db.Numeric(20, 2), nullable=False) activity_statement_period_id = db.Column(db.Integer, nullable=False) statement_period_id = db.Column(db.Integer, nullable=False) payment_name = db.Column(db.String(180), nullable=False) filter_args = typing.TypedDict( 'filter_args', { 'worksheet_payment_custom_ids': typing.List[int], }, total=False, ) @classmethod def get_by_id(cls, obj_id): """Get object from DB by ID property, excluding soft-deleted records. :return: object or None """ return ( db.session.execute( select(cls).where( cls.deleted_at.is_(None), cls.worksheet_payment_custom_id == obj_id, ) ) .scalars() .first() ) @classmethod def filter_for(cls, query: filter_args) -> list: """ Create filters from dict of known parameters. Accepted dict keys: worksheet_payment_custom_ids """ filters = [] worksheet_payment_custom_ids = query.get('worksheet_payment_custom_ids') if worksheet_payment_custom_ids is not None: filters.append( cls.worksheet_payment_custom_id.in_(worksheet_payment_custom_ids) ) return filters @classmethod def default_order(cls): """Define default ordering.""" return cls.created_at.desc() @classmethod def get_by_id_or_error(cls, obj_id, error_status=404, **kwargs): """Get object by ID or raise error, excluding soft-deleted records. :param obj_id: object ID :param error_status: HTTP status code for error response :return: object instance :raises: HTTPException if not found """ obj = cls.get_by_id(obj_id) if obj is None: abort( code=error_status, description=f'{cls.__name__} with id {obj_id} not found', ) return obj def is_sent(self): """Check if a worksheet_payment_custom's send_payments action is complete or running.""" query = ( select(literal_column('1')) .where( and_( literal_column('parent_table_name') == self.__tablename__, literal_column('parent_table_id') == self.worksheet_payment_custom_id, literal_column('action_name') == constants.WORKSHEET_PAYMENT_CUSTOM_ACTIONS.SEND_PAYMENTS, or_( literal_column('action_status') == constants.WORKSHEET_PAYMENT_CUSTOM_STATUSES.COMPLETE, literal_column('action_status') == constants.WORKSHEET_PAYMENT_CUSTOM_STATUSES.RUNNING, ), ) ) .select_from(table('abacus_state')) ) sent_status = db.session.execute(query).scalars().all() return bool(sent_status) @classmethod def delete_by_id_or_error(cls, obj_id, error_status=404, **kwargs): """Soft delete by ID with validation that payment is not already sent.""" obj = cls.get_by_id_or_error(obj_id, error_status) if obj.is_sent(): abort( code=400, description='Cannot delete worksheet payment custom that has already been sent.', ) obj._soft_delete() db.session.commit()