"""Vendor contract model.""" from typing import Any, Dict from flask import g from sqlalchemy import ( Column, DateTime, Enum, Float, ForeignKey, Integer, SmallInteger, String, or_, ) from sqlalchemy.orm import relationship from contracts import response from contracts.connectors import mysql, sentry from contracts.constants import contracts_constants from contracts.models.country import Country from contracts.models.currency import Currency ActiveContract = ['vendor_contract_id', 'vendor_id'] class VendorContract(mysql.BaseModel): """VendorContract class.""" __tablename__ = 'vendor_contract' id = Column(Integer, primary_key=True) # noqa B001, B003 ringtone_publishing_type = Column(Enum('both', 'label', 'orchard', 'none')) oms_type = Column(Enum('both', 'label', 'orchard', 'none')) vendor_id = Column(Integer) physical_track_publishing_type = Column( Enum('both', 'label', 'orchard', 'none'), default='none' ) service_type_id = Column(Integer, ForeignKey('service_type.id')) currency_id = Column(SmallInteger) royalty_collection_commission = Column(Float) royalty_collection_territory = Column(String) cont_start = Column(DateTime) cont_end = Column(DateTime) service_type = relationship('ServiceType') payment_interval = Column(Enum('quarter', 'month'), default='quarter') DEFAULT_FIELDS = ( VendorContract.ringtone_publishing_type, VendorContract.oms_type, VendorContract.vendor_id, ) RINGTONE_PUBLISHING_TYPE = VendorContract.ringtone_publishing_type != 'none' OMS_TYPE = VendorContract.oms_type != 'none' PHYSICAL_TRACK_PUBLISHING_TYPE = VendorContract.physical_track_publishing_type != 'none' def get_digital_mech_admin(vendor_contract_id, vendor_id=None): """Get mech admin by id. The mech admin only applies to contracts that have a defined ringtone publishing type and a defined oms type. Args: vendor_contract_id (int): unique identifier for the vendor contract. vendor_id (int): unique identifier for the vendor (optional). Returns: response.Response: mech admin data if available. """ with mysql.db_session() as session: query = ( session.query(*DEFAULT_FIELDS) .filter(VendorContract.id == vendor_contract_id) .filter(or_(RINGTONE_PUBLISHING_TYPE, OMS_TYPE)) ) if vendor_id: query = query.filter(VendorContract.vendor_id == vendor_id) row = query.one_or_none() if row: return response.Response(row._asdict()) return response.create_not_found_response() def get_active_contract(vendor_id): """Get active contract information for a vendor. Get the data from stored procedure sp_get_active_vendor_contract. Args: vendor_id (int): unique identifier for the vendor. Returns: response.Response: row with active contract information if available. """ connection = mysql._db_engine.raw_connection() results = [] try: cursor = connection.cursor() cursor.callproc(contracts_constants.ACTIVE_CONTRACT_PROCEDURE_NAME, [vendor_id]) results = cursor.fetchone() cursor.close() if results: return response.Response(dict(zip(ActiveContract, results, strict=True))) finally: connection.close() return response.create_not_found_response() def get_contract(contract_id): """Get contract information. Args: contract_id (int): unique identifier for the contract. Returns: response.Response: row with contract information if available. """ with mysql.db_session() as session: row = ( session.query(VendorContract) .filter(VendorContract.id == contract_id) .one_or_none() ) if row: data = { 'id': row.id, 'ringtone_publishing_type': row.ringtone_publishing_type, 'oms_type': row.oms_type, 'vendor_id': row.vendor_id, 'physical_track_publishing_type': row.physical_track_publishing_type, 'service_type_id': row.service_type_id, 'currency_id': row.currency_id, 'start_date': row.cont_start, 'end_date': row.cont_end, 'payment_interval': row.payment_interval, } return response.Response(data) return response.create_not_found_response() def get_physical_mech_admin(vendor_contract_id, vendor_id=None): """Get physical mech admin for account by id. The physical mech admin only applies to contracts that have the physical track publishing type other than 'none'. Args: vendor_contract_id (int): unique identifier for the vendor contract. vendor_id (int): unique identifier for the vendor (optional). Returns: response.Response: mech admin data if available. """ with mysql.db_session() as session: query = ( session.query( VendorContract.vendor_id, VendorContract.physical_track_publishing_type ) .filter(VendorContract.id == vendor_contract_id) .filter(PHYSICAL_TRACK_PUBLISHING_TYPE) ) if vendor_id: query = query.filter(VendorContract.vendor_id == vendor_id) row = query.one_or_none() if row: return response.Response(row._asdict()) return response.create_not_found_response() def get_mech_admin_for_account(vendor_contract_id, vendor_id=None): """Get digital and physical mech admin for account by id. Args: vendor_contract_id (int): unique identifier for the vendor contract. vendor_id (int): unique identifier for the vendor (optional). Returns: response.Response: contains dict with physical and digital mech admin bool value. """ digital_mech_admin = get_digital_mech_admin(vendor_contract_id, vendor_id) physical_mech_admin = get_physical_mech_admin(vendor_contract_id, vendor_id) result = { 'mechadmin_digital': bool(digital_mech_admin), 'mechadmin_physical': bool(physical_mech_admin), } return response.Response(result) def get_contract_service_type(contract_id): """Get the service type for a given contract. Args: contract_id: unique identifier of the contract. Returns: response.Response: contains dict of service type. """ with mysql.db_session() as session: contract = ( session.query(VendorContract) .filter(VendorContract.id == contract_id) .one_or_none() ) if contract: data = {} if contract.service_type: data = contract.service_type.to_dict() return response.Response(data) return response.create_not_found_response() def get_vendor_currency(vendor_id): """Get vendor currency. Args: vendor_id (int): the vendor's unique identifier Returns: response.Response: contains dict of currency. """ with mysql.db_session() as session: currency = ( session.query( VendorContract.id, VendorContract.vendor_id, Currency.currency_id, Currency.code, Currency.symbol, ) .join(Currency, VendorContract.currency_id == Currency.currency_id) .filter(VendorContract.vendor_id == vendor_id) .order_by(VendorContract.id.desc()) .limit(1) .one_or_none() ) if not currency: return response.create_not_found_response() result = dict( id=currency[0], vendor_id=currency[1], currency_id=currency[2], code=currency[3], symbol=currency[4], ) return response.Response(result) def get_contract_royalties(contract_id): """Get royalty info for a contract. Args: contract_id (int): the contract's unique identifier. Returns: response.Response: contains commission and territory info """ with mysql.db_session() as session: contract = ( session.query( VendorContract.id, VendorContract.royalty_collection_commission, VendorContract.royalty_collection_territory, ) .filter(VendorContract.id == contract_id) .one_or_none() ) if not contract: return response.create_not_found_response() result = dict( id=contract[0], commission=contract[1], territories=contract[2].split(',') if contract[2] else [], ) countries = ( session.query(Country.country_code) .filter(Country.id.in_(result['territories'])) .all() ) result['territories'] = [country[0] for country in countries] return response.Response(result) def update_vendor_contract_service_type_id( service_type_id: int, vendor_id: int, contract_id: int ) -> Dict[str, Any]: """Update the service_type_id for a specific vendor_contract. Args: service_type_id(int): service type id of a contract vendor_id(int): unique identifier for the vendor contract_id (int): id of a vendor_contract. Returns: Exception: exception raised. """ try: with mysql.db_session() as session: session.expire_all() result = ( session.query(VendorContract) .filter( VendorContract.id == contract_id, VendorContract.vendor_id == vendor_id, ) .update({'service_type_id': service_type_id}) ) session.commit() if result < 1: raise Exception( f'No contract found for vendor_id {vendor_id} and contract_id {contract_id}.' ) return result except Exception as ex: if sentry.sentry_client: sentry.sentry_client.captureException() g.log.exception(ex.args) raise