#!/usr/bin/env python3 """ Data access layer for UPI inward file processing. Handles CRUD operations and transaction management. """ from typing import List, Optional from logging_config import get_logger from .oracle_connector import get_connector from .models import UPIInwardRecord, ProcessedFile logger = get_logger(__name__) class Repository: """Data access layer for UPI inward processing.""" def __init__(self): self.connector = get_connector() # --------------------------------------------------------- # Account validation (same as NEFT) # --------------------------------------------------------- def validate_account_exists(self, account_number: str) -> bool: if not account_number: return False last12 = str(account_number)[-12:] conn = self.connector.get_connection() try: cursor = conn.cursor() cursor.execute( "SELECT COUNT(*) FROM dep_account WHERE link_accno = :accno", {'accno': last12} ) count = cursor.fetchone()[0] return count > 0 except Exception as e: logger.warning(f"Error validating account {account_number}: {e}") return False finally: cursor.close() conn.close() # --------------------------------------------------------- # UPDATED: UPI BULK INSERT WITH VALIDATION # --------------------------------------------------------- def bulk_insert_transactions(self, transactions: List[UPIInwardRecord]) -> tuple: if not transactions: logger.warning("No transactions to insert") return 0, 0 valid_transactions = [] skipped_count = 0 for txn in transactions: acct = txn.credit_account # Condition 1: account exists account_valid = self.validate_account_exists(acct) # Condition 2: only CREDIT transactions is_credit = (txn.txn_type == "CREDIT") if account_valid and is_credit: # Force DB value to CR txn.txn_type = "CR" valid_transactions.append(txn) else: skipped_count += 1 if not valid_transactions: logger.info(f"All {skipped_count} transactions skipped") return 0, skipped_count conn = self.connector.get_connection() cursor = None try: cursor = conn.cursor() batch_data = [txn.to_dict() for txn in valid_transactions] logger.info(batch_data) insert_sql = """ INSERT INTO pacs_db.upi_inward_api_log ( BANKCODE, RRN, TXN_DATE, TXN_AMT, TXN_TYPE, CREDIT_ACCOUNT, STATUS, NOTE ) VALUES ( :BANKCODE, :RRN, :TXN_DATE, :TXN_AMT, :TXN_TYPE, :CREDIT_ACCOUNT, :STATUS, :NOTE ) """ cursor.executemany(insert_sql, batch_data) logger.info(f"executemany rowcount: {cursor.rowcount}") conn.commit() logger.info("UPI transactions committed successfully") inserted_count = len(valid_transactions) logger.info(f"Inserted {inserted_count} UPI transactions") return inserted_count, skipped_count except Exception as e: if conn: conn.rollback() logger.error(f"Error inserting UPI transactions: {e}", exc_info=True) raise finally: if cursor: cursor.close() conn.close() # --------------------------------------------------------- # FILE TRACKING (UNCHANGED) # --------------------------------------------------------- def is_file_processed(self, filename: str, bankcode: str) -> bool: conn = self.connector.get_connection() cursor = None try: cursor = conn.cursor() cursor.execute( """ SELECT COUNT(*) FROM neft_processed_files WHERE filename = :filename AND bankcode = :bankcode """, {'filename': filename, 'bankcode': bankcode} ) count = cursor.fetchone()[0] return count > 0 except Exception as e: logger.error(f"Error checking processed file: {e}", exc_info=True) return False finally: if cursor: cursor.close() conn.close() def mark_file_processed(self, processed_file: ProcessedFile) -> bool: conn = self.connector.get_connection() cursor = None try: cursor = conn.cursor() file_data = processed_file.to_dict() insert_sql = """ INSERT INTO neft_processed_files ( filename, bankcode, file_path, transaction_count, status, error_message, processed_at ) VALUES ( :filename, :bankcode, :file_path, :transaction_count, :status, :error_message, :processed_at ) """ cursor.execute(insert_sql, file_data) conn.commit() logger.info(f"Marked file as processed: {processed_file.filename}") return True except Exception as e: if conn: conn.rollback() logger.error(f"Error marking file as processed: {e}", exc_info=True) return False finally: if cursor: cursor.close() conn.close()