From 479104a04f5cb1dafe762247ea7e3224ededb96e Mon Sep 17 00:00:00 2001 From: Berkay Date: Sat, 19 Jul 2025 22:22:21 +0300 Subject: [PATCH] payment updated --- .gitignore | 5 +- README.md | 4 + .../Building/endpoints/living_space/router.py | 2 +- .../events/building_parts/supers_events.py | 2 +- .../Building/events/builds/supers_events.py | 2 +- ServicesApi/Builds/Initial/app.py | 3 +- ServicesApi/Builds/Initial/init_enums.py | 24 +- ServicesBank/Finder/Payment/exact_runner.py | 276 +++------- .../Finder/Payment/exact_runner_main_flie.py | 445 ++++++++++++++++ ServicesBank/Finder/Payment/payment_new.py | 493 ++++++++++++++++++ docker-compose.yml | 95 ++-- 11 files changed, 1085 insertions(+), 266 deletions(-) create mode 100644 ServicesBank/Finder/Payment/exact_runner_main_flie.py create mode 100644 ServicesBank/Finder/Payment/payment_new.py diff --git a/.gitignore b/.gitignore index ae315e9..f8ab384 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,7 @@ yarn-error.log* # typescript *.tsbuildinfo -next-env.d.ts \ No newline at end of file +next-env.d.ts + +# Project specific +ServicesBank/Zemberek/ \ No newline at end of file diff --git a/README.md b/README.md index 7fd4b17..8103032 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,7 @@ Todo: Text from bank iban dan build_id bulup | build_id den managment_room[living_space_id] bulup idisine account records default id haline getirelecek + +read_url_content(Url="https://github.com/username/repository") +read_resource(url="https://github.com/username/repository") +search_web(query="specific GitHub repository or topic", domain="github.com") diff --git a/ServicesApi/Builds/Building/endpoints/living_space/router.py b/ServicesApi/Builds/Building/endpoints/living_space/router.py index 3a1582c..b2dad47 100644 --- a/ServicesApi/Builds/Building/endpoints/living_space/router.py +++ b/ServicesApi/Builds/Building/endpoints/living_space/router.py @@ -9,7 +9,7 @@ from Validations.response.pagination import PaginateOnly from Extensions.Middlewares.token_provider import TokenProvider -living_space_endpoint_route = APIRouter(prefix="/living-space", tags=["Living Space Cluster"]) +living_space_endpoint_route = APIRouter(prefix="/living/spaces", tags=["Living Spaces Cluster"]) living_space_list = "LivingSpaceList" diff --git a/ServicesApi/Builds/Building/events/building_parts/supers_events.py b/ServicesApi/Builds/Building/events/building_parts/supers_events.py index 1bc5629..48e77f9 100644 --- a/ServicesApi/Builds/Building/events/building_parts/supers_events.py +++ b/ServicesApi/Builds/Building/events/building_parts/supers_events.py @@ -14,7 +14,7 @@ from Schemas import ( Build, BuildParts, ApiEnumDropdown, - BuildTypes, + # BuildTypes, BuildParts, AccountRecords, ) diff --git a/ServicesApi/Builds/Building/events/builds/supers_events.py b/ServicesApi/Builds/Building/events/builds/supers_events.py index ae9fce0..6f55f56 100644 --- a/ServicesApi/Builds/Building/events/builds/supers_events.py +++ b/ServicesApi/Builds/Building/events/builds/supers_events.py @@ -12,7 +12,7 @@ from Validations.response import ( from Validations.defaults.validations import CommonHeaders from Schemas import ( Addresses, - BuildTypes, + # BuildTypes, Build, BuildSites, BuildParts, diff --git a/ServicesApi/Builds/Initial/app.py b/ServicesApi/Builds/Initial/app.py index 2cfb167..554067b 100644 --- a/ServicesApi/Builds/Initial/app.py +++ b/ServicesApi/Builds/Initial/app.py @@ -17,7 +17,7 @@ if __name__ == "__main__": with get_db() as db_session: if set_alembic: generate_alembic(session=db_session) - exit() + try: create_one_address(db_session=db_session) except Exception as e: @@ -26,6 +26,7 @@ if __name__ == "__main__": init_api_enums_build_types(db_session=db_session) except Exception as e: print(f"Error creating enums: {e}") + exit() try: create_application_defaults(db_session=db_session) except Exception as e: diff --git a/ServicesApi/Builds/Initial/init_enums.py b/ServicesApi/Builds/Initial/init_enums.py index 3e4ab07..0335605 100644 --- a/ServicesApi/Builds/Initial/init_enums.py +++ b/ServicesApi/Builds/Initial/init_enums.py @@ -153,12 +153,32 @@ def init_api_enums_build_types(db_session): { "enum_class": "BuildDuesTypes", "type_code": "BDT-CL", - "type_name": "Close Last Period Receipt", + "type_name": "Close Last Period Debit Receipt", }, { "enum_class": "BuildDuesTypes", "type_code": "BDT-OP", - "type_name": "Open New Period Receipt", + "type_name": "Open New Period Debit Receipt", + }, + { + "enum_class": "BuildDuesTypes", + "type_code": "BDT-OPR", + "type_name": "Open New Period Renewal Receipt", + }, + { + "enum_class": "BuildDuesTypes", + "type_code": "BDT-CLR", + "type_name": "Close Last Period Renewal Receipt", + }, + { + "enum_class": "BuildDuesTypes", + "type_code": "BDT-CLL", + "type_name": "Close Last Period Lawyer Receipt", + }, + { + "enum_class": "BuildDuesTypes", + "type_code": "BDT-OPR", + "type_name": "Open New Period Lawyer Receipt", }, { "enum_class": "BuildDuesTypes", diff --git a/ServicesBank/Finder/Payment/exact_runner.py b/ServicesBank/Finder/Payment/exact_runner.py index 7bdb6ae..1ab8d41 100644 --- a/ServicesBank/Finder/Payment/exact_runner.py +++ b/ServicesBank/Finder/Payment/exact_runner.py @@ -203,176 +203,6 @@ def get_read_payment_type(payment_result_type_uu_id): return None -# Debt pay debt that is paid in same month early bird -def pay_debt_that_is_paid_in_same_month_early_bird(session, payment_type_list, date_from): - # Create aliases for tables - ar = AccountRecords.__table__.alias('ar') - bdp = BuildDecisionBookPayments.__table__.alias('bdp') - - # Calculate day difference expression - day_diff = (func.cast(func.to_char(ar.c.bank_date, 'YYYYMMDD'), Integer) - - func.cast(func.to_char(bdp.c.process_date, 'YYYYMMDD'), Integer)).label('day_df') - - # Calculate absolute difference between fund and payment - amount_diff = func.abs((ar.c.currency_value + ar.c.remainder_balance) - func.abs(bdp.c.debt_to_pay)).label('diff') - - # Calculate fund amount - fund_amount = (ar.c.currency_value + ar.c.remainder_balance).label('fund') - - # Payment correctness check - payment_correct = (ar.c.payment_result_type == bdp.c.payment_types_id).label('payment_correct') - # func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), - - # Build the query using SQLAlchemy - query = session.query( - ar.c.id.label('account_record_id'), - bdp.c.id.label('build_decision_book_payment_id'), - ar.c.living_space_id.label('living'), - amount_diff, - day_diff, - ar.c.process_comment, - func.cast(ar.c.bank_date, Date).label('bank_date'), - func.cast(bdp.c.process_date, Date).label('process_date'), - fund_amount, - bdp.c.debt_to_pay.label('debt'), - payment_correct - ).join( - bdp, - and_( - ar.c.build_parts_id == bdp.c.build_parts_id, - func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), - ar.c.payment_result_type == bdp.c.payment_types_id, - (ar.c.currency_value + ar.c.remainder_balance) == func.abs(bdp.c.debt_to_pay), - ) - ).filter( - ar.c.currency_value > 0, - bdp.c.build_decision_book_id == ar.c.build_decision_book_id, - bdp.c.is_closed.is_(False), - bdp.c.account_is_debit.is_(True), - func.cast(ar.c.bank_date, Date) <= datetime.now(), - func.cast(bdp.c.process_date, Date) <= datetime.now(), - func.cast(ar.c.bank_date, Date) > date_from, - func.cast(bdp.c.process_date, Date) > date_from - ).order_by(ar.c.living_space_id.asc(), day_diff.asc(), amount_diff.asc(), ar.c.bank_date.desc(), bdp.c.process_date.desc()) - return query.all() - - -# Debt pay debt that pay date in between thirty one day -def pay_debt_that_pay_date_in_between_thirty_one_day(session, payment_type_list, date_from): - # Create aliases for tables - ar = AccountRecords.__table__.alias('ar') - bdp = BuildDecisionBookPayments.__table__.alias('bdp') - - # Calculate day difference expression - day_diff = (func.cast(func.to_char(ar.c.bank_date, 'YYYYMMDD'), Integer) - - func.cast(func.to_char(bdp.c.process_date, 'YYYYMMDD'), Integer)).label('day_df') - - # Calculate absolute difference between fund and payment - amount_diff = func.abs((ar.c.currency_value + ar.c.remainder_balance) - - func.abs(bdp.c.debt_to_pay)).label('diff') - - # Calculate fund amount - fund_amount = (ar.c.currency_value + ar.c.remainder_balance).label('fund') - - # Payment correctness check - payment_correct = (ar.c.payment_result_type == bdp.c.payment_types_id).label('payment_correct') - # func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), - - # Build the query using SQLAlchemy - query = session.query( - ar.c.id.label('account_record_id'), - bdp.c.id.label('build_decision_book_payment_id'), - ar.c.living_space_id.label('living'), - amount_diff, - day_diff, - ar.c.process_comment, - func.cast(ar.c.bank_date, Date).label('bank_date'), - func.cast(bdp.c.process_date, Date).label('process_date'), - fund_amount, - bdp.c.debt_to_pay.label('debt'), - payment_correct - ).join( - bdp, - and_( - ar.c.build_parts_id == bdp.c.build_parts_id, - ar.c.payment_result_type == bdp.c.payment_types_id, - (ar.c.currency_value + ar.c.remainder_balance) == func.abs(bdp.c.debt_to_pay), - func.abs(func.cast(ar.c.bank_date, Date) - func.cast(bdp.c.process_date, Date)) < 32, - ) - ).filter( - bdp.c.is_closed.is_(False), - ar.c.currency_value > 0, - bdp.c.account_is_debit.is_(True), - bdp.c.build_decision_book_id == ar.c.build_decision_book_id, - func.cast(ar.c.bank_date, Date) < datetime.now(), - func.cast(bdp.c.process_date, Date) < datetime.now(), - func.cast(ar.c.bank_date, Date) > date_from, - func.cast(bdp.c.process_date, Date) > date_from - ).order_by(ar.c.living_space_id.asc(), day_diff.asc(), amount_diff.asc(), ar.c.bank_date.desc(), bdp.c.process_date.desc()) - return query.all() - - -# Debt pay debt that has sent to do many rows -def pay_debt_that_has_sent_to_do_many_rows(session, payment_type_list, date_from): - # Create aliases for tables - ar = AccountRecords.__table__.alias('ar') - bdp = BuildDecisionBookPayments.__table__.alias('bdp') - - # Calculate day difference expression - day_diff = (func.cast(func.to_char(ar.c.bank_date, 'YYYYMMDD'), Integer) - - func.cast(func.to_char(bdp.c.process_date, 'YYYYMMDD'), Integer)).label('day_df') - - # Calculate absolute difference between fund and payment - amount_diff = func.abs((ar.c.currency_value + ar.c.remainder_balance) - - func.abs(bdp.c.debt_to_pay)).label('diff') - - # Calculate fund amount - fund_amount = (ar.c.currency_value + ar.c.remainder_balance).label('fund') - - # Payment correctness check - payment_correct = (ar.c.payment_result_type == bdp.c.payment_types_id).label('payment_correct') - # func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), - - # Build the query using SQLAlchemy - query = session.query( - ar.c.id.label('account_record_id'), - bdp.c.id.label('build_decision_book_payment_id'), - ar.c.living_space_id.label('living'), - amount_diff, - day_diff, - ar.c.process_comment, - func.cast(ar.c.bank_date, Date).label('bank_date'), - func.cast(bdp.c.process_date, Date).label('process_date'), - fund_amount, - bdp.c.debt_to_pay.label('debt'), - payment_correct - ).join( - bdp, - and_( - ar.c.build_parts_id == bdp.c.build_parts_id, - ar.c.build_decision_book_id == bdp.c.build_decision_book_id, - func.abs(func.cast(ar.c.bank_date, Date) - func.cast(bdp.c.process_date, Date)) < 32, - ar.c.payment_result_type == bdp.c.payment_types_id, - (ar.c.currency_value + ar.c.remainder_balance) >= func.abs(bdp.c.debt_to_pay), - bdp.c.is_closed.is_(False), - ) - ).filter( - ar.c.currency_value > 0, - ar.c.build_decision_book_id == bdp.c.build_decision_book_id, - bdp.c.account_is_debit.is_(True), - func.cast(ar.c.bank_date, Date) < datetime.now(), - func.cast(bdp.c.process_date, Date) < datetime.now(), - func.cast(ar.c.bank_date, Date) > date_from, - func.cast(bdp.c.process_date, Date) > date_from - ).order_by( - ar.c.living_space_id.asc(), - day_diff.asc(), - amount_diff.asc(), - ar.c.bank_date.desc(), - bdp.c.process_date.desc() - ) - return query.all() - if __name__ == "__main__": # import sys @@ -384,56 +214,70 @@ if __name__ == "__main__": session = session_factory() payment_type_list = get_enums_from_database() date_from = '2022-01-01' - early_bird_results = pay_debt_that_is_paid_in_same_month_early_bird(session=session, payment_type_list=payment_type_list, date_from=date_from) - for early_bird_result in early_bird_results: + results = session.query(distinct(AccountRecords.build_parts_id)).filter( + AccountRecords.active == True, func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0, + AccountRecords.bank_date >= date_from, AccountRecords.bank_date <= datetime.now(), AccountRecords.currency_value > 0 + ).order_by(AccountRecords.build_parts_id.asc()).all() + build_parts_ids = [result[0] for result in results] + build_parts_ids.remove(1) + for build_parts_id in build_parts_ids: AccountRecords.set_session(session) BuildDecisionBookPayments.set_session(session) - print(ExactMatch(*early_bird_result).to_dict()) - account_record = AccountRecords.query.filter_by(id=early_bird_result.account_record_id).first() - book_payment = BuildDecisionBookPayments.query.filter_by(id=early_bird_result.build_decision_book_payment_id).first() - fund = account_record.currency_value + account_record.remainder_balance - if fund < abs(book_payment.debt_to_pay): - print("Fund is less than debt to pay", "-" * 500) - continue - if book_payment.is_closed: - print("Payment is closed", "-" * 500) - continue - close_payment_book(account_record=account_record, payment_row_book=book_payment, session=session, value=book_payment.debt_to_pay, is_commission_applicable=False) - count_row += 1 + all_account_records_of_selected_build_parts = session.query(AccountRecords).filter( + AccountRecords.active == True, func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0, + AccountRecords.bank_date >= date_from, AccountRecords.bank_date <= datetime.now(), AccountRecords.currency_value > 0, + AccountRecords.build_parts_id == build_parts_id + ).order_by(AccountRecords.bank_date.desc()).limit(10).offset(0).all() + for account_record in all_account_records_of_selected_build_parts: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + + selected_bank_date = account_record.bank_date + selected_bank_date_month = selected_bank_date.month + selected_bank_date_year = selected_bank_date.year + year_month = f"{selected_bank_date_year}-{selected_bank_date_month:02d}" + print(f"Build decision book id: {account_record.build_decision_book_id}") + total_amount_collected_this_month = session.query(func.sum(AccountRecords.currency_value)).filter( + AccountRecords.currency_value > 0, + AccountRecords.build_decision_book_id == account_record.build_decision_book_id, + func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0, + func.extract('month', AccountRecords.bank_date) == selected_bank_date_month, + func.extract('year', AccountRecords.bank_date) == selected_bank_date_year, + AccountRecords.build_parts_id == build_parts_id + ).scalar() or 0 + total_amount_of_debt_of_this_month = session.query(func.sum(BuildDecisionBookPayments.debt_to_pay)).filter( + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.build_decision_book_id == account_record.build_decision_book_id, + func.extract('month', BuildDecisionBookPayments.process_date) == selected_bank_date_month, + func.extract('year', BuildDecisionBookPayments.process_date) == selected_bank_date_year, + BuildDecisionBookPayments.process_date <= datetime.now() + ).scalar() or 0 + print(f"{year_month} total amount of debt in this month : ", f"{total_amount_of_debt_of_this_month:,.2f}") + print(f"{year_month} total amount collected this month : ", f"{total_amount_collected_this_month:,.2f}") - late_bird_results = pay_debt_that_pay_date_in_between_thirty_one_day(session=session, payment_type_list=payment_type_list, date_from=date_from) - for late_bird_result in late_bird_results: - AccountRecords.set_session(session) - BuildDecisionBookPayments.set_session(session) - print(ExactMatch(*late_bird_result).to_dict()) - account_record = AccountRecords.query.filter_by(id=late_bird_result.account_record_id).first() - book_payment = BuildDecisionBookPayments.query.filter_by(id=late_bird_result.build_decision_book_payment_id).first() - fund = account_record.currency_value + account_record.remainder_balance - if fund < abs(book_payment.debt_to_pay): - print("Fund is less than debt to pay", "-" * 500) - continue - if book_payment.is_closed: - print("Payment is closed", "-" * 500) - continue - close_payment_book(account_record=account_record, payment_row_book=book_payment, session=session, value=book_payment.debt_to_pay, is_commission_applicable=False) - count_row += 1 + avaliable_fund = abs(account_record.currency_value) - abs(account_record.remainder_balance) + total_debt_to_pay_of_this_month = session.query(func.sum(BuildDecisionBookPayments.debt_to_pay)).filter( + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.build_decision_book_id == account_record.build_decision_book_id, + func.extract('month', BuildDecisionBookPayments.process_date) == selected_bank_date_month, + func.extract('year', BuildDecisionBookPayments.process_date) == selected_bank_date_year, + BuildDecisionBookPayments.process_date <= datetime.now() + ).scalar() or 0 + exact_match_with_account_fund_and_debt_to_pay = BuildDecisionBookPayments.query.filter( + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.build_decision_book_id == account_record.build_decision_book_id, + func.extract('month', BuildDecisionBookPayments.process_date) == selected_bank_date_month, + func.extract('year', BuildDecisionBookPayments.process_date) == selected_bank_date_year, + BuildDecisionBookPayments.process_date <= datetime.now(), + BuildDecisionBookPayments.debt_to_pay == avaliable_fund + ).first() + is_fund_available_is_completely_closes_all_debts = avaliable_fund >= total_debt_to_pay_of_this_month + is_one_of_the_debt_to_pay_exactly_matches = avaliable_fund == exact_match_with_account_fund_and_debt_to_pay.debt_to_pay if exact_match_with_account_fund_and_debt_to_pay else False - thirty_three_days = pay_debt_that_has_sent_to_do_many_rows(session=session, payment_type_list=payment_type_list, date_from=date_from) - for thirty_three_day in thirty_three_days: - AccountRecords.set_session(session) - BuildDecisionBookPayments.set_session(session) - print(ExactMatch(*thirty_three_day).to_dict()) - account_record = AccountRecords.query.filter_by(id=thirty_three_day.account_record_id).first() - book_payment = BuildDecisionBookPayments.query.filter_by(id=thirty_three_day.build_decision_book_payment_id).first() - fund = account_record.currency_value + account_record.remainder_balance - if fund < abs(book_payment.debt_to_pay): - print("Fund is less than debt to pay", "-" * 500) - continue - if book_payment.is_closed: - print("Payment is closed", "-" * 500) - continue - close_payment_book(account_record=account_record, payment_row_book=book_payment, session=session, value=book_payment.debt_to_pay, is_commission_applicable=False) - count_row += 1 + print(f"account record id: {account_record.id} | bank date: {account_record.bank_date.date()} | total debt to pay: {total_debt_to_pay_of_this_month:,.2f}") + print(f"avaliable fund: {avaliable_fund:,.2f} | exact match: {is_one_of_the_debt_to_pay_exactly_matches}") + + print(f"Build part processed: {build_parts_id}") session.close() print(f"Processed {count_row} rows") # Parse command line arguments diff --git a/ServicesBank/Finder/Payment/exact_runner_main_flie.py b/ServicesBank/Finder/Payment/exact_runner_main_flie.py new file mode 100644 index 0000000..7bdb6ae --- /dev/null +++ b/ServicesBank/Finder/Payment/exact_runner_main_flie.py @@ -0,0 +1,445 @@ +import arrow +import calendar + +from time import perf_counter + +from decimal import Decimal +from datetime import datetime, timedelta +from Schemas import BuildDecisionBookPayments, AccountRecords, AccountDelayInterest, ApiEnumDropdown, Build, BuildDecisionBook +from sqlalchemy import select,text, Integer, Float, func, distinct, cast, Date, String, literal, desc, and_, or_, case +from sqlalchemy.sql.expression import literal +from Controllers.Postgres.engine import get_session_factory + + +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 + +class ExactMatch: + def __init__(self, + account_record_id, build_decision_book_payment_id, living, diff, day_df, process_comment, + bank_date, process_date, fund, debt, payment_correct): + self.account_record_id = account_record_id + self.build_decision_book_payment_id = build_decision_book_payment_id + self.living = living + self.diff = diff + self.day_df = day_df + self.process_comment = process_comment + self.bank_date = str(bank_date) + self.process_date = str(process_date) + self.fund = fund + self.debt = debt + self.payment_correct = payment_correct + + def to_dict(self): + return { + 'account_record_id': self.account_record_id, + 'build_decision_book_payment_id': self.build_decision_book_payment_id, + # 'process_comment': self.process_comment, + 'living': self.living, + 'diff': self.diff, + 'day_df': self.day_df, + 'bank_date': self.bank_date, + 'process_date': self.process_date, + 'fund': self.fund, + 'debt': self.debt, + 'payment_correct': self.payment_correct + } + +def close_payment_book(payment_row_book, account_record, value, session, is_commission_applicable: bool = False): + """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) + if is_commission_applicable: + AccountDelayInterest.set_session(session) + new_row_interest = AccountDelayInterest.create( + account_record_bank_date=account_record.bank_date, + book_payment_process_date=payment_row_book.process_date, + debt=abs(value), + account_records_id=account_record.id, + account_records_uu_id=str(account_record.uu_id), + build_decision_book_payment_id=saved_row.id, + build_decision_book_payment_uu_id=str(saved_row.uu_id), + replication_id=99, # Triggers for trigger_account_delay_interest calculation [active trigger] + bank_resp_company_id=1, # Evyos interest rates will be applied when id is given + bank_resp_company_uu_id="CMP", + ) + new_row_interest.save() + session.commit() + session.refresh(new_row_interest) + print(f'Commission applicable: amount : {value} bank date: {account_record.bank_date} process date: {payment_row_book.process_date}') + # 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 None + + +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 get_read_payment_type(payment_result_type_uu_id): + payment_type_list = get_enums_from_database() + for payment_type in payment_type_list: + if payment_type.uuid == payment_result_type_uu_id: + return payment_type.key + return None + + +# Debt pay debt that is paid in same month early bird +def pay_debt_that_is_paid_in_same_month_early_bird(session, payment_type_list, date_from): + # Create aliases for tables + ar = AccountRecords.__table__.alias('ar') + bdp = BuildDecisionBookPayments.__table__.alias('bdp') + + # Calculate day difference expression + day_diff = (func.cast(func.to_char(ar.c.bank_date, 'YYYYMMDD'), Integer) - + func.cast(func.to_char(bdp.c.process_date, 'YYYYMMDD'), Integer)).label('day_df') + + # Calculate absolute difference between fund and payment + amount_diff = func.abs((ar.c.currency_value + ar.c.remainder_balance) - func.abs(bdp.c.debt_to_pay)).label('diff') + + # Calculate fund amount + fund_amount = (ar.c.currency_value + ar.c.remainder_balance).label('fund') + + # Payment correctness check + payment_correct = (ar.c.payment_result_type == bdp.c.payment_types_id).label('payment_correct') + # func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), + + # Build the query using SQLAlchemy + query = session.query( + ar.c.id.label('account_record_id'), + bdp.c.id.label('build_decision_book_payment_id'), + ar.c.living_space_id.label('living'), + amount_diff, + day_diff, + ar.c.process_comment, + func.cast(ar.c.bank_date, Date).label('bank_date'), + func.cast(bdp.c.process_date, Date).label('process_date'), + fund_amount, + bdp.c.debt_to_pay.label('debt'), + payment_correct + ).join( + bdp, + and_( + ar.c.build_parts_id == bdp.c.build_parts_id, + func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), + ar.c.payment_result_type == bdp.c.payment_types_id, + (ar.c.currency_value + ar.c.remainder_balance) == func.abs(bdp.c.debt_to_pay), + ) + ).filter( + ar.c.currency_value > 0, + bdp.c.build_decision_book_id == ar.c.build_decision_book_id, + bdp.c.is_closed.is_(False), + bdp.c.account_is_debit.is_(True), + func.cast(ar.c.bank_date, Date) <= datetime.now(), + func.cast(bdp.c.process_date, Date) <= datetime.now(), + func.cast(ar.c.bank_date, Date) > date_from, + func.cast(bdp.c.process_date, Date) > date_from + ).order_by(ar.c.living_space_id.asc(), day_diff.asc(), amount_diff.asc(), ar.c.bank_date.desc(), bdp.c.process_date.desc()) + return query.all() + + +# Debt pay debt that pay date in between thirty one day +def pay_debt_that_pay_date_in_between_thirty_one_day(session, payment_type_list, date_from): + # Create aliases for tables + ar = AccountRecords.__table__.alias('ar') + bdp = BuildDecisionBookPayments.__table__.alias('bdp') + + # Calculate day difference expression + day_diff = (func.cast(func.to_char(ar.c.bank_date, 'YYYYMMDD'), Integer) - + func.cast(func.to_char(bdp.c.process_date, 'YYYYMMDD'), Integer)).label('day_df') + + # Calculate absolute difference between fund and payment + amount_diff = func.abs((ar.c.currency_value + ar.c.remainder_balance) - + func.abs(bdp.c.debt_to_pay)).label('diff') + + # Calculate fund amount + fund_amount = (ar.c.currency_value + ar.c.remainder_balance).label('fund') + + # Payment correctness check + payment_correct = (ar.c.payment_result_type == bdp.c.payment_types_id).label('payment_correct') + # func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), + + # Build the query using SQLAlchemy + query = session.query( + ar.c.id.label('account_record_id'), + bdp.c.id.label('build_decision_book_payment_id'), + ar.c.living_space_id.label('living'), + amount_diff, + day_diff, + ar.c.process_comment, + func.cast(ar.c.bank_date, Date).label('bank_date'), + func.cast(bdp.c.process_date, Date).label('process_date'), + fund_amount, + bdp.c.debt_to_pay.label('debt'), + payment_correct + ).join( + bdp, + and_( + ar.c.build_parts_id == bdp.c.build_parts_id, + ar.c.payment_result_type == bdp.c.payment_types_id, + (ar.c.currency_value + ar.c.remainder_balance) == func.abs(bdp.c.debt_to_pay), + func.abs(func.cast(ar.c.bank_date, Date) - func.cast(bdp.c.process_date, Date)) < 32, + ) + ).filter( + bdp.c.is_closed.is_(False), + ar.c.currency_value > 0, + bdp.c.account_is_debit.is_(True), + bdp.c.build_decision_book_id == ar.c.build_decision_book_id, + func.cast(ar.c.bank_date, Date) < datetime.now(), + func.cast(bdp.c.process_date, Date) < datetime.now(), + func.cast(ar.c.bank_date, Date) > date_from, + func.cast(bdp.c.process_date, Date) > date_from + ).order_by(ar.c.living_space_id.asc(), day_diff.asc(), amount_diff.asc(), ar.c.bank_date.desc(), bdp.c.process_date.desc()) + return query.all() + + +# Debt pay debt that has sent to do many rows +def pay_debt_that_has_sent_to_do_many_rows(session, payment_type_list, date_from): + # Create aliases for tables + ar = AccountRecords.__table__.alias('ar') + bdp = BuildDecisionBookPayments.__table__.alias('bdp') + + # Calculate day difference expression + day_diff = (func.cast(func.to_char(ar.c.bank_date, 'YYYYMMDD'), Integer) - + func.cast(func.to_char(bdp.c.process_date, 'YYYYMMDD'), Integer)).label('day_df') + + # Calculate absolute difference between fund and payment + amount_diff = func.abs((ar.c.currency_value + ar.c.remainder_balance) - + func.abs(bdp.c.debt_to_pay)).label('diff') + + # Calculate fund amount + fund_amount = (ar.c.currency_value + ar.c.remainder_balance).label('fund') + + # Payment correctness check + payment_correct = (ar.c.payment_result_type == bdp.c.payment_types_id).label('payment_correct') + # func.to_char(ar.c.bank_date, 'YYYY-MM') == func.to_char(bdp.c.process_date, 'YYYY-MM'), + + # Build the query using SQLAlchemy + query = session.query( + ar.c.id.label('account_record_id'), + bdp.c.id.label('build_decision_book_payment_id'), + ar.c.living_space_id.label('living'), + amount_diff, + day_diff, + ar.c.process_comment, + func.cast(ar.c.bank_date, Date).label('bank_date'), + func.cast(bdp.c.process_date, Date).label('process_date'), + fund_amount, + bdp.c.debt_to_pay.label('debt'), + payment_correct + ).join( + bdp, + and_( + ar.c.build_parts_id == bdp.c.build_parts_id, + ar.c.build_decision_book_id == bdp.c.build_decision_book_id, + func.abs(func.cast(ar.c.bank_date, Date) - func.cast(bdp.c.process_date, Date)) < 32, + ar.c.payment_result_type == bdp.c.payment_types_id, + (ar.c.currency_value + ar.c.remainder_balance) >= func.abs(bdp.c.debt_to_pay), + bdp.c.is_closed.is_(False), + ) + ).filter( + ar.c.currency_value > 0, + ar.c.build_decision_book_id == bdp.c.build_decision_book_id, + bdp.c.account_is_debit.is_(True), + func.cast(ar.c.bank_date, Date) < datetime.now(), + func.cast(bdp.c.process_date, Date) < datetime.now(), + func.cast(ar.c.bank_date, Date) > date_from, + func.cast(bdp.c.process_date, Date) > date_from + ).order_by( + ar.c.living_space_id.asc(), + day_diff.asc(), + amount_diff.asc(), + ar.c.bank_date.desc(), + bdp.c.process_date.desc() + ) + return query.all() + + +if __name__ == "__main__": + # import sys + + # Default values + # id_given = 1 + count_row = 0 + session_factory = get_session_factory() + session = session_factory() + payment_type_list = get_enums_from_database() + date_from = '2022-01-01' + early_bird_results = pay_debt_that_is_paid_in_same_month_early_bird(session=session, payment_type_list=payment_type_list, date_from=date_from) + for early_bird_result in early_bird_results: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + print(ExactMatch(*early_bird_result).to_dict()) + account_record = AccountRecords.query.filter_by(id=early_bird_result.account_record_id).first() + book_payment = BuildDecisionBookPayments.query.filter_by(id=early_bird_result.build_decision_book_payment_id).first() + fund = account_record.currency_value + account_record.remainder_balance + if fund < abs(book_payment.debt_to_pay): + print("Fund is less than debt to pay", "-" * 500) + continue + if book_payment.is_closed: + print("Payment is closed", "-" * 500) + continue + close_payment_book(account_record=account_record, payment_row_book=book_payment, session=session, value=book_payment.debt_to_pay, is_commission_applicable=False) + count_row += 1 + + late_bird_results = pay_debt_that_pay_date_in_between_thirty_one_day(session=session, payment_type_list=payment_type_list, date_from=date_from) + for late_bird_result in late_bird_results: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + print(ExactMatch(*late_bird_result).to_dict()) + account_record = AccountRecords.query.filter_by(id=late_bird_result.account_record_id).first() + book_payment = BuildDecisionBookPayments.query.filter_by(id=late_bird_result.build_decision_book_payment_id).first() + fund = account_record.currency_value + account_record.remainder_balance + if fund < abs(book_payment.debt_to_pay): + print("Fund is less than debt to pay", "-" * 500) + continue + if book_payment.is_closed: + print("Payment is closed", "-" * 500) + continue + close_payment_book(account_record=account_record, payment_row_book=book_payment, session=session, value=book_payment.debt_to_pay, is_commission_applicable=False) + count_row += 1 + + thirty_three_days = pay_debt_that_has_sent_to_do_many_rows(session=session, payment_type_list=payment_type_list, date_from=date_from) + for thirty_three_day in thirty_three_days: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + print(ExactMatch(*thirty_three_day).to_dict()) + account_record = AccountRecords.query.filter_by(id=thirty_three_day.account_record_id).first() + book_payment = BuildDecisionBookPayments.query.filter_by(id=thirty_three_day.build_decision_book_payment_id).first() + fund = account_record.currency_value + account_record.remainder_balance + if fund < abs(book_payment.debt_to_pay): + print("Fund is less than debt to pay", "-" * 500) + continue + if book_payment.is_closed: + print("Payment is closed", "-" * 500) + continue + close_payment_book(account_record=account_record, payment_row_book=book_payment, session=session, value=book_payment.debt_to_pay, is_commission_applicable=False) + count_row += 1 + session.close() + print(f"Processed {count_row} rows") + # Parse command line arguments + # if len(sys.argv) > 1: + # try: + # id_given = int(sys.argv[1]) + # except ValueError: + # print(f"Error: Invalid limit value '{sys.argv[1]}'. Using default limit={limit}") + \ No newline at end of file diff --git a/ServicesBank/Finder/Payment/payment_new.py b/ServicesBank/Finder/Payment/payment_new.py new file mode 100644 index 0000000..bb121bc --- /dev/null +++ b/ServicesBank/Finder/Payment/payment_new.py @@ -0,0 +1,493 @@ +import arrow +import calendar + +from time import perf_counter + +from decimal import Decimal +from datetime import datetime, timedelta +from Schemas import BuildDecisionBookPayments, AccountRecords, AccountDelayInterest, ApiEnumDropdown, Build, BuildDecisionBook +from sqlalchemy import select,text, Integer, Float, func, distinct, cast, Date, String, literal, desc, and_, or_, case +from sqlalchemy.sql.expression import literal +from Controllers.Postgres.engine import get_session_factory + + +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 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 read_all_account_records(session, date_from, list_payment_type = None): + if list_payment_type: + return session.query(AccountRecords).filter( + func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0, + cast(AccountRecords.bank_date, Date) >= date_from, + cast(AccountRecords.bank_date, Date) <= datetime.now().date(), + AccountRecords.currency_value > 0, + AccountRecords.build_parts_id > 1, + AccountRecords.payment_result_type == list_payment_type.id, + ).order_by(AccountRecords.build_parts_id.asc(), AccountRecords.bank_date.asc()).all() + return session.query(AccountRecords).filter( + func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0, + cast(AccountRecords.bank_date, Date) >= date_from, + cast(AccountRecords.bank_date, Date) <= datetime.now().date(), + AccountRecords.currency_value > 0, + AccountRecords.build_parts_id > 1, + ).order_by(AccountRecords.build_parts_id.asc(), AccountRecords.bank_date.asc()).all() + + +def calculate_debts_for_this_month(session, date_from, list_payment_type, bank_date, build_decision_book_id, build_parts_id): + selected_bank_date_month = bank_date.month + selected_bank_date_year = bank_date.year + return session.query(func.sum(BuildDecisionBookPayments.debt_to_pay)).filter( + BuildDecisionBookPayments.build_decision_book_id == build_decision_book_id, + func.extract('month', BuildDecisionBookPayments.process_date) == int(selected_bank_date_month), + func.extract('year', BuildDecisionBookPayments.process_date) == int(selected_bank_date_year), + BuildDecisionBookPayments.payment_types_id == list_payment_type.id, + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.is_closed == False, + BuildDecisionBookPayments.build_parts_id == build_parts_id, + ).scalar() + + +def calculate_debts_for_previous_month(session, date_from, list_payment_type, bank_date, build_decision_book_id, build_parts_id): + return session.query(func.sum(BuildDecisionBookPayments.debt_to_pay)).filter( + BuildDecisionBookPayments.build_decision_book_id == build_decision_book_id, + cast(BuildDecisionBookPayments.process_date, Date) <= bank_date.date(), + BuildDecisionBookPayments.payment_types_id == list_payment_type.id, + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.is_closed == False, + BuildDecisionBookPayments.build_parts_id == build_parts_id, + ).scalar() + + +def calculate_debts_for_previous_month_without_payment_type(session, date_from, bank_date, build_decision_book_id, build_parts_id): + return session.query(func.sum(BuildDecisionBookPayments.debt_to_pay)).filter( + BuildDecisionBookPayments.build_decision_book_id == build_decision_book_id, + # cast(BuildDecisionBookPayments.process_date, Date) <= bank_date.date(), + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.is_closed == False, + BuildDecisionBookPayments.build_parts_id == build_parts_id, + ).scalar() + + +def get_rows_of_debt_for_this_month(session, date_from, list_payment_type, bank_date, build_decision_book_id, build_parts_id): + selected_bank_date_month = bank_date.month + selected_bank_date_year = bank_date.year + return session.query(BuildDecisionBookPayments).filter( + BuildDecisionBookPayments.build_decision_book_id == build_decision_book_id, + func.extract('month', BuildDecisionBookPayments.process_date) == int(selected_bank_date_month), + func.extract('year', BuildDecisionBookPayments.process_date) == int(selected_bank_date_year), + BuildDecisionBookPayments.payment_types_id == list_payment_type.id, + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.is_closed == False, + BuildDecisionBookPayments.build_parts_id == build_parts_id, + ).order_by(BuildDecisionBookPayments.debt_to_pay.desc()).all() + + +def get_rows_of_debt_for_previous_month(session, date_from, list_payment_type, bank_date, build_decision_book_id, build_parts_id): + return session.query(BuildDecisionBookPayments).filter( + BuildDecisionBookPayments.build_decision_book_id == build_decision_book_id, + cast(BuildDecisionBookPayments.process_date, Date) <= bank_date.date(), + BuildDecisionBookPayments.payment_types_id == list_payment_type.id, + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.is_closed == False, + BuildDecisionBookPayments.build_parts_id == build_parts_id, + ).order_by(BuildDecisionBookPayments.debt_to_pay.desc(), BuildDecisionBookPayments.process_date.desc()).all() + + +def get_rows_of_debt_for_previous_month_without_payment_type(session, date_from, bank_date, build_decision_book_id, build_parts_id): + return session.query(BuildDecisionBookPayments).filter( + BuildDecisionBookPayments.build_decision_book_id == build_decision_book_id, + # cast(BuildDecisionBookPayments.process_date, Date) <= bank_date.date(), + BuildDecisionBookPayments.account_is_debit == True, + BuildDecisionBookPayments.is_closed == False, + BuildDecisionBookPayments.build_parts_id == build_parts_id, + ).order_by(BuildDecisionBookPayments.debt_to_pay.desc(), BuildDecisionBookPayments.process_date.desc()).all() + + +def close_payment_book(payment_row_book, account_record, value, session, is_commission_applicable: bool = False): + """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) + if is_commission_applicable: + AccountDelayInterest.set_session(session) + debt_to_pay_with_commission_applied = payment_row_book.debt_to_pay - value + if debt_to_pay_with_commission_applied < 0: + return None + new_row_interest = AccountDelayInterest.create( + account_record_bank_date=account_record.bank_date, + book_payment_process_date=payment_row_book.process_date, + debt=abs(debt_to_pay_with_commission_applied), + account_records_id=account_record.id, + account_records_uu_id=str(account_record.uu_id), + build_decision_book_payment_id=payment_row_book.id, + build_decision_book_payment_uu_id=str(payment_row_book.uu_id), + replication_id=99, # Triggers for trigger_account_delay_interest calculation [active trigger] + bank_resp_company_id=1, # Evyos interest rates will be applied when id is given + bank_resp_company_uu_id="CMP", + ) + new_row_interest.save() + session.commit() + session.refresh(new_row_interest) + print(f"Applying commission : {payment_row_book.build_parts_id}") + print(f"Account record id : {account_record.id} - {account_record.bank_date.date()}") + print(f"Commission applied : {debt_to_pay_with_commission_applied} | {payment_row_book.debt_to_pay}") + print(f"Build Decision Book id : {payment_row_book.build_decision_book_id} - {payment_row_book.process_date.date()}") + # 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 None + + +def get_available_account_records_fund(session, account_record_id): + return session.query(func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance)).filter( + AccountRecords.id == account_record_id, + ).scalar() + + +def set_account_and_payments_with_this_month_debts(session, date_from, list_payment_types): + # Function 1: Read only matching month and year of account records and close debts for each payment type [Commission Applicable if insufficient funds] + print('Function 1: Read only matching month and year of account records and close debts for each payment type [Commission Applicable if insufficient funds]') + print('---' * 200) + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + BuildDecisionBook.set_session(session) + + for list_payment_type in list_payment_types: + result_all_account_records = read_all_account_records(session, date_from, list_payment_type) + print('list_payment_type', list_payment_type.id, '---' * 50) + for result_all_account_record in result_all_account_records: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + + selected_bank_date = result_all_account_record.bank_date + selected_bank_date_month = selected_bank_date.month + selected_bank_date_year = selected_bank_date.year + avaliable_fund = get_available_account_records_fund(session, result_all_account_record.id) + # print(f'iterating account records id : ', result_all_account_record.id) + # print(f'iterating account records bank_date : ', result_all_account_record.bank_date.date()) + # print(f'iterating account records currency_value : ', result_all_account_record.currency_value) + # print(f'iterating account records remainder_balance : ', result_all_account_record.remainder_balance) + # print(f'iterating account records process comment : ', result_all_account_record.process_comment) + # print(f'iterating account records build_parts_id : ', result_all_account_record.build_parts_id) + # print(f'iterating account records decision_book_id : ', result_all_account_record.build_decision_book_id) + # print(f'iterating account records avaliable_fund : {avaliable_fund:,.2f}') + get_payments_dict = dict( + session=session, date_from=date_from, list_payment_type=list_payment_type, bank_date=selected_bank_date, + build_decision_book_id=result_all_account_record.build_decision_book_id, build_parts_id=result_all_account_record.build_parts_id + ) + debts_for_this_month = calculate_debts_for_this_month(**get_payments_dict) or 0 + print_variable_of_debt = f"{debts_for_this_month:,.2f}" if debts_for_this_month else "No debts found" + # print(f'{selected_bank_date_year}-{selected_bank_date_month:02d}: {print_variable_of_debt}') + if debts_for_this_month > 0 and avaliable_fund > 0: + this_month_debts = get_rows_of_debt_for_this_month(**get_payments_dict) + for this_month_debt in this_month_debts: + if this_month_debt.debt_to_pay <= avaliable_fund: + # print(f"debt process date : {this_month_debt.process_date.date()}") + # print(f"debt amount : {this_month_debt.debt_to_pay:,.2f}") + # print(f"money paid : {this_month_debt.debt_to_pay:,.2f}") + close_payment_book( + payment_row_book=this_month_debt, account_record=result_all_account_record, value=this_month_debt.debt_to_pay, session=session, is_commission_applicable=False + ) + avaliable_fund = get_available_account_records_fund(session, result_all_account_record.id) + # print(f"Remaind Balance : {avaliable_fund:,.2f}") + if avaliable_fund <= 0: + break + # else: + # close_payment_book( + # payment_row_book=this_month_debt, account_record=result_all_account_record, value=avaliable_fund, session=session, is_commission_applicable=True + # ) + # print(f"Insufficient fund for debt :( : {this_month_debt.debt_to_pay:,.2f}") + # print(f"Fund available :( : {avaliable_fund:,.2f}") + else: + pass + # print(f"No debts found for or fund found for {selected_bank_date_year}-{selected_bank_date_month:02d} debt: {debts_for_this_month:,.2f} fund: {avaliable_fund:,.2f}") + + # Function 1: Read only matching month and year of account records and close debts for each payment type [Commission Applicable if insufficient funds] + + +def set_account_and_payments_with_previous_month_debts(session, date_from, list_payment_types): + # Function 2: Read only bank date < decision book payment process date and close debts for each payment type [Commission Applicable if insufficient funds] + print('Function 2: Read only bank date < decision book payment process date and close debts for each payment type [Commission Applicable if insufficient funds]') + print('---' * 200) + for list_payment_type in list_payment_types: + result_all_account_records = read_all_account_records(session, date_from, list_payment_type) + for result_all_account_record in result_all_account_records: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + + selected_bank_date = result_all_account_record.bank_date + selected_bank_date_month = selected_bank_date.month + selected_bank_date_year = selected_bank_date.year + avaliable_fund = get_available_account_records_fund(session, result_all_account_record.id) + print(f'iterating account records id : ', result_all_account_record.id) + print(f'iterating account records bank_date : ', result_all_account_record.bank_date.date()) + print(f'iterating account records currency_value : ', result_all_account_record.currency_value) + print(f'iterating account records remainder_balance : ', result_all_account_record.remainder_balance) + print(f'iterating account records process comment : ', result_all_account_record.process_comment) + print(f'iterating account records build_parts_id : ', result_all_account_record.build_parts_id) + print(f'iterating account records decision_book_id : ', result_all_account_record.build_decision_book_id) + print(f'iterating account records payment type : ', list_payment_type.value, 'id: ', list_payment_type.id) + print(f'iterating account records avaliable_fund : {avaliable_fund:,.2f}') + + get_payments_dict = dict( + session=session, date_from=date_from, list_payment_type=list_payment_type, bank_date=selected_bank_date, + build_decision_book_id=result_all_account_record.build_decision_book_id, build_parts_id=result_all_account_record.build_parts_id + ) + debts_for_this_month = calculate_debts_for_previous_month(**get_payments_dict) or 0 + print_variable_of_debt = f"{debts_for_this_month:,.2f}" if debts_for_this_month else "No debts found" + print(f'{selected_bank_date_year}-{selected_bank_date_month:02d}: {print_variable_of_debt}') + if debts_for_this_month > 0 and avaliable_fund > 0: + this_month_debts = get_rows_of_debt_for_previous_month(**get_payments_dict) + for this_month_debt in this_month_debts: + if this_month_debt.debt_to_pay <= avaliable_fund: + print(f"debt process date : {this_month_debt.process_date.date()}") + print(f"debt amount : {this_month_debt.debt_to_pay:,.2f}") + print(f"money paid : {this_month_debt.debt_to_pay:,.2f}") + print(f"Payment id : {this_month_debt.id}") + close_payment_book( + payment_row_book=this_month_debt, + account_record=result_all_account_record, + value=this_month_debt.debt_to_pay, + session=session, + is_commission_applicable=True + ) + avaliable_fund = get_available_account_records_fund(session, result_all_account_record.id) + print(f"Remaind Balance : {avaliable_fund:,.2f}") + if avaliable_fund <= 0: + break + # else: + # # close_payment_book( + # # payment_row_book=this_month_debt, + # # account_record=result_all_account_record, + # # value=avaliable_fund, + # # session=session, + # # is_commission_applicable=True + # # ) + # print(f"Insufficient fund for debt : {this_month_debt.debt_to_pay:,.2f}") + # print(f"Debt process date : {this_month_debt.process_date.date()}") + # print(f"Debt id : {this_month_debt.id}") + # print(f"Fund available : {avaliable_fund:,.2f}") + # print(f"Payment id : {this_month_debt.id}") + else: + print(f"No debts found for or fund found for {selected_bank_date_year}-{selected_bank_date_month:02d} debt: {debts_for_this_month:,.2f} fund: {avaliable_fund:,.2f}") + + +def set_account_and_payments_with_previous_month_debts_without_payment_type(session, date_from): + # Function 3: Read only bank date < decision book payment process date and close debts for without payment type [Commission Applicable if insufficient funds] + print('Function 3: Read only bank date < decision book payment process date and close debts for without payment type [Commission Applicable if insufficient funds]') + print('---' * 200) + result_all_account_records = read_all_account_records(session, date_from) + for result_all_account_record in result_all_account_records: + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + + selected_bank_date = result_all_account_record.bank_date + selected_bank_date_month = selected_bank_date.month + selected_bank_date_year = selected_bank_date.year + avaliable_fund = get_available_account_records_fund(session, result_all_account_record.id) + print(f'iterating account records id : ', result_all_account_record.id) + print(f'iterating account records bank_date : ', result_all_account_record.bank_date.date()) + print(f'iterating account records currency_value : ', result_all_account_record.currency_value) + print(f'iterating account records remainder_balance : ', result_all_account_record.remainder_balance) + print(f'iterating account records process comment : ', result_all_account_record.process_comment) + print(f'iterating account records build_parts_id : ', result_all_account_record.build_parts_id) + print(f'iterating account records decision_book_id : ', result_all_account_record.build_decision_book_id) + print(f'iterating account records payment type : ', result_all_account_record.payment_result_type) + print(f'iterating account records avaliable_fund : {avaliable_fund:,.2f}') + + get_payments_dict = dict( + session=session, date_from=date_from, bank_date=selected_bank_date, + build_decision_book_id=result_all_account_record.build_decision_book_id, build_parts_id=result_all_account_record.build_parts_id + ) + debts_for_this_month = calculate_debts_for_previous_month_without_payment_type(**get_payments_dict) or 0 + print_variable_of_debt = f"{debts_for_this_month:,.2f}" if debts_for_this_month else "No debts found" + print(f'{selected_bank_date_year}-{selected_bank_date_month:02d}: {print_variable_of_debt}') + if debts_for_this_month > 0 and avaliable_fund > 0: + this_month_debts = get_rows_of_debt_for_previous_month_without_payment_type(**get_payments_dict) + for this_month_debt in this_month_debts: + if this_month_debt.debt_to_pay <= avaliable_fund: + print(f"debt process date : {this_month_debt.process_date.date()}") + print(f"debt amount : {this_month_debt.debt_to_pay:,.2f}") + print(f"money paid : {this_month_debt.debt_to_pay:,.2f}") + print(f"Payment id : {this_month_debt.id}") + print(f"Payment type : {this_month_debt.payment_types_id}") + close_payment_book( + payment_row_book=this_month_debt, + account_record=result_all_account_record, + value=this_month_debt.debt_to_pay, + session=session, + is_commission_applicable=True + ) + avaliable_fund = get_available_account_records_fund(session, result_all_account_record.id) + print(f"Remaind Balance : {avaliable_fund:,.2f}") + if avaliable_fund <= 0: + break + # else: + # close_payment_book( + # payment_row_book=this_month_debt, + # account_record=result_all_account_record, + # value=avaliable_fund, + # session=session, + # is_commission_applicable=True + # ) + # print(f"Insufficient fund for debt : {this_month_debt.debt_to_pay:,.2f}") + # print(f"Debt process date : {this_month_debt.process_date.date()}") + # print(f"Debt id : {this_month_debt.id}") + # print(f"Fund available : {avaliable_fund:,.2f}") + # print(f"Payment id : {this_month_debt.id}") + else: + print(f"No debts found for or fund found for {selected_bank_date_year}-{selected_bank_date_month:02d} debt: {debts_for_this_month:,.2f} fund: {avaliable_fund:,.2f}") + +if __name__ == "__main__": + # import sys + + # Default values + # id_given = 1 + count_row = 0 + session_factory = get_session_factory() + session = session_factory() + list_payment_types = get_enums_from_database() + date_from = '2023-06-30' + + AccountRecords.set_session(session) + BuildDecisionBookPayments.set_session(session) + BuildDecisionBook.set_session(session) + + set_account_and_payments_with_this_month_debts(session, date_from, list_payment_types) + + session.commit() + session.flush() + + set_account_and_payments_with_previous_month_debts(session, date_from, list_payment_types) + + session.commit() + session.flush() + + set_account_and_payments_with_previous_month_debts_without_payment_type(session, date_from) + + session.commit() + session.flush() + + session.close() diff --git a/docker-compose.yml b/docker-compose.yml index 3b09f93..e90d757 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -189,50 +189,59 @@ services: - "8003:8003" # restart: unless-stopped python3 app_accounts.py - # finder_build_from_iban_service: - # container_name: finder_build_from_iban_service - # env_file: - # - api_env.env - # build: - # context: . - # dockerfile: ServicesBank/Finder/BuildFromIban/Dockerfile - # networks: - # - wag-services - # logging: - # driver: "json-file" - # options: - # max-size: "10m" - # max-file: "3" + finder_build_from_iban_service: + container_name: finder_build_from_iban_service + env_file: + - api_env.env + build: + context: . + dockerfile: ServicesBank/Finder/BuildFromIban/Dockerfile + networks: + - wag-services + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" - # finder_build_living_space_service: - # container_name: finder_build_living_space_service - # env_file: - # - api_env.env - # build: - # context: . - # dockerfile: ServicesBank/Finder/BuildLivingSpace/Dockerfile - # networks: - # - wag-services - # logging: - # driver: "json-file" - # options: - # max-size: "10m" - # max-file: "3" + finder_build_living_space_service: + container_name: finder_build_living_space_service + env_file: + - api_env.env + build: + context: . + dockerfile: ServicesBank/Finder/BuildLivingSpace/Dockerfile + networks: + - wag-services + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" - # finder_decision_book_service: - # container_name: finder_decision_book_service - # env_file: - # - api_env.env - # build: - # context: . - # dockerfile: ServicesBank/Finder/DecisionBook/Dockerfile - # networks: - # - wag-services - # logging: - # driver: "json-file" - # options: - # max-size: "10m" - # max-file: "3" + finder_decision_book_service: + container_name: finder_decision_book_service + env_file: + - api_env.env + build: + context: . + dockerfile: ServicesBank/Finder/DecisionBook/Dockerfile + networks: + - wag-services + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + zemberek-api: + build: + context: . + dockerfile: ServicesBank/Zemberek/Dockerfile + container_name: zemberek-api + ports: + - "8111:8111" + restart: unless-stopped finder_payment_service: container_name: finder_payment_service @@ -345,7 +354,7 @@ services: context: . dockerfile: ServicesApi/Builds/Initial/Dockerfile environment: - - SET_ALEMBIC=1 + - SET_ALEMBIC=0 networks: - wag-services env_file: