327 lines
19 KiB
Python
327 lines
19 KiB
Python
import sys
|
|
import arrow
|
|
from decimal import Decimal
|
|
from Schemas import BuildDecisionBookPayments, AccountRecords, ApiEnumDropdown
|
|
|
|
# Counters for tracking conditions
|
|
counter_current_currency_not_positive = 0 # Track when current_currency_value <= 0
|
|
counter_net_amount_not_positive = 0 # Track when net_amount <= 0
|
|
counter_account_records_updated = 0 # Track number of account records updated
|
|
counter_payments_found = 0 # Track how many payments were found
|
|
counter_found_payment_skips = 0 # Track how many times we skip due to found_payment
|
|
counter_payment_exceptions = 0 # Track exceptions during payment processing
|
|
counter_missing_build_parts_id = 0 # Track accounts with missing build_parts_id
|
|
counter_null_payment_types = 0 # Track payments with null payment_types_id
|
|
|
|
|
|
def pay_the_registration(account_record, receive_enum, debit_enum, is_old_record: bool = False, session=None):
|
|
# If no session is provided, create a new one
|
|
if session is None:
|
|
with AccountRecords.new_session() as new_session:
|
|
AccountRecords.set_session(new_session)
|
|
BuildDecisionBookPayments.set_session(new_session)
|
|
return _process_payment(account_record, receive_enum, debit_enum, is_old_record, new_session)
|
|
else:
|
|
# Use the provided session
|
|
AccountRecords.set_session(session)
|
|
BuildDecisionBookPayments.set_session(session)
|
|
return _process_payment(account_record, receive_enum, debit_enum, is_old_record, session)
|
|
|
|
|
|
def _process_payment(account_record, receive_enum, debit_enum, is_old_record, session):
|
|
"""Internal function to process payments with a given session"""
|
|
current_currency_value = float(Decimal(account_record.currency_value)) - float(Decimal(account_record.remainder_balance))
|
|
if not current_currency_value > 0:
|
|
global counter_current_currency_not_positive
|
|
counter_current_currency_not_positive += 1
|
|
return current_currency_value
|
|
|
|
# Check if account_record has build_parts_id
|
|
if account_record.build_parts_id is None:
|
|
global counter_missing_build_parts_id
|
|
counter_missing_build_parts_id += 1
|
|
return current_currency_value
|
|
|
|
process_date = arrow.get(account_record.bank_date)
|
|
account_bank_date_year, account_bank_date_month = (process_date.date().year, process_date.date().month)
|
|
|
|
# First, try to find payments with null payment_types_id
|
|
payment_arguments_debit = [
|
|
BuildDecisionBookPayments.build_parts_id == account_record.build_parts_id,
|
|
BuildDecisionBookPayments.account_records_id == None,
|
|
]
|
|
|
|
# Add date filters if not processing old records
|
|
if not is_old_record:
|
|
payment_arguments_debit.extend([BuildDecisionBookPayments.process_date_y == int(account_bank_date_year), BuildDecisionBookPayments.process_date_m == int(account_bank_date_month)])
|
|
|
|
# First try with debit_enum.id
|
|
payments = BuildDecisionBookPayments.query.filter(*payment_arguments_debit, BuildDecisionBookPayments.payment_types_id == debit_enum.id).order_by(BuildDecisionBookPayments.process_date.asc()).all()
|
|
|
|
# If no payments found, try with null payment_types_id
|
|
if len(payments) == 0:
|
|
payments = BuildDecisionBookPayments.query.filter(*payment_arguments_debit, BuildDecisionBookPayments.payment_types_id == None).order_by(BuildDecisionBookPayments.process_date.asc()).all()
|
|
if len(payments) > 0:
|
|
global counter_null_payment_types
|
|
counter_null_payment_types += len(payments)
|
|
|
|
global counter_payments_found
|
|
counter_payments_found += len(payments)
|
|
|
|
# Debug: Print info about the first few payments found (if any)
|
|
if len(payments) > 0 and account_record.id % 100 == 0: # Only print for every 100th record to avoid too much output
|
|
print(f"DEBUG: Found {len(payments)} payments for account_record {account_record.id}")
|
|
if len(payments) > 0:
|
|
sample_payment = payments[0]
|
|
print(f" Sample payment: ID={getattr(sample_payment, 'id', 'N/A')}, amount={getattr(sample_payment, 'payment_amount', 'N/A')}")
|
|
|
|
if len(payments) == 0:
|
|
# No payments found for this account record
|
|
return current_currency_value
|
|
for payment in payments:
|
|
if not current_currency_value > 0:
|
|
return current_currency_value
|
|
payment_arguments_receive = [
|
|
BuildDecisionBookPayments.build_parts_id == account_record.build_parts_id,
|
|
BuildDecisionBookPayments.payment_plan_time_periods == payment.payment_plan_time_periods,
|
|
BuildDecisionBookPayments.payment_types_id == receive_enum.id,
|
|
BuildDecisionBookPayments.build_decision_book_item_id == payment.build_decision_book_item_id,
|
|
BuildDecisionBookPayments.decision_book_project_id == payment.decision_book_project_id,
|
|
BuildDecisionBookPayments.process_date == payment.process_date,
|
|
]
|
|
if not is_old_record:
|
|
payment_arguments_receive.extend([BuildDecisionBookPayments.process_date_y == int(account_bank_date_year), BuildDecisionBookPayments.process_date_m == int(account_bank_date_month)])
|
|
|
|
payment_received = BuildDecisionBookPayments.query.filter(*payment_arguments_receive).all()
|
|
sum_of_payment_received = sum([abs(payment.payment_amount) for payment in payment_received])
|
|
net_amount = float(abs(Decimal(payment.payment_amount))) - float(abs(Decimal(sum_of_payment_received)))
|
|
if not net_amount > 0:
|
|
global counter_net_amount_not_positive
|
|
counter_net_amount_not_positive += 1
|
|
continue
|
|
if float(abs(current_currency_value)) < float(abs(net_amount)):
|
|
net_amount = float(current_currency_value)
|
|
process_date = arrow.get(payment.process_date)
|
|
try:
|
|
found_payment = BuildDecisionBookPayments.query.filter_by(
|
|
build_parts_id=payment.build_parts_id, payment_plan_time_periods=payment.payment_plan_time_periods,
|
|
payment_types_id=receive_enum.id, build_decision_book_item_id=payment.build_decision_book_item_id,
|
|
decision_book_project_id=payment.decision_book_project_id, process_date=str(process_date),
|
|
).first()
|
|
if found_payment:
|
|
global counter_found_payment_skips
|
|
counter_found_payment_skips += 1
|
|
continue
|
|
created_book_payment = BuildDecisionBookPayments.create(
|
|
payment_plan_time_periods=payment.payment_plan_time_periods, payment_amount=float(abs(net_amount)),
|
|
payment_types_id=receive_enum.id, payment_types_uu_id=str(receive_enum.uu_id),
|
|
process_date=str(process_date), process_date_m=process_date.date().month, process_date_y=process_date.date().year,
|
|
period_time=f"{process_date.year}-{str(process_date.month).zfill(2)}", build_parts_id=payment.build_parts_id,
|
|
build_parts_uu_id=str(payment.build_parts_uu_id), account_records_id=account_record.id,
|
|
account_records_uu_id=str(account_record.uu_id), build_decision_book_item_id=payment.build_decision_book_item_id,
|
|
build_decision_book_item_uu_id=str(payment.build_decision_book_item_uu_id), decision_book_project_id=payment.decision_book_project_id,
|
|
decision_book_project_uu_id=str(payment.decision_book_project_uu_id),
|
|
)
|
|
created_book_payment.save()
|
|
created_payment_amount = float(Decimal(created_book_payment.payment_amount))
|
|
remainder_balance = float(Decimal(account_record.remainder_balance)) + float(abs(created_payment_amount))
|
|
account_record.update(remainder_balance=remainder_balance)
|
|
account_record.save()
|
|
|
|
global counter_account_records_updated
|
|
counter_account_records_updated += 1
|
|
if current_currency_value >= abs(net_amount):
|
|
current_currency_value -= abs(net_amount)
|
|
except Exception as e:
|
|
print("Exception of decision payment:", e)
|
|
global counter_payment_exceptions
|
|
counter_payment_exceptions += 1
|
|
return current_currency_value
|
|
|
|
|
|
def create_direct_payment(account_record, receive_enum, session):
|
|
"""
|
|
Create a direct payment record for an account record without relying on matching BuildDecisionBookPayments
|
|
"""
|
|
try:
|
|
# Calculate the amount to process
|
|
payment_amount = float(Decimal(account_record.currency_value)) - float(Decimal(account_record.remainder_balance))
|
|
if payment_amount <= 0:
|
|
return False
|
|
|
|
# Get process date information
|
|
process_date = arrow.get(account_record.bank_date)
|
|
process_date_y = process_date.date().year
|
|
process_date_m = process_date.date().month
|
|
period_time = f"{process_date_y}-{str(process_date_m).zfill(2)}"
|
|
|
|
# Check if a payment already exists for this account record
|
|
existing_payment = BuildDecisionBookPayments.query.filter_by(account_records_id=account_record.id, payment_types_id=receive_enum.id).first()
|
|
if existing_payment: # Skip if payment already exists
|
|
return False
|
|
|
|
# Create a new payment record directly
|
|
created_book_payment = BuildDecisionBookPayments.create(
|
|
payment_plan_time_periods=1, # Default value
|
|
payment_types_id=receive_enum.id, payment_types_uu_id=str(receive_enum.uu_id), payment_amount=payment_amount, process_date=str(process_date), process_date_y=process_date_y,
|
|
process_date_m=process_date_m, period_time=period_time, account_records_id=account_record.id, account_records_uu_id=str(account_record.uu_id),
|
|
build_parts_id=account_record.build_parts_id, build_parts_uu_id=str(account_record.build_parts_uu_id) if hasattr(account_record, 'build_parts_uu_id') and account_record.build_parts_uu_id else None,
|
|
decision_book_project_id=getattr(account_record, 'decision_book_project_id', None),
|
|
decision_book_project_uu_id=str(account_record.decision_book_project_uu_id) if hasattr(account_record, 'decision_book_project_uu_id') and account_record.decision_book_project_uu_id else None,
|
|
)
|
|
created_book_payment.save()
|
|
|
|
# Update the account record
|
|
remainder_balance = float(Decimal(account_record.remainder_balance)) + float(abs(payment_amount))
|
|
account_record.update(remainder_balance=remainder_balance)
|
|
account_record.save()
|
|
|
|
global counter_account_records_updated
|
|
counter_account_records_updated += 1
|
|
return True
|
|
except Exception as e:
|
|
print(f"Exception in create_direct_payment for account {account_record.id}: {e}")
|
|
global counter_payment_exceptions
|
|
counter_payment_exceptions += 1
|
|
return False
|
|
|
|
|
|
def send_accounts_to_decision_payment():
|
|
with ApiEnumDropdown.new_session() as session:
|
|
# Set the session for all models that will be used
|
|
ApiEnumDropdown.set_session(session)
|
|
AccountRecords.set_session(session)
|
|
BuildDecisionBookPayments.set_session(session)
|
|
|
|
try:
|
|
# Get required enum values
|
|
receive_enum = ApiEnumDropdown.query.filter_by(enum_class="DebitTypes", key="DT-R").first()
|
|
debit_enum = ApiEnumDropdown.query.filter_by(enum_class="DebitTypes", key="DT-D").first()
|
|
if not receive_enum or not debit_enum:
|
|
print("Error: Could not find required enum values")
|
|
return
|
|
|
|
# Check if there are any BuildDecisionBookPayments records at all
|
|
total_payments = BuildDecisionBookPayments.query.count()
|
|
print(f"\n--- DEBUG: Database Statistics ---")
|
|
print(f"Total BuildDecisionBookPayments records in database: {total_payments}")
|
|
|
|
# Check how many have payment_types_id = debit_enum.id
|
|
debit_payments = BuildDecisionBookPayments.query.filter_by(payment_types_id=debit_enum.id).count()
|
|
print(f"BuildDecisionBookPayments with payment_types_id={debit_enum.id} (DT-D): {debit_payments}")
|
|
|
|
# Check how many have account_records_id = None
|
|
null_account_payments = BuildDecisionBookPayments.query.filter(BuildDecisionBookPayments.account_records_id == None).count()
|
|
print(f"BuildDecisionBookPayments with account_records_id=None: {null_account_payments}")
|
|
|
|
# Check a sample payment record
|
|
sample_payment = BuildDecisionBookPayments.query.first()
|
|
if sample_payment:
|
|
print("\n--- Sample BuildDecisionBookPayment ---")
|
|
print(f"ID: {getattr(sample_payment, 'id', 'N/A')}")
|
|
print(f"payment_types_id: {getattr(sample_payment, 'payment_types_id', 'N/A')}")
|
|
print(f"build_parts_id: {getattr(sample_payment, 'build_parts_id', 'N/A')}")
|
|
print(f"account_records_id: {getattr(sample_payment, 'account_records_id', 'N/A')}")
|
|
print(f"process_date_y: {getattr(sample_payment, 'process_date_y', 'N/A')}")
|
|
print(f"process_date_m: {getattr(sample_payment, 'process_date_m', 'N/A')}")
|
|
else:
|
|
print("No BuildDecisionBookPayment records found in the database!")
|
|
|
|
# Check a sample account record
|
|
sample_account = AccountRecords.query.filter(AccountRecords.remainder_balance < AccountRecords.currency_value, AccountRecords.receive_debit == receive_enum.id).first()
|
|
if sample_account:
|
|
print("\n--- Sample AccountRecord ---")
|
|
print(f"ID: {getattr(sample_account, 'id', 'N/A')}")
|
|
print(f"build_parts_id: {getattr(sample_account, 'build_parts_id', 'N/A')}")
|
|
print(f"bank_date: {getattr(sample_account, 'bank_date', 'N/A')}")
|
|
|
|
# Try to find payments for this specific account record
|
|
if sample_account.bank_date:
|
|
process_date = arrow.get(sample_account.bank_date)
|
|
account_bank_date_year, account_bank_date_month = (process_date.date().year, process_date.date().month)
|
|
|
|
print("\n--- Checking for payments for sample account ---")
|
|
print(f"Looking for payments with build_parts_id={sample_account.build_parts_id}, payment_types_id={debit_enum.id}, account_records_id=None")
|
|
|
|
# Try without date filters first
|
|
basic_payments = BuildDecisionBookPayments.query.filter(
|
|
BuildDecisionBookPayments.build_parts_id == sample_account.build_parts_id, BuildDecisionBookPayments.payment_types_id == debit_enum.id,
|
|
BuildDecisionBookPayments.account_records_id == None
|
|
).count()
|
|
print(f"Found {basic_payments} payments without date filters")
|
|
|
|
# Now try with date filters
|
|
dated_payments = BuildDecisionBookPayments.query.filter(
|
|
BuildDecisionBookPayments.build_parts_id == sample_account.build_parts_id, BuildDecisionBookPayments.payment_types_id == debit_enum.id,
|
|
BuildDecisionBookPayments.account_records_id == None, BuildDecisionBookPayments.process_date_y == int(account_bank_date_year),
|
|
BuildDecisionBookPayments.process_date_m == int(account_bank_date_month)
|
|
).count()
|
|
print(f"Found {dated_payments} payments with date filters (year={account_bank_date_year}, month={account_bank_date_month})")
|
|
else:
|
|
print("No matching AccountRecord found for debugging!")
|
|
|
|
# Query for account records that need payment processing
|
|
# Note: We removed the approved_record condition as it was too restrictive
|
|
account_records_list = AccountRecords.query.filter(AccountRecords.remainder_balance < AccountRecords.currency_value, AccountRecords.receive_debit == receive_enum.id
|
|
).order_by(AccountRecords.bank_date.desc()).all()
|
|
|
|
print(f"\nProcessing {len(account_records_list)} account records")
|
|
|
|
# Track how many records were processed with each method
|
|
traditional_method_count = 0
|
|
direct_method_count = 0
|
|
|
|
for account_record in account_records_list:
|
|
# Try the traditional method first
|
|
current_currency_value = pay_the_registration(account_record, receive_enum, debit_enum, False, session)
|
|
|
|
if current_currency_value > 0:
|
|
# If first pass found payments, try with old records too
|
|
pay_the_registration(account_record, receive_enum, debit_enum, True, session)
|
|
traditional_method_count += 1
|
|
else:
|
|
# If traditional method didn't work, try direct payment for all records
|
|
# This will handle records with missing build_parts_id
|
|
if create_direct_payment(account_record, receive_enum, session):
|
|
direct_method_count += 1
|
|
if direct_method_count % 10 == 0: # Only print every 10th record to avoid too much output
|
|
print(f"Direct payment created for account_record {account_record.id}")
|
|
|
|
# Refresh the account record to get updated values
|
|
session.refresh(account_record)
|
|
|
|
# Update status if the remainder balance equals the currency value
|
|
if abs(float(Decimal(account_record.remainder_balance))) == abs(float(Decimal(account_record.currency_value))):
|
|
account_record.update(status_id=97)
|
|
account_record.save()
|
|
|
|
print(f"\nProcessed with traditional method: {traditional_method_count} records")
|
|
print(f"Processed with direct payment method: {direct_method_count} records")
|
|
|
|
print("Payment processing completed successfully")
|
|
except Exception as e:
|
|
print(f"Error in send_accounts_to_decision_payment: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
# Rollback the session in case of error
|
|
session.rollback()
|
|
return
|
|
|
|
if __name__ == "__main__":
|
|
print("Payment Service is running...")
|
|
try:
|
|
send_accounts_to_decision_payment()
|
|
|
|
# Print counter statistics
|
|
print("\n--- Processing Statistics ---")
|
|
print(f"Records where current_currency_value <= 0: {counter_current_currency_not_positive}")
|
|
print(f"Records where net_amount <= 0: {counter_net_amount_not_positive}")
|
|
print(f"Account records updated: {counter_account_records_updated}")
|
|
print(f"Total payments found: {counter_payments_found}")
|
|
print(f"Skips due to found_payment: {counter_found_payment_skips}")
|
|
print(f"Payment exceptions: {counter_payment_exceptions}")
|
|
|
|
except Exception as e:
|
|
print(f"Error: {e}")
|
|
print("Payment Service is finished...")
|