"""contract_lifecycle Model.""" from datetime import date from typing import Optional from abacus_common_logic.connectors.database import db from abacus_common_logic.models import BaseModel, NormalizedDate, NormalizedDateTime from abacus_common_logic.utils.dates import current_timestamp from flask import g from sqlalchemy import Enum, and_, or_ from sqlalchemy.orm import Query from abacus_contract.constants.constants import CONTRACT_LIFECYCLE_STATUSES class ContractLifecycle(BaseModel): """contract_lifecycle Model.""" __tablename__ = 'contract_lifecycle' contract_lifecycle_id = db.Column(db.Integer, primary_key=True) contract_id = db.Column( db.Integer, db.ForeignKey('contract.contract_id'), nullable=False ) contract_lifecycle_schedule_id = db.Column( db.Integer, db.ForeignKey('contract_lifecycle_schedule.contract_lifecycle_schedule_id'), nullable=True, ) lifecycle_status = db.Column( Enum(*CONTRACT_LIFECYCLE_STATUSES, name='lifecycle_status', create_type=False), nullable=False, server_default=CONTRACT_LIFECYCLE_STATUSES.INIT, ) lifecycle_term_start = db.Column(NormalizedDate(), nullable=False) lifecycle_term_end = db.Column(NormalizedDate(), nullable=True) renewal_effective = db.Column(NormalizedDate(), nullable=True) termination_notice_deadline = db.Column(NormalizedDate(), nullable=True) termination_notice_received = db.Column(NormalizedDate(), nullable=True) termination_effective = db.Column(NormalizedDate(), nullable=True) collection_start = db.Column(NormalizedDate(), nullable=True) collection_end = db.Column(NormalizedDate(), nullable=True) last_renewed = db.Column(NormalizedDate(), nullable=True) deleted_at = db.Column(NormalizedDateTime(), nullable=True) deleted_by = db.Column(db.String(180), nullable=True) @classmethod def bulk_update(cls, contract_lifecycles: list): """Update one or more contract_lifecycle records. Args: contract_lifecycles (list): a list of contract_lifecycle records that need to be updated with certain fields eg: {'contract_lifecycle_id': 1, 'lifecycle_status': 'active'} """ try: if len(contract_lifecycles) == 0: return last_modified = current_timestamp() for contract_lifecycle in contract_lifecycles: contract_lifecycle.update( { 'last_modified': last_modified, 'last_modified_by': g.user_details.get('id'), } ) db.session.bulk_update_mappings(cls, contract_lifecycles) db.session.commit() except Exception as e: db.session.rollback() raise e @classmethod def get_by_ids(cls, contract_lifecycle_ids: list) -> list: """Get contract lifecycle by their identifiers. Args: contract_lifecycle_ids(list): a list of ids of the contract_lifecycle """ return cls.query.filter( cls.contract_lifecycle_id.in_(contract_lifecycle_ids), cls.deleted_at.is_(None), cls.deleted_by.is_(None), ).all() @classmethod def get_by_contract_id_and_schedule_id( cls, contract_id: int, contract_lifecycle_schedule_id: int ) -> dict: """Get a contract_lifecycle by contract_id and contract_lifecycle_schedule_id. Args: contract_id (int): id of the contract contract_lifecycle_schedule_id (int): id of the contract_lifecycle_schedule Returns: a contract_lifecycle record if exist """ return cls.query.filter( cls.contract_id == contract_id, cls.contract_lifecycle_schedule_id == contract_lifecycle_schedule_id, cls.deleted_at.is_(None), cls.deleted_by.is_(None), ).first() @classmethod def get_by_contract_id(cls, contract_id: int) -> Optional['ContractLifecycle']: """Get a contract_lifecycle by contract_id. Args: contract_id (int): id of the contract Returns: a contract_lifecycle record if exist """ return ( cls.query.filter( cls.contract_id == contract_id, cls.deleted_at.is_(None), cls.deleted_by.is_(None), ) .order_by(cls.contract_lifecycle_id.desc()) .first() ) @classmethod def get_by_contract_ids(cls, contract_ids: list) -> list: """Get a list contract_lifecycles by contract_ids. Args: contract_ids (list): List ids of the contract Returns: a list of contract_lifecycle records if exist """ return cls.query.filter( cls.contract_id.in_(contract_ids), cls.deleted_at.is_(None), cls.deleted_by.is_(None), ).all() @classmethod def get_by_lifecycle_status(cls, lifecycle_status: str) -> Query: """Get contract_lifecycle records by lifecycle_status. Args: lifecycle_status (str): one of CONTRACT_LIFECYCLE_STATUSES Returns: a query for getting contract_lifecycle records by lifecycle_status """ return cls.query.filter( cls.lifecycle_status == lifecycle_status, cls.deleted_at.is_(None), cls.deleted_by.is_(None), ) @classmethod def get_by_renewal_effective( cls, limit: int, offset: int, renewal_effective: date ) -> tuple: """Get 'active' contract_lifecycle records by renewal_effective date. Args: limit (int): number of records to return offset (int): number of records to skip before returning data renewal_effective (date): date on which contract is set to renew Returns: a tuple of results and total_count """ query = cls.get_by_lifecycle_status(CONTRACT_LIFECYCLE_STATUSES.ACTIVE).filter( cls.renewal_effective <= renewal_effective ) items = query.limit(limit).offset(offset).all() total_count = len(query.all()) return items, total_count @classmethod def get_by_termination_effective_or_collection_end( cls, limit: int, offset: int, termination_date: date ) -> tuple: """Get contract_lifecycle records by termination_effective or collection_end date. Args: limit (int): number of records to return offset (int): number of records to skip before returning data termination_date (date): date on which contract is set to terminated Returns: a tuple of items and total_count """ query = cls.query.filter( or_( and_( cls.lifecycle_status == CONTRACT_LIFECYCLE_STATUSES.TO_BE_TERMINATED, cls.termination_effective <= termination_date, ), and_( cls.lifecycle_status == CONTRACT_LIFECYCLE_STATUSES.IN_COLLECTION_PERIOD, cls.collection_end <= termination_date, ), ), cls.deleted_at.is_(None), cls.deleted_by.is_(None), ) items = query.limit(limit).offset(offset).all() total_count = query.count() return items, total_count @classmethod def get_by_activable( cls, limit: int, offset: int, lifecycle_term_start: date, ) -> tuple: """Get contract_lifecycle records based on init state and lifecycle_term_start. Args: limit (int): number of records to return offset (int): number of records to skip before returning data lifecycle_term_start (date): date on which contract is set to activate Returns: a tuple of results and total_count """ query = cls.get_by_lifecycle_status(CONTRACT_LIFECYCLE_STATUSES.INIT).filter( cls.lifecycle_term_start <= lifecycle_term_start ) items = query.limit(limit).offset(offset).all() total_count = len(query.all()) return items, total_count