updated payment service
This commit is contained in:
parent
88afa6b329
commit
9edc6cb6a0
|
|
@ -17,6 +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:
|
||||
|
|
|
|||
|
|
@ -115,6 +115,26 @@ def init_api_enums_build_types(db_session):
|
|||
"type_code": "BDT-I",
|
||||
"type_name": "Information",
|
||||
},
|
||||
{
|
||||
"enum_class": "BuildDuesTypes",
|
||||
"type_code": "BDT-CL",
|
||||
"type_name": "Close Last Period Receipt",
|
||||
},
|
||||
{
|
||||
"enum_class": "BuildDuesTypes",
|
||||
"type_code": "BDT-OP",
|
||||
"type_name": "Open New Period Receipt",
|
||||
},
|
||||
{
|
||||
"enum_class": "BuildDuesTypes",
|
||||
"type_code": "BDT-CT",
|
||||
"type_name": "Commission Type",
|
||||
},
|
||||
{
|
||||
"enum_class": "BuildDuesTypes",
|
||||
"type_code": "BDT-FPR",
|
||||
"type_name": "Fixation Payment Receipt",
|
||||
},
|
||||
{
|
||||
"enum_class": "AccountingReceiptTypes",
|
||||
"type_code": "ART-A",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from Schemas.account.account import (
|
|||
AccountDetail,
|
||||
AccountRecordExchanges,
|
||||
AccountRecords,
|
||||
AccountDelayInterest,
|
||||
)
|
||||
from Schemas.account.iban import (
|
||||
BuildIbans,
|
||||
|
|
@ -124,6 +125,7 @@ __all__ = [
|
|||
"AccountDetail",
|
||||
"AccountRecordExchanges",
|
||||
"AccountRecords",
|
||||
"AccountDelayInterest",
|
||||
"BuildIbans",
|
||||
"BuildIbanDescription",
|
||||
"RelationshipEmployee2PostCode",
|
||||
|
|
|
|||
|
|
@ -315,6 +315,45 @@ class AccountRecordExchanges(CrudCollection):
|
|||
)
|
||||
|
||||
|
||||
class AccountDelayInterest(CrudCollection):
|
||||
|
||||
__tablename__ = "account_delay_interest"
|
||||
__exclude__fields__ = []
|
||||
|
||||
interest_turn: Mapped[str] = mapped_column(String(10), nullable=False, server_default="akdi")
|
||||
interest_rate: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, server_default="0")
|
||||
delay_day: Mapped[int] = mapped_column(Integer, nullable=False, comment="Delay in days", server_default="0")
|
||||
daily_rate: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default="0")
|
||||
interest: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default="0")
|
||||
bsmv: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default="0")
|
||||
kkdf: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default="0")
|
||||
total: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default="0")
|
||||
|
||||
account_record_bank_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), nullable=True)
|
||||
book_payment_process_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), nullable=True)
|
||||
debt: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False)
|
||||
|
||||
approving_at: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), nullable=True)
|
||||
approved_record: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="0")
|
||||
approving_person_id: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
approving_person_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Approving Person UU ID")
|
||||
|
||||
bank_resp_company_id: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
bank_resp_company_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Bank Response Company UU ID")
|
||||
|
||||
account_records_id: Mapped[int] = mapped_column(ForeignKey("account_records.id"), nullable=False)
|
||||
account_records_uu_id: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
build_decision_book_payment_id: Mapped[int] = mapped_column(ForeignKey("build_decision_book_payments.id"), nullable=False)
|
||||
build_decision_book_payment_uu_id: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
new_build_decision_book_payment_id: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
new_build_decision_book_payment_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="New Build Decision Book Payment UU ID")
|
||||
|
||||
__table_args__ = (
|
||||
Index("_account_delay_interest_ndx_00", account_records_id, build_decision_book_payment_id),
|
||||
{"comment": "Account Delay Interest Information"},
|
||||
)
|
||||
|
||||
|
||||
class AccountRecords(CrudCollection):
|
||||
"""
|
||||
build_decision_book_id = kaydın sorumlu olduğu karar defteri
|
||||
|
|
@ -324,48 +363,20 @@ class AccountRecords(CrudCollection):
|
|||
|
||||
__tablename__ = "account_records"
|
||||
__exclude__fields__ = []
|
||||
__enum_list__ = [
|
||||
("receive_debit", "DebitTypes", "D"),
|
||||
("budget_type", "BudgetType", "B"),
|
||||
]
|
||||
__enum_list__ = [("receive_debit", "DebitTypes", "D"),("budget_type", "BudgetType", "B")]
|
||||
|
||||
iban: Mapped[str] = mapped_column(
|
||||
String(64), nullable=False, comment="IBAN Number of Bank"
|
||||
)
|
||||
bank_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), nullable=False, comment="Bank Transaction Date"
|
||||
)
|
||||
|
||||
currency_value: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Currency Value"
|
||||
)
|
||||
bank_balance: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Bank Balance"
|
||||
)
|
||||
currency: Mapped[str] = mapped_column(
|
||||
String(5), nullable=False, comment="Unit of Currency"
|
||||
)
|
||||
additional_balance: Mapped[float] = mapped_column(
|
||||
Numeric(20, 6), nullable=False, comment="Additional Balance"
|
||||
)
|
||||
channel_branch: Mapped[str] = mapped_column(
|
||||
String(120), nullable=False, comment="Branch Bank"
|
||||
)
|
||||
process_name: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Bank Process Type Name"
|
||||
)
|
||||
process_type: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Bank Process Type"
|
||||
)
|
||||
process_comment: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Transaction Record Comment"
|
||||
)
|
||||
process_garbage: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Transaction Record Garbage"
|
||||
)
|
||||
bank_reference_code: Mapped[str] = mapped_column(
|
||||
String, nullable=False, comment="Bank Reference Code"
|
||||
)
|
||||
iban: Mapped[str] = mapped_column(String(64), nullable=False, comment="IBAN Number of Bank")
|
||||
bank_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), nullable=False, comment="Bank Transaction Date")
|
||||
currency_value: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, comment="Currency Value")
|
||||
bank_balance: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, comment="Bank Balance")
|
||||
currency: Mapped[str] = mapped_column(String(5), nullable=False, comment="Unit of Currency")
|
||||
additional_balance: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, comment="Additional Balance")
|
||||
channel_branch: Mapped[str] = mapped_column(String(120), nullable=False, comment="Branch Bank")
|
||||
process_name: Mapped[str] = mapped_column(String, nullable=False, comment="Bank Process Type Name")
|
||||
process_type: Mapped[str] = mapped_column(String, nullable=False, comment="Bank Process Type")
|
||||
process_comment: Mapped[str] = mapped_column(String, nullable=False, comment="Transaction Record Comment")
|
||||
process_garbage: Mapped[str] = mapped_column(String, nullable=True, comment="Transaction Record Garbage")
|
||||
bank_reference_code: Mapped[str] = mapped_column(String, nullable=False, comment="Bank Reference Code")
|
||||
|
||||
add_comment_note: Mapped[str] = mapped_column(String, server_default="")
|
||||
is_receipt_mail_send: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
|
@ -378,100 +389,45 @@ class AccountRecords(CrudCollection):
|
|||
bank_date_w: Mapped[int] = mapped_column(SmallInteger)
|
||||
bank_date_d: Mapped[int] = mapped_column(SmallInteger)
|
||||
|
||||
approving_accounting_record: Mapped[bool] = mapped_column(
|
||||
Boolean, server_default="0"
|
||||
)
|
||||
accounting_receipt_date: Mapped[TIMESTAMP] = mapped_column(
|
||||
TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00"
|
||||
)
|
||||
approving_accounting_record: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
accounting_receipt_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), server_default="1900-01-01 00:00:00")
|
||||
accounting_receipt_number: Mapped[int] = mapped_column(Integer, server_default="0")
|
||||
status_id: Mapped[int] = mapped_column(SmallInteger, server_default="0")
|
||||
|
||||
approved_record: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
import_file_name: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="XLS Key"
|
||||
)
|
||||
|
||||
receive_debit: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
receive_debit_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Debit UU ID"
|
||||
)
|
||||
budget_type: Mapped[int] = mapped_column(
|
||||
ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
)
|
||||
budget_type_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Budget Type UU ID"
|
||||
)
|
||||
import_file_name: Mapped[str] = mapped_column(String, nullable=True, comment="XLS Key")
|
||||
receive_debit: Mapped[int] = mapped_column(ForeignKey("api_enum_dropdown.id"), nullable=True)
|
||||
receive_debit_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Debit UU ID")
|
||||
budget_type: Mapped[int] = mapped_column(ForeignKey("api_enum_dropdown.id"), nullable=True)
|
||||
budget_type_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Budget Type UU ID")
|
||||
company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=True)
|
||||
company_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Company UU ID"
|
||||
)
|
||||
send_company_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("companies.id"), nullable=True
|
||||
)
|
||||
send_company_uu_id = mapped_column(
|
||||
String, nullable=True, comment="Send Company UU ID"
|
||||
)
|
||||
|
||||
company_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Company UU ID")
|
||||
send_company_id: Mapped[int] = mapped_column(ForeignKey("companies.id"), nullable=True)
|
||||
send_company_uu_id = mapped_column(String, nullable=True, comment="Send Company UU ID")
|
||||
send_person_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
send_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Send Person UU ID"
|
||||
)
|
||||
approving_accounting_person: Mapped[int] = mapped_column(
|
||||
ForeignKey("people.id"), nullable=True
|
||||
)
|
||||
approving_accounting_person_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Approving Accounting Person UU ID"
|
||||
)
|
||||
|
||||
living_space_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_living_space.id"), nullable=True
|
||||
)
|
||||
living_space_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Living Space UU ID"
|
||||
)
|
||||
send_person_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Send Person UU ID")
|
||||
approving_accounting_person: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
approving_accounting_person_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Approving Accounting Person UU ID")
|
||||
living_space_id: Mapped[int] = mapped_column(ForeignKey("build_living_space.id"), nullable=True)
|
||||
living_space_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Living Space UU ID")
|
||||
customer_id: Mapped[int] = mapped_column(ForeignKey("people.id"), nullable=True)
|
||||
customer_uu_id = mapped_column(String, nullable=True, comment="Customer UU ID")
|
||||
|
||||
build_id: Mapped[int] = mapped_column(ForeignKey("build.id"), nullable=True)
|
||||
build_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build UU ID"
|
||||
)
|
||||
build_parts_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_parts.id"), nullable=True
|
||||
)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Parts UU ID"
|
||||
)
|
||||
build_decision_book_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book.id"), nullable=True
|
||||
)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(
|
||||
String, nullable=True, comment="Build Decision Book UU ID"
|
||||
)
|
||||
# payment_result_type = Mapped[int] = mapped_column(
|
||||
# ForeignKey("api_enum_dropdown.id"), nullable=True
|
||||
# )
|
||||
# payment_result_type_uu_id: Mapped[str] = mapped_column(
|
||||
# String, nullable=True, comment="Payment Result Type UU ID"
|
||||
# )
|
||||
build_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Build UU ID")
|
||||
build_parts_id: Mapped[int] = mapped_column(ForeignKey("build_parts.id"), nullable=True)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Build Parts UU ID")
|
||||
build_decision_book_id: Mapped[int] = mapped_column(ForeignKey("build_decision_book.id"), nullable=True)
|
||||
build_decision_book_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Build Decision Book UU ID")
|
||||
payment_result_type: Mapped[int] = mapped_column(ForeignKey("api_enum_dropdown.id"), nullable=True)
|
||||
payment_result_type_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Payment Result Type UU ID")
|
||||
is_commission_applied: Mapped[bool] = mapped_column(Boolean, server_default="0")
|
||||
|
||||
__table_args__ = (
|
||||
Index("_budget_records_ndx_00", is_receipt_mail_send, bank_date),
|
||||
Index(
|
||||
"_budget_records_ndx_01",
|
||||
iban,
|
||||
bank_date,
|
||||
bank_reference_code,
|
||||
bank_balance,
|
||||
unique=True,
|
||||
),
|
||||
Index("_budget_records_ndx_01", iban, bank_date, bank_reference_code, bank_balance, unique=True),
|
||||
Index("_budget_records_ndx_02", status_id, bank_date),
|
||||
{
|
||||
"comment": "Bank Records that are related to building and financial transactions"
|
||||
},
|
||||
{"comment": "Bank Records that are related to building and financial transactions"},
|
||||
)
|
||||
|
||||
# def payment_budget_record_close(self):
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from Schemas.base_imports import (
|
|||
mapped_column,
|
||||
Mapped,
|
||||
)
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
class BuildIbans(CrudCollection):
|
||||
|
|
@ -44,6 +45,30 @@ class BuildIbans(CrudCollection):
|
|||
{"comment": "IBANs related to money transactions due to building objects"},
|
||||
)
|
||||
|
||||
class CompanyDelayInterest(CrudCollection):
|
||||
|
||||
__tablename__ = "company_delay_interest"
|
||||
|
||||
company_id: Mapped[int] = mapped_column(Integer, ForeignKey("companies.id"), nullable=True)
|
||||
company_uu_id: Mapped[str] = mapped_column(String, nullable=True)
|
||||
|
||||
build_id: Mapped[int] = mapped_column(Integer, ForeignKey("build.id"), nullable=True)
|
||||
build_uu_id: Mapped[str] = mapped_column(String, nullable=True)
|
||||
|
||||
daily_interest_type: Mapped[str] = mapped_column(String(24), nullable=True)
|
||||
daily_interest_rate: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default=text("0"))
|
||||
bsmv_rate: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default=text("0"))
|
||||
kkdf_rate: Mapped[float] = mapped_column(Numeric(20, 6), nullable=False, server_default=text("0"))
|
||||
|
||||
start_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), nullable=False)
|
||||
stop_date: Mapped[TIMESTAMP] = mapped_column(TIMESTAMP(timezone=True), nullable=False, server_default=text("'2900-01-01 03:00:00+03'::timestamptz"))
|
||||
|
||||
__table_args__ = (
|
||||
Index("_company_delay_interest_ndx_01", "company_id", "build_id", "start_date", unique=True),
|
||||
Index("ix_company_delay_interest_build_uu_id", "company_id", "build_id"),
|
||||
{"comment": "Company Delay Interest Information"},
|
||||
)
|
||||
|
||||
|
||||
class BuildIbanDescription(CrudCollection):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -570,13 +570,14 @@ class BuildDecisionBookPayments(CrudCollection):
|
|||
build_parts_id: Mapped[int] = mapped_column(ForeignKey("build_parts.id"), nullable=False)
|
||||
build_parts_uu_id: Mapped[str] = mapped_column(String, nullable=False, comment="Build Part UUID")
|
||||
decision_book_project_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("build_decision_book_projects.id"),
|
||||
nullable=True,
|
||||
comment="Decision Book Project ID",
|
||||
ForeignKey("build_decision_book_projects.id"), nullable=True, comment="Decision Book Project ID",
|
||||
)
|
||||
decision_book_project_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Decision Book Project UUID")
|
||||
account_records_id: Mapped[int] = mapped_column(ForeignKey("account_records.id"), nullable=True)
|
||||
account_records_uu_id: Mapped[str] = mapped_column(String, nullable=True, comment="Account Record UU ID")
|
||||
is_closed: Mapped[bool] = mapped_column(Boolean, server_default="0", comment="Is Decision Book Payment Closed")
|
||||
debt_to_pay: Mapped[float] = mapped_column(Numeric(16, 2), server_default="0", comment="Debt To Pay")
|
||||
debt_paid: Mapped[float] = mapped_column(Numeric(16, 2), server_default="0", comment="Debt Paid")
|
||||
|
||||
# budget_records_id: Mapped[int] = mapped_column(ForeignKey("account_records.id"), nullable=True)
|
||||
# budget_records_uu_id: Mapped[str] = mapped_column(
|
||||
|
|
|
|||
|
|
@ -7,38 +7,85 @@ end_time = perf_counter()
|
|||
elapsed = end_time - start_time
|
||||
print(f'{elapsed:.3f} : seconds')
|
||||
print('shallow_copy_list', len(shallow_copy_list))
|
||||
|
||||
texts = [
|
||||
"D 1 MÜBERRA BALTACI ŞUBAT 2016 O 4245N6000892 MÜBERRA BALTAC",
|
||||
"BERAT VARNALI-GÜNEŞ APT NO-6 ŞUBAT 2016 ÖDEMESİ",
|
||||
"BERAT VARNALI-GÜNEŞ APT NO-6 NİSAN 2016 ÖDEMESİ",
|
||||
"GÖNÜL ARISOY- AİDAT*GÖNÜL ARISOY*H2406115923882",
|
||||
"GÜLSER MAY - 8 NOLU DAIRENIN MAYIS 2016 ONARIM BEDELI",
|
||||
"İREM YÜKSEKOL 12 NOLU DAİRE ARALIK AİDAT VE TAMİRAT PARASI",
|
||||
"İREM YÜKSEKOL 12 NOLU DAİRE EKİM AİDAT VE TAMİRAT PARASI",
|
||||
"GÜÇLÜ FATİH ERGÜN FATİH ERGÜN GÜÇLÜ D7 AİDAT",
|
||||
"KASIM ARALIK 2015 OCAK 2016 TADILAT YUSUF EDEPLI DAIRE 10",
|
||||
"muberra baltacı daıre 1 yakıt bedelı*MÜBERRA BALTACI*H2406175186422",
|
||||
"YEŞİM ŞİMŞEK 5 NOLU DAİRE HAMDİYE AKAGÜNDÜZ",
|
||||
"* HESAP İŞLETİM MASRAFI İADE (BTÇG) * 4245/ 76099902*MUH.HESA",
|
||||
"4245 0500749 numarali hesap kapama",
|
||||
"İREM YÜKSEKOL 12 NOLU DAİRE OCAK AİDAT VE TAMİRAT PARASI",
|
||||
"GÜÇLÜ FATİH ERGÜN FATİH ERGÜN GÜÇLÜ D7 AİDAT",
|
||||
"MUSTAFA EDEPLİ NO:11 MART AİDATI +20 TL ESKİ BORÇ *ALİ İHSAN EDEPLİ *4245X10Ç42",
|
||||
"muberra baltaci daire 1 yakit bedeli*MUBERRA BALTACI*H2502245307227",
|
||||
"OSMAN KILINÇ*0111*OSMAN KILINÇ - AİDAT - SUBAT*2238807",
|
||||
"GÖNÜL ARISOY GÖNÜL ARISOY TARAFINDAN AKTA",
|
||||
"AIDAT BEDELI*MEHMET KARATAY*H2504537864455",
|
||||
"ELİFCAN DEMİRTAŞ*0062*CEP-EFTEMRİ-DAİRE 8 . AİDAT ÖDEMESİ*0130484",
|
||||
"BALTACI MÜBERRA MÜBERRA BALTACI D:1YAKIT BED",
|
||||
"SEZEN KONUR GÜNEŞ APARTMAN AİDATI 12 NUMARA",
|
||||
"MEHMET KARATAY - HESABA AKTARILAN OCAK-SUBAT 2017 AIDAT",
|
||||
"Osman Kilinc*0111*osman kilinc - 2024 - ekim*8614131*FAST",
|
||||
"GÖNÜL ARISOY GÖNÜL ARISOY TARAFINDAN AKTA",
|
||||
"GÖNÜL ARISOY GÖNÜL ARISOY TARAFINDAN AKTA",
|
||||
"BALTACI MÜBERRA MÜBERRA BALTACI D:1YAKIT BED",
|
||||
"SONGÜL VAR-2NOLU DAİRE KASIM 2015 ÖDEME",
|
||||
"GÖNÜL ARISOY (9 NUMARA AİDAT)*GÖNÜL ARISOY*H2211022448099",
|
||||
"BALTACI MÜBERRA MÜBERRA BALTACI D:1YAKIT BED",
|
||||
"GÜÇLÜ FATİH ERGÜN FATİH ERGÜN GÜÇLÜ D7 AİDAT",
|
||||
"2 nolu daire - TEMMUZ Ç4 TADİLAT*SONGÜL VAR*H2408373383590",
|
||||
"GÜÇLÜ FATİH ERGÜN FATİH ERGÜN GÜÇLÜ D7 AİDAT",
|
||||
"ELİFCAN DEMİRTAŞ*0062*ARALIK AYI AİDAT DAİRE 8*443Ç00*FAST",
|
||||
"GÜÇLÜ FATİH ERGÜN FATİH ERGÜN GÜÇLÜ D7 AİDAT",
|
||||
"ERİNÇ KARATAŞ 9 NOLU DAİRE AĞUSTOS AYI 15 GÜNLÜK AİDATI",
|
||||
"HASAN CİHAN ŞENKÜÇÜK*0046*AİDAT*1690685*FAST",
|
||||
"DAMLA GÖRMEZOĞLU*0099*6 nolu daire Kemal bey dava Ekim Kasım*2242091694*FAST",
|
||||
"Müberra Baltacı Daire 1 Yakıt bedli*MÜBERRA BALTACI*H2107153811822",
|
||||
"Osman Kılınç*0111*osman kılınç - aidat - Ç1- nisan*Ş12506*FAST",
|
||||
"mübarra baltacı 1 nolu daire yakıt farkı şubat ayı dahil*MÜBERRA BALTACI*H210Ç5745006",
|
||||
"ELİFCAN DEMİRTAŞ*0062*CEP-EFTEMRİ-DAİRE 8 EYLUL Ç0 AİDAT*0520317"
|
||||
]
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
1. Stage (Incoming Money)
|
||||
|
||||
# BuildDecisionBookPayments are reverse records of AccountRecords
|
||||
|
||||
AccountRecords.approved_record == True
|
||||
AccountRecords.living_space_id is not None
|
||||
# AccountRecords.receive_debit = Credit Receiver (Incoming money from client) / Debit Sender (Debt to be paid by system)
|
||||
|
||||
# AccountRecords.receive_debit = Credit Receiver (Incoming money from client) / Debit Sender (Debt to be paid by system)
|
||||
|
||||
1.1
|
||||
AccountRecords.currency_value > 0 Received Money Transaction +
|
||||
AccountRecords.currency_value > AccountRecords.remainder_balance () You have extra money in system account
|
||||
Money consumed => AccountRecords.currency_value != abs(AccountRecords.remainder_balance) singluar iban
|
||||
Some payment done but money not yet all money is consumed => AccountRecords.currency_value + AccountRecords.remainder_balance != 0
|
||||
|
||||
AccountRecords.currency_value > 0 Received Money Transaction +
|
||||
AccountRecords.currency_value > AccountRecords.remainder_balance () You have extra money in system account
|
||||
Money consumed => AccountRecords.currency_value != abs(AccountRecords.remainder_balance) singluar iban
|
||||
Some payment done but money not yet all money is consumed => AccountRecords.currency_value + AccountRecords.remainder_balance != 0
|
||||
|
||||
AccountRecords.currency_value = AccountRecords.remainder_balance (There is no money that individual has in system)
|
||||
AccountRecords.bank_date (Date money arrived)
|
||||
AccountRecords.process_type (Type of bank transaction)
|
||||
|
||||
1.2
|
||||
AccountRecords.currency_value < 0 Sent Money Transaction -
|
||||
|
||||
AccountRecords.currency_value < 0 Sent Money Transaction -
|
||||
|
||||
2. Stage (Payment Match Process)
|
||||
Parse : BuildDecisionBookPayments.process_date (Year / Month / Day / Time)
|
||||
BuildDecisionBookPayments.account_records_id == None ( Payment is not assigned to any account record)
|
||||
BuildDecisionBookPayments.payment_types_id == debit_enum.id (Payment type is debit)
|
||||
Parse : BuildDecisionBookPayments.process_date (Year / Month / Day / Time)
|
||||
BuildDecisionBookPayments.account_records_id == None ( Payment is not assigned to any account record)
|
||||
BuildDecisionBookPayments.payment_types_id == debit_enum.id (Payment type is debit)
|
||||
|
||||
2.1 Check current month has any payment to due Payment Month == Money Arrived Month
|
||||
2.2 Check previous months has any payment to due Payment Month < Money Arrived Month
|
||||
|
||||
3. Stage (Payment Assignment Process)
|
||||
Do payment set left money to account record as AccountRecords.remainder_balance
|
||||
|
||||
|
||||
Do payment set left money to account record as AccountRecords.remainder_balance
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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")
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
import arrow
|
||||
import calendar
|
||||
import time
|
||||
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from Schemas import BuildDecisionBookPayments, AccountRecords, ApiEnumDropdown, Build, BuildDecisionBook, AccountDelayInterest
|
||||
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 interest_calculate import hesapla_gecikme_faizi
|
||||
# from ServicesApi.Schemas.account.account import AccountRecords, AccountDelayInterest
|
||||
# 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, BuildDecisionBookPayments.active == True,
|
||||
).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, BuildDecisionBookPayments.active == True).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, BuildDecisionBookPayments.active == 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, BuildDecisionBookPayments.active == True
|
||||
).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,
|
||||
active=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
|
||||
# close_last_period_receipt_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-CL").first() # Close Last Period Receipt
|
||||
# open_new_period_receipt_enum_shallow = ApiEnumDropdown.query.filter_by(enum_class="BuildDuesTypes", key="BDT-OP").first() # Open New Period Receipt
|
||||
|
||||
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
|
||||
)
|
||||
# build_dues_types.close_last_period_receipt = ApiEnumDropdownShallowCopy(
|
||||
# close_last_period_receipt_enum_shallow.id, str(close_last_period_receipt_enum_shallow.uu_id), close_last_period_receipt_enum_shallow.enum_class, close_last_period_receipt_enum_shallow.key, close_last_period_receipt_enum_shallow.value
|
||||
# )
|
||||
# build_dues_types.open_new_period_receipt = ApiEnumDropdownShallowCopy(
|
||||
# open_new_period_receipt_enum_shallow.id, str(open_new_period_receipt_enum_shallow.uu_id), open_new_period_receipt_enum_shallow.enum_class, open_new_period_receipt_enum_shallow.key, open_new_period_receipt_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,
|
||||
# build_dues_types.close_last_period_receipt,
|
||||
# build_dues_types.open_new_period_receipt
|
||||
]
|
||||
|
||||
|
||||
def do_payments(session, build_id: int, work_date: datetime):
|
||||
"""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.
|
||||
"""
|
||||
|
||||
# Set session for all models
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
today = datetime.now()
|
||||
|
||||
# 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",
|
||||
BuildDecisionBook.active == True
|
||||
).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 work_date.month == today.month and work_date.year == today.year:
|
||||
last_date_of_process_date = today
|
||||
|
||||
# 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, BuildDecisionBookPayments.active == True,
|
||||
).order_by(BuildDecisionBookPayments.process_date.asc(), BuildDecisionBookPayments.debt_to_pay.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.active == True,
|
||||
AccountRecords.currency_value > 0, AccountRecords.currency_value > func.abs(AccountRecords.remainder_balance), *date_query_account_tuple,
|
||||
).order_by(AccountRecords.bank_date.asc(), AccountRecords.remainder_balance.desc()).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 calculate_interest_from_date_paid_to_date_debt_to_pay(session, payment_value, payment, record):
|
||||
"""Calculate interest from date paid to date debt to pay."""
|
||||
AccountDelayInterest.set_session(session)
|
||||
today = datetime.now()
|
||||
calculated_interest = hesapla_gecikme_faizi(borc=payment_value, vade_tarihi=payment.process_date, islem_tarihi=today, faiz_turu="gecikme")
|
||||
if not calculated_interest.toplam > 0:
|
||||
return None
|
||||
print('interest', dict(
|
||||
debt_to_pay=payment_value, payment_process_date=str(payment.process_date.date()), record_bank_date=str(record.bank_date.date()),
|
||||
available_money=abs(record.currency_value) - abs(record.remainder_balance), account_records_id=record.id, build_decision_book_payment_id=payment.id,
|
||||
new_build_decision_book_payment_id=payment.build_decision_book_id, interest_rate=calculated_interest.gunluk_oran, delay_day=calculated_interest.gecikme_gunu,
|
||||
daily_rate=calculated_interest.gunluk_oran, interest=calculated_interest.faiz, bsmv=calculated_interest.bsmv, kkdf=calculated_interest.kkdf,
|
||||
total=calculated_interest.toplam,
|
||||
))
|
||||
# new_row_account_delay_interest = AccountDelayInterest.create(
|
||||
# account_records_id=record.id, account_records_uu_id=str(record.uu_id), build_decision_book_payment_id=payment.id, build_decision_book_payment_uu_id=str(payment.uu_id),
|
||||
# new_build_decision_book_payment_id=payment.build_decision_book_id, new_build_decision_book_payment_uu_id=str(payment.build_decision_book_uu_id),
|
||||
# interest_turn="gecikme", interest_rate=calculated_interest.gunluk_oran, delay_day=calculated_interest.gecikme_gunu, daily_rate=calculated_interest.gunluk_oran,
|
||||
# interest=calculated_interest.faiz, bsmv=calculated_interest.bsmv, kkdf=calculated_interest.kkdf, total=calculated_interest.toplam,
|
||||
# )
|
||||
# new_row_account_delay_interest.save()
|
||||
# session.commit()
|
||||
# session.refresh(new_row_account_delay_interest)
|
||||
# return new_row_account_delay_interest
|
||||
|
||||
|
||||
def do_payments_of_overdue_payments(session, 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()
|
||||
fund_finished = lambda money_spend, money_in_account: money_spend == money_in_account
|
||||
payments_made, total_amount_paid, paid_count = 0, 0, 0
|
||||
|
||||
# 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()
|
||||
# )
|
||||
|
||||
priority_uuids = [pt.uuid for pt in payment_type_list]
|
||||
case_order = case(*[(AccountRecords.payment_result_type_uu_id == str(uuid), index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
case_order_payment = case(*[(BuildDecisionBookPayments.payment_types_uu_id == str(uuid), index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
# for payment_type in payment_type_list:
|
||||
query_of_payments = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.account_is_debit == True, BuildDecisionBookPayments.active == True,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) < find_first_day_of_month(today)
|
||||
).order_by(
|
||||
# Order by process_date in ascending order (oldest first)
|
||||
cast(BuildDecisionBookPayments.process_date, Date).asc(), BuildDecisionBookPayments.payment_amount.desc(),
|
||||
case_order_payment.asc(),
|
||||
).all()
|
||||
for payment_row in query_of_payments:
|
||||
# Get the payment_row's process_date to find closest bank_date in AccountRecords
|
||||
# First get all eligible records
|
||||
money_to_pay_rows = AccountRecords.query.filter(
|
||||
AccountRecords.build_parts_id == payment_row.build_parts_id,
|
||||
# AccountRecords.payment_result_type == payment_type.id,
|
||||
AccountRecords.active == True,
|
||||
AccountRecords.currency_value > 0,
|
||||
func.abs(AccountRecords.currency_value) > func.abs(AccountRecords.remainder_balance),
|
||||
# Filter for bank_dates that are valid for this payment
|
||||
# cast(AccountRecords.bank_date, Date) <= arrow.get(payment_row.process_date).to("Europe/Istanbul").date()
|
||||
).order_by(
|
||||
# Order by bank_date in ascending order (oldest first)
|
||||
cast(AccountRecords.bank_date, Date).asc(),
|
||||
case_order.asc(),
|
||||
).all()
|
||||
|
||||
# Sort the results in Python by the absolute difference between dates
|
||||
if money_to_pay_rows:
|
||||
payment_date = payment_row.process_date.date() if hasattr(payment_row.process_date, 'date') else payment_row.process_date
|
||||
money_to_pay_rows.sort(key=lambda x: abs((x.bank_date.date() if hasattr(x.bank_date, 'date') else x.bank_date) - payment_date))
|
||||
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):
|
||||
print('More Money than 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)
|
||||
calculate_interest_from_date_paid_to_date_debt_to_pay(session=session, payment_value=abs(debt_to_pay), payment=payment_row, record=money_to_pay_row)
|
||||
break # More fund to spend so go to next BuildDecisionBookPayments
|
||||
else:
|
||||
print('All Money is spent ---------------------------------------------------------------------------------------------------------------------------------')
|
||||
payments_made += 1
|
||||
total_amount_paid += available_funds
|
||||
paid_count += 1
|
||||
close_payment_book(payment_row, money_to_pay_row, abs(available_funds), session)
|
||||
calculate_interest_from_date_paid_to_date_debt_to_pay(session=session, payment_value=abs(available_funds), payment=payment_row, record=money_to_pay_row)
|
||||
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)
|
||||
|
||||
|
||||
def do_regular_monthly_payers_payment_function(session, build_id: int, period_count: int = 1, skip_count: int = 0):
|
||||
today = datetime.now()
|
||||
Build.set_session(session)
|
||||
BuildDecisionBook.set_session(session)
|
||||
|
||||
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_books = BuildDecisionBook.query.filter(
|
||||
BuildDecisionBook.build_id == build_id, cast(BuildDecisionBook.expiry_starts, Date) <= today.date(), BuildDecisionBook.active == True,
|
||||
).order_by(BuildDecisionBook.expiry_starts.desc()).limit(period_count + skip_count).offset(skip_count).all()
|
||||
if not decision_books:
|
||||
raise ValueError(f"Decision book not found for build with id {build_id}")
|
||||
if skip_count > 0:
|
||||
decision_books = decision_books[skip_count:]
|
||||
first_decision_book = decision_books[-1]
|
||||
work_date = find_first_day_of_month(first_decision_book.expiry_starts)
|
||||
while work_date <= today:
|
||||
do_payments(session=session, build_id=build_id, work_date=work_date)
|
||||
work_date, _ = add_month_to_date(work_date)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
today, build_id, start_time = datetime.now(), 1, perf_counter()
|
||||
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
|
||||
print("\n===== PROCESSING PAYMENTS =====\n")
|
||||
print("Starting payment processing at:", str(today))
|
||||
|
||||
# Process payments for current month first
|
||||
print("\n2. Processing current month payments...")
|
||||
do_payments(session=session, build_id=build_id, work_date=today)
|
||||
|
||||
# Do regular monthly payers payments
|
||||
print("\n1. Processing regular monthly payers payments...")
|
||||
do_regular_monthly_payers_payment_function(session=session, build_id=build_id, period_count=4, skip_count=0)
|
||||
|
||||
# Commit and flush session to prevent session caused errors
|
||||
session.commit()
|
||||
session.flush()
|
||||
|
||||
# Process payments for previous months
|
||||
print("\n2. Processing previous months payments...")
|
||||
total_amount_of_available_funds = session.query(func.sum(AccountRecords.currency_value)).filter(
|
||||
AccountRecords.currency_value > 0, func.abs(AccountRecords.currency_value) > func.abs(AccountRecords.remainder_balance),
|
||||
AccountRecords.build_parts_id.isnot(None), AccountRecords.active == True
|
||||
).scalar()
|
||||
print(f"Total amount of available funds: {total_amount_of_available_funds:,.2f}")
|
||||
total_amount_of_debt_to_pay = session.query(func.sum(BuildDecisionBookPayments.debt_to_pay)).filter(
|
||||
BuildDecisionBookPayments.debt_to_pay > 0,
|
||||
BuildDecisionBookPayments.account_is_debit == True,
|
||||
BuildDecisionBookPayments.active == True
|
||||
).scalar()
|
||||
print(f"Total amount of debt to pay: {total_amount_of_debt_to_pay:,.2f}")
|
||||
|
||||
if total_amount_of_available_funds - total_amount_of_debt_to_pay > 0:
|
||||
do_payments_of_overdue_payments(session=session, build_id=build_id)
|
||||
print("\n===== PAYMENT PROCESSING COMPLETE =====\n")
|
||||
|
||||
# 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,137 @@
|
|||
import arrow
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import Literal
|
||||
from datetime import date
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class GecikmeFaizSonucu:
|
||||
faiz_turu: str
|
||||
gecikme_gunu: int
|
||||
gunluk_oran: float
|
||||
faiz: float
|
||||
bsmv: float
|
||||
kkdf: float
|
||||
toplam: float
|
||||
|
||||
|
||||
# Faiz türüne göre aylık faiz oranı (%)
|
||||
GLOBAL_FAIZ_ORANLARI = {"akdi": 5.0, "gecikme": 5.3}
|
||||
|
||||
# Vergi oranları (%)
|
||||
GLOBAL_BSMV_ORANI = 15.0
|
||||
GLOBAL_KKDF_ORANI = 15.0
|
||||
|
||||
|
||||
def hesapla_komisyon(vade_tarihi: date, islem_tarihi: date, tutar: float, oran: float) -> float:
|
||||
gecikme = (islem_tarihi - vade_tarihi).days
|
||||
if gecikme <= 0:
|
||||
return 0.0
|
||||
komisyon = tutar * oran * gecikme
|
||||
return round(komisyon, 2)
|
||||
|
||||
|
||||
def hesapla_gunluk_oran(
|
||||
faiz_orani: float, tip: Literal["MIR", "YIR"], gun: int = 0
|
||||
) -> float:
|
||||
"""
|
||||
orijinal_oran: Yıllık ya da aylık faiz oranı (örneğin %12 için 0.12)
|
||||
tip: 'MIR' (Monthly Interest Rate) ya da 'YIR' (Yearly Interest Rate)
|
||||
"""
|
||||
faiz_orani = faiz_orani / 100
|
||||
if tip.upper() == "MIR":
|
||||
if gun < 1:
|
||||
gun = 30
|
||||
return faiz_orani / gun # varsayılan aylık 30 gün
|
||||
elif tip.upper() == "YIR":
|
||||
if gun < 1:
|
||||
gun = 360
|
||||
return faiz_orani / gun # varsayılan yıl 365 gün
|
||||
else:
|
||||
raise ValueError("Faiz tipi yalnızca 'MIR' ya da 'YIR' olabilir.")
|
||||
|
||||
|
||||
def hesapla_gecikme_faizi(
|
||||
borc: float, vade_tarihi: date, islem_tarihi: date | None = None, faiz_orani: float = None, faiz_turu: str = "akdi", bsmv_orani: float = None, kkdf_orani: float = None,
|
||||
) -> GecikmeFaizSonucu:
|
||||
|
||||
if islem_tarihi is None:
|
||||
islem_tarihi = date.today()
|
||||
gecikme_gun = (arrow.get(islem_tarihi) - arrow.get(vade_tarihi)).days
|
||||
if gecikme_gun <= 0:
|
||||
return GecikmeFaizSonucu(faiz_turu, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
# Varsayılan oranları içerden bağla
|
||||
if faiz_orani is None:
|
||||
faiz_orani = GLOBAL_FAIZ_ORANLARI.get(faiz_turu, 5.0)
|
||||
if bsmv_orani is None:
|
||||
bsmv_orani = GLOBAL_BSMV_ORANI
|
||||
if kkdf_orani is None:
|
||||
kkdf_orani = GLOBAL_KKDF_ORANI
|
||||
|
||||
faiz_tipi = "MIR" if faiz_turu == "akdi" else "YIR"
|
||||
gunluk_oran = hesapla_gunluk_oran(faiz_orani, faiz_tipi)
|
||||
|
||||
faiz_tutari = Decimal(borc) * Decimal(gunluk_oran) * Decimal(gecikme_gun)
|
||||
bsmv = Decimal(faiz_tutari) * Decimal(bsmv_orani / 100)
|
||||
kkdf = Decimal(faiz_tutari) * Decimal(kkdf_orani / 100)
|
||||
toplam_faiz = Decimal(faiz_tutari) + Decimal(bsmv) + Decimal(kkdf)
|
||||
|
||||
return GecikmeFaizSonucu(
|
||||
faiz_turu=faiz_turu, gecikme_gunu=gecikme_gun, gunluk_oran=round(gunluk_oran, 6), faiz=round(faiz_tutari, 2), bsmv=round(bsmv, 2), kkdf=round(kkdf, 2), toplam=round(toplam_faiz, 4),
|
||||
)
|
||||
|
||||
"""
|
||||
2025 Güncel Oranlar (TCMB verilerine göre)
|
||||
tip: 'MIR' (Monthly Interest Rate) ya da 'YIR' (Yearly Interest Rate)
|
||||
Faiz Türü Aylık Oran Günlük Yaklaşık Açıklama
|
||||
Akdi Faiz %5,00 %0,1667 Asgari üzerindeki borca uygulanır
|
||||
Gecikme Faizi %5,30 %0,1767 Asgari tutarın ödenmeyen kısmına uygulanır
|
||||
|
||||
CREATE TABLE public.account_delay_interest (
|
||||
|
||||
account_records_id int4 NOT NULL,
|
||||
build_decision_book_payment_id int4 NOT NULL,
|
||||
build_decision_book_payment_refid varchar(100) NOT NULL,
|
||||
interest_turn varchar(10) NOT NULL,
|
||||
interest_rate numeric(10, 2) NOT NULL,
|
||||
delay_day int2 NOT NULL,
|
||||
daily_rate numeric(20, 6) NOT NULL,
|
||||
interest numeric(20, 6) NOT NULL,
|
||||
bsmv numeric(20, 6) NOT NULL,
|
||||
kkdf numeric(20, 6) NOT NULL,
|
||||
total numeric(20, 6) NOT NULL,
|
||||
new_build_decision_book_payment_id int4 NULL,
|
||||
approved_record bool false NOT NULL,
|
||||
approving_person int4 NOT NULL,
|
||||
approving_at timestamptz NULL,
|
||||
|
||||
id serial4 NOT NULL,
|
||||
uu_id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
expiry_starts timestamptz DEFAULT now() NOT NULL,
|
||||
expiry_ends timestamptz DEFAULT now() NOT NULL,
|
||||
created_at timestamptz DEFAULT now() NOT NULL,
|
||||
updated_at timestamptz DEFAULT now() NOT NULL,
|
||||
CONSTRAINT account_delay_interest_pkey PRIMARY KEY (id))
|
||||
|
||||
CREATE UNIQUE INDEX _account_delay_interest_ndx_01 ON public.account_delay_interest USING btree (account_records_id, build_decision_book_payment_id);
|
||||
CREATE INDEX _account_delay_interest_ndx_02 ON public.account_delay_interest USING btree (build_decision_book_payment_id);
|
||||
CREATE UNIQUE INDEX _account_delay_interest_ndx_03 ON public.account_delay_interest USING btree (build_decision_book_payment_refid);
|
||||
CREATE UNIQUE INDEX _account_delay_interest_ndx_04 ON public.account_delay_interest USING btree (uu_id);
|
||||
|
||||
-- Column comments
|
||||
COMMENT ON COLUMN public.account_delay_interest.build_decision_book_payment_refid IS 'ref id';
|
||||
COMMENT ON COLUMN public.account_delay_interest.interest_turn IS 'faiz turu';
|
||||
COMMENT ON COLUMN public.account_delay_interest.interest_rate IS 'uygulanan faiz';
|
||||
COMMENT ON COLUMN public.account_delay_interest.delay_day IS 'gecikme_gunu';
|
||||
COMMENT ON COLUMN public.account_delay_interest.daily_rate IS 'gunluk_oran';
|
||||
COMMENT ON COLUMN public.account_delay_interest.interest IS 'faiz';
|
||||
COMMENT ON COLUMN public.account_delay_interest.bsmv IS 'bsmv';
|
||||
COMMENT ON COLUMN public.account_delay_interest.kkdf IS 'kkdf';
|
||||
COMMENT ON COLUMN public.account_delay_interest.total IS 'toplam';
|
||||
COMMENT ON COLUMN public.account_delay_interest.approved_record IS 'onaylandı';
|
||||
COMMENT ON COLUMN public.account_delay_interest.approving_person IS 'onaylayan';
|
||||
COMMENT ON COLUMN public.account_delay_interest.approving_at IS 'onay zamani';
|
||||
"""
|
||||
|
|
@ -1,326 +0,0 @@
|
|||
import sys
|
||||
import arrow
|
||||
from decimal import Decimal
|
||||
from Schemas import BuildDecisionBookPayments, AccountRecords, ApiEnumDropdown
|
||||
|
||||
# Counters for tracking conditions
|
||||
counter_current_currency_not_positive = 0 # Track when current_currency_value <= 0
|
||||
counter_net_amount_not_positive = 0 # Track when net_amount <= 0
|
||||
counter_account_records_updated = 0 # Track number of account records updated
|
||||
counter_payments_found = 0 # Track how many payments were found
|
||||
counter_found_payment_skips = 0 # Track how many times we skip due to found_payment
|
||||
counter_payment_exceptions = 0 # Track exceptions during payment processing
|
||||
counter_missing_build_parts_id = 0 # Track accounts with missing build_parts_id
|
||||
counter_null_payment_types = 0 # Track payments with null payment_types_id
|
||||
|
||||
|
||||
def pay_the_registration(account_record, receive_enum, debit_enum, is_old_record: bool = False, session=None):
|
||||
# If no session is provided, create a new one
|
||||
if session is None:
|
||||
with AccountRecords.new_session() as new_session:
|
||||
AccountRecords.set_session(new_session)
|
||||
BuildDecisionBookPayments.set_session(new_session)
|
||||
return _process_payment(account_record, receive_enum, debit_enum, is_old_record, new_session)
|
||||
else:
|
||||
# Use the provided session
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
return _process_payment(account_record, receive_enum, debit_enum, is_old_record, session)
|
||||
|
||||
|
||||
def _process_payment(account_record, receive_enum, debit_enum, is_old_record, session):
|
||||
"""Internal function to process payments with a given session"""
|
||||
current_currency_value = float(Decimal(account_record.currency_value)) - float(Decimal(account_record.remainder_balance))
|
||||
if not current_currency_value > 0:
|
||||
global counter_current_currency_not_positive
|
||||
counter_current_currency_not_positive += 1
|
||||
return current_currency_value
|
||||
|
||||
# Check if account_record has build_parts_id
|
||||
if account_record.build_parts_id is None:
|
||||
global counter_missing_build_parts_id
|
||||
counter_missing_build_parts_id += 1
|
||||
return current_currency_value
|
||||
|
||||
process_date = arrow.get(account_record.bank_date)
|
||||
account_bank_date_year, account_bank_date_month = (process_date.date().year, process_date.date().month)
|
||||
|
||||
# First, try to find payments with null payment_types_id
|
||||
payment_arguments_debit = [
|
||||
BuildDecisionBookPayments.build_parts_id == account_record.build_parts_id,
|
||||
BuildDecisionBookPayments.account_records_id == None,
|
||||
]
|
||||
|
||||
# Add date filters if not processing old records
|
||||
if not is_old_record:
|
||||
payment_arguments_debit.extend([BuildDecisionBookPayments.process_date_y == int(account_bank_date_year), BuildDecisionBookPayments.process_date_m == int(account_bank_date_month)])
|
||||
|
||||
# First try with debit_enum.id
|
||||
payments = BuildDecisionBookPayments.query.filter(*payment_arguments_debit, BuildDecisionBookPayments.payment_types_id == debit_enum.id).order_by(BuildDecisionBookPayments.process_date.asc()).all()
|
||||
|
||||
# If no payments found, try with null payment_types_id
|
||||
if len(payments) == 0:
|
||||
payments = BuildDecisionBookPayments.query.filter(*payment_arguments_debit, BuildDecisionBookPayments.payment_types_id == None).order_by(BuildDecisionBookPayments.process_date.asc()).all()
|
||||
if len(payments) > 0:
|
||||
global counter_null_payment_types
|
||||
counter_null_payment_types += len(payments)
|
||||
|
||||
global counter_payments_found
|
||||
counter_payments_found += len(payments)
|
||||
|
||||
# Debug: Print info about the first few payments found (if any)
|
||||
if len(payments) > 0 and account_record.id % 100 == 0: # Only print for every 100th record to avoid too much output
|
||||
print(f"DEBUG: Found {len(payments)} payments for account_record {account_record.id}")
|
||||
if len(payments) > 0:
|
||||
sample_payment = payments[0]
|
||||
print(f" Sample payment: ID={getattr(sample_payment, 'id', 'N/A')}, amount={getattr(sample_payment, 'payment_amount', 'N/A')}")
|
||||
|
||||
if len(payments) == 0:
|
||||
# No payments found for this account record
|
||||
return current_currency_value
|
||||
for payment in payments:
|
||||
if not current_currency_value > 0:
|
||||
return current_currency_value
|
||||
payment_arguments_receive = [
|
||||
BuildDecisionBookPayments.build_parts_id == account_record.build_parts_id,
|
||||
BuildDecisionBookPayments.payment_plan_time_periods == payment.payment_plan_time_periods,
|
||||
BuildDecisionBookPayments.payment_types_id == receive_enum.id,
|
||||
BuildDecisionBookPayments.build_decision_book_item_id == payment.build_decision_book_item_id,
|
||||
BuildDecisionBookPayments.decision_book_project_id == payment.decision_book_project_id,
|
||||
BuildDecisionBookPayments.process_date == payment.process_date,
|
||||
]
|
||||
if not is_old_record:
|
||||
payment_arguments_receive.extend([BuildDecisionBookPayments.process_date_y == int(account_bank_date_year), BuildDecisionBookPayments.process_date_m == int(account_bank_date_month)])
|
||||
|
||||
payment_received = BuildDecisionBookPayments.query.filter(*payment_arguments_receive).all()
|
||||
sum_of_payment_received = sum([abs(payment.payment_amount) for payment in payment_received])
|
||||
net_amount = float(abs(Decimal(payment.payment_amount))) - float(abs(Decimal(sum_of_payment_received)))
|
||||
if not net_amount > 0:
|
||||
global counter_net_amount_not_positive
|
||||
counter_net_amount_not_positive += 1
|
||||
continue
|
||||
if float(abs(current_currency_value)) < float(abs(net_amount)):
|
||||
net_amount = float(current_currency_value)
|
||||
process_date = arrow.get(payment.process_date)
|
||||
try:
|
||||
found_payment = BuildDecisionBookPayments.query.filter_by(
|
||||
build_parts_id=payment.build_parts_id, payment_plan_time_periods=payment.payment_plan_time_periods,
|
||||
payment_types_id=receive_enum.id, build_decision_book_item_id=payment.build_decision_book_item_id,
|
||||
decision_book_project_id=payment.decision_book_project_id, process_date=str(process_date),
|
||||
).first()
|
||||
if found_payment:
|
||||
global counter_found_payment_skips
|
||||
counter_found_payment_skips += 1
|
||||
continue
|
||||
created_book_payment = BuildDecisionBookPayments.create(
|
||||
payment_plan_time_periods=payment.payment_plan_time_periods, payment_amount=float(abs(net_amount)),
|
||||
payment_types_id=receive_enum.id, payment_types_uu_id=str(receive_enum.uu_id),
|
||||
process_date=str(process_date), process_date_m=process_date.date().month, process_date_y=process_date.date().year,
|
||||
period_time=f"{process_date.year}-{str(process_date.month).zfill(2)}", build_parts_id=payment.build_parts_id,
|
||||
build_parts_uu_id=str(payment.build_parts_uu_id), account_records_id=account_record.id,
|
||||
account_records_uu_id=str(account_record.uu_id), build_decision_book_item_id=payment.build_decision_book_item_id,
|
||||
build_decision_book_item_uu_id=str(payment.build_decision_book_item_uu_id), decision_book_project_id=payment.decision_book_project_id,
|
||||
decision_book_project_uu_id=str(payment.decision_book_project_uu_id),
|
||||
)
|
||||
created_book_payment.save()
|
||||
created_payment_amount = float(Decimal(created_book_payment.payment_amount))
|
||||
remainder_balance = float(Decimal(account_record.remainder_balance)) + float(abs(created_payment_amount))
|
||||
account_record.update(remainder_balance=remainder_balance)
|
||||
account_record.save()
|
||||
|
||||
global counter_account_records_updated
|
||||
counter_account_records_updated += 1
|
||||
if current_currency_value >= abs(net_amount):
|
||||
current_currency_value -= abs(net_amount)
|
||||
except Exception as e:
|
||||
print("Exception of decision payment:", e)
|
||||
global counter_payment_exceptions
|
||||
counter_payment_exceptions += 1
|
||||
return current_currency_value
|
||||
|
||||
|
||||
def create_direct_payment(account_record, receive_enum, session):
|
||||
"""
|
||||
Create a direct payment record for an account record without relying on matching BuildDecisionBookPayments
|
||||
"""
|
||||
try:
|
||||
# Calculate the amount to process
|
||||
payment_amount = float(Decimal(account_record.currency_value)) - float(Decimal(account_record.remainder_balance))
|
||||
if payment_amount <= 0:
|
||||
return False
|
||||
|
||||
# Get process date information
|
||||
process_date = arrow.get(account_record.bank_date)
|
||||
process_date_y = process_date.date().year
|
||||
process_date_m = process_date.date().month
|
||||
period_time = f"{process_date_y}-{str(process_date_m).zfill(2)}"
|
||||
|
||||
# Check if a payment already exists for this account record
|
||||
existing_payment = BuildDecisionBookPayments.query.filter_by(account_records_id=account_record.id, payment_types_id=receive_enum.id).first()
|
||||
if existing_payment: # Skip if payment already exists
|
||||
return False
|
||||
|
||||
# Create a new payment record directly
|
||||
created_book_payment = BuildDecisionBookPayments.create(
|
||||
payment_plan_time_periods=1, # Default value
|
||||
payment_types_id=receive_enum.id, payment_types_uu_id=str(receive_enum.uu_id), payment_amount=payment_amount, process_date=str(process_date), process_date_y=process_date_y,
|
||||
process_date_m=process_date_m, period_time=period_time, account_records_id=account_record.id, account_records_uu_id=str(account_record.uu_id),
|
||||
build_parts_id=account_record.build_parts_id, build_parts_uu_id=str(account_record.build_parts_uu_id) if hasattr(account_record, 'build_parts_uu_id') and account_record.build_parts_uu_id else None,
|
||||
decision_book_project_id=getattr(account_record, 'decision_book_project_id', None),
|
||||
decision_book_project_uu_id=str(account_record.decision_book_project_uu_id) if hasattr(account_record, 'decision_book_project_uu_id') and account_record.decision_book_project_uu_id else None,
|
||||
)
|
||||
created_book_payment.save()
|
||||
|
||||
# Update the account record
|
||||
remainder_balance = float(Decimal(account_record.remainder_balance)) + float(abs(payment_amount))
|
||||
account_record.update(remainder_balance=remainder_balance)
|
||||
account_record.save()
|
||||
|
||||
global counter_account_records_updated
|
||||
counter_account_records_updated += 1
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Exception in create_direct_payment for account {account_record.id}: {e}")
|
||||
global counter_payment_exceptions
|
||||
counter_payment_exceptions += 1
|
||||
return False
|
||||
|
||||
|
||||
def send_accounts_to_decision_payment():
|
||||
with ApiEnumDropdown.new_session() as session:
|
||||
# Set the session for all models that will be used
|
||||
ApiEnumDropdown.set_session(session)
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
|
||||
try:
|
||||
# Get required enum values
|
||||
receive_enum = ApiEnumDropdown.query.filter_by(enum_class="DebitTypes", key="DT-R").first()
|
||||
debit_enum = ApiEnumDropdown.query.filter_by(enum_class="DebitTypes", key="DT-D").first()
|
||||
if not receive_enum or not debit_enum:
|
||||
print("Error: Could not find required enum values")
|
||||
return
|
||||
|
||||
# Check if there are any BuildDecisionBookPayments records at all
|
||||
total_payments = BuildDecisionBookPayments.query.count()
|
||||
print(f"\n--- DEBUG: Database Statistics ---")
|
||||
print(f"Total BuildDecisionBookPayments records in database: {total_payments}")
|
||||
|
||||
# Check how many have payment_types_id = debit_enum.id
|
||||
debit_payments = BuildDecisionBookPayments.query.filter_by(payment_types_id=debit_enum.id).count()
|
||||
print(f"BuildDecisionBookPayments with payment_types_id={debit_enum.id} (DT-D): {debit_payments}")
|
||||
|
||||
# Check how many have account_records_id = None
|
||||
null_account_payments = BuildDecisionBookPayments.query.filter(BuildDecisionBookPayments.account_records_id == None).count()
|
||||
print(f"BuildDecisionBookPayments with account_records_id=None: {null_account_payments}")
|
||||
|
||||
# Check a sample payment record
|
||||
sample_payment = BuildDecisionBookPayments.query.first()
|
||||
if sample_payment:
|
||||
print("\n--- Sample BuildDecisionBookPayment ---")
|
||||
print(f"ID: {getattr(sample_payment, 'id', 'N/A')}")
|
||||
print(f"payment_types_id: {getattr(sample_payment, 'payment_types_id', 'N/A')}")
|
||||
print(f"build_parts_id: {getattr(sample_payment, 'build_parts_id', 'N/A')}")
|
||||
print(f"account_records_id: {getattr(sample_payment, 'account_records_id', 'N/A')}")
|
||||
print(f"process_date_y: {getattr(sample_payment, 'process_date_y', 'N/A')}")
|
||||
print(f"process_date_m: {getattr(sample_payment, 'process_date_m', 'N/A')}")
|
||||
else:
|
||||
print("No BuildDecisionBookPayment records found in the database!")
|
||||
|
||||
# Check a sample account record
|
||||
sample_account = AccountRecords.query.filter(AccountRecords.remainder_balance < AccountRecords.currency_value, AccountRecords.receive_debit == receive_enum.id).first()
|
||||
if sample_account:
|
||||
print("\n--- Sample AccountRecord ---")
|
||||
print(f"ID: {getattr(sample_account, 'id', 'N/A')}")
|
||||
print(f"build_parts_id: {getattr(sample_account, 'build_parts_id', 'N/A')}")
|
||||
print(f"bank_date: {getattr(sample_account, 'bank_date', 'N/A')}")
|
||||
|
||||
# Try to find payments for this specific account record
|
||||
if sample_account.bank_date:
|
||||
process_date = arrow.get(sample_account.bank_date)
|
||||
account_bank_date_year, account_bank_date_month = (process_date.date().year, process_date.date().month)
|
||||
|
||||
print("\n--- Checking for payments for sample account ---")
|
||||
print(f"Looking for payments with build_parts_id={sample_account.build_parts_id}, payment_types_id={debit_enum.id}, account_records_id=None")
|
||||
|
||||
# Try without date filters first
|
||||
basic_payments = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.build_parts_id == sample_account.build_parts_id, BuildDecisionBookPayments.payment_types_id == debit_enum.id,
|
||||
BuildDecisionBookPayments.account_records_id == None
|
||||
).count()
|
||||
print(f"Found {basic_payments} payments without date filters")
|
||||
|
||||
# Now try with date filters
|
||||
dated_payments = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.build_parts_id == sample_account.build_parts_id, BuildDecisionBookPayments.payment_types_id == debit_enum.id,
|
||||
BuildDecisionBookPayments.account_records_id == None, BuildDecisionBookPayments.process_date_y == int(account_bank_date_year),
|
||||
BuildDecisionBookPayments.process_date_m == int(account_bank_date_month)
|
||||
).count()
|
||||
print(f"Found {dated_payments} payments with date filters (year={account_bank_date_year}, month={account_bank_date_month})")
|
||||
else:
|
||||
print("No matching AccountRecord found for debugging!")
|
||||
|
||||
# Query for account records that need payment processing
|
||||
# Note: We removed the approved_record condition as it was too restrictive
|
||||
account_records_list = AccountRecords.query.filter(AccountRecords.remainder_balance < AccountRecords.currency_value, AccountRecords.receive_debit == receive_enum.id
|
||||
).order_by(AccountRecords.bank_date.desc()).all()
|
||||
|
||||
print(f"\nProcessing {len(account_records_list)} account records")
|
||||
|
||||
# Track how many records were processed with each method
|
||||
traditional_method_count = 0
|
||||
direct_method_count = 0
|
||||
|
||||
for account_record in account_records_list:
|
||||
# Try the traditional method first
|
||||
current_currency_value = pay_the_registration(account_record, receive_enum, debit_enum, False, session)
|
||||
|
||||
if current_currency_value > 0:
|
||||
# If first pass found payments, try with old records too
|
||||
pay_the_registration(account_record, receive_enum, debit_enum, True, session)
|
||||
traditional_method_count += 1
|
||||
else:
|
||||
# If traditional method didn't work, try direct payment for all records
|
||||
# This will handle records with missing build_parts_id
|
||||
if create_direct_payment(account_record, receive_enum, session):
|
||||
direct_method_count += 1
|
||||
if direct_method_count % 10 == 0: # Only print every 10th record to avoid too much output
|
||||
print(f"Direct payment created for account_record {account_record.id}")
|
||||
|
||||
# Refresh the account record to get updated values
|
||||
session.refresh(account_record)
|
||||
|
||||
# Update status if the remainder balance equals the currency value
|
||||
if abs(float(Decimal(account_record.remainder_balance))) == abs(float(Decimal(account_record.currency_value))):
|
||||
account_record.update(status_id=97)
|
||||
account_record.save()
|
||||
|
||||
print(f"\nProcessed with traditional method: {traditional_method_count} records")
|
||||
print(f"Processed with direct payment method: {direct_method_count} records")
|
||||
|
||||
print("Payment processing completed successfully")
|
||||
except Exception as e:
|
||||
print(f"Error in send_accounts_to_decision_payment: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# Rollback the session in case of error
|
||||
session.rollback()
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Payment Service is running...")
|
||||
try:
|
||||
send_accounts_to_decision_payment()
|
||||
|
||||
# Print counter statistics
|
||||
print("\n--- Processing Statistics ---")
|
||||
print(f"Records where current_currency_value <= 0: {counter_current_currency_not_positive}")
|
||||
print(f"Records where net_amount <= 0: {counter_net_amount_not_positive}")
|
||||
print(f"Account records updated: {counter_account_records_updated}")
|
||||
print(f"Total payments found: {counter_payments_found}")
|
||||
print(f"Skips due to found_payment: {counter_found_payment_skips}")
|
||||
print(f"Payment exceptions: {counter_payment_exceptions}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
print("Payment Service is finished...")
|
||||
|
|
@ -6,11 +6,13 @@ from decimal import Decimal
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
from Schemas import BuildDecisionBookPayments, AccountRecords, ApiEnumDropdown, Build, BuildDecisionBook
|
||||
# from ServicesApi.Schemas.building.decision_book import BuildDecisionBookPayments, BuildDecisionBook
|
||||
# from ServicesApi.Schemas.building.build import Build
|
||||
# from ServicesApi.Schemas.account.account import AccountRecords
|
||||
# from ServicesApi.Schemas.others.enums import ApiEnumDropdown
|
||||
from time import perf_counter
|
||||
from sqlalchemy import select, func, distinct, cast, Date, String, literal, desc, and_, or_
|
||||
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):
|
||||
|
|
@ -23,6 +25,18 @@ def find_first_day_of_month(date_value):
|
|||
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):
|
||||
|
|
@ -83,194 +97,44 @@ def get_enums_from_database():
|
|||
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.
|
||||
def retrieve_remainder_balance_set_if_needed(account_record_id: int, session):
|
||||
"""Update the remainder_balance of an account after spending money.
|
||||
|
||||
Args:
|
||||
build_parts_id: The build part ID to calculate payments for
|
||||
account_record: The account record to update
|
||||
amount_spent: The amount spent in this transaction
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
float: The total amount paid (absolute value)
|
||||
bool: True if all money is spent, False otherwise
|
||||
"""
|
||||
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)
|
||||
|
||||
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 check_current_debt_to_pay_from_database_is_closed(ref_id: int, session):
|
||||
session.commit()
|
||||
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")
|
||||
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):
|
||||
|
|
@ -286,6 +150,8 @@ def close_payment_book(payment_row_book, account_record, value, session):
|
|||
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(
|
||||
|
|
@ -317,538 +183,126 @@ def close_payment_book(payment_row_book, account_record, value, session):
|
|||
saved_row = new_row.save()
|
||||
session.commit()
|
||||
session.refresh(saved_row)
|
||||
session.flush()
|
||||
return 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 update_account_remainder_if_spent(account_record, ref_id: str, session):
|
||||
"""Update the remainder_balance of an account after spending money.
|
||||
def refresh_ids_to_iterate_account_records(session, limit, offset):
|
||||
return session.query(AccountRecords.id).filter(
|
||||
AccountRecords.active == True, func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0,
|
||||
AccountRecords.currency_value > 0,
|
||||
).order_by(AccountRecords.bank_date.desc()).limit(limit).offset(offset).all()
|
||||
|
||||
|
||||
def get_ids_to_iterate_account_records(session, limit, offset):
|
||||
"""Get account records to process with pagination.
|
||||
|
||||
Args:
|
||||
account_record: The account record to update
|
||||
amount_spent: The amount spent in this transaction
|
||||
session: Database session
|
||||
limit: Maximum number of records to return
|
||||
offset: Number of records to skip
|
||||
|
||||
Returns:
|
||||
bool: True if all money is spent, False otherwise
|
||||
List of account record IDs
|
||||
"""
|
||||
AccountRecords.set_session(session)
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
session.commit()
|
||||
sum_of_paid = 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 sum_of_paid:
|
||||
return False
|
||||
debit_row = BuildDecisionBookPayments.query.filter_by(ref_id=ref_id).first()
|
||||
account_record_to_update = AccountRecords.query.filter_by(id=account_record.id).first()
|
||||
account_record_to_update.remainder_balance = (-1 * abs(sum_of_paid)) or 0
|
||||
account_record_to_update.save()
|
||||
session.commit()
|
||||
session.refresh(account_record_to_update)
|
||||
# Get the current remainder balance
|
||||
if abs(sum_of_paid) == abs(account_record_to_update.currency_value):
|
||||
return True
|
||||
return False
|
||||
today = datetime.now()
|
||||
return session.query(AccountRecords.id).filter(
|
||||
AccountRecords.active == True, func.abs(AccountRecords.currency_value) - func.abs(AccountRecords.remainder_balance) > 0,
|
||||
AccountRecords.currency_value > 0, AccountRecords.build_parts_id.isnot(None), cast(AccountRecords.bank_date, Date) < find_first_day_of_month(today).date()
|
||||
).order_by(AccountRecords.bank_date.desc()).limit(limit).offset(offset).all()
|
||||
|
||||
|
||||
def update_all_spent_accounts(session):
|
||||
"""Update remainder_balance for all accounts with payments.
|
||||
def get_read_payment_type(payment_result_type_uu_id, payment_type_list):
|
||||
for payment_type in payment_type_list:
|
||||
if payment_type.uuid == payment_result_type_uu_id:
|
||||
return payment_type.key
|
||||
return None
|
||||
|
||||
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.
|
||||
|
||||
def same_month_actions(limit=10, offset=0):
|
||||
"""Main function to process account records and find debits.
|
||||
|
||||
Args:
|
||||
session: Database session
|
||||
limit: Maximum number of records to process
|
||||
offset: Number of records to skip
|
||||
"""
|
||||
with AccountRecords.new_session() as session:
|
||||
# Set sessions for models
|
||||
today, build_id, start_time = datetime.now(), 1, perf_counter()
|
||||
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
payment_type_list = get_enums_from_database()
|
||||
|
||||
print(f"Processing with limit={limit}, offset={offset}")
|
||||
account_records = get_ids_to_iterate_account_records(session=session, limit=limit, offset=offset)
|
||||
print(f"Found {len(account_records)} account records to process")
|
||||
|
||||
for account_record in account_records:
|
||||
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()
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_record.id).first()
|
||||
payment_result_type_uu_id = account_record_selected.payment_result_type_uu_id
|
||||
|
||||
updated_count = 0
|
||||
# Extract month and year from the bank transaction date instead of using current date
|
||||
bank_date = account_record_selected.bank_date
|
||||
bank_month = bank_date.month
|
||||
bank_year = bank_date.year
|
||||
priority_uuids = [payment_result_type_uu_id, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_result_type_uu_id]]
|
||||
order_payment_by_type = case(*[(BuildDecisionBookPayments.payment_types_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
|
||||
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
|
||||
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(AccountRecords.bank_date.asc()).all()
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
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(money_to_pay_row.remainder_balance)
|
||||
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:', 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
|
||||
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
|
||||
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(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(AccountRecords.bank_date.asc()).all()
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
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(money_to_pay_row.remainder_balance)
|
||||
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)
|
||||
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(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(AccountRecords.bank_date.asc()).all()
|
||||
for money_to_pay_row in money_to_pay_rows:
|
||||
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(money_to_pay_row.remainder_balance)
|
||||
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)
|
||||
same_month_build_decision_book_debits = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.account_is_debit.is_(True),
|
||||
BuildDecisionBookPayments.active.is_(True),
|
||||
BuildDecisionBookPayments.build_parts_id == account_record_selected.build_parts_id,
|
||||
func.extract('month', BuildDecisionBookPayments.process_date) == bank_month,
|
||||
func.extract('year', BuildDecisionBookPayments.process_date) == bank_year,
|
||||
BuildDecisionBookPayments.is_closed.is_(False),
|
||||
# BuildDecisionBookPayments.payment_types_uu_id == payment_result_type_uu_id,
|
||||
).order_by(order_payment_by_type, BuildDecisionBookPayments.process_date.desc()).all()
|
||||
match_payment_type = get_read_payment_type(payment_result_type_uu_id, payment_type_list)
|
||||
print('fund', dict(
|
||||
id=account_record_selected.id,
|
||||
build_parts_id=account_record_selected.build_parts_id,
|
||||
payment_result_type_uu_id=match_payment_type,
|
||||
bank_date=arrow.get(account_record_selected.bank_date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance,
|
||||
length_matches=len(same_month_build_decision_book_debits),
|
||||
))
|
||||
for same_month_build_decision_book_debit in same_month_build_decision_book_debits:
|
||||
match_payment_type = get_read_payment_type(same_month_build_decision_book_debit.payment_types_uu_id, payment_type_list)
|
||||
print('debt', dict(
|
||||
id=same_month_build_decision_book_debit.id,
|
||||
payment_type=match_payment_type,
|
||||
debt_date=arrow.get(same_month_build_decision_book_debit.process_date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
payment_amount=same_month_build_decision_book_debit.payment_amount,
|
||||
debt_paid=same_month_build_decision_book_debit.debt_paid,
|
||||
debt_to_pay=same_month_build_decision_book_debit.debt_to_pay,
|
||||
is_closed=same_month_build_decision_book_debit.is_closed,
|
||||
))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_time = perf_counter()
|
||||
import sys
|
||||
|
||||
print("\n===== PROCESSING PAYMENTS =====\n")
|
||||
print("Starting payment processing at:", datetime.now())
|
||||
# Default values
|
||||
limit = 10
|
||||
offset = 0
|
||||
|
||||
# Process payments for current month first
|
||||
print("\n1. Processing current month payments...")
|
||||
do_payments_of_this_month()
|
||||
# Parse command line arguments
|
||||
if len(sys.argv) > 1:
|
||||
try:
|
||||
limit = int(sys.argv[1])
|
||||
except ValueError:
|
||||
print(f"Error: Invalid limit value '{sys.argv[1]}'. Using default limit={limit}")
|
||||
|
||||
# Process payments for previous months
|
||||
print("\n2. Processing previous months payments...")
|
||||
do_payments_of_previos_months()
|
||||
if len(sys.argv) > 2:
|
||||
try:
|
||||
offset = int(sys.argv[2])
|
||||
except ValueError:
|
||||
print(f"Error: Invalid offset value '{sys.argv[2]}'. Using default offset={offset}")
|
||||
|
||||
print("\n===== PAYMENT PROCESSING COMPLETE =====\n")
|
||||
print("Payment processing completed at:", datetime.now())
|
||||
same_month_actions(limit=limit, offset=offset)
|
||||
|
||||
# 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()
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
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, func, distinct, cast, Date, String, literal, desc, and_, or_, case
|
||||
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 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),
|
||||
)
|
||||
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
|
||||
|
||||
|
||||
def pay_book_payment_by_account_records_id(account_records_id: int, session, payment_type_list: list):
|
||||
timezone, today = 'Europe/Istanbul', datetime.now()
|
||||
AccountRecords.set_session(session)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
if not account_record_selected:
|
||||
print(f"Account record not found with id: {account_records_id}")
|
||||
return
|
||||
# raise ValueError(f"Account record not found with id: {account_records_id}")
|
||||
if not account_record_selected.currency_value + account_record_selected.remainder_balance > 0:
|
||||
print(f"Account record {account_record_selected.id} has no money to pay {account_record_selected.currency_value + account_record_selected.remainder_balance}")
|
||||
return
|
||||
# raise ValueError(f"Account record {account_record_selected.id} has no money to pay {account_record_selected.currency_value + account_record_selected.remainder_balance}")
|
||||
|
||||
BuildDecisionBook.set_session(session)
|
||||
selected_decision_book = BuildDecisionBook.query.filter_by(id=account_record_selected.build_decision_book_id).first()
|
||||
if not selected_decision_book:
|
||||
print(f"Build decision book not found with id: {account_record_selected.build_decision_book_id}")
|
||||
return
|
||||
# raise ValueError(f"Build decision book not found with id: {account_record_selected.build_decision_book_id}")
|
||||
|
||||
BuildDecisionBookPayments.set_session(session)
|
||||
payment_result_type_uu_id = account_record_selected.payment_result_type_uu_id or payment_type_list[0]
|
||||
priority_uuids = [payment_result_type_uu_id, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_result_type_uu_id]]
|
||||
order_payment_by_type = case(*[(BuildDecisionBookPayments.payment_types_uu_id == uuid, index) for index, uuid in enumerate(priority_uuids)], else_=len(priority_uuids))
|
||||
|
||||
payment_value_func = lambda account_record_selected: account_record_selected.currency_value + account_record_selected.remainder_balance
|
||||
period_start_date = arrow.get(selected_decision_book.expiry_starts).to(timezone).datetime
|
||||
period_end_date = arrow.get(selected_decision_book.expiry_ends).to(timezone).datetime
|
||||
bank_date_from = arrow.get(account_record_selected.bank_date).to(timezone).datetime
|
||||
month_start_date = find_first_day_of_month(bank_date_from)
|
||||
payment_type_list = [payment_result_type_uu_id, *[pt.uuid for pt in payment_type_list if pt.uuid != payment_result_type_uu_id]]
|
||||
|
||||
# Do payments of period_start_date to bank_date_from [This months payment]
|
||||
print('Do payments of period_start_date to bank_date_from [This months payment]')
|
||||
for payment_type in payment_type_list:
|
||||
# No matter what paymnet_uu_id check for debt and money amount to clarify debt close !
|
||||
selected_payments = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.account_is_debit.is_(True),
|
||||
BuildDecisionBookPayments.active.is_(True),
|
||||
BuildDecisionBookPayments.build_parts_id == account_record_selected.build_parts_id,
|
||||
BuildDecisionBookPayments.payment_types_uu_id == payment_type,
|
||||
BuildDecisionBookPayments.is_closed.is_(False),
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= month_start_date,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) <= bank_date_from,
|
||||
).order_by(BuildDecisionBookPayments.process_date.desc()).all()
|
||||
print(
|
||||
'dates period_start_date:', month_start_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'period_end_date:', bank_date_from.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
"payment_type", get_read_payment_type(payment_type),
|
||||
)
|
||||
print('fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=payment_value_func(account_record_selected),
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
if not money_available > 0:
|
||||
print(f"Account record {account_record_selected.id} has no money to pay {money_available}")
|
||||
break
|
||||
# raise ValueError(f"Account record {account_record_selected.id} has no money to pay {money_available}")
|
||||
|
||||
for selected_payment in selected_payments:
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
if selected_payment.is_closed:
|
||||
continue
|
||||
print('debt', dict(
|
||||
id=selected_payment.id, payment_type=selected_payment.payment_types_id,
|
||||
debt_date=arrow.get(selected_payment.process_date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
payment_amount=selected_payment.payment_amount, debt_paid=selected_payment.debt_paid,
|
||||
debt_to_pay=selected_payment.debt_to_pay, is_closed=selected_payment.is_closed,
|
||||
))
|
||||
if money_available > (-1 * selected_payment.debt_to_pay):
|
||||
close_payment_book(
|
||||
payment_row_book=selected_payment,
|
||||
account_record=account_record_selected,
|
||||
value=(-1 * selected_payment.debt_to_pay),
|
||||
session=session,
|
||||
is_commission_applicable=False
|
||||
)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
print('updated fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=money_available,
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
print(f"There is still money to pay for this account record {account_record_selected.id} fund to continue : {money_available}")
|
||||
continue
|
||||
else:
|
||||
close_payment_book(
|
||||
payment_row_book=selected_payment,
|
||||
account_record=account_record_selected,
|
||||
value=money_available,
|
||||
session=session,
|
||||
is_commission_applicable=False
|
||||
)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
print('updated fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=money_available,
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
print(f"All payments for account record {account_record_selected.id} have been paid.")
|
||||
break
|
||||
|
||||
# Do payments of period_start_date to bank_date_from [Build Decision Book Payments of period]
|
||||
print('Do payments of period_start_date to bank_date_from [Build Decision Book Payments of period]')
|
||||
|
||||
# for payment_type in payment_type_list:
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
if not money_available > 0:
|
||||
print(f"Account record {account_record_selected.id} has no money to pay {money_available}")
|
||||
return
|
||||
|
||||
print(f"No debt is found for account record {account_record_selected.id} for this month. Fund is still available: {money_available}")
|
||||
selected_payments = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.account_is_debit.is_(True),
|
||||
BuildDecisionBookPayments.active.is_(True),
|
||||
BuildDecisionBookPayments.is_closed.is_(False),
|
||||
BuildDecisionBookPayments.build_parts_id == account_record_selected.build_parts_id,
|
||||
# BuildDecisionBookPayments.payment_types_uu_id == payment_type,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= period_start_date,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) <= bank_date_from,
|
||||
).order_by(BuildDecisionBookPayments.process_date.desc(), order_payment_by_type).all()
|
||||
print(
|
||||
'dates period_start_date:', period_start_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'period_end_date:', bank_date_from.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
"payment_type", get_read_payment_type(payment_type),
|
||||
)
|
||||
for selected_payment in selected_payments:
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
if selected_payment.is_closed:
|
||||
continue
|
||||
print('debt', dict(
|
||||
id=selected_payment.id, payment_type=selected_payment.payment_types_id,
|
||||
debt_date=arrow.get(selected_payment.process_date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
payment_amount=selected_payment.payment_amount, debt_paid=selected_payment.debt_paid,
|
||||
debt_to_pay=selected_payment.debt_to_pay, is_closed=selected_payment.is_closed,
|
||||
))
|
||||
if money_available > (-1 * selected_payment.debt_to_pay):
|
||||
close_payment_book(
|
||||
payment_row_book=selected_payment, account_record=account_record_selected, value=(-1 * selected_payment.debt_to_pay), session=session, is_commission_applicable=True
|
||||
)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
print('updated fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=money_available,
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
print(f"There is still money to pay for this account record {account_record_selected.id} fund to continue : {money_available}")
|
||||
continue
|
||||
else:
|
||||
close_payment_book(
|
||||
payment_row_book=selected_payment, account_record=account_record_selected, value=money_available, session=session, is_commission_applicable=True
|
||||
)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
print('updated fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=money_available,
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
print(f"All payments for account record {account_record_selected.id} have been paid.")
|
||||
break
|
||||
|
||||
# Do all payments of period_start_date to current_date [All payments]
|
||||
print('Do all payments of period_start_date to current_date [All payments]')
|
||||
# for payment_type in payment_type_list:
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
if not money_available > 0:
|
||||
print(f"Account record {account_record_selected.id} has no money to pay {money_available}")
|
||||
return
|
||||
|
||||
print(f"No debt is found for account record {account_record_selected.id} for this month. Fund is still available: {money_available}")
|
||||
selected_payments = BuildDecisionBookPayments.query.filter(
|
||||
BuildDecisionBookPayments.account_is_debit.is_(True),
|
||||
BuildDecisionBookPayments.active.is_(True),
|
||||
BuildDecisionBookPayments.is_closed.is_(False),
|
||||
BuildDecisionBookPayments.build_parts_id == account_record_selected.build_parts_id,
|
||||
# BuildDecisionBookPayments.payment_types_uu_id == payment_type,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) >= period_start_date,
|
||||
cast(BuildDecisionBookPayments.process_date, Date) <= today.date(),
|
||||
).order_by(BuildDecisionBookPayments.process_date.desc(), order_payment_by_type).all()
|
||||
print(
|
||||
'dates period_start_date:', period_start_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'period_end_date:', today.date().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
"payment_type", get_read_payment_type(payment_type),
|
||||
)
|
||||
for selected_payment in selected_payments:
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
if selected_payment.is_closed:
|
||||
continue
|
||||
print('debt', dict(
|
||||
id=selected_payment.id, payment_type=selected_payment.payment_types_id,
|
||||
debt_date=arrow.get(selected_payment.process_date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
payment_amount=selected_payment.payment_amount, debt_paid=selected_payment.debt_paid,
|
||||
debt_to_pay=selected_payment.debt_to_pay, is_closed=selected_payment.is_closed,
|
||||
))
|
||||
if money_available > (-1 * selected_payment.debt_to_pay):
|
||||
close_payment_book(
|
||||
payment_row_book=selected_payment, account_record=account_record_selected, value=(-1 * selected_payment.debt_to_pay), session=session, is_commission_applicable=True
|
||||
)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
print('updated fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=money_available,
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
print(f"There is still money to pay for this account record {account_record_selected.id} fund to continue : {money_available}")
|
||||
continue
|
||||
else:
|
||||
close_payment_book(
|
||||
payment_row_book=selected_payment, account_record=account_record_selected, value=money_available, session=session, is_commission_applicable=True
|
||||
)
|
||||
account_record_selected = AccountRecords.query.filter_by(id=account_records_id).first()
|
||||
money_available = payment_value_func(account_record_selected)
|
||||
print('updated fund', dict(
|
||||
id=account_record_selected.id, build_parts_id=account_record_selected.build_parts_id, money_available=money_available,
|
||||
bank_date=arrow.get(bank_date_from).format('YYYY-MM-DD HH:mm:ss'), currency_value=account_record_selected.currency_value,
|
||||
remainder_balance=account_record_selected.remainder_balance, length_matches=len(selected_payments),
|
||||
))
|
||||
print(f"All payments for account record {account_record_selected.id} have been paid.")
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
# Default values
|
||||
id_given = 1
|
||||
session_factory = get_session_factory()
|
||||
session = session_factory()
|
||||
payment_type_list = get_enums_from_database()
|
||||
|
||||
# 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}")
|
||||
|
||||
result = pay_book_payment_by_account_records_id(account_records_id=id_given, session=session, payment_type_list=payment_type_list)
|
||||
|
|
@ -339,17 +339,17 @@ services:
|
|||
# ports:
|
||||
# - "8005:8005"
|
||||
|
||||
# initializer_service:
|
||||
# container_name: initializer_service
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ServicesApi/Builds/Initial/Dockerfile
|
||||
# environment:
|
||||
# - SET_ALEMBIC=1
|
||||
# networks:
|
||||
# - wag-services
|
||||
# env_file:
|
||||
# - api_env.env
|
||||
initializer_service:
|
||||
container_name: initializer_service
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ServicesApi/Builds/Initial/Dockerfile
|
||||
environment:
|
||||
- SET_ALEMBIC=1
|
||||
networks:
|
||||
- wag-services
|
||||
env_file:
|
||||
- api_env.env
|
||||
# mem_limit: 512m
|
||||
# cpus: 0.5
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Navigate to the project root directory
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "Building and starting the finder_payment_service container..."
|
||||
docker compose up --build -d finder_payment_service
|
||||
|
||||
echo "Waiting for container to be ready..."
|
||||
sleep 5
|
||||
|
||||
echo "Running the Python script inside the container..."
|
||||
docker exec finder_payment_service python /draft/readme.py
|
||||
|
||||
echo "Done! Check the output above."
|
||||
Loading…
Reference in New Issue