updated payment service
This commit is contained in:
385
ServicesBank/Finder/Payment/draft/old_runner_second.py
Normal file
385
ServicesBank/Finder/Payment/draft/old_runner_second.py
Normal file
@@ -0,0 +1,385 @@
|
||||
import arrow
|
||||
import calendar
|
||||
import time
|
||||
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from Schemas import BuildDecisionBookPayments, AccountRecords, ApiEnumDropdown, Build, BuildDecisionBook
|
||||
from time import perf_counter
|
||||
from sqlalchemy import select, func, distinct, cast, Date, String, literal, desc, and_, or_, case
|
||||
from Controllers.Postgres.engine import get_session_factory
|
||||
#from ServicesApi.Schemas.account.account import AccountRecords
|
||||
#from ServicesApi.Schemas.building.decision_book import BuildDecisionBookPayments
|
||||
|
||||
|
||||
def find_last_day_of_month(date_value):
|
||||
today = date_value.date()
|
||||
_, last_day = calendar.monthrange(today.year, today.month)
|
||||
return datetime(today.year, today.month, last_day, 23, 59, 59)
|
||||
|
||||
def find_first_day_of_month(date_value):
|
||||
today = date_value.date()
|
||||
return datetime(today.year, today.month, 1)
|
||||
|
||||
|
||||
def add_month_to_date(date_value):
|
||||
month_to_process = date_value.month
|
||||
year_to_process = date_value.year
|
||||
if date_value.month == 12:
|
||||
month_to_process = 1
|
||||
year_to_process += 1
|
||||
else:
|
||||
month_to_process += 1
|
||||
_, last_day = calendar.monthrange(year_to_process, month_to_process)
|
||||
return datetime(year_to_process, month_to_process, 1), datetime(year_to_process, month_to_process, last_day)
|
||||
|
||||
|
||||
class BuildDuesTypes:
|
||||
|
||||
def __init__(self):
|
||||
self.debit: ApiEnumDropdownShallowCopy = None
|
||||
self.add_debit: ApiEnumDropdownShallowCopy = None
|
||||
self.renovation: ApiEnumDropdownShallowCopy = None
|
||||
self.lawyer_expence: ApiEnumDropdownShallowCopy = None
|
||||
self.service_fee: ApiEnumDropdownShallowCopy = None
|
||||
self.information: ApiEnumDropdownShallowCopy = None
|
||||
|
||||
|
||||
class ApiEnumDropdownShallowCopy:
|
||||
id: int
|
||||
uuid: str
|
||||
enum_class: str
|
||||
key: str
|
||||
value: str
|
||||
|
||||
def __init__(self, id: int, uuid: str, enum_class: str, key: str, value: str):
|
||||
self.id = id
|
||||
self.uuid = uuid
|
||||
self.enum_class = enum_class
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
|
||||
def retrieve_remainder_balance_set_if_needed(account_record_id: int, session):
|
||||
"""Update the remainder_balance of an account after spending money.
|
||||
|
||||
Args:
|
||||
account_record: The account record to update
|
||||
amount_spent: The amount spent in this transaction
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
bool: True if all money is spent, False otherwise
|
||||
"""
|
||||
AccountRecords.set_session(session)
|
||||
|
||||
account_record_remainder_balance = session.query(func.sum(func.abs(BuildDecisionBookPayments.payment_amount))).filter(
|
||||
BuildDecisionBookPayments.account_records_id == account_record_id,
|
||||
BuildDecisionBookPayments.account_is_debit == False
|
||||
).scalar()
|
||||
|
||||
if not account_record_remainder_balance:
|
||||
account_record_remainder_balance = 0
|
||||
|
||||
account_record = AccountRecords.query.filter_by(id=account_record_id).first()
|
||||
account_record.remainder_balance = -1 * abs(account_record_remainder_balance) if account_record_remainder_balance != 0 else 0
|
||||
account_record.save()
|
||||
return account_record_remainder_balance
|
||||
|
||||
|
||||
def retrieve_current_debt_to_pay_from_database(ref_id: int, session):
|
||||
debt_to_pay = session.query(func.sum(BuildDecisionBookPayments.payment_amount)).filter(BuildDecisionBookPayments.ref_id == ref_id).scalar()
|
||||
return abs(debt_to_pay)
|
||||
|
||||
|
||||
def check_current_debt_to_pay_from_database_is_closed(ref_id: int, session):
|
||||
session.commit()
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
payment_row_book = BuildDecisionBookPayments.query.filter(BuildDecisionBookPayments.ref_id == str(ref_id), BuildDecisionBookPayments.account_is_debit == True).first()
|
||||
debt_paid = session.query(func.sum(func.abs(BuildDecisionBookPayments.payment_amount))).filter(
|
||||
BuildDecisionBookPayments.ref_id == str(ref_id), BuildDecisionBookPayments.account_is_debit == False
|
||||
).scalar()
|
||||
payment_row_book.debt_paid = abs(debt_paid) if debt_paid else 0 # Debt Reminder is how much money is needed to close record
|
||||
payment_row_book.debt_to_pay = abs(payment_row_book.payment_amount) - abs(payment_row_book.debt_paid) # Debt To Pay is how much money is needed to close record
|
||||
payment_row_book.is_closed = payment_row_book.debt_to_pay == 0
|
||||
payment_row_book.save()
|
||||
|
||||
|
||||
def close_payment_book(payment_row_book, account_record, value, session):
|
||||
"""Create a credit entry in BuildDecisionBookPayments to close a debt.
|
||||
|
||||
Args:
|
||||
payment_row_book: The debit entry to be paid
|
||||
account_record: The account record containing the funds
|
||||
value: The amount to pay
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
The newly created payment record
|
||||
"""
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
account_record_remainder_balance = retrieve_remainder_balance_set_if_needed(account_record_id=account_record.id, session=session)
|
||||
print(f'NOT Updated remainder balance: {account_record_remainder_balance} | Account record id: {account_record.id}')
|
||||
|
||||
# Create a new credit entry (payment)
|
||||
new_row = BuildDecisionBookPayments.create(
|
||||
ref_id=str(payment_row_book.uu_id),
|
||||
payment_plan_time_periods=payment_row_book.payment_plan_time_periods,
|
||||
period_time=payment_row_book.period_time,
|
||||
currency=payment_row_book.currency,
|
||||
account_records_id=account_record.id,
|
||||
account_records_uu_id=str(account_record.uu_id),
|
||||
build_parts_id=payment_row_book.build_parts_id,
|
||||
build_parts_uu_id=str(payment_row_book.build_parts_uu_id),
|
||||
payment_amount=abs(value), # Negative for credit entries
|
||||
payment_types_id=payment_row_book.payment_types_id,
|
||||
payment_types_uu_id=str(payment_row_book.payment_types_uu_id),
|
||||
process_date_m=payment_row_book.process_date.month,
|
||||
process_date_y=payment_row_book.process_date.year,
|
||||
process_date=payment_row_book.process_date,
|
||||
build_decision_book_item_id=payment_row_book.build_decision_book_item_id if payment_row_book.build_decision_book_item_id else None,
|
||||
build_decision_book_item_uu_id=str(payment_row_book.build_decision_book_item_uu_id) if payment_row_book.build_decision_book_item_uu_id else None,
|
||||
decision_book_project_id=payment_row_book.decision_book_project_id if payment_row_book.decision_book_project_id else None,
|
||||
decision_book_project_uu_id=str(payment_row_book.decision_book_project_uu_id) if payment_row_book.decision_book_project_uu_id else None,
|
||||
build_decision_book_id=payment_row_book.build_decision_book_id if payment_row_book.build_decision_book_id else None,
|
||||
build_decision_book_uu_id=str(payment_row_book.build_decision_book_uu_id) if payment_row_book.build_decision_book_uu_id else None,
|
||||
is_confirmed=True,
|
||||
account_is_debit=False,
|
||||
)
|
||||
|
||||
# Save the new payment record
|
||||
saved_row = new_row.save()
|
||||
session.commit()
|
||||
session.refresh(saved_row)
|
||||
account_record_remainder_balance = retrieve_remainder_balance_set_if_needed(account_record_id=account_record.id, session=session)
|
||||
print(f'Updated remainder balance: {account_record_remainder_balance} | Account record id: {account_record.id}')
|
||||
check_current_debt_to_pay_from_database_is_closed(ref_id=new_row.ref_id, session=session)
|
||||
return account_record_remainder_balance
|
||||
|
||||
|
||||
def get_enums_from_database():
|
||||
build_dues_types = BuildDuesTypes()
|
||||
with ApiEnumDropdown.new_session() as session:
|
||||
|
||||
ApiEnumDropdown.set_session(session)
|
||||
|
||||
debit_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-D").first() # Debit
|
||||
add_debit_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-A").first() # Add Debit
|
||||
renovation_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-R").first() # Renovation
|
||||
late_payment_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-L").first() # Lawyer expence
|
||||
service_fee_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-S").first() # Service fee
|
||||
information_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-I").first() # Information
|
||||
|
||||
build_dues_types.debit = ApiEnumDropdownShallowCopy(
|
||||
debit_enum_shallow.id, str(debit_enum_shallow.uu_id), debit_enum_shallow.enum_class, debit_enum_shallow.key, debit_enum_shallow.value
|
||||
)
|
||||
build_dues_types.add_debit = ApiEnumDropdownShallowCopy(
|
||||
add_debit_enum_shallow.id, str(add_debit_enum_shallow.uu_id), add_debit_enum_shallow.enum_class, add_debit_enum_shallow.key, add_debit_enum_shallow.value
|
||||
)
|
||||
build_dues_types.renovation = ApiEnumDropdownShallowCopy(
|
||||
renovation_enum_shallow.id, str(renovation_enum_shallow.uu_id), renovation_enum_shallow.enum_class, renovation_enum_shallow.key, renovation_enum_shallow.value
|
||||
)
|
||||
build_dues_types.lawyer_expence = ApiEnumDropdownShallowCopy(
|
||||
late_payment_enum_shallow.id, str(late_payment_enum_shallow.uu_id), late_payment_enum_shallow.enum_class, late_payment_enum_shallow.key, late_payment_enum_shallow.value
|
||||
)
|
||||
build_dues_types.service_fee = ApiEnumDropdownShallowCopy(
|
||||
service_fee_enum_shallow.id, str(service_fee_enum_shallow.uu_id), service_fee_enum_shallow.enum_class, service_fee_enum_shallow.key, service_fee_enum_shallow.value
|
||||
)
|
||||
build_dues_types.information = ApiEnumDropdownShallowCopy(
|
||||
information_enum_shallow.id, str(information_enum_shallow.uu_id), information_enum_shallow.enum_class, information_enum_shallow.key, information_enum_shallow.value
|
||||
)
|
||||
return [build_dues_types.debit, build_dues_types.lawyer_expence, build_dues_types.add_debit, build_dues_types.renovation, build_dues_types.service_fee, build_dues_types.information]
|
||||
|
||||
|
||||
def do_payments(build_id: int, work_date: datetime, run_now_bool: bool = False):
|
||||
"""Process payments for the current month's unpaid debts.
|
||||
This function retrieves account records with available funds and processes
|
||||
payments for current month's unpaid debts in order of payment type priority.
|
||||
"""
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
|
||||
# Set session for all models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
# Get payment types in priority order
|
||||
payment_type_list = get_enums_from_database()
|
||||
fund_finished = lambda money_spend, money_in_account: money_spend == money_in_account
|
||||
payments_made, total_amount_paid, paid_count = 0, 0, 0
|
||||
target_build = Build.query.filter(Build.id == build_id).first()
|
||||
if not target_build:
|
||||
raise ValueError(f"Build with id {build_id} not found")
|
||||
|
||||
decision_book = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == build_id, cast(BuildDecisionBook.expiry_starts, Date) <= work_date.date(),
|
||||
cast(BuildDecisionBook.expiry_ends, Date) >= work_date.date(), BuildDecisionBook.decision_type == "RBM"
|
||||
).first()
|
||||
if not decision_book:
|
||||
raise ValueError(f"Decision book not found for build with id {build_id}")
|
||||
|
||||
period_date_start = decision_book.expiry_starts
|
||||
period_date_end = decision_book.expiry_ends
|
||||
period_id = decision_book.id
|
||||
|
||||
first_date_of_process_date = find_first_day_of_month(datetime(work_date.year, work_date.month, 1))
|
||||
last_date_of_process_date = find_last_day_of_month(datetime(work_date.year, work_date.month, 1))
|
||||
if run_now_bool:
|
||||
last_date_of_process_date = work_date
|
||||
print('first_date_of_process_date', first_date_of_process_date)
|
||||
print('last_date_of_process_date', last_date_of_process_date)
|
||||
|
||||
# Current month date filter
|
||||
date_query_tuple = (
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= first_date_of_process_date.date(), cast(BuildDecisionBookPayments.process_date, Date) <= last_date_of_process_date.date()
|
||||
)
|
||||
date_query_account_tuple = (
|
||||
cast(AccountRecords.bank_date, Date) >= first_date_of_process_date.date(), cast(AccountRecords.bank_date, Date) <= last_date_of_process_date.date()
|
||||
)
|
||||
for payment_type in payment_type_list:
|
||||
query_of_payments = BuildDecisionBookPayments.query.filter(*date_query_tuple)
|
||||
query_of_payments = query_of_payments.filter(
|
||||
BuildDecisionBookPayments.payment_types_id == payment_type.id, BuildDecisionBookPayments.account_is_debit == True,
|
||||
BuildDecisionBookPayments.is_closed == False
|
||||
).order_by(BuildDecisionBookPayments.process_date.desc()).all()
|
||||
# priority_uuids = [payment_type.uuid, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_type.uuid]]
|
||||
# case_order = case(*[(AccountRecords.payment_result_type_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
|
||||
for payment_row in query_of_payments:
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == payment_row.build_parts_id, AccountRecords.payment_result_type == payment_type.id,
|
||||
AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance), *date_query_account_tuple
|
||||
).order_by(case_order, AccountRecords.bank_date.asc()).all()
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
# Get remainder balance from database regardless trust over AccountRecords row from first query
|
||||
remainder_balance_from_database = retrieve_remainder_balance_set_if_needed(money_to_pay_row.id, session)
|
||||
available_funds = abs(money_to_pay_row.currency_value) - abs(remainder_balance_from_database)
|
||||
# BuildDecisionBookPayments must be refreshed to check is there still money to be paid for this row
|
||||
debt_to_pay = retrieve_current_debt_to_pay_from_database(ref_id=payment_row.ref_id, session=session)
|
||||
if debt_to_pay == 0:
|
||||
break # For session caused errors double check the database FOR multi process actions
|
||||
if abs(available_funds) > abs(debt_to_pay):
|
||||
payments_made += 1
|
||||
total_amount_paid += debt_to_pay
|
||||
paid_count += 1
|
||||
close_payment_book(payment_row, money_to_pay_row, abs(debt_to_pay), session)
|
||||
break # More fund to spend so go to next BuildDecisionBookPayments
|
||||
else:
|
||||
payments_made += 1
|
||||
total_amount_paid += available_funds
|
||||
paid_count += 1
|
||||
close_payment_book(payment_row, money_to_pay_row, abs(available_funds), session)
|
||||
continue # Fund has finished so go to next AccountRecords
|
||||
|
||||
print('payments_made', payments_made)
|
||||
print('total_amount_paid', total_amount_paid)
|
||||
print('paid_count', paid_count)
|
||||
session.close()
|
||||
session_factory.remove() # Clean up the session from the registry
|
||||
|
||||
|
||||
def do_payments_of_previos_months(build_id: int):
|
||||
"""Process payments for the previous month's unpaid debts.
|
||||
This function retrieves account records with available funds and processes
|
||||
payments for previous month's unpaid debts in order of payment type priority.
|
||||
"""
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
|
||||
# Set session for all models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
# Get payment types in priority order
|
||||
payment_type_list = get_enums_from_database()
|
||||
print('payment_type_list', payment_type_list)
|
||||
fund_finished = lambda money_spend, money_in_account: money_spend == money_in_account
|
||||
payments_made, total_amount_paid, paid_count = 0, 0, 0
|
||||
target_build = Build.query.filter(Build.id == build_id).first()
|
||||
if not target_build:
|
||||
raise ValueError(f"Build with id {build_id} not found")
|
||||
|
||||
now = datetime.now()
|
||||
decision_book = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == build_id, cast(BuildDecisionBook.expiry_starts, Date) <= now.date(),
|
||||
cast(BuildDecisionBook.expiry_ends, Date) >= now.date(), BuildDecisionBook.decision_type == "RBM"
|
||||
).first()
|
||||
if not decision_book:
|
||||
raise ValueError(f"Decision book not found for build with id {build_id}")
|
||||
|
||||
period_date_start = decision_book.expiry_starts
|
||||
period_date_end = decision_book.expiry_ends
|
||||
period_id = decision_book.id
|
||||
|
||||
print(dict(
|
||||
period_date_start=str(period_date_start), period_date_end=str(period_date_end), period_id=period_id
|
||||
))
|
||||
|
||||
first_date_of_process_date = find_first_day_of_month(datetime(period_date_start.year, period_date_start.month, 1))
|
||||
last_date_of_process_date = find_first_day_of_month(datetime(now.year, now.month - 1, 1))
|
||||
print('first_date_of_process_date', first_date_of_process_date)
|
||||
print('last_date_of_process_date', last_date_of_process_date)
|
||||
|
||||
# Current month date filter
|
||||
date_query_tuple = (
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= first_date_of_process_date.date(), cast(BuildDecisionBookPayments.process_date, Date) <= last_date_of_process_date.date()
|
||||
)
|
||||
|
||||
for payment_type in payment_type_list:
|
||||
query_of_payments = BuildDecisionBookPayments.query.filter(
|
||||
*date_query_tuple, BuildDecisionBookPayments.payment_types_id == payment_type.id, BuildDecisionBookPayments.account_is_debit == True
|
||||
).order_by(BuildDecisionBookPayments.process_date.desc()).all()
|
||||
print('length of debit', len(query_of_payments))
|
||||
priority_uuids = [payment_type.uuid, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_type.uuid]]
|
||||
case_order = case(*[(AccountRecords.payment_result_type_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
for payment_row in query_of_payments:
|
||||
print('-'* 100)
|
||||
print('BuildDecisionBookPayments result: ',dict(
|
||||
process_date=str(payment_row.process_date), ref_id=payment_row.ref_id, payment_amount=payment_row.payment_amount, build_parts_id=payment_row.build_parts_id,
|
||||
payment_types_id=payment_row.payment_types_id, account_is_debit=payment_row.account_is_debit
|
||||
))
|
||||
print('-'* 100)
|
||||
|
||||
|
||||
def do_regular_monthly_payers_payment_function(work_date: datetime, build_id: int, month_count: int = 23, year_count: int = 2):
|
||||
start_date = work_date - timedelta(days=365 * year_count)
|
||||
start_date = find_first_day_of_month(start_date)
|
||||
for i in range(month_count):
|
||||
start_date, _ = add_month_to_date(start_date)
|
||||
do_payments(build_id, work_date=start_date, run_now_bool=False)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_time = perf_counter()
|
||||
|
||||
print("\n===== PROCESSING PAYMENTS =====\n")
|
||||
print("Starting payment processing at:", datetime.now())
|
||||
|
||||
build_id = 1
|
||||
# Do regular monthly payers payments
|
||||
print("\n1. Processing regular monthly payers payments...")
|
||||
do_regular_monthly_payers_payment_function(work_date=datetime.now(), build_id=build_id, month_count=23, year_count=2)
|
||||
|
||||
# Process payments for current month first
|
||||
print("\n2. Processing current month payments...")
|
||||
do_payments(build_id=build_id, work_date=datetime.now(), run_now_bool=True)
|
||||
|
||||
# Process payments for previous months
|
||||
print("\n2. Processing previous months payments...")
|
||||
# do_regular_monthly_payers_payment_function(work_date=datetime.now(), build_id=build_id, month_count=23, year_count=2)
|
||||
|
||||
print("\n===== PAYMENT PROCESSING COMPLETE =====\n")
|
||||
print("Payment processing completed at:", datetime.now())
|
||||
|
||||
# Analyze the payment situation after processing payments
|
||||
print("\n===== ANALYZING PAYMENT SITUATION AFTER PROCESSING =====\n")
|
||||
# analyze_payment_function()
|
||||
|
||||
end_time = perf_counter()
|
||||
print(f"\n{end_time - start_time:.3f} : seconds")
|
||||
@@ -0,0 +1,869 @@
|
||||
import arrow
|
||||
import calendar
|
||||
import time
|
||||
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from Schemas import BuildDecisionBookPayments, AccountRecords, ApiEnumDropdown, Build, BuildDecisionBook
|
||||
from time import perf_counter
|
||||
from sqlalchemy import select, func, distinct, cast, Date, String, literal, desc, and_, or_, case
|
||||
from Controllers.Postgres.engine import get_session_factory
|
||||
#from ServicesApi.Schemas.account.account import AccountRecords
|
||||
#from ServicesApi.Schemas.building.decision_book import BuildDecisionBookPayments
|
||||
|
||||
|
||||
def find_last_day_of_month(date_value):
|
||||
today = date_value.date()
|
||||
_, last_day = calendar.monthrange(today.year, today.month)
|
||||
return datetime(today.year, today.month, last_day, 23, 59, 59)
|
||||
|
||||
def find_first_day_of_month(date_value):
|
||||
today = date_value.date()
|
||||
return datetime(today.year, today.month, 1)
|
||||
|
||||
|
||||
class BuildDuesTypes:
|
||||
|
||||
def __init__(self):
|
||||
self.debit: ApiEnumDropdownShallowCopy = None
|
||||
self.add_debit: ApiEnumDropdownShallowCopy = None
|
||||
self.renovation: ApiEnumDropdownShallowCopy = None
|
||||
self.lawyer_expence: ApiEnumDropdownShallowCopy = None
|
||||
self.service_fee: ApiEnumDropdownShallowCopy = None
|
||||
self.information: ApiEnumDropdownShallowCopy = None
|
||||
|
||||
|
||||
class ApiEnumDropdownShallowCopy:
|
||||
id: int
|
||||
uuid: str
|
||||
enum_class: str
|
||||
key: str
|
||||
value: str
|
||||
|
||||
def __init__(self, id: int, uuid: str, enum_class: str, key: str, value: str):
|
||||
self.id = id
|
||||
self.uuid = uuid
|
||||
self.enum_class = enum_class
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
|
||||
def get_enums_from_database():
|
||||
build_dues_types = BuildDuesTypes()
|
||||
with ApiEnumDropdown.new_session() as session:
|
||||
|
||||
ApiEnumDropdown.set_session(session)
|
||||
|
||||
debit_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-D").first() # Debit
|
||||
add_debit_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-A").first() # Add Debit
|
||||
renovation_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-R").first() # Renovation
|
||||
late_payment_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-L").first() # Lawyer expence
|
||||
service_fee_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-S").first() # Service fee
|
||||
information_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-I").first() # Information
|
||||
|
||||
build_dues_types.debit = ApiEnumDropdownShallowCopy(
|
||||
debit_enum_shallow.id, str(debit_enum_shallow.uu_id), debit_enum_shallow.enum_class, debit_enum_shallow.key, debit_enum_shallow.value
|
||||
)
|
||||
build_dues_types.add_debit = ApiEnumDropdownShallowCopy(
|
||||
add_debit_enum_shallow.id, str(add_debit_enum_shallow.uu_id), add_debit_enum_shallow.enum_class, add_debit_enum_shallow.key, add_debit_enum_shallow.value
|
||||
)
|
||||
build_dues_types.renovation = ApiEnumDropdownShallowCopy(
|
||||
renovation_enum_shallow.id, str(renovation_enum_shallow.uu_id), renovation_enum_shallow.enum_class, renovation_enum_shallow.key, renovation_enum_shallow.value
|
||||
)
|
||||
build_dues_types.lawyer_expence = ApiEnumDropdownShallowCopy(
|
||||
late_payment_enum_shallow.id, str(late_payment_enum_shallow.uu_id), late_payment_enum_shallow.enum_class, late_payment_enum_shallow.key, late_payment_enum_shallow.value
|
||||
)
|
||||
build_dues_types.service_fee = ApiEnumDropdownShallowCopy(
|
||||
service_fee_enum_shallow.id, str(service_fee_enum_shallow.uu_id), service_fee_enum_shallow.enum_class, service_fee_enum_shallow.key, service_fee_enum_shallow.value
|
||||
)
|
||||
build_dues_types.information = ApiEnumDropdownShallowCopy(
|
||||
information_enum_shallow.id, str(information_enum_shallow.uu_id), information_enum_shallow.enum_class, information_enum_shallow.key, information_enum_shallow.value
|
||||
)
|
||||
return [build_dues_types.debit, build_dues_types.lawyer_expence, build_dues_types.add_debit, build_dues_types.renovation, build_dues_types.service_fee, build_dues_types.information]
|
||||
|
||||
|
||||
def generate_total_paid_amount_for_spesific_build_part_id(build_parts_id: int, session):
|
||||
"""
|
||||
Calculate the total amount paid for a specific build part ID.
|
||||
|
||||
Args:
|
||||
build_parts_id: The build part ID to calculate payments for
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
float: The total amount paid (absolute value)
|
||||
"""
|
||||
payment_query = session.query(func.sum(BuildDecisionBookPayments.payment_amount)).filter(
|
||||
BuildDecisionBookPayments.build_parts_id == build_parts_id,
|
||||
BuildDecisionBookPayments.account_is_debit == False,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= '2022-01-01'
|
||||
).scalar()
|
||||
return payment_query if payment_query is not None else 0
|
||||
|
||||
|
||||
def generate_total_debt_amount_for_spesific_build_part_id(build_parts_id: int, session):
|
||||
|
||||
# Use SQLAlchemy's func.sum to calculate the total debts
|
||||
# For total debt, we want to include ALL debts, both processed and unprocessed
|
||||
result = session.query(
|
||||
func.sum(BuildDecisionBookPayments.payment_amount)
|
||||
).filter(
|
||||
BuildDecisionBookPayments.build_parts_id == build_parts_id,
|
||||
BuildDecisionBookPayments.account_is_debit == True,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= '2022-01-01'
|
||||
).scalar()
|
||||
|
||||
# Return 0 if no debts found, otherwise return the absolute value of the sum
|
||||
return abs(result) if result is not None else 0
|
||||
|
||||
|
||||
def generate_total_amount_that_user_has_in_account(account_record: AccountRecords, session):
|
||||
# Get total amount that user has in account
|
||||
result = session.query(
|
||||
func.sum(AccountRecords.currency_value)
|
||||
).filter(
|
||||
AccountRecords.build_parts_id == account_record.build_parts_id,
|
||||
AccountRecords.currency_value > 0,
|
||||
cast(AccountRecords.bank_date, Date) >= '2022-01-01'
|
||||
).scalar()
|
||||
|
||||
# Return 0 if no payments found, otherwise return the absolute value of the sum
|
||||
return abs(result)
|
||||
|
||||
|
||||
|
||||
def _print_debt_details(debt, session):
|
||||
"""Helper function to print detailed information about an unpaid debt.
|
||||
|
||||
Args:
|
||||
debt: The BuildDecisionBookPayments object representing the debt
|
||||
session: Database session
|
||||
"""
|
||||
# Get the sum of payments for this debt
|
||||
payments_sum = session.query(
|
||||
func.sum(BuildDecisionBookPayments.payment_amount)
|
||||
).filter(
|
||||
BuildDecisionBookPayments.ref_id == debt.ref_id,
|
||||
BuildDecisionBookPayments.account_is_debit == False
|
||||
).scalar() or 0
|
||||
|
||||
# Calculate remaining amount
|
||||
debit_amount = abs(debt.payment_amount)
|
||||
remaining = debit_amount - abs(payments_sum)
|
||||
payment_percentage = (abs(payments_sum) / debit_amount) * 100 if debit_amount > 0 else 0
|
||||
|
||||
# Format the date for display
|
||||
date_str = debt.process_date.strftime('%Y-%m-%d') if debt.process_date else 'Unknown date'
|
||||
|
||||
|
||||
def analyze_payment_function():
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
# Set session for all models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
|
||||
order_pay = get_enums_from_database()
|
||||
# Get distinct build_parts_id values from account records with positive currency_value
|
||||
# This avoids redundant processing of the same build_parts_id
|
||||
distinct_build_parts = session.query(
|
||||
distinct(AccountRecords.build_parts_id)
|
||||
).filter(
|
||||
AccountRecords.build_parts_id.isnot(None),
|
||||
AccountRecords.currency_value > 0,
|
||||
AccountRecords.bank_date >= '2022-01-01'
|
||||
).order_by(AccountRecords.build_parts_id.desc()).all()
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
for build_part_id_tuple in distinct_build_parts:
|
||||
build_part_id = build_part_id_tuple[0] # Extract the ID from the tuple
|
||||
process_date = datetime.now()
|
||||
last_date_of_process_date = datetime(process_date.year, process_date.month, 1) - timedelta(days=1)
|
||||
first_date_of_process_date = datetime(process_date.year, process_date.month, 1)
|
||||
|
||||
print(f"\n{'=' * 50}")
|
||||
print(f"ACCOUNT ANALYSIS FOR BUILD PART ID: {build_part_id}")
|
||||
print(f"{'=' * 50}")
|
||||
|
||||
# Calculate total paid amount for this build_part_id
|
||||
total_amount_paid = generate_total_paid_amount_for_spesific_build_part_id(build_part_id, session)
|
||||
|
||||
# Calculate total debt amount for this build_part_id
|
||||
total_debt_amount = generate_total_debt_amount_for_spesific_build_part_id(build_part_id, session)
|
||||
|
||||
# Get total amount in account for this build_part_id
|
||||
account_record = AccountRecords()
|
||||
account_record.build_parts_id = build_part_id
|
||||
total_amount_in_account = generate_total_amount_that_user_has_in_account(account_record, session)
|
||||
|
||||
# Calculate remaining amount to be paid
|
||||
amount_need_to_paid = total_debt_amount - total_amount_paid
|
||||
total_amount_that_user_need_to_transfer = abs(amount_need_to_paid) - abs(total_amount_in_account)
|
||||
|
||||
# Print summary with clear descriptions
|
||||
print(f"PAYMENT SUMMARY:")
|
||||
print(f" • Total debt amount: {total_debt_amount:,.2f} TL")
|
||||
print(f" • Amount already paid: {total_amount_paid:,.2f} TL")
|
||||
print(f" • Remaining debt to be collected: {amount_need_to_paid:,.2f} TL")
|
||||
print(f" • Current account balance: {total_amount_in_account:,.2f} TL")
|
||||
|
||||
if total_amount_that_user_need_to_transfer > 0:
|
||||
print(f" • Additional funds needed: {total_amount_that_user_need_to_transfer:,.2f} TL")
|
||||
elif amount_need_to_paid <= 0:
|
||||
print(f" • Account is fully paid with no outstanding debt")
|
||||
else:
|
||||
print(f" • Sufficient funds available to close all debt")
|
||||
# Show debt coverage percentage
|
||||
if total_debt_amount > 0:
|
||||
# Calculate current coverage (already paid)
|
||||
current_coverage_percentage = (total_amount_paid / total_debt_amount) * 100
|
||||
|
||||
# Calculate potential coverage (including available funds)
|
||||
potential_coverage = min(100, ((total_amount_paid + total_amount_in_account) / total_debt_amount) * 100)
|
||||
|
||||
# Display both percentages
|
||||
print(f" • Current debt coverage: {current_coverage_percentage:.2f}%")
|
||||
print(f" • Potential debt coverage with available funds: {potential_coverage:.2f}%")
|
||||
|
||||
# Analyze unpaid debts for each payment type
|
||||
print("\nUNPAID DEBTS ANALYSIS BY PAYMENT TYPE:")
|
||||
for payment_type in order_pay:
|
||||
# Get unpaid debts for current month
|
||||
date_query_current = (
|
||||
BuildDecisionBookPayments.process_date >= first_date_of_process_date,
|
||||
BuildDecisionBookPayments.process_date <= process_date
|
||||
)
|
||||
date_query_previous = (
|
||||
BuildDecisionBookPayments.process_date < first_date_of_process_date,
|
||||
)
|
||||
|
||||
current_unpaid_debts = get_unpaid_debts(build_parts_id=build_part_id, session=session, debit_type=payment_type, date_query=date_query_current)
|
||||
|
||||
# Get unpaid debts from previous months
|
||||
previous_unpaid_debts = get_unpaid_debts(build_parts_id=build_part_id, session=session, debit_type=payment_type, date_query=date_query_previous)
|
||||
|
||||
# Calculate totals
|
||||
current_total = sum(abs(debt[2]) for debt in current_unpaid_debts)
|
||||
previous_total = sum(abs(debt[2]) for debt in previous_unpaid_debts)
|
||||
grand_total = current_total + previous_total
|
||||
|
||||
# Print summary for this payment type
|
||||
if current_unpaid_debts or previous_unpaid_debts:
|
||||
print(f" • {payment_type.key}: Total unpaid: {grand_total:,.2f} TL")
|
||||
|
||||
# Current month details
|
||||
if current_unpaid_debts:
|
||||
print(f" - Current month: {len(current_unpaid_debts)} debts, {current_total:,.2f} TL")
|
||||
# Show details of each unpaid debt if there aren't too many
|
||||
# if len(current_unpaid_debts) <= 3:
|
||||
# for debt in current_unpaid_debts:
|
||||
# _print_debt_details(debt, session)
|
||||
|
||||
# Previous months details
|
||||
if previous_unpaid_debts:
|
||||
print(f" - Previous months: {len(previous_unpaid_debts)} debts, {previous_total:,.2f} TL")
|
||||
# Show details of each unpaid debt if there aren't too many
|
||||
# if len(previous_unpaid_debts) <= 3:
|
||||
# for debt in previous_unpaid_debts:
|
||||
# _print_debt_details(debt, session)
|
||||
else:
|
||||
print(f" • {payment_type.key}: All debts paid")
|
||||
print(f"{'=' * 50}\n")
|
||||
|
||||
|
||||
def close_payment_book(payment_row_book, account_record, value, session):
|
||||
"""Create a credit entry in BuildDecisionBookPayments to close a debt.
|
||||
|
||||
Args:
|
||||
payment_row_book: The debit entry to be paid
|
||||
account_record: The account record containing the funds
|
||||
value: The amount to pay
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
The newly created payment record
|
||||
"""
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
|
||||
# Create a new credit entry (payment)
|
||||
new_row = BuildDecisionBookPayments.create(
|
||||
ref_id=str(payment_row_book.uu_id),
|
||||
payment_plan_time_periods=payment_row_book.payment_plan_time_periods,
|
||||
period_time=payment_row_book.period_time,
|
||||
currency=payment_row_book.currency,
|
||||
account_records_id=account_record.id,
|
||||
account_records_uu_id=str(account_record.uu_id),
|
||||
build_parts_id=payment_row_book.build_parts_id,
|
||||
build_parts_uu_id=str(payment_row_book.build_parts_uu_id),
|
||||
payment_amount=abs(value), # Negative for credit entries
|
||||
payment_types_id=payment_row_book.payment_types_id,
|
||||
payment_types_uu_id=str(payment_row_book.payment_types_uu_id),
|
||||
process_date_m=payment_row_book.process_date.month,
|
||||
process_date_y=payment_row_book.process_date.year,
|
||||
process_date=payment_row_book.process_date,
|
||||
build_decision_book_item_id=payment_row_book.build_decision_book_item_id if payment_row_book.build_decision_book_item_id else None,
|
||||
build_decision_book_item_uu_id=str(payment_row_book.build_decision_book_item_uu_id) if payment_row_book.build_decision_book_item_uu_id else None,
|
||||
decision_book_project_id=payment_row_book.decision_book_project_id if payment_row_book.decision_book_project_id else None,
|
||||
decision_book_project_uu_id=str(payment_row_book.decision_book_project_uu_id) if payment_row_book.decision_book_project_uu_id else None,
|
||||
build_decision_book_id=payment_row_book.build_decision_book_id if payment_row_book.build_decision_book_id else None,
|
||||
build_decision_book_uu_id=str(payment_row_book.build_decision_book_uu_id) if payment_row_book.build_decision_book_uu_id else None,
|
||||
is_confirmed=True,
|
||||
account_is_debit=False,
|
||||
)
|
||||
|
||||
# Save the new payment record
|
||||
saved_row = new_row.save()
|
||||
session.commit()
|
||||
session.refresh(saved_row)
|
||||
session.flush()
|
||||
return saved_row
|
||||
|
||||
|
||||
def update_account_remainder_if_spent(account_record, ref_id: str, session):
|
||||
"""Update the remainder_balance of an account after spending money.
|
||||
|
||||
Args:
|
||||
account_record: The account record to update
|
||||
amount_spent: The amount spent in this transaction
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
bool: True if all money is spent, False otherwise
|
||||
"""
|
||||
|
||||
account_record_remainder_balance = session.query(func.sum(func.abs(BuildDecisionBookPayments.payment_amount))).filter(
|
||||
BuildDecisionBookPayments.account_records_id == account_record.id,
|
||||
BuildDecisionBookPayments.account_is_debit == False
|
||||
).scalar()
|
||||
|
||||
if not account_record_remainder_balance:
|
||||
return 0
|
||||
|
||||
if account_record_remainder_balance:
|
||||
account_record.remainder_balance = account_record_remainder_balance
|
||||
account_record.save()
|
||||
return account_record_remainder_balance
|
||||
|
||||
|
||||
def check_book_payment_is_still_has_debt_to_be_paid(ref_id: str, session):
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
debit_row_debt_to_pay = session.query(func.sum(BuildDecisionBookPayments.payment_amount)).filter(BuildDecisionBookPayments.ref_id == ref_id).scalar()
|
||||
if not debit_row_debt_to_pay:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def update_all_spent_accounts(session):
|
||||
"""Update remainder_balance for all accounts with payments.
|
||||
|
||||
This function finds account records in BuildDecisionBookPayments and updates
|
||||
their remainder_balance based on the sum of payments made, regardless of whether
|
||||
all funds have been spent or not.
|
||||
|
||||
Args:
|
||||
session: Database session
|
||||
"""
|
||||
with AccountRecords.new_session() as session:
|
||||
# Set sessions for models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
|
||||
# Get distinct account_records_id values from BuildDecisionBookPayments
|
||||
distinct_account_ids = session.query(BuildDecisionBookPayments.account_records_id).filter(
|
||||
BuildDecisionBookPayments.account_records_id.isnot(None),
|
||||
BuildDecisionBookPayments.account_is_debit == False # Credit entries (payments)
|
||||
).distinct().all()
|
||||
|
||||
updated_count = 0
|
||||
|
||||
for account_id_tuple in distinct_account_ids:
|
||||
account_id = account_id_tuple[0]
|
||||
|
||||
# Get the account record
|
||||
account = AccountRecords.query.filter_by(id=account_id).first()
|
||||
if not account or not account.build_parts_id or account.currency_value <= 0:
|
||||
continue
|
||||
|
||||
# Calculate the sum of payments made using this account
|
||||
# Note: payment_amount is negative for credit entries, so we need to use abs() to get the positive amount
|
||||
payment_query = session.query(func.sum(func.abs(BuildDecisionBookPayments.payment_amount))).filter(
|
||||
BuildDecisionBookPayments.account_records_id == account_id,
|
||||
BuildDecisionBookPayments.account_is_debit == False # Credit entries (payments)
|
||||
)
|
||||
|
||||
payment_sum = payment_query.scalar() or 0
|
||||
|
||||
# Update remainder_balance for ALL accounts, regardless of payment_sum value
|
||||
threshold = Decimal('0.01')
|
||||
fully_spent = abs(payment_sum) >= abs(account.currency_value) - threshold
|
||||
status = "All funds spent" if fully_spent else "Partial payment"
|
||||
|
||||
# Store the positive value in remainder_balance
|
||||
account.remainder_balance = -1 * abs(payment_sum)
|
||||
account.save()
|
||||
updated_count += 1
|
||||
session.commit()
|
||||
print(f"\nTotal accounts updated: {updated_count}")
|
||||
|
||||
|
||||
# def find_amount_to_pay_by_ref_id(ref_id, session):
|
||||
# """Calculate the remaining amount to pay for a specific debt reference ID.
|
||||
|
||||
# Args:
|
||||
# ref_id: The reference ID of the debt (this is the uu_id of the debt record)
|
||||
# session: Database session
|
||||
|
||||
# Returns:
|
||||
# float: The remaining amount to pay
|
||||
# """
|
||||
# # Get the original debt amount - the debt is identified by its uu_id which is passed as ref_id
|
||||
# debit = BuildDecisionBookPayments.query.filter(
|
||||
# BuildDecisionBookPayments.uu_id == ref_id,
|
||||
# BuildDecisionBookPayments.account_is_debit == True
|
||||
# ).first()
|
||||
# if not debit:
|
||||
# return 0 # No debit found, nothing to pay
|
||||
|
||||
# debit_amount = abs(debit.payment_amount) # Ensure positive value for debit amount
|
||||
|
||||
# # Get the sum of payments already made for this debt
|
||||
# # The ref_id in credit records points to the uu_id of the original debit
|
||||
# # Note: payment_amount is negative for credit entries, so we use abs() to get positive values
|
||||
# credit_amount = session.query(
|
||||
# func.sum(func.abs(BuildDecisionBookPayments.payment_amount))
|
||||
# ).filter(
|
||||
# BuildDecisionBookPayments.ref_id == str(ref_id),
|
||||
# BuildDecisionBookPayments.account_is_debit == False
|
||||
# ).scalar() or 0
|
||||
# # Calculate remaining amount to pay
|
||||
# remaining = abs(debit_amount) - abs(credit_amount)
|
||||
# # Ensure we don't return negative values
|
||||
# if remaining < 0:
|
||||
# return 0
|
||||
|
||||
# return remaining
|
||||
|
||||
def get_unpaid_debts_via_build_parts(build_parts_id: int, session, debit_type, date_query: tuple):
|
||||
"""Find BuildDecisionBookPayments entries where the debt has NOT been fully paid for a specific debit type.
|
||||
|
||||
This function identifies payments where the sum of payments is less than the debit amount,
|
||||
meaning the debt has not been fully closed.
|
||||
|
||||
Args:
|
||||
build_parts_id: The build part ID to check
|
||||
session: Database session
|
||||
debit_type: The specific debit type to check
|
||||
date_query: Tuple of date filters to apply
|
||||
|
||||
Returns:
|
||||
list: List of unpaid debt entries (full BuildDecisionBookPayments objects)
|
||||
|
||||
query:
|
||||
SELECT
|
||||
bpf.ref_id,
|
||||
bpf.process_date,
|
||||
ABS(COALESCE(SUM(bpf.payment_amount), 0)) AS total_payments
|
||||
FROM public.build_decision_book_payments AS bpf
|
||||
GROUP BY
|
||||
bpf.ref_id,
|
||||
bpf.process_date
|
||||
HAVING ABS(COALESCE(SUM(bpf.payment_amount), 0)) > 0
|
||||
order by bpf.process_date
|
||||
"""
|
||||
# Create a subquery for the payment sums without executing it separately
|
||||
payment_sums_subquery = select(
|
||||
BuildDecisionBookPayments.ref_id,
|
||||
BuildDecisionBookPayments.process_date,
|
||||
func.abs(func.coalesce(func.sum(BuildDecisionBookPayments.payment_amount), 0)).label("total_payments")
|
||||
).filter(
|
||||
BuildDecisionBookPayments.build_parts_id == build_parts_id,
|
||||
BuildDecisionBookPayments.payment_types_id == debit_type.id,
|
||||
*date_query
|
||||
).group_by(
|
||||
BuildDecisionBookPayments.ref_id, BuildDecisionBookPayments.process_date
|
||||
).having(
|
||||
func.abs(func.coalesce(func.sum(BuildDecisionBookPayments.payment_amount), 0)) > 0
|
||||
).order_by(BuildDecisionBookPayments.process_date.desc())
|
||||
|
||||
# Use the subquery directly in the main query
|
||||
payment_sums = session.execute(payment_sums_subquery).all()
|
||||
payment_sums_list = []
|
||||
for item in payment_sums:
|
||||
payment_sums_list.append({"ref_id": item[0], "process_date": item[1], "total_payments": item[2]})
|
||||
return payment_sums
|
||||
|
||||
|
||||
def get_unpaid_debts(session, debit_type, date_query: tuple):
|
||||
"""Find BuildDecisionBookPayments entries where the debt has NOT been fully paid for a specific debit type.
|
||||
|
||||
This function identifies payments where the sum of payments is less than the debit amount,
|
||||
meaning the debt has not been fully closed.
|
||||
|
||||
Args:
|
||||
build_parts_id: The build part ID to check
|
||||
session: Database session
|
||||
debit_type: The specific debit type to check
|
||||
date_query: Tuple of date filters to apply
|
||||
|
||||
Returns:
|
||||
list: List of unpaid debt entries (full BuildDecisionBookPayments objects)
|
||||
|
||||
query:
|
||||
SELECT
|
||||
bpf.ref_id,
|
||||
bpf.process_date,
|
||||
ABS(COALESCE(SUM(bpf.payment_amount), 0)) AS total_payments
|
||||
FROM public.build_decision_book_payments AS bpf
|
||||
GROUP BY
|
||||
bpf.ref_id,
|
||||
bpf.process_date
|
||||
HAVING ABS(COALESCE(SUM(bpf.payment_amount), 0)) > 0
|
||||
order by bpf.process_date
|
||||
"""
|
||||
# Create a subquery for the payment sums without executing it separately
|
||||
do_payments_set = lambda ref, date, total: {"ref_id": ref, "process_date": date, "total_payments": total}
|
||||
payment_sums_subquery = select(BuildDecisionBookPayments.ref_id, BuildDecisionBookPayments.process_date,
|
||||
func.abs(func.coalesce(func.sum(BuildDecisionBookPayments.payment_amount), 0)).label("total_payments")
|
||||
).filter(BuildDecisionBookPayments.payment_types_id == debit_type.id, *date_query
|
||||
).group_by(BuildDecisionBookPayments.ref_id, BuildDecisionBookPayments.process_date
|
||||
).having(func.abs(func.coalesce(func.sum(BuildDecisionBookPayments.payment_amount), 0)) > 0
|
||||
).order_by(BuildDecisionBookPayments.process_date.asc())
|
||||
|
||||
payment_sums = session.execute(payment_sums_subquery).all()
|
||||
return [do_payments_set(item[0], item[1], item[2]) for item in payment_sums]
|
||||
|
||||
|
||||
def do_payments_of_this_month(build_id: int = 1):
|
||||
"""Process payments for the current month's unpaid debts.
|
||||
This function retrieves account records with available funds and processes
|
||||
payments for current month's unpaid debts in order of payment type priority.
|
||||
"""
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
|
||||
# Set session for all models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
# Get payment types in priority order
|
||||
payment_type_list = get_enums_from_database()
|
||||
fund_finished = lambda money_spend, money_in_account: money_spend == money_in_account
|
||||
payments_made, total_amount_paid, paid_count = 0, 0, 0
|
||||
target_build = Build.query.filter(Build.id == build_id).first()
|
||||
if not target_build:
|
||||
raise ValueError(f"Build with id {build_id} not found")
|
||||
|
||||
now = datetime.now()
|
||||
decision_book = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == build_id, cast(BuildDecisionBook.expiry_starts, Date) <= now.date(),
|
||||
cast(BuildDecisionBook.expiry_ends, Date) >= now.date(), BuildDecisionBook.decision_type == "RBM"
|
||||
).first()
|
||||
if not decision_book:
|
||||
raise ValueError(f"Decision book not found for build with id {build_id}")
|
||||
|
||||
period_date_start = decision_book.expiry_starts
|
||||
period_date_end = decision_book.expiry_ends
|
||||
period_id = decision_book.id
|
||||
|
||||
first_date_of_process_date = find_first_day_of_month(now)
|
||||
last_date_of_process_date = find_last_day_of_month(now)
|
||||
|
||||
# Current month date filter
|
||||
date_query_tuple = (
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= first_date_of_process_date.date(), cast(BuildDecisionBookPayments.process_date, Date) <= last_date_of_process_date.date()
|
||||
)
|
||||
|
||||
update_all_spent_accounts(session)
|
||||
|
||||
for payment_type in payment_type_list:
|
||||
unpaid_debts = get_unpaid_debts(session, payment_type, date_query_tuple)
|
||||
for unpaid_debt in unpaid_debts:
|
||||
amount_to_pay = unpaid_debt["total_payments"]
|
||||
ref_id = unpaid_debt["ref_id"]
|
||||
process_date = unpaid_debt["process_date"]
|
||||
debit_row = BuildDecisionBookPayments.query.filter(BuildDecisionBookPayments.uu_id == ref_id).first()
|
||||
build_parts_id = debit_row.build_parts_id
|
||||
priority_uuids = [payment_type.uuid, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_type.uuid]]
|
||||
case_order = case(*[(AccountRecords.payment_result_type_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == build_parts_id, AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance),
|
||||
cast(AccountRecords.bank_date, Date) >= first_date_of_process_date.date(), cast(AccountRecords.bank_date, Date) <= last_date_of_process_date.date(),
|
||||
).order_by(case_order, AccountRecords.bank_date.asc()).all()
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
account_remainder_balance = update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
available_money = abs(money_to_pay_row.currency_value) - abs(account_remainder_balance)
|
||||
if not available_money > 0:
|
||||
continue
|
||||
if available_money > amount_to_pay:
|
||||
print('NOT All money is spent amount_to_pay:', amount_to_pay, 'debit_row payment_amount:', debit_row.payment_amount)
|
||||
close_payment_book(debit_row, money_to_pay_row, amount_to_pay, session)
|
||||
total_amount_paid += amount_to_pay
|
||||
paid_count += 1
|
||||
payments_made += 1
|
||||
account_remainder_balance = update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
break
|
||||
elif available_money <= amount_to_pay:
|
||||
print('All money is spent amount_to_pay:', amount_to_pay, 'debit_row payment_amount:', debit_row.payment_amount)
|
||||
close_payment_book(debit_row, money_to_pay_row, available_money, session)
|
||||
total_amount_paid += available_money
|
||||
paid_count += 1
|
||||
payments_made += 1
|
||||
account_remainder_balance = update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
continue
|
||||
else:
|
||||
print(f"Something else happened available_money: {available_money}, amount_to_pay: {amount_to_pay}")
|
||||
|
||||
update_all_spent_accounts(session)
|
||||
|
||||
print('payments_made', payments_made)
|
||||
print('total_amount_paid', total_amount_paid)
|
||||
print('paid_count', paid_count)
|
||||
|
||||
|
||||
def do_payments_of_previos_months(build_id: int = 1):
|
||||
"""Process payments for previous months' unpaid debts.
|
||||
|
||||
This function retrieves account records with available funds and processes
|
||||
payments for previous months' unpaid debts in order of payment type priority.
|
||||
"""
|
||||
"""Process payments for the current month's unpaid debts.
|
||||
This function retrieves account records with available funds and processes
|
||||
payments for current month's unpaid debts in order of payment type priority.
|
||||
"""
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
|
||||
# Set session for all models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
# Get payment types in priority order
|
||||
payment_type_list = get_enums_from_database()
|
||||
fund_finished = lambda money_spend, money_in_account: money_spend == money_in_account
|
||||
payments_made, total_amount_paid, paid_count = 0, 0, 0
|
||||
target_build = Build.query.filter(Build.id == build_id).first()
|
||||
if not target_build:
|
||||
raise ValueError(f"Build with id {build_id} not found")
|
||||
|
||||
now = datetime.now()
|
||||
decision_book = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == build_id,
|
||||
cast(BuildDecisionBook.expiry_starts, Date) <= now.date(),
|
||||
cast(BuildDecisionBook.expiry_ends, Date) >= now.date(),
|
||||
BuildDecisionBook.decision_type == "RBM"
|
||||
).first()
|
||||
if not decision_book:
|
||||
raise ValueError(f"Decision book not found for build with id {build_id}")
|
||||
early_date = datetime(now.year - 1, now.month, now.day)
|
||||
early_decision_book = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == build_id,
|
||||
cast(BuildDecisionBook.expiry_starts, Date) <= early_date.date(),
|
||||
cast(BuildDecisionBook.expiry_ends, Date) >= early_date.date(),
|
||||
BuildDecisionBook.decision_type == "RBM"
|
||||
).first()
|
||||
|
||||
period_date_start = decision_book.expiry_starts
|
||||
period_date_end = decision_book.expiry_ends
|
||||
period_id = decision_book.id
|
||||
early_period_date_start = early_decision_book.expiry_starts
|
||||
early_period_date_end = early_decision_book.expiry_ends
|
||||
early_period_id = early_decision_book.id
|
||||
|
||||
first_date_of_process_date = arrow.get(period_date_start).datetime
|
||||
last_date_of_process_date = now
|
||||
|
||||
# Current month date filter
|
||||
date_query_tuple = (
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= first_date_of_process_date.date(),
|
||||
cast(BuildDecisionBookPayments.process_date, Date) <= last_date_of_process_date.date()
|
||||
)
|
||||
|
||||
update_all_spent_accounts(session)
|
||||
|
||||
for payment_type in payment_type_list:
|
||||
unpaid_debts = get_unpaid_debts(session, payment_type, date_query_tuple)
|
||||
print('length unpaid debts: ', len(unpaid_debts))
|
||||
for unpaid_debt in unpaid_debts:
|
||||
amount_to_pay = unpaid_debt["total_payments"]
|
||||
ref_id = unpaid_debt["ref_id"]
|
||||
process_date = unpaid_debt["process_date"]
|
||||
debit_row = BuildDecisionBookPayments.query.filter(BuildDecisionBookPayments.uu_id == ref_id).first()
|
||||
build_parts_id = debit_row.build_parts_id
|
||||
priority_uuids = [payment_type.uuid, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_type.uuid]]
|
||||
case_order = case(*[(AccountRecords.payment_result_type_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == build_parts_id, AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance),
|
||||
cast(AccountRecords.bank_date, Date) >= find_first_day_of_month(process_date).date(), cast(AccountRecords.bank_date, Date) <= find_last_day_of_month(process_date).date(),
|
||||
).order_by(case_order, AccountRecords.bank_date.asc()).all()
|
||||
if not money_to_pay_rows:
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == build_parts_id, AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance),
|
||||
cast(AccountRecords.bank_date, Date) >= first_date_of_process_date.date(), cast(AccountRecords.bank_date, Date) <= last_date_of_process_date.date(),
|
||||
).order_by(case_order, AccountRecords.bank_date.asc()).all()
|
||||
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
account_remainder_balance = update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
money_to_pay_row = AccountRecords.query.filter(AccountRecords.id == money_to_pay_row.id).first()
|
||||
available_money = abs(money_to_pay_row.currency_value) - abs(account_remainder_balance)
|
||||
if not available_money > 0:
|
||||
continue
|
||||
if available_money > amount_to_pay:
|
||||
print('NOT All money is spent amount_to_pay:', amount_to_pay, 'debit_row payment_amount:', debit_row.payment_amount)
|
||||
close_payment_book(debit_row, money_to_pay_row, amount_to_pay, session)
|
||||
total_amount_paid += amount_to_pay
|
||||
paid_count += 1
|
||||
payments_made += 1
|
||||
update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
break
|
||||
elif available_money <= amount_to_pay:
|
||||
print('All money is spent amount_to_pay:', available_money, 'debit_row payment_amount:', debit_row.payment_amount)
|
||||
close_payment_book(debit_row, money_to_pay_row, available_money, session)
|
||||
total_amount_paid += available_money
|
||||
paid_count += 1
|
||||
payments_made += 1
|
||||
update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
continue
|
||||
else:
|
||||
print(f"Something else happened available_money: {available_money}, amount_to_pay: {amount_to_pay}")
|
||||
|
||||
print('This years decision book payments')
|
||||
print('payments_made', payments_made)
|
||||
print('total_amount_paid', total_amount_paid)
|
||||
print('paid_count', paid_count)
|
||||
payments_made, total_amount_paid, paid_count = 0, 0, 0
|
||||
update_all_spent_accounts(session)
|
||||
first_date_of_process_date = arrow.get(early_period_date_start).datetime
|
||||
last_date_of_process_date = arrow.get(early_period_date_end).datetime
|
||||
|
||||
# Early month date filter
|
||||
date_query_tuple = (
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= first_date_of_process_date.date(), cast(BuildDecisionBookPayments.process_date, Date) <= last_date_of_process_date.date()
|
||||
)
|
||||
|
||||
for payment_type in payment_type_list:
|
||||
unpaid_debts = get_unpaid_debts(session, payment_type, date_query_tuple)
|
||||
print('length unpaid debts: ', len(unpaid_debts))
|
||||
for unpaid_debt in unpaid_debts:
|
||||
amount_to_pay = unpaid_debt["total_payments"]
|
||||
ref_id = unpaid_debt["ref_id"]
|
||||
process_date = unpaid_debt["process_date"]
|
||||
debit_row = BuildDecisionBookPayments.query.filter(BuildDecisionBookPayments.uu_id == ref_id).first()
|
||||
build_parts_id = debit_row.build_parts_id
|
||||
first_date_of_process_date = find_first_day_of_month(process_date)
|
||||
last_date_of_process_date = find_last_day_of_month(process_date)
|
||||
priority_uuids = [payment_type.uuid, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_type.uuid]]
|
||||
case_order = case(*[(AccountRecords.payment_result_type_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == build_parts_id, AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance),
|
||||
cast(AccountRecords.bank_date, Date) >= first_date_of_process_date.date(), cast(AccountRecords.bank_date, Date) <= last_date_of_process_date.date(),
|
||||
).order_by(case_order, AccountRecords.bank_date.asc()).all()
|
||||
if not money_to_pay_rows:
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == build_parts_id, AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance),
|
||||
cast(AccountRecords.bank_date, Date) >= first_date_of_process_date.date(), cast(AccountRecords.bank_date, Date) <= last_date_of_process_date.date(),
|
||||
).order_by(case_order, AccountRecords.bank_date.asc()).all()
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
account_remainder_balance = update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
money_to_pay_row = AccountRecords.query.filter(AccountRecords.id == money_to_pay_row.id).first()
|
||||
available_money = abs(money_to_pay_row.currency_value) - abs(account_remainder_balance)
|
||||
if not available_money > 0:
|
||||
continue
|
||||
if available_money > amount_to_pay:
|
||||
print('NOT All money is spent amount_to_pay:', amount_to_pay, 'debit_row payment_amount:', debit_row.payment_amount)
|
||||
close_payment_book(debit_row, money_to_pay_row, amount_to_pay, session)
|
||||
total_amount_paid += amount_to_pay
|
||||
paid_count += 1
|
||||
payments_made += 1
|
||||
update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
break
|
||||
elif available_money <= amount_to_pay:
|
||||
print('All money is spent amount_to_pay:', available_money, 'debit_row payment_amount:', debit_row.payment_amount)
|
||||
close_payment_book(debit_row, money_to_pay_row, available_money, session)
|
||||
total_amount_paid += available_money
|
||||
paid_count += 1
|
||||
payments_made += 1
|
||||
update_account_remainder_if_spent(account_record=money_to_pay_row, ref_id=debit_row.ref_id, session=session)
|
||||
continue
|
||||
else:
|
||||
print(f"Something else happened available_money: {available_money}, amount_to_pay: {amount_to_pay}")
|
||||
|
||||
update_all_spent_accounts(session)
|
||||
print('Early years decision book payments')
|
||||
print('payments_made', payments_made)
|
||||
print('total_amount_paid', total_amount_paid)
|
||||
print('paid_count', paid_count)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_time = perf_counter()
|
||||
|
||||
print("\n===== PROCESSING PAYMENTS =====\n")
|
||||
print("Starting payment processing at:", datetime.now())
|
||||
|
||||
# Process payments for current month first
|
||||
print("\n1. Processing current month payments...")
|
||||
do_payments_of_this_month()
|
||||
|
||||
# Process payments for previous months
|
||||
print("\n2. Processing previous months payments...")
|
||||
do_payments_of_previos_months()
|
||||
|
||||
print("\n===== PAYMENT PROCESSING COMPLETE =====\n")
|
||||
print("Payment processing completed at:", datetime.now())
|
||||
|
||||
# Analyze the payment situation after processing payments
|
||||
print("\n===== ANALYZING PAYMENT SITUATION AFTER PROCESSING =====\n")
|
||||
# analyze_payment_function()
|
||||
|
||||
end_time = perf_counter()
|
||||
print(f"\n{end_time - start_time:.3f} : seconds")
|
||||
|
||||
|
||||
# # Create a subquery to get the sum of payments for each debit's uu_id
|
||||
# # For credit entries, ref_id points to the original debit's uu_id
|
||||
# payment_sums = session.query(
|
||||
# BuildDecisionBookPayments.ref_id.label('original_debt_id'),
|
||||
# func.sum(func.abs(BuildDecisionBookPayments.payment_amount)).label('payment_sum')
|
||||
# ).filter(
|
||||
# BuildDecisionBookPayments.account_is_debit == False # Credit entries only
|
||||
# ).group_by(BuildDecisionBookPayments.ref_id).subquery()
|
||||
|
||||
# # Main query to find debits with their payment sums
|
||||
# query = session.query(BuildDecisionBookPayments)
|
||||
|
||||
# # Join with payment sums - cast uu_id to string to match ref_id type
|
||||
# query = query.outerjoin(
|
||||
# payment_sums,
|
||||
# func.cast(BuildDecisionBookPayments.uu_id, String) == payment_sums.c.original_debt_id
|
||||
# )
|
||||
|
||||
# # Filter for debits of the specified build part and payment type
|
||||
# query = query.filter(
|
||||
# BuildDecisionBookPayments.build_parts_id == build_parts_id,
|
||||
# BuildDecisionBookPayments.payment_types_id == debit_type.id,
|
||||
# BuildDecisionBookPayments.account_is_debit == True, # Debit entries only
|
||||
# )
|
||||
|
||||
# # Apply date filters if provided
|
||||
# if date_query:
|
||||
# for date_filter in date_query:
|
||||
# query = query.filter(date_filter)
|
||||
|
||||
# # Filter for debits that are not fully paid
|
||||
# # (payment_sum < debit_amount or payment_sum is NULL)
|
||||
# query = query.filter(
|
||||
# or_(
|
||||
# payment_sums.c.payment_sum.is_(None),
|
||||
# func.coalesce(payment_sums.c.payment_sum, 0) < func.abs(BuildDecisionBookPayments.payment_amount)
|
||||
# )
|
||||
# )
|
||||
|
||||
# # Execute the query and return the results
|
||||
# results = query.order_by(BuildDecisionBookPayments.process_date).all()
|
||||
199
ServicesBank/Finder/Payment/draft/readme.py
Normal file
199
ServicesBank/Finder/Payment/draft/readme.py
Normal file
@@ -0,0 +1,199 @@
|
||||
import arrow
|
||||
|
||||
from decimal import Decimal
|
||||
from datetime import datetime
|
||||
from time import perf_counter
|
||||
from sqlalchemy import select, func, distinct, cast, Date, String, literal, desc, and_, or_, case, join, alias, text
|
||||
from interest_calculate import hesapla_gecikme_faizi
|
||||
from Controllers.Postgres.engine import get_session_factory
|
||||
|
||||
from Schemas import (
|
||||
BuildDecisionBookPayments,
|
||||
AccountRecords,
|
||||
ApiEnumDropdown,
|
||||
Build,
|
||||
BuildDecisionBook,
|
||||
AccountDelayInterest,
|
||||
BuildParts,
|
||||
)
|
||||
|
||||
# from ServicesApi.Schemas.account.account import AccountRecords, AccountDelayInterest
|
||||
# from ServicesApi.Schemas.building.decision_book import BuildDecisionBookPayments, BuildDecisionBook
|
||||
# from ServicesApi.Schemas.building.build import Build, BuildParts
|
||||
# from ServicesApi.Schemas.others.enums import ApiEnumDropdown
|
||||
|
||||
"""
|
||||
BuildDuesTypes BDT-S Service fee (Service fee)
|
||||
BuildDuesTypes BDT-I Information (Information)
|
||||
BuildDuesTypes BDT-D Bina Aidat (Debit)
|
||||
BuildDuesTypes BDT-A Bina Ek Aidat (Add Debit)
|
||||
BuildDuesTypes BDT-R Bina Tadilat (Renovation)
|
||||
BuildDuesTypes BDT-L Bina Yasal Harcama (Lawyer expence)
|
||||
BuildDuesTypes BDT-CL Close Last Period Receipt (Close Last Period Receipt)
|
||||
BuildDuesTypes BDT-OP Open New Period Receipt (Open New Period Receipt)
|
||||
"""
|
||||
|
||||
|
||||
def joined_decision_book_sql_query(build_decision_book_id: int=49):
|
||||
subquery = select(
|
||||
AccountRecords.build_parts_id,
|
||||
BuildParts.part_code,
|
||||
BuildParts.part_no,
|
||||
AccountRecords.build_decision_book_id,
|
||||
func.sum(AccountRecords.currency_value).label('paid')
|
||||
).select_from(
|
||||
AccountRecords.__table__.join(
|
||||
BuildParts,
|
||||
AccountRecords.build_parts_id == BuildParts.id
|
||||
)
|
||||
).where(
|
||||
AccountRecords.build_decision_book_id == build_decision_book_id
|
||||
).group_by(
|
||||
AccountRecords.build_parts_id,
|
||||
BuildParts.part_code,
|
||||
BuildParts.part_no,
|
||||
AccountRecords.build_decision_book_id
|
||||
).alias('build_parts_to_account_records')
|
||||
|
||||
query = select(
|
||||
subquery.c.build_decision_book_id,
|
||||
subquery.c.build_parts_id,
|
||||
subquery.c.part_code,
|
||||
subquery.c.part_no,
|
||||
subquery.c.paid,
|
||||
func.sum(BuildDecisionBookPayments.payment_amount).label('debt'),
|
||||
(func.sum(BuildDecisionBookPayments.payment_amount) + subquery.c.paid).label('total')
|
||||
).select_from(
|
||||
subquery.join(
|
||||
BuildDecisionBookPayments,
|
||||
(BuildDecisionBookPayments.build_parts_id == subquery.c.build_parts_id) &
|
||||
(BuildDecisionBookPayments.build_decision_book_id == subquery.c.build_decision_book_id)
|
||||
)
|
||||
).group_by(
|
||||
subquery.c.build_decision_book_id,
|
||||
subquery.c.build_parts_id,
|
||||
subquery.c.part_code,
|
||||
subquery.c.part_no,
|
||||
subquery.c.paid
|
||||
).order_by(
|
||||
subquery.c.part_no
|
||||
)
|
||||
return query
|
||||
|
||||
|
||||
def print_query_result_like_dateabse_rows(results, book_id, expiry_starts, expiry_ends):
|
||||
start_time = perf_counter()
|
||||
print(f"\nResults for build_decision_book_id={book_id}:")
|
||||
print(f"Selected decision book starts at: {expiry_starts} - ends at: {expiry_ends}")
|
||||
print("-" * 80)
|
||||
print(f"{'Build ID':<10} {'Parts ID':<10} {'Part Code':<15} {'Part No':<10} {'Paid (odenen)':<15} {'Debt (borc)':<15} {'Total':<15}")
|
||||
print("-" * 80)
|
||||
|
||||
for row in results:
|
||||
print(f"{row.build_decision_book_id:<10} {row.build_parts_id:<10} {row.part_code:<15} {row.part_no:<10} {row.paid:<15.2f} {row.debt:<15.2f} {row.total:<15.2f}")
|
||||
|
||||
print("-" * 80)
|
||||
print(f"Total rows: {len(results)}")
|
||||
print(f"Query execution time: {perf_counter() - start_time:.4f} seconds")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
"""
|
||||
Waiting for container to be ready...
|
||||
Running the Python script inside the container...
|
||||
Old book ID: 49
|
||||
New book ID: 50
|
||||
Old book starts at: 2024-07-01 - ends at: 2025-06-30
|
||||
New book starts at: 2025-07-01 - ends at: 2026-06-30
|
||||
Done! Check the output above.
|
||||
"""
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
start_time = perf_counter()
|
||||
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
build_decision_book_id, timezone = 49, "Europe/Istanbul"
|
||||
selected_decision_book = BuildDecisionBook.query.filter_by(id=build_decision_book_id).first()
|
||||
|
||||
old_expiry_starts = arrow.get(selected_decision_book.expiry_starts).to(timezone).date()
|
||||
old_expiry_ends = arrow.get(selected_decision_book.expiry_ends).to(timezone).date()
|
||||
old_book_id = selected_decision_book.id
|
||||
|
||||
results = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == selected_decision_book.build_id,
|
||||
BuildDecisionBook.expiry_ends > old_expiry_ends).order_by(BuildDecisionBook.expiry_ends.asc()
|
||||
).all()
|
||||
new_book = results[0] if len(results) > 0 else None
|
||||
if not new_book:
|
||||
raise ValueError(f"New book not found after => {build_decision_book_id}. Contant your admin for future updates")
|
||||
|
||||
new_expiry_starts = arrow.get(new_book.expiry_starts).to(timezone).date()
|
||||
new_expiry_ends = arrow.get(new_book.expiry_ends).to(timezone).date()
|
||||
new_book_id = new_book.id
|
||||
|
||||
joined_query = joined_decision_book_sql_query(build_decision_book_id=build_decision_book_id)
|
||||
results = session.execute(joined_query).fetchall()
|
||||
print_query_result_like_dateabse_rows(results=results, book_id=old_book_id, expiry_starts=old_expiry_starts, expiry_ends=old_expiry_ends)
|
||||
|
||||
for result in results:
|
||||
if result.total > 0:
|
||||
print(f'User has extra money in old book {result.total} | build part id : {result.build_parts_id} | part code : {result.part_code} | part no : {result.part_no}')
|
||||
elif result.total < 0:
|
||||
print(f'User has debt in new book {result.total} | build part id : {result.build_parts_id} | part code : {result.part_code} | part no : {result.part_no}')
|
||||
else:
|
||||
print(f'User has no debt or extra money in old book {result.total} | build part id : {result.build_parts_id} | part code : {result.part_code} | part no : {result.part_no}')
|
||||
session.close()
|
||||
|
||||
# print(f"Old book ID: {old_book_id}")
|
||||
# print(f"New book ID: {new_book_id}")
|
||||
# print(f"New book starts at: {new_expiry_starts} - ends at: {new_expiry_ends}")
|
||||
|
||||
# old_account_records = session.query(func.sum(AccountRecords.currency_value) - func.sum(BuildDecisionBookPayments.payment_amount)).filter(
|
||||
# old_expiry_starts <= cast(AccountRecords.bank_date, Date), old_expiry_ends >= cast(AccountRecords.bank_date, Date),
|
||||
# AccountRecords.currency_value > 0,
|
||||
# func.abs(AccountRecords.currency_value) > func.abs(BuildDecisionBookPayments.payment_amount)
|
||||
# ).scalar()
|
||||
# new_account_records = session.query(func.sum(AccountRecords.currency_value) - func.sum(BuildDecisionBookPayments.payment_amount)).filter(
|
||||
# new_expiry_starts <= cast(AccountRecords.bank_date, Date), new_expiry_ends >= cast(AccountRecords.bank_date, Date),
|
||||
# AccountRecords.currency_value > 0,
|
||||
# func.abs(AccountRecords.currency_value) > func.abs(BuildDecisionBookPayments.payment_amount)
|
||||
# ).scalar()
|
||||
# print(f"Old account records: {old_account_records:,.2f}")
|
||||
# print(f"New account records: {new_account_records:,.2f}")
|
||||
|
||||
# build_to_iterate = Build.query.filter_by(id=build_id).first()
|
||||
|
||||
# iterate_decision_books = BuildDecisionBook.query.filter_by(build_id=build_to_iterate.id).all()
|
||||
|
||||
# for decision_book_to_iterate in iterate_decision_books:
|
||||
# joined_query = joined_decision_book_sql_query(build_decision_book_id=decision_book_to_iterate.id)
|
||||
# print(f"Build Decision Book ID: {decision_book_to_iterate.id}")
|
||||
# results = session.execute(joined_query).fetchall()
|
||||
|
||||
# print(f"\nResults for build_decision_book_id={decision_book_to_iterate.id}:")
|
||||
# print("-" * 80)
|
||||
# print(f"{'Build ID':<10} {'Parts ID':<10} {'Part Code':<15} {'Part No':<10} {'Paid (odenen)':<15} {'Debt (borc)':<15}")
|
||||
# print("-" * 80)
|
||||
|
||||
# for row in results:
|
||||
# print(f"{row.build_decision_book_id:<10} {row.build_parts_id:<10} {row.part_code:<15} {row.part_no:<10} {row.odenen:<15.2f} {row.borc:<15.2f}")
|
||||
|
||||
# print("-" * 80)
|
||||
# print(f"Total rows: {len(results)}")
|
||||
# print(f"Query execution time: {perf_counter() - start_time:.4f} seconds")
|
||||
|
||||
# print(f"\nResults for build_decision_book_id={build_decision_book_id}:")
|
||||
# print(f"Selected decision book starts at: {old_expiry_starts} - ends at: {old_expiry_ends}")
|
||||
# print("-" * 80)
|
||||
# print(f"{'Build ID':<10} {'Parts ID':<10} {'Part Code':<15} {'Part No':<10} {'Paid (odenen)':<15} {'Debt (borc)':<15}")
|
||||
# print("-" * 80)
|
||||
|
||||
# for row in results:
|
||||
# print(f"{row.build_decision_book_id:<10} {row.build_parts_id:<10} {row.part_code:<15} {row.part_no:<10} {row.odenen:<15.2f} {row.borc:<15.2f}")
|
||||
|
||||
# print("-" * 80)
|
||||
# print(f"Total rows: {len(results)}")
|
||||
# print(f"Query execution time: {perf_counter() - start_time:.4f} seconds")
|
||||
Reference in New Issue
Block a user