product
This commit is contained in:
200
db/repository.py
Normal file
200
db/repository.py
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Data access layer for ACH 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 TransactionRecord, ProcessedFile
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class Repository:
|
||||
"""Data access layer for ACH processing."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize repository with connector."""
|
||||
self.connector = get_connector()
|
||||
|
||||
def bulk_insert_transactions(self, transactions: List[TransactionRecord]) -> int:
|
||||
"""
|
||||
Bulk insert transaction records into ach_api_log.
|
||||
|
||||
Args:
|
||||
transactions: List of TransactionRecord objects
|
||||
|
||||
Returns:
|
||||
Number of inserted records
|
||||
"""
|
||||
if not transactions:
|
||||
logger.warning("No transactions to insert")
|
||||
return 0
|
||||
|
||||
conn = self.connector.get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Prepare batch data
|
||||
batch_data = [txn.to_dict() for txn in transactions]
|
||||
|
||||
# Execute batch insert
|
||||
insert_sql = """
|
||||
INSERT INTO ach_api_log (
|
||||
narration, status, bankcode, jrnl_id,
|
||||
tran_date, cbs_acct, tran_amt, TXNIND
|
||||
) VALUES (
|
||||
:narration, :status, :bankcode, :jrnl_id,
|
||||
:tran_date, :cbs_acct, :tran_amt, :TXNIND
|
||||
)
|
||||
"""
|
||||
|
||||
cursor.executemany(insert_sql, batch_data)
|
||||
conn.commit()
|
||||
|
||||
count = len(transactions)
|
||||
logger.info(f"Successfully inserted {count} transactions into ach_api_log")
|
||||
return count
|
||||
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
logger.error(f"Error inserting transactions: {e}", exc_info=True)
|
||||
raise
|
||||
finally:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
def is_file_processed(self, filename: str) -> bool:
|
||||
"""
|
||||
Check if file has already been processed.
|
||||
|
||||
Args:
|
||||
filename: Name of the file to check
|
||||
|
||||
Returns:
|
||||
True if file is in processed list, False otherwise
|
||||
"""
|
||||
conn = self.connector.get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT COUNT(*) FROM ach_processed_files WHERE filename = :filename",
|
||||
{'filename': filename}
|
||||
)
|
||||
count = cursor.fetchone()[0]
|
||||
return count > 0
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking processed file: {e}")
|
||||
return False
|
||||
finally:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
def mark_file_processed(self, processed_file: ProcessedFile) -> bool:
|
||||
"""
|
||||
Insert record into ach_processed_files to mark file as processed.
|
||||
|
||||
Args:
|
||||
processed_file: ProcessedFile object with file metadata
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
conn = self.connector.get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
|
||||
file_data = processed_file.to_dict()
|
||||
insert_sql = """
|
||||
INSERT INTO ach_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:
|
||||
conn.rollback()
|
||||
logger.error(f"Error marking file as processed: {e}", exc_info=True)
|
||||
return False
|
||||
finally:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
def get_processed_files(self, bankcode: Optional[str] = None) -> List[str]:
|
||||
"""
|
||||
Get list of processed filenames.
|
||||
|
||||
Args:
|
||||
bankcode: Optional bankcode filter
|
||||
|
||||
Returns:
|
||||
List of filenames that have been processed
|
||||
"""
|
||||
conn = self.connector.get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
|
||||
if bankcode:
|
||||
cursor.execute(
|
||||
"SELECT filename FROM ach_processed_files WHERE bankcode = :bankcode ORDER BY processed_at DESC",
|
||||
{'bankcode': bankcode}
|
||||
)
|
||||
else:
|
||||
cursor.execute("SELECT filename FROM ach_processed_files ORDER BY processed_at DESC")
|
||||
|
||||
filenames = [row[0] for row in cursor.fetchall()]
|
||||
return filenames
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error retrieving processed files: {e}")
|
||||
return []
|
||||
finally:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
def verify_tables_exist(self):
|
||||
"""
|
||||
Verify that required database tables exist.
|
||||
If tables are missing, terminate the program.
|
||||
"""
|
||||
conn = self.connector.get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if ach_api_log table exists
|
||||
try:
|
||||
cursor.execute("SELECT COUNT(*) FROM ach_api_log WHERE ROWNUM = 1")
|
||||
logger.info("✓ ach_api_log table exists")
|
||||
except Exception as e:
|
||||
logger.error(f"✗ ach_api_log table not found: {e}")
|
||||
raise SystemExit("FATAL: ach_api_log table must be created manually before running this application")
|
||||
|
||||
# Check if ach_processed_files table exists
|
||||
try:
|
||||
cursor.execute("SELECT COUNT(*) FROM ach_processed_files WHERE ROWNUM = 1")
|
||||
logger.info("✓ ach_processed_files table exists")
|
||||
except Exception as e:
|
||||
logger.error(f"✗ ach_processed_files table not found: {e}")
|
||||
raise SystemExit("FATAL: ach_processed_files table must be created manually before running this application")
|
||||
|
||||
logger.info("Database tables verified successfully")
|
||||
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error verifying tables: {e}", exc_info=True)
|
||||
raise SystemExit(f"FATAL: Error verifying database tables: {e}")
|
||||
finally:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user