from app.extensions import db from datetime import datetime, timezone from app.utils.logger import logger from app.enums.loan_status import LoanStatus from sqlalchemy.exc import IntegrityError class Repayment(db.Model): __tablename__ = "repayments" id = db.Column( db.Integer, primary_key=True, autoincrement=True, ) loan_id = db.Column(db.String(50), nullable=False) customer_id = db.Column(db.String(50), nullable=False) product_id = db.Column(db.String(20), nullable=True) transaction_id = db.Column(db.String(50), nullable=False) initiated_by = db.Column(db.String(50), nullable=True) salary_amount = db.Column(db.Float, nullable=True, default=0.0) created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) repay_date = db.Column(db.DateTime, nullable=True) verify_date = db.Column(db.DateTime, nullable=True) repay_result = db.Column(db.String(10), nullable=True) repay_description = db.Column(db.String(100), nullable=True) verify_result = db.Column(db.String(10), nullable=True) verify_description = db.Column(db.String(100), nullable=True) def __repr__(self): return f'' def to_dict(self): """ Convert the Repayment object to a dictionary format for JSON serialization. """ return { 'Id': self.id, "customerId": self.customer_id, 'loanId': self.loan_id, 'productId': self.product_id, 'repayResult': self.repay_result, 'repayDescription': self.repay_description, 'verifyResult': self.verify_result, 'verifyDescription': self.verify_description, 'transactionId': self.transaction_id, 'initiatedBy':self.initiated_by, 'salaryAmount':self.salary_amount, 'repayDate': self.repay_date.isoformat() if self.repay_date else None, 'VerifyDate': self.verify_date.isoformat() if self.verify_date else None, } @classmethod def create_repayment(cls, repayment_data): if repayment_data["LoanStatus"] not in [LoanStatus.ACTIVE, LoanStatus.START_REPAY,LoanStatus.ACTIVE_PARTIAL]: raise ValueError(f"Repayment cannot be processed. Loan status: ({repayment_data['LoanStatus']})") repayment = cls( customer_id=repayment_data["customerId"], loan_id=repayment_data["loanId"], product_id=repayment_data["productId"], transaction_id=repayment_data["transactionId"], created_at=datetime.now(timezone.utc), updated_at=datetime.now(timezone.utc), initiated_by= repayment_data["initiatedBy"], salary_amount=repayment_data["salaryAmount"] ) try: db.session.add(repayment) db.session.commit() logger.info("Repayment record committed.") return repayment except IntegrityError as err: logger.error(f"Database integrity error: {err}") return {"error": "Integrity error", "details": str(err)} @classmethod def add_repayment(cls, data: dict): """ Create and persist a new repayment record. """ logger.info(f"Received repayment data: {data}") try: new_repayment = cls( loan_id=data["loanId"], customer_id=data["customerId"], product_id=data.get("productId"), transaction_id=data["transactionId"], initiated_by=data.get("initiatedBy"), salary_amount=float(data.get("salaryAmount", 0.0)), repay_date=( datetime.strptime(data["repayDate"], "%Y-%m-%d") .replace(tzinfo=timezone.utc) if data.get("repayDate") else None ), repay_result=data.get("repayResult"), repay_description=data.get("repayDescription"), verify_result=data.get("verifyResult"), verify_description=data.get("verifyDescription"), verify_date=( datetime.strptime(data["verifyDate"], "%Y-%m-%d") .replace(tzinfo=timezone.utc) if data.get("verifyDate") else None ), ) db.session.add(new_repayment) db.session.commit() logger.info("Repayment record committed.") return new_repayment except Exception as e: db.session.rollback() logger.error(f"Error adding repayment data: {e}") raise @classmethod def get_repayment_by_transaction_id(cls, transaction_id): return cls.query.filter_by(transaction_id=transaction_id).first() @classmethod def get_repayment_by_id(cls, id): return cls.query.filter_by(id=id).first() @classmethod def set_repay_date(cls, repayment_id, customer_id): """ Update the repay date of the loan with the given loan_id. """ # Retrieve repayment repayment = cls.query.get(repayment_id) if not repayment: raise ValueError(f"repayment with ID {repayment_id} does not exist.") # Check if customer_id matches if repayment.customer_id != customer_id: raise ValueError(f"Customer ID {customer_id} does not match the repayment's customer ID.") current_time = datetime.now() logger.info(f"What is now ======= ==== ==> : {current_time}") # Update repayment date repayment.repay_date = current_time # Commit changes to database try: logger.info(f"Updating repay date for repayment ID {repayment_id} to {current_time}") db.session.commit() return repayment.to_dict() except Exception as e: db.session.rollback() logger.error(f"Failed to update repay date: {e}") raise e @classmethod def set_repay_verify_date(cls, repayment_id, customer_id): """ Update the repayment verify date of the loan with the given repayment_id. """ # Retrieve repayment repayment = cls.query.get(repayment_id) if not repayment: raise ValueError(f"repayment with ID {repayment_id} does not exist.") # Check if customer_id matches if repayment.customer_id != customer_id: raise ValueError(f"Customer ID {customer_id} does not match the repayment's customer ID.") current_time = datetime.now() logger.info(f"What is now ======= ==== ==> : {current_time}") # Update repayment verify_date repayment.verify_date = current_time # Commit changes to database try: logger.info(f"Updating repay verify date for repayment ID {repayment_id} to {current_time}") db.session.commit() except Exception as e: db.session.rollback() logger.error(f"Failed to update repay verify date: {e}") raise e @classmethod def set_repay_result(cls, repayment_id, result, description): logger.info("repay result called") """ Update the repayment result and description of the repayment with the given repayment_id. """ # Retrieve loan repayment = cls.query.get(repayment_id) if not repayment: raise ValueError(f"repayment with ID {repayment_id} does not exist.") # Update repayment result and description repayment.repay_result = result repayment.repay_description = description # Commit changes to database try: logger.info(f"Updating repayment result for repayment ID {repayment_id} to {result} with description {description}") db.session.commit() except Exception as e: db.session.rollback() logger.error(f"Failed to update repayment result: {e}") raise @classmethod def set_verify_date_result(cls, repayment_id, result, description): """ Update the verify result and description of the repayment with the given repayment_id. """ # Retrieve repayment repayment = cls.query.get(repayment_id) if not repayment: raise ValueError(f"repayment with ID {repayment_id} does not exist.") # Update disburse result and description repayment.verify_result = result repayment.verify_description = description # Commit changes to database try: logger.info(f"Updating verify result for repayment ID {repayment_id} to {result} with description {description}") db.session.commit() except Exception as e: db.session.rollback() logger.error(f"Failed to update verify result: {e}") raise @classmethod def get_latest_repayment_without_repay_date(cls): """ Get the latest repayment without a repay date. """ return cls.query.filter( cls.repay_date.is_(None) ).order_by(cls.created_at.desc()).first() @classmethod def get_latest_repayment_with_loanId(cls, loan_id): """ Get the latest repayment with loan Id. """ return cls.query.filter( cls.loan_id == loan_id ).order_by(cls.created_at.desc()).first() @classmethod def get_latest_loan_with_repay_date(cls): """ Get the latest repayment with a repay date and no verification date. """ return cls.query.filter( cls.repay_date.isnot(None), cls.verify_date.is_(None) ).order_by(cls.created_at.desc()).first()