diff --git a/api_configs/__init__.py b/api_configs/__init__.py index 226b10e..5d667a6 100644 --- a/api_configs/__init__.py +++ b/api_configs/__init__.py @@ -10,6 +10,7 @@ from api_configs.configs import ( TestMongo, RelationAccess, EmailConfig, + TestDatabase, ) __all__ = [ @@ -24,4 +25,5 @@ __all__ = [ "TestMongo", "RelationAccess", "EmailConfig", + "TestDatabase", ] diff --git a/api_configs/configs.py b/api_configs/configs.py index 884ab5e..50a0d0b 100644 --- a/api_configs/configs.py +++ b/api_configs/configs.py @@ -123,7 +123,7 @@ class TestDatabase: SQL: str = "postgresql+psycopg2" USERNAME: str = "berkay_wag_user" PASSWORD: str = "berkay_wag_user_password" - HOST: str = "10.10.2.44" + HOST: str = "10.10.2.46" PORT: str = "5434" DATABASE_NAME: str = "wag_database" DATABASE_URL = f"{SQL}://{USERNAME}:{PASSWORD}@{HOST}:{PORT}/{DATABASE_NAME}" diff --git a/api_events/events/account/account_records.py b/api_events/events/account/account_records.py index 22d6e05..a7d6fa0 100644 --- a/api_events/events/account/account_records.py +++ b/api_events/events/account/account_records.py @@ -10,6 +10,7 @@ from api_validations.validations_request import ( from api_validations.core_response import AlchemyJsonResponse from api_events.events.abstract_class import MethodToEvent, ActionsSchema from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject +from api_validations.validations_response.account import AccountListResponse from databases import ( AccountRecords, BuildIbans, @@ -30,8 +31,8 @@ class AccountRecordsListEventMethods(MethodToEvent): "208e6273-17ef-44f0-814a-8098f816b63a": "account_records_list_flt_res", } __event_validation__ = { - "7192c2aa-5352-4e36-98b3-dafb7d036a3d": ListOptions, - "208e6273-17ef-44f0-814a-8098f816b63a": ListOptions, + "7192c2aa-5352-4e36-98b3-dafb7d036a3d": AccountListResponse, + "208e6273-17ef-44f0-814a-8098f816b63a": AccountListResponse, } @classmethod diff --git a/api_events/events/application/authentication.py b/api_events/events/application/authentication.py index f7d3ffa..c7fbf40 100644 --- a/api_events/events/application/authentication.py +++ b/api_events/events/application/authentication.py @@ -414,9 +414,9 @@ class AuthenticationCreatePasswordEventMethods(MethodToEvent): if found_user := Users.filter_one( Users.password_token == data.password_token ).data: - found_user.create_password(password=data.password) - found_user.password_token = None - Users.save() + found_user.create_password(found_user=found_user, password=data.password) + found_user.password_token = "" + found_user.save() send_email_completed = send_email( subject=f"Dear {found_user.user_tag}, your password has been changed.", receivers=[str(found_user.email)], diff --git a/api_events/events/building/building_build_parts.py b/api_events/events/building/building_build_parts.py index 0ead676..49e23f6 100644 --- a/api_events/events/building/building_build_parts.py +++ b/api_events/events/building/building_build_parts.py @@ -25,7 +25,9 @@ class BuildingBuildPartsListEventMethods(MethodToEvent): __event_keys__ = { "b860e37a-e19b-4c45-9543-461241f7587c": "building_build_parts_list" } - __event_validation__ = {"b860e37a-e19b-4c45-9543-461241f7587c": BuildPartsListResponse} + __event_validation__ = { + "b860e37a-e19b-4c45-9543-461241f7587c": BuildPartsListResponse + } @classmethod def building_build_parts_list( diff --git a/api_events/events/decision_book/decision_book_payment.py b/api_events/events/decision_book/decision_book_payment.py new file mode 100644 index 0000000..79b503f --- /dev/null +++ b/api_events/events/decision_book/decision_book_payment.py @@ -0,0 +1,169 @@ +from typing import Union + +from fastapi.responses import JSONResponse +from fastapi import status + +from api_validations.validations_response.parts import BuildPartsListResponse +from databases import ( + Build, + BuildParts, +) +from api_events.events.abstract_class import MethodToEvent, ActionsSchema +from api_objects.auth.token_objects import EmployeeTokenObject, OccupantTokenObject +from api_validations.core_response import AlchemyJsonResponse + +from api_validations.validations_request import ( + InsertBuildParts, + UpdateBuildParts, + ListOptions, +) +from databases.sql_models.building.decision_book import BuildDecisionBookPayments + + +class DecisionBookPaymentListEventMethods(MethodToEvent): + + event_type = "SELECT" + __event_keys__ = { + "49bb8ab8-520d-4676-a159-aaf84f37f372": "decision_book_payment_list" + } + __event_validation__ = {"49bb8ab8-520d-4676-a159-aaf84f37f372": None} + + @classmethod + def decision_book_payment_list( + cls, + list_options: ListOptions, + token_dict: Union[EmployeeTokenObject, OccupantTokenObject], + ): + """ + SELECT payment_plan_time_periods, process_date, payment_amount, currency, payment_types_id, + payment_types_uu_id, period_time, process_date_y, process_date_m, build_decision_book_item_id, + build_decision_book_item_uu_id, decision_book_project_id, decision_book_project_uu_id, build_parts_id, + build_parts_uu_id, id, uu_id, ref_id, created_at, updated_at, cryp_uu_id, created_by, created_by_id, + updated_by, updated_by_id, confirmed_by, confirmed_by_id, is_confirmed, replication_id, deleted, + active, is_notification_send, is_email_send, expiry_starts, expiry_ends, account_records_id, + account_records_uu_id FROM public.build_decision_book_payments; + """ + from sqlalchemy import func, select, union_all, extract, Integer + + build_parts_id, build_decision_book_id = 7, "" + payment_types_id_recv, payment_types_id_deb = 46, 45 + + BuildDecisionBookPayments.filter_attr = list_options + # Define the subqueries + debit_subquery = ( + select( + BuildDecisionBookPayments.payment_plan_time_periods, + func.sum(BuildDecisionBookPayments.payment_amount).label("debit"), + func.cast(0, Integer).label("recv"), + func.max(BuildDecisionBookPayments.process_date).label("ls"), + ) + .where( + BuildDecisionBookPayments.build_parts_id == build_parts_id, + BuildDecisionBookPayments.payment_types_id == payment_types_id_deb, + BuildDecisionBookPayments.build_decision_book_id + == build_decision_book_id, + extract("year", func.current_date()) + == extract("year", BuildDecisionBookPayments.process_date), + extract("month", func.current_date()) + == extract("month", BuildDecisionBookPayments.process_date), + ) + .group_by(BuildDecisionBookPayments.payment_plan_time_periods) + ) + + recv_subquery = ( + select( + BuildDecisionBookPayments.payment_plan_time_periods, + func.cast(0, Integer).label("debit"), + func.sum(BuildDecisionBookPayments.payment_amount).label("recv"), + func.max(BuildDecisionBookPayments.process_date).label("ls"), + ) + .where( + BuildDecisionBookPayments.build_parts_id == build_parts_id, + BuildDecisionBookPayments.payment_types_id == payment_types_id_recv, + BuildDecisionBookPayments.build_decision_book_id + == build_decision_book_id, + extract("year", func.current_date()) + == extract("year", BuildDecisionBookPayments.process_date), + extract("month", func.current_date()) + == extract("month", BuildDecisionBookPayments.process_date), + ) + .group_by(BuildDecisionBookPayments.payment_plan_time_periods) + ) + + # Combine the subqueries using union_all + combined_subquery = union_all(debit_subquery, recv_subquery).alias("AA") + + # Final query + final_query = select( + combined_subquery.c.payment_plan_time_periods, + func.sum(combined_subquery.c.debit).label("debit"), + func.sum(combined_subquery.c.recv).label("recv"), + combined_subquery.c.ls.label("Last Seen"), + ).group_by( + combined_subquery.c.payment_plan_time_periods, combined_subquery.c.ls + ) + + # Execute the query + book_payments_month = BuildDecisionBookPayments.session.execute( + final_query + ).fetchall() + print("book_payments_month", book_payments_month) + + debit_subquery = ( + select( + BuildDecisionBookPayments.payment_plan_time_periods, + func.sum(BuildDecisionBookPayments.payment_amount).label("debit"), + func.cast(0, Integer).label("recv"), + func.max(BuildDecisionBookPayments.process_date).label("ls"), + ) + .where( + BuildDecisionBookPayments.build_parts_id == build_parts_id, + BuildDecisionBookPayments.payment_types_id == payment_types_id_deb, + BuildDecisionBookPayments.build_decision_book_id + == build_decision_book_id, + ) + .group_by(BuildDecisionBookPayments.payment_plan_time_periods) + ) + + recv_subquery = ( + select( + BuildDecisionBookPayments.payment_plan_time_periods, + func.cast(0, Integer).label("debit"), + func.sum(BuildDecisionBookPayments.payment_amount).label("recv"), + func.max(BuildDecisionBookPayments.process_date).label("ls"), + ) + .where( + BuildDecisionBookPayments.build_parts_id == build_parts_id, + BuildDecisionBookPayments.payment_types_id == payment_types_id_recv, + BuildDecisionBookPayments.build_decision_book_id + == build_decision_book_id, + ) + .group_by(BuildDecisionBookPayments.payment_plan_time_periods) + ) + + # Combine the subqueries using union_all + combined_subquery = union_all(debit_subquery, recv_subquery).alias("AA") + + # Final query + final_query = select( + combined_subquery.c.payment_plan_time_periods, + func.sum(combined_subquery.c.debit).label("debit"), + func.sum(combined_subquery.c.recv).label("recv"), + combined_subquery.c.ls.label("Last Seen"), + ).group_by( + combined_subquery.c.payment_plan_time_periods, combined_subquery.c.ls + ) + + # Execute the query + book_payments = BuildDecisionBookPayments.session.execute( + final_query + ).fetchall() + print("book_payments", book_payments) + return AlchemyJsonResponse( + completed=True, + message="Building Parts Records are listed", + result=[book_payments, book_payments_month], + cls_object=BuildParts, + response_model=BuildPartsListResponse, + filter_attributes=list_options, + ) diff --git a/api_library/date_time_actions/date_functions.py b/api_library/date_time_actions/date_functions.py index f5d27f8..07ebccc 100644 --- a/api_library/date_time_actions/date_functions.py +++ b/api_library/date_time_actions/date_functions.py @@ -14,7 +14,7 @@ class DateTimeLocal: def find_last_day_of_month(self, date_value): today = self.get(date_value).date() _, last_day = calendar.monthrange(today.year, today.month) - return self.get(today.year, today.month, last_day) + return self.get(today.year, today.month, last_day, 23, 59, 59).to(self.timezone) def find_first_day_of_month(self, date_value): today = self.get(date_value).date() diff --git a/api_validations/core_response.py b/api_validations/core_response.py index a85cf3c..851b8da 100644 --- a/api_validations/core_response.py +++ b/api_validations/core_response.py @@ -121,7 +121,9 @@ class AlchemyJsonResponse: } include_joins = dict( include_joins=( - filter_attributes.include_joins if filter_attributes.include_joins else [] + filter_attributes.include_joins + if filter_attributes.include_joins + else [] ) ) data = [] diff --git a/api_validations/validations_response/account.py b/api_validations/validations_response/account.py new file mode 100644 index 0000000..07ee4e2 --- /dev/null +++ b/api_validations/validations_response/account.py @@ -0,0 +1,138 @@ +from typing import Optional +from api_validations.core_validations import BaseModelRegular +from api_validations.validations_request import ( + PydanticBaseModel, + PydanticBaseModelValidation, + CrudRecordValidation, + CrudRecords, +) + + +class AccountListValidation: + tr = { + **CrudRecordValidation.tr, + "iban": "IBAN Numarası", + "bank_date": "Banka İşlem Tarihi", + "currency_value": "Para Birimi Değeri", + "bank_balance": "Banka Bakiyesi", + "currency": "Para Birimi Birimi", + "additional_balance": "Ek Bakiye", + "channel_branch": "Şube Banka", + "process_name": "Banka İşlem Türü Adı", + "process_type": "Banka İşlem Türü", + "process_comment": "İşlem Kayıt Yorumu", + "process_garbage": "İşlem Kayıt Çöpü", + "bank_reference_code": "Banka Referans Kodu", + "add_comment_note": "Yorum Not Ekle", + "is_receipt_mail_send": "Makbuz Posta Gönderildi", + "found_from": "Bulunduğu Yer", + "similarity": "Benzerlik", + "remainder_balance": "Kalan Bakiye", + "bank_date_y": "Banka İşlem Yılı", + "bank_date_m": "Banka İşlem Ayı", + "bank_date_w": "Banka İşlem Haftası", + "bank_date_d": "Banka İşlem Günü", + "approving_accounting_record": "Onaylayan Muhasebe Kaydı", + "accounting_receipt_date": "Muhasebe Makbuz Tarihi", + "accounting_receipt_number": "Muhasebe Makbuz Numarası", + "approved_record": "Onaylanmış Kayıt", + "import_file_name": "İçe Aktarım Dosya Adı", + "receive_debit": "Alacak Borç", + "receive_debit_uu_id": "Alacak Borç UU Kimliği", + "budget_type": "Bütçe Türü", + "budget_type_uu_id": "Bütçe Türü UU Kimliği", + "company_uu_id": "Şirket UU Kimliği", + "send_company_uu_id": "Şirket UU Kimliği Gönder", + "send_person_uu_id": "Kişi UU Kimliği Gönder", + "approving_accounting_person_uu_id": "Onaylayan Muhasebe Kişi UU Kimliği", + "living_space_uu_id": "Yaşam Alanı UU Kimliği", + "customer_uu_id": "Müşteri UU Kimliği", + "build_uu_id": "Yapı UU Kimliği", + "build_parts_uu_id": "Yapı Parça UU Kimliği", + "build_decision_book_uu_id": "Yapı Karar Defteri UU Kimliği", + } + en = { + **CrudRecordValidation.en, + "iban": "IBAN Number", + "bank_date": "Bank Transaction Date", + "currency_value": "Currency Value", + "bank_balance": "Bank Balance", + "currency": "Unit of Currency", + "additional_balance": "Additional Balance", + "channel_branch": "Branch Bank", + "process_name": "Bank Process Type Name", + "process_type": "Bank Process Type", + "process_comment": "Transaction Record Comment", + "process_garbage": "Transaction Record Garbage", + "bank_reference_code": "Bank Reference Code", + "add_comment_note": "Add Comment Note", + "is_receipt_mail_send": "Receipt Mail Send", + "found_from": "Found From", + "similarity": "Similarity", + "remainder_balance": "Remainder Balance", + "bank_date_y": "Bank Date Year", + "bank_date_m": "Bank Date Month", + "bank_date_w": "Bank Date Week", + "bank_date_d": "Bank Date Day", + "approving_accounting_record": "Approving Accounting Record", + "accounting_receipt_date": "Accounting Receipt Date", + "accounting_receipt_number": "Accounting Receipt Number", + "approved_record": "Approved Record", + "import_file_name": "Import File Name", + "receive_debit": "Receive Debit", + "receive_debit_uu_id": "Receive Debit UU ID", + "budget_type": "Budget Type", + "budget_type_uu_id": "Budget Type UU ID", + "company_uu_id": "Company UU ID", + "send_company_uu_id": "Send Company UU ID", + "send_person_uu_id": "Send Person UU ID", + "approving_accounting_person_uu_id": "Approving Accounting Person UU ID", + "living_space_uu_id": "Living Space UU ID", + "customer_uu_id": "Customer UU ID", + "build_uu_id": "Build UU ID", + "build_parts_uu_id": "Build Parts UU ID", + "build_decision_book_uu_id": "Build Decision Book UU ID", + } + + +class AccountListResponse(BaseModelRegular, CrudRecords, AccountListValidation): + + iban: Optional[str] = None + bank_date: Optional[str] = None + currency_value: Optional[str] = None + bank_balance: Optional[str] = None + currency: Optional[str] = None + additional_balance: Optional[str] = None + channel_branch: Optional[str] = None + process_name: Optional[str] = None + process_type: Optional[str] = None + process_comment: Optional[str] = None + process_garbage: Optional[str] = None + bank_reference_code: Optional[str] = None + add_comment_note: Optional[str] = None + is_receipt_mail_send: Optional[str] = None + found_from: Optional[str] = None + similarity: Optional[str] = None + remainder_balance: Optional[str] = None + bank_date_y: Optional[str] = None + bank_date_m: Optional[str] = None + bank_date_w: Optional[str] = None + bank_date_d: Optional[str] = None + approving_accounting_record: Optional[str] = None + accounting_receipt_date: Optional[str] = None + accounting_receipt_number: Optional[str] = None + approved_record: Optional[str] = None + import_file_name: Optional[str] = None + receive_debit: Optional[str] = None + receive_debit_uu_id: Optional[str] = None + budget_type: Optional[str] = None + budget_type_uu_id: Optional[str] = None + company_uu_id: Optional[str] = None + send_company_uu_id: Optional[str] = None + send_person_uu_id: Optional[str] = None + approving_accounting_person_uu_id: Optional[str] = None + living_space_uu_id: Optional[str] = None + customer_uu_id: Optional[str] = None + build_uu_id: Optional[str] = None + build_parts_uu_id: Optional[str] = None + build_decision_book_uu_id: Optional[str] = None diff --git a/api_validations/validations_response/parts.py b/api_validations/validations_response/parts.py index d77aa64..fffe1ca 100644 --- a/api_validations/validations_response/parts.py +++ b/api_validations/validations_response/parts.py @@ -40,6 +40,7 @@ class BuildPartsListValidation: "part_type_uu_id": "Part Type UUID", } + class BuildPartsListResponse(BaseModelRegular, CrudRecords, BuildPartsListValidation): address_gov_code: Optional[str] = None part_no: Optional[int] = None diff --git a/databases/sql_models/building/decision_book.py b/databases/sql_models/building/decision_book.py index dd0eab7..58c3bad 100644 --- a/databases/sql_models/building/decision_book.py +++ b/databases/sql_models/building/decision_book.py @@ -872,6 +872,12 @@ class BuildDecisionBookPayments(CrudCollection): build_decision_book_item_uu_id: Mapped[str] = mapped_column( String, nullable=False, comment="Decision Book Item UUID" ) + # 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="Decision Book UUID" + # ) build_parts_id: Mapped[int] = mapped_column( ForeignKey("build_parts.id"), nullable=False ) diff --git a/databases/sql_models/postgres_database.py b/databases/sql_models/postgres_database.py index d7307fd..f8c92af 100644 --- a/databases/sql_models/postgres_database.py +++ b/databases/sql_models/postgres_database.py @@ -1,4 +1,5 @@ -from api_configs import WagDatabase +# from api_configs import WagDatabase +from api_configs import TestDatabase as WagDatabase from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker diff --git a/databases/sql_models/sql_operations.py b/databases/sql_models/sql_operations.py index b074d00..2c22e51 100644 --- a/databases/sql_models/sql_operations.py +++ b/databases/sql_models/sql_operations.py @@ -77,7 +77,6 @@ class FilterAttributes: @classmethod def add_query_to_filter(cls, filter_query, filter_list): - print("filter_list", filter_list) return ( filter_query.order_by( getattr(cls, filter_list.get("order_field")).desc() diff --git a/test_environment/test_payment.py b/test_environment/test_payment.py new file mode 100644 index 0000000..cd0537e --- /dev/null +++ b/test_environment/test_payment.py @@ -0,0 +1,96 @@ +import pprint + +import arrow + +from api_library.date_time_actions.date_functions import client_arrow, system_arrow +from databases.sql_models.building.decision_book import BuildDecisionBookPayments + + +def decision_book_payment_list(): + """ + SELECT payment_plan_time_periods, process_date, payment_amount, currency, payment_types_id, + payment_types_uu_id, period_time, process_date_y, process_date_m, build_decision_book_item_id, + build_decision_book_item_uu_id, decision_book_project_id, decision_book_project_uu_id, build_parts_id, + build_parts_uu_id, id, uu_id, ref_id, created_at, updated_at, cryp_uu_id, created_by, created_by_id, + updated_by, updated_by_id, confirmed_by, confirmed_by_id, is_confirmed, replication_id, deleted, + active, is_notification_send, is_email_send, expiry_starts, expiry_ends, account_records_id, + account_records_uu_id FROM public.build_decision_book_payments; + """ + from sqlalchemy import func, select, union_all, extract, Integer + + build_parts_id, build_decision_book_id = 7, "" + payment_types_id_recv, payment_types_id_deb = 46, 45 + book_payments = BuildDecisionBookPayments.filter_all( + BuildDecisionBookPayments.build_parts_id == build_parts_id, + BuildDecisionBookPayments.process_date + <= system_arrow.find_last_day_of_month(arrow.now()).__str__(), + ).data + is_datetime_is_this_month = lambda x: arrow.get(x).format( + "YYYY-MM" + ) == arrow.now().format("YYYY-MM") + + dict_books, dict_books_monthly = {}, {} + for book_payment in book_payments: + book_payment: BuildDecisionBookPayments = book_payment + if is_datetime_is_this_month(book_payment.process_date): + if book_payment.payment_plan_time_periods not in dict_books_monthly: + dict_books_monthly[book_payment.payment_plan_time_periods] = { + "debt": 0, + "recv": 0, + "ls": str(book_payment.process_date), + } + if book_payment.payment_types_id == payment_types_id_deb: + dict_books_monthly[book_payment.payment_plan_time_periods][ + "debt" + ] += book_payment.payment_amount + else: + dict_books_monthly[book_payment.payment_plan_time_periods][ + "recv" + ] += book_payment.payment_amount + if arrow.get(str(book_payment.process_date)) > arrow.get( + dict_books_monthly[book_payment.payment_plan_time_periods]["ls"] + ): + dict_books_monthly[book_payment.payment_plan_time_periods]["ls"] = str( + book_payment.process_date + ) + dict_books_monthly[book_payment.payment_plan_time_periods]["balance"] = ( + dict_books_monthly[book_payment.payment_plan_time_periods]["debt"] + + dict_books_monthly[book_payment.payment_plan_time_periods]["recv"] + ) + + if book_payment.payment_plan_time_periods not in dict_books: + dict_books[book_payment.payment_plan_time_periods] = { + "debt": 0, + "recv": 0, + "ls": str(book_payment.process_date), + } + date_process = dict_books[book_payment.payment_plan_time_periods]["ls"] + if arrow.get(str(date_process)) < arrow.get(str(book_payment.process_date)): + dict_books[book_payment.payment_plan_time_periods]["ls"] = str( + book_payment.process_date + ) + if book_payment.payment_types_id == payment_types_id_deb: + dict_books[book_payment.payment_plan_time_periods][ + "debt" + ] += book_payment.payment_amount + else: + dict_books[book_payment.payment_plan_time_periods][ + "recv" + ] += book_payment.payment_amount + dict_books[book_payment.payment_plan_time_periods]["balance"] = ( + dict_books[book_payment.payment_plan_time_periods]["debt"] + + dict_books[book_payment.payment_plan_time_periods]["recv"] + ) + + return dict( + Month=[{**item, "type": key} for key, item in dict_books_monthly.items()], + Total=[{**item, "type": key} for key, item in dict_books.items()], + ) + +result = decision_book_payment_list() +print('result', result) +pprint.pprint(result, indent=2) +# for key, val in result.items(): +# print('key', key) +# for item in val: +# print(item)