"""Vendor Payment Hold Model. Model for getting details about vendor_payment_hold. """ from owsresponse import response from sqlalchemy import BigInteger from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import Enum from sqlalchemy import func from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import Text from sqlalchemy.orm import noload from sqlalchemy.orm import relationship from ows_accounting.constants import error from ows_accounting.constants import payment_holds as constants from ows_accounting.models import vendor_payment_hold_log from ows_accounting.utils import mysql class VendorPaymentHold(mysql.BaseModel): """Class for vendor_payment_hold table.""" __tablename__ = 'vendor_payment_hold' # SQLite autoincrement feature, requires that the column’s type is exactly # INTEGER so adding a sqlite variant for BigInt. hold_id = Column( 'id', BigInteger().with_variant(Integer, 'sqlite'), primary_key=True, autoincrement=True) creator_id = Column(String(10), nullable=False) status = Column(Enum('active', 'inactive'), nullable=False, index=True) description = Column(Text) vendor_id = Column(Integer, nullable=False, index=True) created_time = Column(DateTime, server_default=func.now()) last_updated = Column( DateTime, server_default=func.now(), onupdate=func.now()) hold_logs = relationship(vendor_payment_hold_log.VendorPaymentHoldLog) def as_dict(self): """Return object as dict. Returns: dict: Dictionary representation of the object. """ hold_dict = { 'hold_id': self.hold_id, 'creator_id': self.creator_id, 'status': self.status, 'description': self.description, 'vendor_id': self.vendor_id, 'created_time': self.created_time.isoformat(), 'last_updated': self.last_updated.isoformat(), 'vendor_payment_hold_log': [] } if self.hold_logs: hold_dict['vendor_payment_hold_log'] = [ each_hold.as_dict() for each_hold in self.hold_logs] return hold_dict def add_log(self, user_id, description, status, action='update'): """Get all holds by status. Args: user_id (str): editing user id. description (str): description of hold. status (str): status of hold. action (str): action on hold. """ hold_log = vendor_payment_hold_log.VendorPaymentHoldLog( user_id=user_id, action=action, description=description, status=status) self.hold_logs.append(hold_log) @mysql.wrap_db_errors def get_all_holds_by_status(status, limit=None, offset=None, vendor_ids=None): """Get all holds by status. Args: status (str): status of hold. limit (int): limit number of result records. offset (int): offset start of result records when limit is set. vendor_ids (list): list of vendor identifiers. Optional param. Returns: Response: list of hold objects without log details. """ with mysql.holds_db_session(False) as session: full_query = session.query(VendorPaymentHold).options( noload(VendorPaymentHold.hold_logs)).filter( VendorPaymentHold.status == status) if vendor_ids: full_query = full_query.filter(VendorPaymentHold.vendor_id.in_( vendor_ids)) if limit: full_query = full_query.limit(limit).offset(offset) holds = full_query.all() all_holds = [each_hold.as_dict() for each_hold in holds] return response.Response(all_holds) @mysql.wrap_db_errors def count_holds_by_status(status, vendor_ids=None): """Get number of holds by status, optionally filter by vendor ids. Args: status (str): status of hold. vendor_ids (list): list of vendor identifiers. Optional param. Returns: Response: with number of holds. """ with mysql.holds_db_session(False) as session: query = session.query( func.count(VendorPaymentHold.hold_id)).filter( VendorPaymentHold.status == status) if vendor_ids: query = query.filter(VendorPaymentHold.vendor_id.in_(vendor_ids)) total_records = query.scalar() return response.Response(message=total_records) @mysql.wrap_db_errors def get_hold_by_id(hold_id): """Get all holds by status. Args: hold_id (str): Hold id. Returns: Response: hold objects with log details. """ with mysql.holds_db_session(False) as session: hold_obj = session.query(VendorPaymentHold).outerjoin( VendorPaymentHold.hold_logs).filter( VendorPaymentHold.hold_id == hold_id).one_or_none() if not hold_obj: return response.create_not_found_response( constants.NO_HOLDS_FOUND_FOR_ID) return response.Response(hold_obj.as_dict()) @mysql.wrap_db_errors def create_hold_for_vendor(vendor_id, creator_id, status, description): """Create a new hold and log for vendor_id. Args: vendor_id (int): vendor identifier. creator_id (str): OA user id in grass format. status (str): Hold status. description (str): description for new hold. Returns: Response: New hold object with log data. """ with mysql.holds_db_session(False) as session: hold = VendorPaymentHold( creator_id=creator_id, status=status, description=description, vendor_id=vendor_id) hold.add_log(creator_id, description, status, action='create') session.add(hold) session.commit() session.refresh(hold) return response.Response(message=hold.as_dict()) @mysql.wrap_db_errors def update_hold_by_id(hold_id, editor_id, status, description=''): """Update hold data and add a log entry for it. Args: hold_id (int): Hold id. editor_id (str): OA user id in grass format who is editing the hold. status (str): Hold status. description (str): description for new hold. (Optional) Returns: Response: Updated hold object. """ with mysql.holds_db_session(False) as session: hold = session.query(VendorPaymentHold).filter( VendorPaymentHold.hold_id == hold_id).one_or_none() if not hold: return response.create_not_found_response( constants.INVALID_HOLD_ID) if hold.status == 'inactive': return response.create_error_response( error.ERROR_CODE_INVALID_REQUEST, constants.INACTIVE_HOLD_EDIT) hold.status = status hold.description = description hold.add_log(editor_id, description, status, 'update') session.merge(hold) session.commit() session.refresh(hold) return response.Response(message=hold.as_dict()) @mysql.wrap_db_errors def get_all_holds_by_vendor(vendor_id): """Get all holds by vendor id. Args: vendor_id (int): vendor id. Returns: Response: list of hold objects without log details for this vendor. """ with mysql.holds_db_session(False) as session: holds = session.query(VendorPaymentHold).options( noload(VendorPaymentHold.hold_logs)).filter( VendorPaymentHold.vendor_id == vendor_id).all() if not holds: return response.create_not_found_response( constants.NO_HOLDS_FOUND_FOR_VENDOR) all_holds = [each_hold.as_dict() for each_hold in holds] return response.Response(all_holds) @mysql.wrap_db_errors def get_active_holds_for_vendor(vendor_id): """Get current active holds for a vendor. Args: vendor_id (int): vendor identifier. Returns: Response: hold object. """ with mysql.holds_db_session(False) as session: hold = session.query(VendorPaymentHold).options( noload(VendorPaymentHold.hold_logs)).filter( VendorPaymentHold.vendor_id == vendor_id).filter( VendorPaymentHold.status == 'active').one_or_none() if not hold: return response.create_not_found_response( constants.NO_HOLDS_FOUND_FOR_VENDOR) return response.Response(hold.as_dict())