from django.forms import ValidationError
from rest_framework import viewsets
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from django.db.models import Sum
from django.utils.timezone import make_aware
from rest_framework.decorators import action
import datetime as datetime_timedelta

from loans.helpers.lines_of_credit_helper import filter_lines_of_credit_reports
from .serializers import *
from .models import *
from .helper import *
from ledgers.serializers import SystemTransactionsSerializer
from django.http import Http404
from ussdbanking.helpers import get_branch_wallet_chart

# being refined
from .report_helpers.repayment_helper import filter_repayment_reports
from .report_helpers.arrears_helper import filter_arrear_reports
from .report_helpers.due_vs_repayment_helper import filter_due_repayment_reports
from .report_helpers.expected_repayment_helper import filter_expected_repayment_reports
from .report_helpers.loan_due_installments_helper import filter_due_installment_reports
from .report_helpers.cleared_off_loans_helper import filter_cleared_off_reports
from .report_helpers.loans_to_cleared_helper import filter_to_be_cleared_reports
from .report_helpers.par_report_helper import filter_par_reports
from .report_helpers.written_off_helper import filter_written_off_reports
from .report_helpers.write_off_helper import filter_write_off_reports
from .report_helpers.ageing_report_helper import filter_ageing_reports
from general.helper import date_time_zone_convert

# being refined

# new refine
from .helpers.arrears_helper import filter_loan_arrear_reports
from .helpers.par_report_helper import filter_loan_par_reports
from .helpers.due_vs_repayment_helper import filter_loan_dues_repayment_reports
from .helpers.ageing_report_helper import filter_loan_aging_reports
from .helpers.expected_repayment_helper import filter_loans_expected_repayment_reports
from .helpers.par_aging_report_helper import filter_loan_par_aging_reports
from .helpers.write_off_helper import filter_loan_writeoff_reports, get_list_to_writeoff
from .helpers.written_off_helper import filter_loan_writtenoff_reports
from .helpers.cleared_off_helper import filter_loan_clearedoff_reports
from .helpers.repayment_helper import filter_loan_repayment_reports
from .helpers.rescheduled_loans_helper import filter_loan_rescheduled_reports
from .helpers.performing_loans_helper import filter_loan_performing_reports
from .helpers.loan_savings_helper import filter_loan_savings_reports
from .helpers.loan_tracking_helper import filter_loan_tracking_reports
from .helpers.loan_disbursement_helper import filter_loan_disbursement_reports
from .helpers.green_loan_tracking_helper import filter_green_loan_tracking_reports
from .helpers.general_helper import validate_date
from .report_helpers.loan_funders_report_helper import *
from .report_helpers.loan_waiver_reports_helper import get_loan_interest_waiver_report, get_loan_penalty_waiver_report

# new refined

from django.contrib.auth import get_user_model
from questbanker_api.utils import get_current_user
from savings.models import (
    SavingAccount,
    FixedDeposit,
    SavingsProductInterestPayment,
    SavingsProduct,
)
from ledgers.models import *
from ledgers.ledgers_helper import *
from savings.models import (
    SavingAccountTransactions,
    AccountBookings,
    AccountBookingPayments,
)
from exservices.exservices_helper import send_customer_sms
from general.helper import update_loan_penalty_dates, arrears_per_term
import threading
import csv
import io
from django.conf import settings
from rest_framework.parsers import MultiPartParser
from general.models import BulkTempMMBankingSubscriptonImports
from mmbanking.models import MobileBankingSubscription

from savings.savings_helper import save_reciever_transactions, save_sender_transactions


import random
from datetime import date, timedelta


class GreenFinanceValueChainViewSet(viewsets.ModelViewSet):
    serializer_class = GreenFinanceValueChainSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        try:
            org = self.request.user.user_organisation_branch.branch_organisation
            return GreenFinanceValueChain.objects.filter(organisation=org)
        except AttributeError:
            return GreenFinanceValueChain.objects.none()

    def perform_create(self, serializer):
        org = self.request.user.user_organisation_branch.branch_organisation
        user = self.request.user

        # Save the value chain
        value_chain = serializer.save(organisation=org, created_by=user)

        # Link the three global nodes
        default_nodes = GreenFinanceValueChainNode.objects.filter(
            value_chain_node__in=["production", "processing", "marketing"]
        )
        value_chain.nodes.set(default_nodes)

    def perform_update(self, serializer):
        try:
            org = self.request.user.user_organisation_branch.branch_organisation
            user = self.request.user
            value_chain = serializer.save(updated_by=user)

            # Nodes remain unchanged; no need to modify them
        except AttributeError:
            raise ValidationError("User must be associated with an organization")


class GreenFinanceValueChainNodeViewSet(viewsets.ModelViewSet):
    serializer_class = GreenFinanceValueChainNodeSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return GreenFinanceValueChainNode.objects.all()

    def perform_create(self, serializer):
        try:
            org = self.request.user.user_organisation_branch.branch_organisation
            serializer.save(organisation=org, created_by=self.request.user)
        except AttributeError:
            raise ValidationError("User must be associated with an organization")


class LoanProductView(viewsets.ModelViewSet):
    serializer_class = LoanProductsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        return LoanProduct.objects.filter(
            is_deleted=False, organisation__id=organisation_id
        )

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        organisation = Organisation.objects.get(pk=organisation_id)
        # Fill session organisation account details

        product_name = self.request.data.get("product_name")
        parent_chart = "sys-111"
        parent = get_chart_of_account_by_code(parent_chart, organisation)
        if parent:
            account_line = parent.account_line
            account_code = generate_chart_of_account_code(
                parent.id, account_line, organisation_id
            )
            chart = OrganisationSubAccount.objects.create(
                account_name=product_name,
                account_line=account_line,
                account_organisation=organisation,
                account_code=account_code,
                parent_id=parent,
                added_by=self.request.user.id,
                allow_sub_accounts=False,
            )

            # if accrual create chart of accounts
            interest_accrued_account = None
            penalty_accrued_account = None
            interest_receivable_account = None
            penalty_receivable_account = None
            expenses_from_interest_account = None
            expenses_from_penality_account = None
            interest_income_chart = None
            accounting_type = OrganisationSetting.objects.filter(
                setting_key="account_type",
                setting_value="accrual",
                org_setting=organisation,
            ).first()
            if accounting_type:
                # interest accrued
                parent_chart = "sys-411"
                parent = get_chart_of_account_by_code(parent_chart, organisation)
                account_line = parent.account_line
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name + ":INTEREST INCOME ACCRUED"
                interest_accrued_account = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

                # penalty accrued
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name + ":PENALTY ACCRUED"
                penalty_accrued_account = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

                # Income receivable
                parent_chart = "sys-113"
                parent = get_chart_of_account_by_code(parent_chart, organisation)
                account_line = parent.account_line
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name + ":INTEREST INCOME RECEIVABLE"
                interest_receivable_account = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

                # penalty receivable
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name + ":PENALTY RECEIVABLE"
                penalty_receivable_account = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

                # expenses
                # interest waived
                parent_chart = "sys-512"
                parent = get_chart_of_account_by_code(parent_chart, organisation)
                account_line = parent.account_line
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name + ":EXPENSE FROM INTEREST WAIVED"
                expenses_from_interest_account = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

                # penalty waived
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name + ":EXPENSE FROM PENALTY WAIVED"
                expenses_from_penality_account = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

            else:
                # interest income
                parent_chart = "sys-4115"
                parent = get_chart_of_account_by_code(parent_chart, organisation)
                account_line = parent.account_line
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name
                interest_income_chart = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

                # penality income
                parent_chart = "sys-4116"
                parent = get_chart_of_account_by_code(parent_chart, organisation)
                account_line = parent.account_line
                account_code = generate_chart_of_account_code(
                    parent.id, account_line, organisation_id
                )
                account_name = product_name
                penalty_income_chart = OrganisationSubAccount.objects.create(
                    account_name=account_name,
                    account_line=account_line,
                    account_organisation=organisation,
                    account_code=account_code,
                    parent_id=parent,
                    added_by=self.request.user.id,
                    allow_sub_accounts=False,
                )

            accrual_accounts = {
                "expenses_from_penality_account": expenses_from_penality_account,
                "expenses_from_interest_account": expenses_from_interest_account,
                "penalty_receivable_account": penalty_receivable_account,
                "interest_receivable_account": interest_receivable_account,
                "penalty_accrued_account": penalty_accrued_account,
                "interest_accrued_account": interest_accrued_account,
                "interest_income_chart": interest_income_chart,
                "penalty_income_chart": penalty_income_chart,
            }

            serializer.save(
                **accrual_accounts,
                organisation=organisation,
                loan_product_added_by=self.request.user,
                chart=chart,
            )

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        product = LoanProduct.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = LoanProductsSerializer(product, read_only=True).data
        loan_product = serializer.save()

        # update the charts
        product_chart = OrganisationSubAccount.objects.get(pk=product.chart.id)
        product_chart.account_name = loan_product.product_name
        product_chart.save()

        # income
        if product.interest_income_chart:
            product_chart = OrganisationSubAccount.objects.filter(
                id=product.interest_income_chart.id
            ).first()
            product_chart.account_name = loan_product.product_name
            product_chart.save()

        # penalty
        if product.penalty_income_chart:
            product_chart = OrganisationSubAccount.objects.filter(
                id=product.penalty_income_chart.id
            ).first()
            product_chart.account_name = loan_product.product_name
            product_chart.save()

        new_details = LoanProductsSerializer(loan_product, read_only=True).data
        message = f"Updated Loan Product: {loan_product.product_name}"
        add_system_audit_trail(
            "loans",
            "update_loan_product",
            message,
            "",
            old_details,
            new_details,
            self.request.user,
            branch,
        )


class LoanProductChargesView(viewsets.ModelViewSet):
    serializer_class = LoanProductChargesSerializer
    queryset = LoanProductCharges.objects.filter(is_deleted=False)

    def perform_create(self, serializer):
        serializer.save(loan_charge_added_by=self.request.user)

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        charge = LoanProductCharges.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = LoanProductChargesSerializer(charge, read_only=True).data
        loan_charge = serializer.save()
        new_details = LoanProductChargesSerializer(loan_charge, read_only=True).data
        message = f"Updated Loan Charge: {loan_charge.name} Loan Product: {loan_charge.loan_product.product_name}"
        add_system_audit_trail(
            "loans",
            "update_loan_product_charge",
            message,
            "",
            old_details,
            new_details,
            self.request.user,
            branch,
        )


class ListLoanApplicationsView(viewsets.ModelViewSet):
    serializer_class = LoanApplicationsSerializer
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "organisation_branch",
        "status",
        "loan_application_product",
        "customer",
    ]
    search_fields = ("status",)
    ordering_fields = [
        "status",
    ]
    lookup_field = "id"

    def get_queryset(self):
        status_filter = self.request.query_params.get("status_filter", None)
        loan_officer_id = self.request.query_params.get("loan_officer_id", None)
        loan_type_filter = self.request.query_params.get("loan_type", None)
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )

        # Base filter: exclude external loans by default unless explicitly requested
        base_filter = {
            "deleted": False,
            "is_deleted": False,
            "organisation_branch__id": organisation_branch_id,
        }
        if loan_type_filter != "external":
            base_filter["loan_type"] = "standard"

        if status_filter:
            status_filter = status_filter.split(",")
            return LoanApplication.objects.filter(
                status__in=status_filter, **base_filter
            ).order_by("-id")

        if loan_officer_id:
            self.pagination_class = None
            return LoanApplication.objects.filter(
                loan_officer__id=loan_officer_id, **base_filter
            ).order_by("-id")

        if self.kwargs and "id" in self.kwargs:
            # For single loan retrieval, respect loan_type filter
            if loan_type_filter == "external":
                return LoanApplication.objects.filter(loan_type="external").order_by(
                    "-id"
                )
            return LoanApplication.objects.filter(loan_type="standard").order_by("-id")

        return LoanApplication.objects.filter(**base_filter).order_by("-id")


class LoanApplicationView(viewsets.ModelViewSet):
    serializer_class = LoanApplicationSerializer
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "organisation_branch",
        "status",
        "loan_application_product",
        "customer",
    ]
    search_fields = ("status", "customer")
    ordering_fields = [
        "status",
    ]
    lookup_field = "id"

    def get_queryset(self):
        loan_type_filter = self.request.query_params.get("loan_type", None)

        if self.kwargs and "id" in self.kwargs:
            # For single loan retrieval, respect loan_type filter
            if loan_type_filter == "external":
                return LoanApplication.objects.filter(loan_type="external").order_by(
                    "-id"
                )
            return LoanApplication.objects.filter(loan_type="standard").order_by("-id")

        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )

        # Exclude external loans by default unless explicitly requested
        if loan_type_filter == "external":
            return LoanApplication.objects.filter(
                is_deleted=False,
                deleted=False,
                organisation_branch__id=organisation_branch_id,
                loan_type="external",
            ).order_by("-id")

        return LoanApplication.objects.filter(
            is_deleted=False,
            deleted=False,
            organisation_branch__id=organisation_branch_id,
            loan_type="standard",
        ).order_by("-id")

    def perform_create(self, serializer):
        send_sms = self.request.data.get("send_sms")
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )
        organisation_branch = OrganisationBranch.objects.get(pk=organisation_branch_id)
        new_application = serializer.save(
            loan_app_added_by=self.request.user, organisation_branch=organisation_branch
        )

        application = LoanApplication.objects.get(pk=new_application.id)
        application.auto_payments = application.loan_application_product.auto_payments
        application.auto_pay_penalty = (
            application.loan_application_product.auto_pay_penalty
        )
        application.save()

        if application and send_sms:
            f_amount = f"{float(application.loan_amount):,}"
            pdt_name = application.loan_application_product.product_name
            sms_msg = (
                "Dear "
                + application.customer.name.capitalize()
                + ", You have applied for a ("
                + pdt_name
                + " loan) Amount UGX: "
                + f_amount
                + ", its awaiting approval\n"
                + organisation_branch.branch_organisation.short_name
            )
            data = {
                "sms_key": "loan_application_sms",
                "customer": application.customer,
                "user": self.request.user,
                "branch_id": organisation_branch_id,
                "sms_msg": sms_msg,
                "loan": application,
            }
            send_customer_sms(data)

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        application = LoanApplication.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = LoanApplicationSerializer(application, read_only=True).data
        loan_application = serializer.save()
        new_details = LoanApplicationSerializer(loan_application, read_only=True).data

        if loan_application.status == "cleared_off":
            sms = (
                "Dear "
                + loan_application.customer.name.capitalize()
                + ",   your loan of "
                + str(loan_application.loan_amount)
                + " is settled. Thanks for banking with "
                + (
                    branch.branch_organisation.short_name
                    if branch.branch_organisation.short_name
                    else ""
                )
                + "! You qualify for another loan. Contact us for details."
            )
            data = {
                "sms_key": "loan_application_sms",
                "customer": loan_application.customer,
                "user": self.request.user,
                "branch_id": branchid,
                "sms_msg": sms,
                "loan": loan_application,
            }
            send_customer_sms(data)

        message = f"Updated Loan Application: {loan_application.loan_application_product.product_name}({loan_application.loan_amount}) For {loan_application.customer.name}"
        add_system_audit_trail(
            "loans",
            "update_loan_application",
            message,
            loan_application.reason_for_delete,
            old_details,
            new_details,
            self.request.user,
            branch,
        )


class LoanApplicationIncomeSourceView(viewsets.ModelViewSet):
    serializer_class = LoanIncomeSourceSerializer
    queryset = LoanIncomeSource.objects.filter(deleted=False)
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = ["loan_application", "id", "source_name"]
    search_fields = ("loan_application", "source_name")
    ordering_fields = [
        "source_name",
    ]

    def perform_create(self, serializer):
        serializer.save(
            loan_income_source_added_by=self.request.user,
        )

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        income_source = LoanIncomeSource.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = LoanIncomeSourceSerializer(income_source, read_only=True).data
        loan_income_source = serializer.save()
        new_details = LoanIncomeSourceSerializer(
            loan_income_source, read_only=True
        ).data

        message = f"Updated Income Source. Loan: {loan_income_source.loan_application.loan_application_product.product_name}({loan_income_source.loan_application.loan_amount}) For {loan_income_source.loan_application.customer.name}"
        add_system_audit_trail(
            "loans",
            "update_loan_income_source",
            message,
            "",
            old_details,
            new_details,
            self.request.user,
            branch,
        )

    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            branchid = get_current_user(self.request, "organisation_branch_id", 1)
            branch = OrganisationBranch.objects.get(id=branchid)
            if instance.id:
                details = LoanIncomeSourceSerializer(instance, read_only=True).data
                message = f"Removed Income Source. Loan: {instance.loan_application.loan_application_product.product_name}({instance.loan_application.loan_amount}) For {instance.loan_application.customer.name}"
                add_system_audit_trail(
                    "loans",
                    "delete_loan_income_source",
                    message,
                    "",
                    details,
                    {},
                    self.request.user,
                    branch,
                )
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)


class LoanApplicationSecurityView(viewsets.ModelViewSet):
    serializer_class = LoanApplicationSecuritySerializer
    queryset = LoanApplicationSecurity.objects.filter(deleted=False)
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = ["loan_application", "id", "description"]
    search_fields = ("loan_application", "description")
    ordering_fields = ["loan_application", "description"]

    def perform_create(self, serializer):
        serializer.save(
            loan_app_sec_added_by=self.request.user,
        )

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        security = LoanApplicationSecurity.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = LoanApplicationSecuritySerializer(security, read_only=True).data
        loan_security = serializer.save()
        new_details = LoanApplicationSecuritySerializer(
            loan_security, read_only=True
        ).data
        message = f"Updated Security. Loan: {loan_security.loan_application.loan_application_product.product_name}({loan_security.loan_application.loan_amount}) For {loan_security.loan_application.customer.name}"
        add_system_audit_trail(
            "loans",
            "update_loan_security",
            message,
            "",
            old_details,
            new_details,
            self.request.user,
            branch,
        )

    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            branchid = get_current_user(self.request, "organisation_branch_id", 1)
            branch = OrganisationBranch.objects.get(id=branchid)
            if instance.id:
                details = LoanApplicationSecuritySerializer(
                    instance, read_only=True
                ).data
                message = f"Removed Security. Loan: {instance.loan_application.loan_application_product.product_name}({instance.loan_application.loan_amount}) For {instance.loan_application.customer.name}"
                add_system_audit_trail(
                    "loans",
                    "delete_loan_security",
                    message,
                    "",
                    details,
                    {},
                    self.request.user,
                    branch,
                )
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)


class LoanApplicationApprovalView(viewsets.ModelViewSet):
    serializer_class = LoanApplicationApprovalSerializer
    queryset = LoanApplicationApproval.objects.filter(deleted=False)
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "loan_application",
        "id",
    ]
    search_fields = ("loan_application",)
    ordering_fields = [
        "loan_application",
    ]

    def perform_create(self, serializer):
        send_sms = self.request.data.get("send_sms")
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )
        organisation_branch = OrganisationBranch.objects.get(pk=organisation_branch_id)
        loan_approval = serializer.save(
            loan_approval_added_by=self.request.user,
        )

        if loan_approval:
            loan_approval_data = LoanApplicationApproval.objects.get(
                pk=loan_approval.id
            )
            loan_approval_data.grace_period_type = (
                loan_approval_data.loan_application.grace_period_type
            )
            loan_approval_data.save()

            loan_details = LoanApplication.objects.filter(
                id=loan_approval_data.loan_application.id
            ).first()
            if loan_details:
                loan_details.status = "approved"
                loan_details.save()

        if loan_approval and send_sms:
            f_amount = f"{float(loan_approval.loan_amount):,}"
            pdt_name = (
                loan_approval.loan_application.loan_application_product.product_name
            )
            sms_msg = (
                "Dear "
                + loan_approval.loan_application.customer.name.capitalize()
                + ", Your ("
                + pdt_name
                + " loan) Amount UGX: "
                + f_amount
                + " has been approved, its awaiting Disbursement\n"
                + (
                    organisation_branch.branch_organisation.short_name
                    if organisation_branch.branch_organisation.short_name
                    else ""
                )
            )
            data = {
                "sms_key": "loan_approval_sms",
                "customer": loan_approval.loan_application.customer,
                "user": self.request.user,
                "branch_id": organisation_branch_id,
                "sms_msg": sms_msg,
                "loan": loan_approval.loan_application,
            }
            send_customer_sms(data)

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        approval = LoanApplicationApproval.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = LoanApplicationApprovalSerializer(approval, read_only=True).data
        loan_approval = serializer.save()
        new_details = LoanApplicationApprovalSerializer(
            loan_approval, read_only=True
        ).data
        message = f"Updated Loan Approval: {loan_approval.loan_application.loan_application_product.product_name}({loan_approval.loan_amount}) For {loan_approval.loan_application.customer.name}"
        add_system_audit_trail(
            "loans",
            "update_loan_approval",
            message,
            "",
            old_details,
            new_details,
            self.request.user,
            branch,
        )


class LoanApplicationClientGurantorsView(viewsets.ModelViewSet):
    serializer_class = LoanGuarantorsSerializer
    queryset = LoanGuarantors.objects.filter(deleted=False)
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "loan_application",
        "id",
    ]
    search_fields = ("loan_application",)
    ordering_fields = [
        "loan_application",
    ]

    def perform_create(self, serializer):
        serializer.save(loan_guarantor_added_by=self.request.user)

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        request_data = self.request.data
        id = request_data.get("id", None)
        trans_type = request_data.get("trans_type", None)
        if trans_type == "delete" and id:
            guarantors = LoanGuarantors.objects.get(
                pk=self.kwargs.get("id", self.kwargs.get("pk"))
            )
            details = LoanGuarantorsSerializer(guarantors, read_only=True).data
            message = f"Removed Guarantor: {guarantors.customer.name} Loan: {guarantors.loan_application.loan_application_product.product_name}({guarantors.loan_application.loan_amount}) For {guarantors.loan_application.customer.name}"
            add_system_audit_trail(
                "loans",
                "delete_guarantor",
                message,
                "",
                details,
                {},
                self.request.user,
                branch,
            )
            LoanGuarantors.objects.filter(pk=id).update(deleted=True, deleted_at=timezone.now(), deleted_by=self.request.user)

    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            branchid = get_current_user(self.request, "organisation_branch_id", 1)
            branch = OrganisationBranch.objects.get(id=branchid)
            if instance.id:
                details = LoanGuarantorsSerializer(instance, read_only=True).data
                message = f"Removed Guarantor. Loan: {instance.loan_application.loan_application_product.product_name}({instance.loan_application.loan_amount}) For {instance.loan_application.customer.name}"
                add_system_audit_trail(
                    "loans",
                    "delete_guarantor",
                    message,
                    "",
                    details,
                    {},
                    self.request.user,
                    branch,
                )
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)


class OrganisationClientNonMembersView(viewsets.ModelViewSet):
    serializer_class = OrganisationClientNonMembersSerializer
    queryset = OrganisationClientNonMembers.objects.all()
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = (
        "client_name",
        "id",
        "customer",
    )
    search_fields = ("client_name", "customer__name")
    ordering_fields = [
        "client_name",
    ]

    def perform_create(self, serializer):
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )
        organisation_branch = OrganisationBranch.objects.get(pk=organisation_branch_id)
        serializer.save(
            organisation_non_member_added_by=self.request.user,
            organisation_branch=organisation_branch,
        )


class NonMemberLoanGuarantorsView(viewsets.ModelViewSet):
    serializer_class = NonMemberLoanGuarantorsSerializer
    queryset = NonMemberLoanGuarantors.objects.all()
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "organisation_client_non_member",
        "loan_application",
    ]
    search_fields = ("loan_application",)
    ordering_fields = [
        "loan_application",
    ]

    def perform_create(self, serializer):
        serializer.save(organ_non_member_guarantor_added_by=self.request.user)

    def perform_update(self, serializer):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        guarantors = NonMemberLoanGuarantors.objects.get(
            pk=self.kwargs.get("id", self.kwargs.get("pk"))
        )
        old_details = NonMemberLoanGuarantorsSerializer(guarantors, read_only=True).data
        loan_guarantors = serializer.save()
        new_details = NonMemberLoanGuarantorsSerializer(
            loan_guarantors, read_only=True
        ).data
        message = f"Update Non Member Guarantor. Loan: {guarantors.loan_application.loan_application_product.product_name}({guarantors.loan_application.loan_amount}) For {guarantors.loan_application.customer.name}"
        add_system_audit_trail(
            "loans",
            "update_loan_non_member_guarantor",
            message,
            "",
            old_details,
            new_details,
            self.request.user,
            branch,
        )

    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            branchid = get_current_user(self.request, "organisation_branch_id", 1)
            branch = OrganisationBranch.objects.get(id=branchid)
            if instance.id:
                guarantor = instance.organisation_client_non_member
                details = OrganisationClientNonMembersSerializer(
                    guarantor, read_only=True
                ).data
                message = f"Removed Non Member Guarantor. Loan: {instance.loan_application.loan_application_product.product_name}({instance.loan_application.loan_amount}) For {instance.loan_application.customer.name}"
                add_system_audit_trail(
                    "loans",
                    "delete_guarantor",
                    message,
                    "",
                    details,
                    {},
                    self.request.user,
                    branch,
                )
                instance.delete()
        except Http404:
            pass
        return Response(status=status.HTTP_204_NO_CONTENT)


class LoanDisbursementActiveAccountsView(APIView):

    def get(self, request, format=None):
        branch_id = get_current_user(self.request, "organisation_branch_id", None)

        customer_id = request.GET.get("customer_id", None)
        loan_id = request.GET.get("id", None)
        credit_group_account = request.GET.get("is_credit_group_account", False)

        response = {}
        accounts = []
        bank_accounts = []
        cash_accounts = []

        print("*************************************************************")

        if loan_id:
            loan_details = LoanApplication.objects.get(pk=loan_id)
            if loan_details.loan_group and credit_group_account == "True":
                customer_id = loan_details.loan_group.id

        if customer_id:
            all_accounts = SavingAccount.objects.filter(
                account_customer__id=customer_id, status="active", deleted=False
            ).all()
            for account in all_accounts:
                balance = get_account_balance(account.id)
                accounts.append(
                    {
                        "id": account.account_product.accounts_chart.id,
                        "account_code": account.account_no,
                        "account_name": account.account_product.accounts_chart.account_name,
                        "account_id": account.id,
                        "balance": balance['balance_raw'] if balance else 0,
                    }
                )

        all_cash_accounts = CashAccounts.objects.filter(
            teller=request.user, status="active"
        )
        for cash_account in all_cash_accounts:
            cash_accounts.append(
                {
                    "id": cash_account.chart.id,
                    "account_name": cash_account.chart.account_name,
                    "account_code": cash_account.chart.account_code,
                    "account_id": cash_account.id,
                }
            )

        all_bank_accounts = BankAccounts.objects.filter(
            branch=branch_id, status="active"
        )
        for bank_account in all_bank_accounts:
            bank_accounts.append(
                {
                    "id": bank_account.chart.id,
                    "account_name": bank_account.chart.account_name,
                    "account_code": bank_account.chart.account_code,
                    "account_number": bank_account.account_number,
                    "bank_name": bank_account.bank_name,
                    "account_id": bank_account.id,
                }
            )

        response["accounts"] = accounts
        response["bank_accounts"] = bank_accounts
        response["cash_accounts"] = cash_accounts
        return Response(response, status=status.HTTP_200_OK)


class LoanDisbursementView(APIView):

    def post(self, request, format=None):
        amount = self.request.data.get("loan_amount")
        disburse_method = self.request.data.get("disburse_method")
        selected_account = self.request.data.get("account")
        send_sms = self.request.data.get("send_sms")
        voucher_no = self.request.data.get("voucher_no")
        loan_disbursement_date = self.request.data.get("loan_disbursement_date")
        loan_start_date = self.request.data.get("loan_start_date")
        customer_id = self.request.data.get("customer_id")
        loan_application_id = self.request.data.get("loan_application_id")
        account_id = self.request.data.get("account_id")

        loan_details = {
            "amount": amount,
            "disburse_method": disburse_method,
            "selected_account": selected_account,
            "send_sms": send_sms,
            "voucher_no": voucher_no,
            "loan_disbursement_date": loan_disbursement_date,
            "loan_start_date": loan_start_date,
            "customer_id": customer_id,
            "loan_application_id": loan_application_id,
            "account_id": account_id,
            "apply_charges": True,
        }

        return process_loan_disbursement(request, loan_details)


class EditLoanDisbursementView(APIView):
    def post(self, request, format=None):
        branchid = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branchid)
        results = process_edit_loan_disbursement(self.request)
        return results

    def generate_audit_log(self, request):
        audit_list = []
        loan_application_id = request.data.get("loan_application_id")
        loan_application = LoanApplication.objects.get(pk=loan_application_id)
        loan_disbursement = LoanApplicationDisbursement.objects.filter(
            loan_application=loan_application
        ).first()

        if loan_disbursement:
            old_details = LoanApplicationDisbursementSerializer(
                loan_disbursement, read_only=True
            ).data
            loan_main_payments = LoanMainTransactions.objects.filter(
                deleted=False, loan_application=loan_application
            )
            if loan_main_payments:
                for loan_main_payment in loan_main_payments:
                    if loan_main_payment.system_transaction:
                        details = SystemTransactionsSerializer(
                            loan_main_payment.system_transaction, read_only=True
                        ).data
                        message = f'Deleted {get_loan_payment_type_desc(loan_main_payment.transaction_type)}. Loan: {loan_disbursement.loan_application.loan_application_product.product_name}({old_details["loan_amount"]}) For {loan_disbursement.loan_application.customer.name}'
                        audit_list.append(
                            {"type": "payment", "message": message, "data": details}
                        )
            disburse_details = LoanApplicationDisbursementSerializer(
                LoanApplicationDisbursement.objects.filter(
                    loan_application=loan_application
                ).first(),
                read_only=True,
            ).data
            message = f'Update Loan Disbursement: {loan_disbursement.loan_application.loan_application_product.product_name}({disburse_details["loan_amount"]}) For {loan_disbursement.loan_application.customer.name}'
            audit_list.append(
                {"type": "disbursement", "message": message, "data": disburse_details}
            )
        return audit_list


class GenerateLoanScheduleView(APIView):
    def get(self, request, format=None):
        response = []
        loan_id = request.GET.get("loan_id", None)
        if loan_id:
            loan_details = LoanApplication.objects.get(pk=loan_id)
            if (
                loan_details.status == "disbursed"
                or loan_details.status == "cleared_off"
                or loan_details.status == "written_off"
            ):
                # get loan schedule from db
                loan_repayment_schedules = LoanRepaymentSchedule.objects.filter(
                    loan_application=loan_details, deleted=False, status="active"
                ).order_by("id")
                serializer = LoanRepaymentScheduleSerializer(
                    loan_repayment_schedules, many=True
                )
                response = serializer.data

            else:
                response = generate_loan_schedules(request, loan_id)
        return Response({"count": len(response), "results": response})

    def post(self, request, format=None):
        data = self.request.data if self.request.data else None
        response = generate_loan_schedules(request, loan_data=data)
        return Response({"count": len(response), "results": response})


class LoanScheduleWithPaymentsView(APIView):
    def get(self, request, format=None):
        response = []
        loan_id = request.GET.get("loan_id", None)
        if loan_id:
            response = loan_schedules_with_payments(loan_id)
        return Response(
            {
                "count": len(response["loan_schedules"]),
                "results": response["loan_schedules"],
                "schedule_due": response["schedule_due"],
                "next_schedule": response["next_schedule"],
                "reschedule": response["reschedule"],
            }
        )


class LoanSchedulePaymentView(APIView):
    def post(self, request, format=None):
        loan_id = self.request.data.get("loan_application_id", None)
        branch_id = get_current_user(self.request, "organisation_branch_id", None)
        organisation_branch = OrganisationBranch.objects.get(pk=branch_id)

        if loan_id:
            loan_application = LoanApplication.objects.get(pk=loan_id)
            if not loan_application:
                return Response(
                    {"message": "No loan application found"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            amount_paid = (
                float(self.request.data.get("amount_paid"))
                if self.request.data.get("amount_paid")
                else 0
            )
            principal_paid = (
                float(self.request.data.get("principal_paid"))
                if self.request.data.get("principal_paid")
                else 0
            )
            int_paid = (
                float(self.request.data.get("int_paid"))
                if self.request.data.get("int_paid")
                else 0
            )
            penalty_paid = (
                float(self.request.data.get("penalty_paid"))
                if self.request.data.get("penalty_paid")
                else 0
            )
            payment_method = self.request.data.get("payment_method")
            account = self.request.data.get("account")
            account_id = self.request.data.get("account_id")
            date_added = self.request.data.get("date_added")
            voucher_no = self.request.data.get("voucher_no")
            cheque = self.request.data.get("cheque")
            send_sms = self.request.data.get("send_sms")

            if not payment_method:
                return Response(
                    {"message": "No loan payment method provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )
            if not account:
                return Response(
                    {"message": "No account provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )
            if not date_added:
                return Response(
                    {"message": "No date provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if (
                amount_paid == 0
                and principal_paid == 0
                and int_paid == 0
                and penalty_paid == 0
            ):
                return Response(
                    {"message": "Nothing being paid"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if payment_method == "offset" and not account_id:
                return Response(
                    {"message": "No account provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if payment_method == "offset":
                savings_account = SavingAccount.objects.get(pk=account_id)
                if not savings_account:
                    return Response(
                        {"message": "No account provided"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    )

            # verify payment amounts against balances
            principal_bal, interest_bal, penalty_bal, written_off_amount = loan_balance(
                loan_id
            )
            if round(principal_paid, 2) > round(principal_bal, 2):
                message = "Principal Amount: Field has an amount that exceeds the principal balance of the loan"
                return Response(
                    {"message": message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            if round(int_paid, 2) > round(interest_bal, 2):
                message = "Interest Amount: Field has an amount that exceeds the interest balance of the loan"
                return Response(
                    {"message": message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            if round(penalty_paid, 2) > round(penalty_bal, 2):
                message = "Accumulated Penalty: Field has an amount that exceeds the penalty balance of the loan"
                return Response(
                    {"message": message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            selected_account = OrganisationSubAccount.objects.get(pk=account)
            if not selected_account:
                return Response(
                    {"message": "account not found"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if payment_method == "offset":
                # check account balance
                account_balance = get_account_balance(account_id)["balance_raw"]
                if loan_application.loan_group:
                    account_balance = get_group_memebr_account_balance(
                        SavingAccount.objects.get(pk=account_id),
                        loan_application.customer,
                    )
                if account_balance < amount_paid:
                    return Response(
                        {"message": "Insufficient balance on account"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    )

            payment_details = {
                "amount_paid": amount_paid,
                "principal_paid": principal_paid,
                "int_paid": int_paid,
                "penalty_paid": penalty_paid,
                "payment_method": payment_method,
                "account": account,
                "date_added": date_added,
                "voucher_no": voucher_no,
                "cheque": cheque,
                "loan_id": loan_id,
                "account_id": account_id,
            }
            payment = process_loan_payment(payment_details, request)

            if payment:
                f_amount = f"{float(principal_paid + int_paid + penalty_paid):,}"
                pdt_name = loan_application.loan_application_product.product_name
                sms_msg = (
                    "Dear "
                    + loan_application.customer.name.capitalize()
                    + ", Loan Payment: ("
                    + pdt_name
                    + " loan) Amount UGX: "
                    + f_amount
                    + "\n"
                    + (
                        organisation_branch.branch_organisation.short_name
                        if organisation_branch.branch_organisation.short_name
                        else ""
                    )
                )

                # Auto clear off loans
                loan_application = LoanApplication.objects.get(pk=loan_id)
                loan_details = LoanApplicationSerializer(loan_application).data
                remaing_balance = loan_details["loan_balance"]["total_with_penalty"]

                if (
                    float(remaing_balance) < 1
                    and loan_application.loan_application_product.auto_clear_off
                ):
                    loan_application.status = "cleared_off"
                    loan_application.save()
                    release_with_held_shares_savings(loan_application)

                if payment_method == "offset":
                    savings_account = SavingAccount.objects.filter(
                        account_product__accounts_chart=selected_account
                    ).first()
                    sms_msg = (
                        "Dear "
                        + loan_application.customer.name.capitalize()
                        + ", Loan Payment: ("
                        + pdt_name
                        + " loan) Amount UGX: "
                        + f_amount
                        + " on A/C:"
                        + savings_account.account_no
                        + ". Thanks for saving with "
                        + (
                            organisation_branch.branch_organisation.short_name
                            if organisation_branch.branch_organisation.short_name
                            else ""
                        )
                    )

                if send_sms:
                    data = {
                        "sms_key": "loan_payment_sms",
                        "customer": loan_application.customer,
                        "user": self.request.user,
                        "branch_id": branch_id,
                        "sms_msg": sms_msg,
                        "loan": loan_application,
                    }
                    send_customer_sms(data)

                return Response(
                    {
                        "message": "payment made successfully",
                        "results": self.request.data,
                    },
                    status=status.HTTP_200_OK,
                )

            return Response(
                {"message": "Error while making payment"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )
        return Response(
            {"message": "Faild: No loan application"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )


class LoanRecoveryView(APIView):
    serializer_class = LoanRecoverySerializer

    def post(self, request, format=None):
        request_data = self.request.data
        loan_id = self.request.data.get("loan_application_id", None)
        branch_id = get_current_user(self.request, "organisation_branch_id", None)
        organisation_branch = OrganisationBranch.objects.get(pk=branch_id)

        if loan_id:
            loan_application = LoanApplication.objects.get(pk=loan_id)
            if not loan_application:
                return Response(
                    {"message": "No loan application found"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            recovered_ammount = request_data.get("recovered_ammount", 0)
            payment_method = self.request.data.get("payment_method")
            account = self.request.data.get("account", None)
            account_id = self.request.data.get("account_id")
            recovery_date = self.request.data.get("recovery_date")
            voucher_no = self.request.data.get("voucher_no")
            income_account = self.request.data.get("income_account")

            if not payment_method:
                return Response(
                    {"message": "No payment method provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if not account:
                return Response(
                    {"message": "No account provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if not recovery_date:
                return Response(
                    {"message": "No date provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if recovered_ammount == 0:
                return Response(
                    {"message": "Nothing being paid"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if payment_method == "offset" and not account_id:
                return Response(
                    {"message": "No account provided"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )
            if not income_account:
                return Response(
                    {"message": "No Income Account Selected"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            credit_account = OrganisationSubAccount.objects.get(pk=income_account)
            if not credit_account:
                return Response(
                    {"message": "No Income Account Selected"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            selected_account = OrganisationSubAccount.objects.get(pk=account)
            if not selected_account:
                return Response(
                    {"message": "account not found"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

            if payment_method == "offset":
                savings_account = SavingAccount.objects.get(pk=account_id)
                if not savings_account:
                    return Response(
                        {"message": "No account provided"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    )
            if payment_method == "offset":
                # check account balance
                account_balance = get_account_balance(account_id)["balance_raw"]
                if loan_application.loan_group:
                    account_balance = get_group_memebr_account_balance(
                        SavingAccount.objects.get(pk=account_id),
                        loan_application.customer,
                    )
                if account_balance < float(recovered_ammount):
                    return Response(
                        {"message": "Insufficient balance on account"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    )

            loan_recovery_details = {
                "recovered_ammount": recovered_ammount,
                "payment_method": payment_method,
                "debit_account": selected_account,
                "recovery_date": recovery_date,
                "voucher_no": voucher_no,
                "credit_account": credit_account,
                "loan_id": loan_id,
                "account_id": account_id,
            }
            payment = process_loan_recovery(loan_recovery_details, request)

            if payment:
                f_amount = f"{float(recovered_ammount):,}"
                pdt_name = loan_application.loan_application_product.product_name
                sms_msg = (
                    "Dear "
                    + loan_application.customer.name.capitalize()
                    + ", Loan Payment: ("
                    + pdt_name
                    + " loan) Amount UGX: "
                    + f_amount
                    + "\n"
                    + (
                        organisation_branch.branch_organisation.short_name
                        if organisation_branch.branch_organisation.short_name
                        else ""
                    )
                )

                if payment_method == "offset":
                    savings_account = SavingAccount.objects.filter(
                        account_product__accounts_chart=selected_account
                    ).first()
                    sms_msg = (
                        "Dear "
                        + loan_application.customer.name.capitalize()
                        + ", Loan Payment: ("
                        + pdt_name
                        + " loan) Amount UGX: "
                        + f_amount
                        + " on A/C:"
                        + savings_account.account_no
                        + ". Thanks for saving with "
                        + (
                            organisation_branch.branch_organisation.short_name
                            if organisation_branch.branch_organisation.short_name
                            else ""
                        )
                    )
                data = {
                    "sms_key": "loan_payment_sms",
                    "customer": loan_application.customer,
                    "user": self.request.user,
                    "branch_id": branch_id,
                    "sms_msg": sms_msg,
                    "loan": loan_application,
                }
                send_customer_sms(data)

                return Response(
                    {"message": "Recovery payment made successfully"},
                    status=status.HTTP_200_OK,
                )
            return Response(
                {"message": "Error while making Recovery payment"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )
        return Response(
            {"message": "Faild: No loan application"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )


class LoanWriteOffView(APIView):
    def get(self, request):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        branch_id = get_current_user(self.request, "organisation_branch_id", None)
        as_at = (datetime.today()).strftime("%Y-%m-%d")
        search = self.request.GET.get("search", None)
        product_id = self.request.query_params.get("product_id", None)
        officer_id = self.request.query_params.get("officer_id", None)
        loan_filter = {
            "is_deleted": False,
            "status": "disbursed",
            "organisation_id": organisation_id,
            "branch_id": branch_id,
        }
        extra_sql_filters = " AND write_off_days > 0"

        if product_id:
            loan_filter["loan_product_id"] = product_id

        if officer_id:
            loan_filter["loan_officer_id"] = officer_id

        if search:
            extra_sql_filters = (
                " AND write_off_days > 0 AND (name LIKE %"
                + search
                + "% OR member_number LIKE "
                + search
                + "% OR old_member_number  LIKE "
                + search
                + "% )"
            )
        loans = get_list_to_writeoff(loan_filter, as_at, extra_sql_filters)
        return Response({"count": len(loans), "results": loans})


class LoanWrittenOffView(viewsets.ModelViewSet):
    serializer_class = LoanWrittenOffSerializer

    def get_queryset(self):
        filter_query = {"loan_application__status": "written_off", "deleted": False}
        loan_id = self.request.GET.get("loan_id", None)
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )
        if loan_id:
            filter_query["loan_application__id"] = loan_id
        if organisation_branch_id:
            filter_query["loan_application__organisation_branch__id"] = (
                organisation_branch_id
            )
        return LoanWrittenOff.objects.filter(**filter_query)

    def perform_create(self, serializer):
        # Loan Write off.
        request_data = self.request.data
        organisation_id = get_current_user(self.request, "organisation_id", 1)
        write_off_chart = get_write_off_account(
            self.request, request_data.get("loan_application")
        )
        loan_application = LoanApplication.objects.get(
            pk=request_data.get("loan_application")
        )

        amount = request_data.get("loan_writeoff_ammount")
        write_off_date = self.request.data.get("write_off_date")
        description = self.request.data.get("description")

        if loan_application:

            if write_off_chart:

                # Register loss due to write off.
                heading = (
                    "Loan Write Off: ("
                    + loan_application.loan_application_product.product_name
                    + ") for customer "
                    + loan_application.customer.name
                )
                payment_method = "setllement"
                loan_account = loan_application.loan_application_product.chart.id
                transaction = None
                # Generate reference number
                reference_no = generate_reference_no(
                    write_off_chart.account_line, organisation_id, "ln-w"
                )

                transaction = SystemTransactions.objects.create(
                    amount=amount,
                    heading=heading,
                    reference_no=reference_no,
                    record_date=write_off_date,
                    payment_method=payment_method,
                    voucher_no="",
                    debit_chart_id=write_off_chart.id,
                    credit_chart_id=loan_account,
                    branch=loan_application.organisation_branch,
                    added_by=self.request.user,
                )

                if not transaction:
                    return Response(
                        {"message": "Transaction failed"}, status=status.HTTP_200_OK
                    )

                loan_application.status = "written_off"
                loan_application.save()

                serializer.save(
                    loan_writeoff_added_by=self.request.user,
                    loan_system_transaction=transaction,
                    loan_writeoff_ammount=amount,
                    write_off_date=write_off_date,
                    description=description,
                )


class LoanInterestWaiveredView(viewsets.ModelViewSet):
    serializer_class = LoanInterestWaiveredSerializer

    def perform_create(self, serializer):
        # Loan Interest Waiver.
        request_data = self.request.data
        organisation_id = get_current_user(self.request, "organisation_id", 1)
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )
        loan_application = LoanApplication.objects.get(
            pk=request_data.get("loan_application")
        )
        amount = request_data.get("amount")
        date_added = self.request.data.get("date_added")
        comment = request_data.get("comment")

        organisation = Organisation.objects.get(
            pk=get_current_user(self.request, "organisation_id", None)
        )
        accounting_type = OrganisationSetting.objects.filter(
            setting_key="account_type",
            setting_value="accrual",
            org_setting=organisation,
        ).first()
        if accounting_type:
            interest_waivered_chart = get_interest_waiver_account(
                self.request, request_data.get("loan_application")
            )
            if loan_application:
                if interest_waivered_chart:
                    # Register loss due to interest waiver.
                    heading = (
                        "Loan Interest Waivered: ("
                        + loan_application.loan_application_product.product_name
                        + ") for customer "
                        + loan_application.customer.name
                    )
                    payment_method = "non_cash"
                    loan_account = loan_application.loan_application_product.chart.id
                    # Generate reference number
                    reference_no = generate_reference_no(
                        interest_waivered_chart.account_line, organisation_id, "ln-w"
                    )
                    transaction = SystemTransactions.objects.create(
                        amount=amount,
                        heading=heading,
                        reference_no=reference_no,
                        payment_method=payment_method,
                        voucher_no="",
                        debit_chart_id=interest_waivered_chart.id,
                        credit_chart_id=loan_account,
                        branch_id=organisation_branch_id,
                        added_by=self.request.user,
                    )
                    if not transaction:
                        return Response(
                            {"message": "Transaction failed"}, status=status.HTTP_200_OK
                        )
                    data = {
                        "heading": heading,
                        "amount": amount,
                        "payment_method": payment_method,
                        "loan_application": loan_application,
                        "ref_no": reference_no,
                        "voucher_no": "",
                        "transaction_type": "LoanInterestWaivered",
                        "loan_main_transaction_added_by": self.request.user,
                        "payment_date": date_added,
                        "system_transaction": transaction,
                    }
                    loan_main_transaction = LoanMainTransactions.objects.create(**data)
                    if loan_main_transaction:
                        # Save Interest Waiver
                        serializer.save(
                            loan_interest_waivered_added_by=self.request.user,
                            loan_main_transaction=loan_main_transaction,
                            date_added=date_added,
                            last_updated=date_added,
                        )

                        # Auto clear off loans
                        loan_application = LoanApplication.objects.get(
                            pk=loan_application.id
                        )
                        loan_details = LoanApplicationSerializer(loan_application).data
                        remaing_balance = loan_details["loan_balance"][
                            "total_with_penalty"
                        ]

                        if (
                            float(remaing_balance) < 1
                            and loan_application.loan_application_product.auto_clear_off
                        ):
                            loan_application.status = "cleared_off"
                            loan_application.save()
                            release_with_held_shares_savings(loan_application)
        else:
            if loan_application:
                # Save Penalty Waiver
                loan_repayment_schedules = LoanRepaymentSchedule.objects.filter(
                    deleted=False, loan_application=loan_application, status="active"
                ).order_by("id")
                if not loan_repayment_schedules:
                    return False

                # get waived off installements
                schedules_to_waive_off = []
                interest_waived = float(amount)
                interest_waivered_bal = 0
                for loan_repayment_schedule in loan_repayment_schedules:
                    loan_payments_totals = LoanPayments.objects.filter(
                        loan_application=loan_application,
                        loan_repayment_schedule=loan_repayment_schedule,
                        payment_status="normal",
                    ).aggregate(total_int_paid=Sum("int_paid"))

                    is_paid_off = "false"
                    # total schedule waived Interest
                    schedule_interest_waived = LoanInterestWaivered.objects.filter(
                        loan_repayment_schedule=loan_repayment_schedule,
                        loan_application=loan_application,
                    ).aggregate(total_schedule_interest_waived=Sum("amount"))[
                        "total_schedule_interest_waived"
                    ]
                    total_schedule_interest_waived = (
                        schedule_interest_waived if schedule_interest_waived else 0
                    )

                    # get total interest paid, and comapare with waived, expected
                    total_int_paid = (
                        loan_payments_totals["total_int_paid"]
                        if loan_payments_totals["total_int_paid"] is not None
                        else 0
                    )

                    # spread interest waivered
                    total_schedule_interest_waived = (
                        total_schedule_interest_waived + interest_waivered_bal
                    )
                    _interest_waivered_bal = (
                        loan_repayment_schedule.interest_expected
                        - (total_schedule_interest_waived + total_int_paid)
                    )
                    if _interest_waivered_bal < 0:
                        interest_waivered_bal = interest_waivered_bal + abs(
                            _interest_waivered_bal
                        )
                        total_schedule_interest_waived = (
                            loan_repayment_schedule.interest_expected - total_int_paid
                        )

                    if float(loan_repayment_schedule.interest_expected) <= float(
                        total_int_paid + total_schedule_interest_waived
                    ):
                        is_paid_off = "true"

                    if is_paid_off == "false" and interest_waived > 0:
                        interest_balance = (
                            loan_repayment_schedule.interest_expected - total_int_paid
                        )
                        if float(interest_waived) == float(interest_balance):
                            schedules_to_waive_off.append(
                                {
                                    "schedule": loan_repayment_schedule.id,
                                    "amount": interest_waived,
                                }
                            )
                            interest_waived = 0
                            break
                        else:
                            waive_amount = (
                                interest_balance
                                if interest_waived > interest_balance
                                else interest_waived
                            )
                            schedules_to_waive_off.append(
                                {
                                    "schedule": loan_repayment_schedule.id,
                                    "amount": waive_amount,
                                }
                            )
                            if interest_waived - waive_amount > 0:
                                interest_waived = interest_waived - waive_amount
                            else:
                                break

                # waive off selected installemnets
                if len(schedules_to_waive_off) > 0:
                    count = 0
                    for schedule in schedules_to_waive_off:
                        loan_repayment_schedule = LoanRepaymentSchedule.objects.get(
                            pk=schedule["schedule"]
                        )
                        if count == 0:
                            serializer.save(
                                loan_repayment_schedule=loan_repayment_schedule,
                                loan_interest_waivered_added_by=self.request.user,
                                date_added=date_added,
                                amount=schedule["amount"],
                                last_updated=date_added,
                            )
                        else:
                            LoanInterestWaivered.objects.create(
                                loan_application=loan_application,
                                loan_interest_waivered_added_by=self.request.user,
                                loan_repayment_schedule=loan_repayment_schedule,
                                amount=schedule["amount"],
                                comment=comment,
                                date_added=date_added,
                                last_updated=date_added,
                            )
                        count += 1

                # Auto clear off loans
                loan_application = LoanApplication.objects.get(pk=loan_application.id)
                loan_details = LoanApplicationSerializer(loan_application).data
                remaing_balance = loan_details["loan_balance"]["total_with_penalty"]

                if (
                    float(remaing_balance) < 1
                    and loan_application.loan_application_product.auto_clear_off
                ):
                    loan_application.status = "cleared_off"
                    loan_application.save()
                    release_with_held_shares_savings(loan_application)


class LoanPenaltyWaiveredView(viewsets.ModelViewSet):
    serializer_class = LoanPenaltyWaiveredSerializer

    def perform_create(self, serializer):
        # Loan Penalty Waiver.
        request_data = self.request.data
        organisation_id = get_current_user(self.request, "organisation_id", 1)
        organisation_branch_id = get_current_user(
            self.request, "organisation_branch_id", None
        )
        loan_application = LoanApplication.objects.get(
            pk=request_data.get("loan_application")
        )
        amount = request_data.get("amount")
        date_added = self.request.data.get("date_added")

        organisation = Organisation.objects.get(
            pk=get_current_user(self.request, "organisation_id", None)
        )
        accounting_type = OrganisationSetting.objects.filter(
            setting_key="account_type",
            setting_value="accrual",
            org_setting=organisation,
        ).first()
        if accounting_type:
            penalty_waivered_chart = get_write_off_account(
                self.request, request_data.get("loan_application")
            )
            if loan_application:
                if penalty_waivered_chart:
                    # Register loss due to Penalty Waiver.
                    heading = (
                        "Loan Penalty Waivered: ("
                        + loan_application.loan_application_product.product_name
                        + ") for customer "
                        + loan_application.customer.name
                    )
                    payment_method = "non_cash"
                    loan_account = loan_application.loan_application_product.chart.id
                    # Generate reference number
                    reference_no = generate_reference_no(
                        penalty_waivered_chart.account_line, organisation_id, "ln-w"
                    )
                    transaction = SystemTransactions.objects.create(
                        amount=amount,
                        heading=heading,
                        reference_no=reference_no,
                        payment_method=payment_method,
                        voucher_no="",
                        debit_chart_id=penalty_waivered_chart.id,
                        credit_chart_id=loan_account,
                        branch_id=organisation_branch_id,
                        added_by=self.request.user,
                    )
                    if not transaction:
                        return Response(
                            {"message": "Transaction failed"}, status=status.HTTP_200_OK
                        )
                    data = {
                        "heading": heading,
                        "amount": amount,
                        "payment_method": payment_method,
                        "loan_application": loan_application,
                        "ref_no": reference_no,
                        "voucher_no": "",
                        "transaction_type": "LoanPenaltyWaivered",
                        "loan_main_transaction_added_by": self.request.user,
                        "payment_date": date_added,
                        "system_transaction": transaction,
                    }
                    loan_main_transaction = LoanMainTransactions.objects.create(**data)
                    if loan_main_transaction:
                        # Save Penalty Waiver for accrual
                        serializer.save(
                            loan_penalty_waivered_added_by=self.request.user,
                            loan_main_transaction=loan_main_transaction,
                            date_added=date_added,
                            last_updated=date_added,
                        )

                        # Auto clear off loans
                        loan_application = LoanApplication.objects.get(
                            pk=loan_application.id
                        )
                        loan_details = LoanApplicationSerializer(loan_application).data
                        remaing_balance = loan_details["loan_balance"][
                            "total_with_penalty"
                        ]

                        if (
                            float(remaing_balance) < 1
                            and loan_application.loan_application_product.auto_clear_off
                        ):
                            loan_application.status = "cleared_off"
                            loan_application.save()
                            release_with_held_shares_savings(loan_application)

                    if not loan_main_transaction:
                        return Response(
                            {"message": "Faild double entry posting"},
                            status=status.HTTP_200_OK,
                        )
        else:
            if loan_application:
                # Save Penalty Waiver
                serializer.save(
                    loan_penalty_waivered_added_by=self.request.user,
                    date_added=date_added,
                    last_updated=date_added,
                )

                # Auto clear off loans
                loan_application = LoanApplication.objects.get(pk=loan_application.id)
                loan_details = LoanApplicationSerializer(loan_application).data
                remaing_balance = loan_details["loan_balance"]["total_with_penalty"]

                if (
                    float(remaing_balance) < 1
                    and loan_application.loan_application_product.auto_clear_off
                ):
                    loan_application.status = "cleared_off"
                    loan_application.save()
                    release_with_held_shares_savings(loan_application)


class LoanApplicationsViewView(viewsets.ModelViewSet):
    serializer_class = LoanApplicationsViewSerializer
    queryset = LoanApplicationsView.objects.filter(is_deleted=False)
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "id",
    ]
    search_fields = (
        "name",
        "member_number",
        "old_member_number",
    )
    ordering_fields = ["id", "name", "member_number"]

    def get_queryset(self):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        return LoanApplicationsView.objects.filter(
            is_deleted=False, organisation_id=organisation_id
        ).order_by("-id")


class RescheduleLoansView(APIView):
    def post(self, request, format=None):
        loan_details = request.data
        if not loan_details:
            return Response({"message": "rescheduling data missing"})

        organisation_branch_id = get_current_user(
            request, "organisation_branch_id", None
        )
        if not organisation_branch_id:
            return Response({"message": "organisation branch missing"})

        loan_details["organisation_branch"] = organisation_branch_id
        loan_application_id = loan_details["loan_application"]
        if not loan_application_id:
            return Response({"message": "rescheduling application Id"})

        principal_bal, interest_bal, penalty_bal, written_off_amount = loan_balance(
            loan_application_id
        )
        serializer = RescheduledLoansSerializer(data=loan_details)
        if not serializer.is_valid():
            return Response({"message": "verification failed"})

        new_schedule = serializer.save(loan_reschedule_added_by=request.user)

        # update interest expect for rescheduled loan
        rescheduled_loan_details = RescheduledLoans.objects.get(pk=new_schedule.id)
        rescheduled_loan_details.principal_amount = principal_bal
        rescheduled_loan_details.save()

        # update payment details
        interest, principal, total = loan_payment(
            request, rescheduled_loan_details.loan_application.id, "rescheduling", None
        )
        rescheduled_loan_details.interest_expected = interest
        rescheduled_loan_details.total_payment = total
        rescheduled_loan_details.save()

        data = {"status": "rescheduled"}
        LoanRepaymentSchedule.objects.filter(
            loan_application=rescheduled_loan_details.loan_application, status="active"
        ).update(**data)

        # generate new loan schedules
        new_loan_schedules = generate_loan_schedules(
            request, rescheduled_loan_details.loan_application.id, "rescheduling", None
        )
        if len(new_loan_schedules) > 0:
            count = 0

            # increment if already rescheduled
            loan_current_schedules = (
                LoanRepaymentSchedule.objects.filter(
                    loan_application=rescheduled_loan_details.loan_application
                )
                .order_by("-payment_number")
                .first()
            )
            if loan_current_schedules:
                count = loan_current_schedules.payment_number + 1

            for new_loan_schedule in new_loan_schedules:
                # save new loan schedules
                schedule_data = {
                    "principal_expected": new_loan_schedule["principal_expected"],
                    "loan_application": rescheduled_loan_details.loan_application,
                    "interest_expected": new_loan_schedule["interest_expected"],
                    "total_payment": new_loan_schedule["total_payment"],
                    "ending_balance": new_loan_schedule["ending_balance"],
                    "starting_balance": new_loan_schedule["starting_balance"],
                    "payment_number": count,
                    "expected_date": new_loan_schedule["expected_date"],
                    "loan_schedule_added_by": request.user,
                    "reschedule_id": new_schedule.id,
                }
                LoanRepaymentSchedule.objects.create(**schedule_data)
                count = count + 1

        return Response({"message": "Loan rescheduled successfully"})


class LoansTopUpView(APIView):
    def post(self, request, format=None):
        if not request.data:
            return Response(
                {"message": "topup data missing"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

        organisation_branch_id = get_current_user(
            request, "organisation_branch_id", None
        )
        if not organisation_branch_id:
            return Response(
                {"message": "organisation branch missing"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

        loan_details = request.data
        loan_details["organisation_branch"] = organisation_branch_id
        serializer = LoansTopUpSerializer(data=loan_details)
        if not serializer.is_valid():
            return Response(
                {"message": "verification failed"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

        new_loan_topup = serializer.save(loan_topup_added_by=request.user)
        if new_loan_topup:
            top_up_data = {
                "loan_topup_id": new_loan_topup.id,
                "organisation_branch_id": organisation_branch_id,
                "request": request,
            }
            response = process_loan_top_up(top_up_data)
            if response:
                return Response({"message": "Loan topup successfully"})

            LoanTopUp.objects.filter(id=new_loan_topup.id).update(deleted=True, deleted_at=timezone.now())
        return Response(
            {"message": "Failed to topup loan"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )


class LoanPenaltyView(viewsets.ModelViewSet):
    queryset = LoanPenalty.objects.filter(deleted=False).order_by("id")
    serializer_class = LoanPenaltySerializer
    filter_backends = (
        SearchFilter,
        OrderingFilter,
        DjangoFilterBackend,
    )
    filterset_fields = [
        "loan_application",
    ]
    search_fields = ("loan_application",)
    ordering_fields = [
        "loan_application",
    ]

    def perform_create(self, serializer):
        request_data = self.request.data
        date_added = request_data.get("date_added")
        if "T" in date_added:
            date_added = request_data.get("date_added").split("T")[0]

        current_time = datetime.now()
        loan_application = LoanApplication.objects.get(
            pk=request_data.get("loan_application")
        )
        penalty_date = make_aware(
            datetime.strptime(
                date_added + current_time.strftime(" %H:%M:%S"), "%Y-%m-%d %H:%M:%S"
            )
        )
        loan_schedule = LoanRepaymentSchedule.objects.filter(
            loan_application=loan_application,
            status="active",
            expected_date__lte=penalty_date,
        ).last()

        if not loan_schedule:
            loan_schedule = LoanRepaymentSchedule.objects.filter(
                loan_application=loan_application,
                status="active",
                expected_date__lte=current_time,
            ).last()

        if loan_schedule:
            loan_disbursement = LoanApplicationDisbursement.objects.filter(
                loan_application=loan_schedule.loan_application
            ).first()
            arrear_maturity_days = 0
            arrear_maturity_days += (
                arrears_per_term(
                    loan_disbursement.arrear_grace_period,
                    loan_disbursement.arrears_period_type,
                )
                if loan_disbursement.arrear_grace_period > 0
                else 0
            )
            loan_schedule_expected_date = (
                loan_schedule.expected_date
                + datetime_timedelta.timedelta(days=arrear_maturity_days)
            )
            penality_from_date = loan_schedule_expected_date
            penality_to_date = penality_from_date + datetime_timedelta.timedelta(days=1)

            if (penalty_date - penality_to_date).days > 0:
                penality_to_date = penalty_date

            arrear_days = (penalty_date - penality_to_date).days
            if arrear_days < 0:
                arrear_days = 0
            laon_schedule_payments = LoanPayments.objects.filter(
                loan_repayment_schedule=loan_schedule,
                loan_application=loan_application,
                date_added__lte=penalty_date,
                payment_status="normal",
            ).aggregate(
                total_princ_paid=Sum("princ_paid"), total_int_paid=Sum("int_paid")
            )
            laon_schedule_princ_payment = (
                laon_schedule_payments["total_princ_paid"]
                if laon_schedule_payments["total_princ_paid"]
                else 0
            )
            laon_schedule_int_payment = (
                laon_schedule_payments["total_int_paid"]
                if laon_schedule_payments["total_int_paid"]
                else 0
            )

            principal_balance = (
                loan_schedule.principal_expected - laon_schedule_princ_payment
            )
            interest_balance = (
                loan_schedule.interest_expected - laon_schedule_int_payment
            )
            penalty_rate = (
                loan_disbursement.penalty_rate
                if loan_disbursement.penalty_rate > 0
                else loan_application.loan_application_product.penalty_rate
            )
            penalty_period_type = (
                loan_disbursement.penalty_period_type
                if loan_disbursement.penalty_period_type
                else loan_application.loan_application_product.penalty_period_type
            )

            serializer.save(
                status="manual",
                loan_repayment_schedule=loan_schedule,
                date_added=penalty_date,
                date_from=penality_from_date,
                date_to=penality_to_date,
                principal_arrears=principal_balance,
                interest_arrears=interest_balance,
                penalty_rate=penalty_rate,
                penalty_period_type=penalty_period_type,
                arrear_days=arrear_days,
                loan_penality_added_by=self.request.user,
                expected_pay_date=loan_schedule_expected_date,
            )


class LoanSectorsView(viewsets.ModelViewSet):
    serializer_class = LoanSectorsSerializer
    queryset = LoanSectors.objects.filter(deleted=False)

    def get_queryset(self):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        organisation = Organisation.objects.get(pk=organisation_id)
        return LoanSectors.objects.filter(organisation=organisation)

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        organisation = Organisation.objects.get(pk=organisation_id)
        serializer.save(
            organisation=organisation, loan_sector_added_by=self.request.user
        )


class LoanSubSectorsView(viewsets.ModelViewSet):
    serializer_class = LoanSubSectorsSerializer
    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ['loan_sector']

    def get_queryset(self):
        return LoanSubSectors.objects.filter(deleted=False)

    def perform_create(self, serializer):
        serializer.save(loan_sub_sector_added_by=self.request.user)


class LoansReportFiltersView(APIView):
    def get(self, request, format=None):
        filter_type = request.GET.get("filter", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)
        filter_1 = []
        filter_2 = []
        if not filter_type or filter_type not in [
            "client_type_officer",
            "gender_officer",
            "officer_branch",
            "client_type_product",
            "product_officer",
            "gender_product",
            "product_branch",
            "gender_branch",
            "client_type_branch",
            "group_branch",
            "product_sector",
        ]:
            return Response({"message": "Missing filter type"})

        disbursed_loans = LoanApplication.objects.filter(
            deleted=False, organisation_branch__branch_organisation__id=organisation_id
        ).values_list("loan_officer__id", flat=True)
        if filter_type == "client_type_officer":
            # staffs
            user_officers = Staff.objects.filter(
                staff_organisation__id=organisation_id,
                id__in=disbursed_loans,
                is_active=True,
            )
            for user_officer in user_officers:
                filter_1.append({"id": user_officer.id, "name": user_officer.name})

            # client types
            client_types = CustomerType.objects.filter(organisation__id=organisation_id)
            for client_type in client_types:
                filter_2.append(
                    {"id": client_type.id, "name": client_type.customer_type}
                )

        if filter_type == "gender_officer":
            # staffs
            user_officers = Staff.objects.filter(
                staff_organisation__id=organisation_id,
                id__in=disbursed_loans,
                is_active=True,
            )
            for user_officer in user_officers:
                filter_1.append({"id": user_officer.id, "name": user_officer.name})

            # gender
            filter_2 = [
                {"id": "M", "name": "Male"},
                {"id": "F", "name": "Female"},
                {"id": "O", "name": "Other"},
            ]

        if filter_type == "officer_branch":
            # staffs
            user_officers = Staff.objects.filter(
                staff_organisation__id=organisation_id, is_active=True
            )
            for user_officer in user_officers:
                filter_1.append({"id": user_officer.id, "name": user_officer.name})

            # branches
            branches = OrganisationBranch.objects.filter(
                branch_organisation__id=organisation_id
            )
            for branch in branches:
                filter_2.append({"id": branch.id, "name": branch.name})

        if filter_type == "client_type_product":
            # profucts
            loan_products = LoanProduct.objects.filter(
                organisation__id=organisation_id, is_deleted=False
            )
            for loan_product in loan_products:
                filter_1.append(
                    {"id": loan_product.id, "name": loan_product.product_name}
                )

            # client types
            client_types = CustomerType.objects.filter(organisation__id=organisation_id)
            for client_type in client_types:
                filter_2.append(
                    {"id": client_type.id, "name": client_type.customer_type}
                )

        if filter_type == "product_officer":
            # staffs
            user_officers = Staff.objects.filter(
                staff_organisation__id=organisation_id,
                id__in=disbursed_loans,
                is_active=True,
            )
            for user_officer in user_officers:
                filter_1.append({"id": user_officer.id, "name": user_officer.name})

            # profucts
            loan_products = LoanProduct.objects.filter(
                organisation__id=organisation_id, is_deleted=False
            )
            for loan_product in loan_products:
                filter_2.append(
                    {"id": loan_product.id, "name": loan_product.product_name}
                )

        if filter_type == "gender_product":
            # profucts
            loan_products = LoanProduct.objects.filter(
                organisation__id=organisation_id, is_deleted=False
            )
            for loan_product in loan_products:
                filter_1.append(
                    {"id": loan_product.id, "name": loan_product.product_name}
                )

            # gender
            filter_2 = [
                {"id": "M", "name": "Male"},
                {"id": "F", "name": "Female"},
                {"id": "O", "name": "Other"},
            ]

        if filter_type == "product_branch":
            # profucts
            loan_products = LoanProduct.objects.filter(
                organisation__id=organisation_id, is_deleted=False
            )
            for loan_product in loan_products:
                filter_1.append(
                    {"id": loan_product.id, "name": loan_product.product_name}
                )

            # branches
            branches = OrganisationBranch.objects.filter(
                branch_organisation__id=organisation_id
            )
            for branch in branches:
                filter_2.append({"id": branch.id, "name": branch.name})

        if filter_type == "gender_branch":
            # branches
            branches = OrganisationBranch.objects.filter(
                branch_organisation__id=organisation_id
            )
            for branch in branches:
                filter_1.append({"id": branch.id, "name": branch.name})

            # gender
            filter_2 = [
                {"id": "M", "name": "Male"},
                {"id": "F", "name": "Female"},
                {"id": "O", "name": "Other"},
            ]

        if filter_type == "client_type_branch":
            # branches
            branches = OrganisationBranch.objects.filter(
                branch_organisation__id=organisation_id
            )
            for branch in branches:
                filter_1.append({"id": branch.id, "name": branch.name})

            # client types
            client_types = CustomerType.objects.filter(organisation__id=organisation_id)
            for client_type in client_types:
                filter_2.append(
                    {"id": client_type.id, "name": client_type.customer_type}
                )

        if filter_type == "group_branch":
            # branches
            branches = OrganisationBranch.objects.filter(
                branch_organisation__id=organisation_id
            )
            for branch in branches:
                filter_1.append({"id": branch.id, "name": branch.name})

            # groups
            groups = Customer.objects.filter(
                customer_branch__branch_organisation__id=organisation_id,
                branch_customer_type__has_members=True,
            )
            for group in groups:
                filter_2.append({"id": group.id, "name": group.name})
        if filter_type == "product_sector":
            # profucts
            loan_products = LoanProduct.objects.filter(
                organisation__id=organisation_id, is_deleted=False
            )
            for loan_product in loan_products:
                filter_1.append(
                    {"id": loan_product.id, "name": loan_product.product_name}
                )

            # sectors
            sectors = LoanSectors.objects.filter(organisation__id=organisation_id)
            for sector in sectors:
                filter_2.append({"id": sector.id, "name": sector.name})

        return Response({"filter_1": filter_1, "filter_2": filter_2})


class LinesOfCreditReportFiltersView(APIView):
    def get(self, request, format=None):
        filter_type = request.GET.get("filter", None)
        filter_1 = []
        filter_2 = []

        valid_filters = [
            "gender_agriculture",
            "value_chain_agriculture",
            # 'value_chain_node_agriculture'
        ]

        if not filter_type or filter_type not in valid_filters:
            return Response({"message": "Missing or invalid filter type"})

        # Gender filter options
        if filter_type == "gender_agriculture":
            filter_1 = [
                {"id": "M", "name": "Male"},
                {"id": "F", "name": "Female"},
                {"id": "O", "name": "Other"},
            ]

        # Value chain filter options
        if filter_type == "value_chain_agriculture":
            from loans.models import GreenFinanceValueChain

            chains = GreenFinanceValueChain.objects.all()
            for chain in chains:
                filter_1.append({"id": chain.id, "name": chain.name})

        # Value chain node filter options
        #     if filter_type == 'value_chain_node_agriculture':
        #        from loans.models import GreenFinanceValueChain
        #        from loans.models import GreenFinanceValueChainNode

        # # Get value chains → filter_1
        #        value_chains = GreenFinanceValueChain.objects.all()
        #        for chain in value_chains:
        #              filter_1.append({
        #                "id": chain.id,
        #                "name": chain.name
        #             })

        # # Get value chain nodes → filter_2
        #        nodes = GreenFinanceValueChainNode.objects.all()
        #        for node in nodes:
        #              filter_2.append({
        #                 "id": node.id,
        #                 "name": node.value_chain_node
        #             })

        return Response({"filter_1": filter_1, "filter_2": filter_2})


class LoansTrackingReportFiltersDataView(APIView):

    def post(self, request, format=None):
        filter_1 = self.request.data.get("filter_1")
        filter_2 = self.request.data.get("filter_2")
        report_date = self.request.data.get("report_date")
        report_filter = self.request.data.get("report_filter")

        if not report_filter or report_filter not in [
            "client_type_officer",
            "gender_officer",
            "officer_branch",
            "client_type_product",
            "product_officer",
            "gender_product",
            "product_branch",
            "gender_branch",
            "client_type_branch",
            "group_branch",
            "product_sector",
        ]:
            return Response({"results": [], "count": 0})

        if not validate_date(report_date):
            return Response({"results": [], "count": 0})

        if not isinstance(filter_1, list):
            return Response({"results": [], "count": 0})

        if not isinstance(filter_2, list):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": report_date,
        }

        return filter_loan_tracking_reports(report_filters)


class LoansDisbursementReportFiltersDataView(APIView):

    def post(self, request, format=None):
        filter_1 = self.request.data.get("filter_1")
        filter_2 = self.request.data.get("filter_2")
        start_date = self.request.data.get("start_date")
        end_date = self.request.data.get("end_date")
        report_filter = self.request.data.get("report_filter")
        include_fully_paid = self.request.data.get("include_fully_paid", False)

        if not report_filter or report_filter not in [
            "client_type_officer",
            "gender_officer",
            "officer_branch",
            "client_type_product",
            "product_officer",
            "gender_product",
            "product_branch",
            "gender_branch",
            "client_type_branch",
            "group_branch",
            "product_sector",
        ]:
            return Response({"message": "Missing filter type"})

        if not validate_date(start_date) or not validate_date(end_date):
            return Response({"results": [], "count": 0})

        if not isinstance(filter_1, list):
            return Response({"results": [], "count": 0})

        if not isinstance(filter_2, list):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start_date": start_date,
            "end_date": end_date,
            "include_fully_paid": include_fully_paid,
        }

        return filter_loan_disbursement_reports(report_filters)


class CustomerLoansView(viewsets.ModelViewSet):
    serializer_class = LoanApplicationsViewSerializer

    def get_queryset(self):
        customer_id = self.request.GET.get("id", None)
        status = self.request.GET.get("status", None)

        if status:
            status = status.split(",")

        if not status:
            status = ["disbursed"]

        return (
            LoanApplicationsView.objects.filter(
                customer_id=customer_id, is_deleted=False, status__in=status
            )
            .order_by("-id")
            .distinct("id")
        )


class LoansLedgerView(APIView):
    def get(self, request, format=None):
        loan_id = request.GET.get("id", None)
        response = {}

        loans_payments_data = []
        repayment_schedules_object = []

        # get days in arrears
        current = timezone.now()
        current = current.strftime("%Y-%m-%d")
        arrear_details = get_loan_arrears_details(
            loan_id=loan_id, as_at=current, payment_transaction_id=None
        )
        organisation_id = get_current_user(self.request, "organisation_id", None)

        loan_details = (
            LoanApplicationDisbursement.objects.filter(
                loan_application__id=loan_id, deleted=False
            )
            .order_by("-id")
            .first()
        )

        if loan_details:
            disbusersment_object = {
                "id": loan_details.id,
                "ref_no": "",
                "voucher": "",
                "arrear_days": "",
                "princ": loan_details.total_principal_expected,
                "int": loan_details.total_interest_expected,
                "total": loan_details.total_expected,
                "loan_disbursement_date": loan_details.loan_disbursement_date,
                "date_added": loan_details.date_added,
            }

            ext_loan_ledger = OrganisationSetting.objects.filter(
                org_setting__id=organisation_id, setting_key="ext_loan_ledger"
            ).first()
            if ext_loan_ledger:

                # schedules
                repayment_schedules = LoanRepaymentSchedule.objects.filter(
                    loan_application__id=loan_id, status="active", deleted=False
                ).order_by("id")
                for repayment_schedule in repayment_schedules:
                    repayment_schedules_object.append(
                        {
                            "id": repayment_schedule.id,
                            "transaction_type": "schedule_due",
                            "ref_no": "",
                            "voucher": "",
                            "princ": repayment_schedule.principal_expected,
                            "int": repayment_schedule.interest_expected,
                            "arrear_days": "",
                            "penalty": 0,
                            "total": repayment_schedule.total_payment,
                            "date_added": repayment_schedule.expected_date,
                            "payment_date": repayment_schedule.expected_date,
                        }
                    )

                # payments
                loan_paymenttransactions = (
                    LoanPaymentTransaction.objects.filter(
                        loan_application__id=loan_id, deleted=False
                    )
                    .all()
                    .order_by("id")
                )
                for loan_paymenttransaction in loan_paymenttransactions:
                    loan_payments = LoanPayments.objects.filter(
                        loan_payment_transaction=loan_paymenttransaction,
                        payment_status="normal",
                        deleted=False,
                    ).aggregate(
                        total_int_paid=Sum("int_paid"),
                        total_princ_paid=Sum("princ_paid"),
                        total_penalty_paid=Sum("penalty_paid"),
                    )
                    princ_paid = (
                        loan_payments["total_princ_paid"]
                        if loan_payments["total_princ_paid"]
                        else 0
                    )
                    int_paid = (
                        loan_payments["total_int_paid"]
                        if loan_payments["total_int_paid"]
                        else 0
                    )
                    penalty_paid = (
                        loan_payments["total_penalty_paid"]
                        if loan_payments["total_penalty_paid"]
                        else 0
                    )

                    if loan_paymenttransaction.transaction_status == "normal":
                        # get days in arrears
                        arrear_details = get_loan_arrears_details(
                            loan_id=loan_paymenttransaction.loan_application.id,
                            as_at=None,
                            payment_transaction_id=loan_paymenttransaction.id,
                        )
                        loans_payments_data.append(
                            {
                                "id": loan_paymenttransaction.id,
                                "transaction_type": "repayment",
                                "ref_no": "",
                                "voucher": "",
                                "princ": princ_paid,
                                "int": int_paid,
                                "arrear_days": (
                                    arrear_details["arrear_days"]
                                    if arrear_details["arrear_days"] > 0
                                    else ""
                                ),
                                "penalty": penalty_paid,
                                "total": penalty_paid + princ_paid + int_paid,
                                "date_added": loan_paymenttransaction.date_added,
                                "payment_date": loan_paymenttransaction.payment_date,
                            }
                        )

                        if (arrear_details["total_principal"] - princ_paid) > 0 or (
                            arrear_details["total_interest"] - int_paid
                        ) > 0:
                            princ_due = arrear_details["total_principal"] - princ_paid
                            int_due = arrear_details["total_interest"] - int_paid

                            loans_payments_data.append(
                                {
                                    "id": loan_paymenttransaction.id,
                                    "transaction_type": "total_due_amount",
                                    "ref_no": "",
                                    "voucher": "",
                                    "princ": princ_due,
                                    "int": int_due,
                                    "arrear_days": "",
                                    "penalty": 0,
                                    "total": int_due + princ_due,
                                    "date_added": loan_paymenttransaction.date_added
                                    + timedelta(minutes=10),
                                    "payment_date": loan_paymenttransaction.payment_date
                                    + timedelta(minutes=10),
                                }
                            )

                # interest waivers
                loan_schedules = LoanRepaymentSchedule.objects.filter(
                    loan_application__id=loan_id, status="active"
                ).order_by("payment_number")
                for loan_schedule in loan_schedules:
                    interests_waived = LoanInterestWaivered.objects.filter(
                        loan_application__id=loan_id,
                        loan_repayment_schedule=loan_schedule,
                    ).aggregate(total_amount=Sum("amount"))["total_amount"]
                    interests_waived_obj = (
                        LoanInterestWaivered.objects.filter(
                            loan_application__id=loan_id,
                            loan_repayment_schedule=loan_schedule,
                        )
                        .order_by("id")
                        .first()
                    )
                    if interests_waived_obj:
                        loans_payments_data.append(
                            {
                                "id": interests_waived_obj.id,
                                "transaction_type": "interest_waiver",
                                "ref_no": "",
                                "voucher": "",
                                "princ": "",
                                "arrear_days": "",
                                "int": interests_waived if interests_waived else 0,
                                "penalty": "",
                                "total": interests_waived if interests_waived else 0,
                                "date_added": interests_waived_obj.last_updated,
                                "payment_date": interests_waived_obj.date_added,
                            }
                        )

            else:
                # payments
                # print("-- else ----------------------------------------------------")
                loan_paymenttransactions = (
                    LoanPaymentTransaction.objects.filter(
                        loan_application__id=loan_id, deleted=False
                    )
                    .all()
                    .order_by("id")
                )
                for loan_paymenttransaction in loan_paymenttransactions:
                    loan_payments = LoanPayments.objects.filter(
                        loan_payment_transaction=loan_paymenttransaction,
                        payment_status="normal",
                        deleted=False,
                    ).aggregate(
                        total_int_paid=Sum("int_paid"),
                        total_princ_paid=Sum("princ_paid"),
                        total_penalty_paid=Sum("penalty_paid"),
                    )
                    princ_paid = (
                        loan_payments["total_princ_paid"]
                        if loan_payments["total_princ_paid"]
                        else 0
                    )
                    int_paid = (
                        loan_payments["total_int_paid"]
                        if loan_payments["total_int_paid"]
                        else 0
                    )
                    penalty_paid = (
                        loan_payments["total_penalty_paid"]
                        if loan_payments["total_penalty_paid"]
                        else 0
                    )
                    # print(loan_payments.id)
                    # print("******************* principal paid "+ str(princ_paid))
                    # print("******************* interest paid "+ str(int_paid))
                    # print("******************* penalty paid "+ str(penalty_paid))
                    # print("------------ total ------------------")
                    # print(str(penalty_paid + princ_paid + int_paid))

                    if loan_paymenttransaction.transaction_status == "normal":
                        # get days in arrears
                        arrear_details = get_loan_arrears_details(
                            loan_id=loan_paymenttransaction.loan_application.id,
                            as_at=None,
                            payment_transaction_id=loan_paymenttransaction.id,
                        )

                        loans_payments_data.append(
                            {
                                "id": loan_paymenttransaction.id,
                                "transaction_type": "repayment",
                                "ref_no": "",
                                "voucher": "",
                                "princ": princ_paid,
                                "int": int_paid,
                                "arrear_days": (
                                    arrear_details["arrear_days"]
                                    if arrear_details["arrear_days"] > 0
                                    else ""
                                ),
                                "penalty": penalty_paid,
                                "total": penalty_paid + princ_paid + int_paid,
                                "date_added": loan_paymenttransaction.date_added,
                                "payment_date": loan_paymenttransaction.payment_date,
                            }
                        )

                # interest waivers
                loan_schedules = LoanRepaymentSchedule.objects.filter(
                    loan_application__id=loan_id, status="active"
                ).order_by("payment_number")
                for loan_schedule in loan_schedules:
                    interests_waived = LoanInterestWaivered.objects.filter(
                        loan_application__id=loan_id,
                        loan_repayment_schedule=loan_schedule,
                    ).aggregate(total_amount=Sum("amount"))["total_amount"]
                    interests_waived_obj = (
                        LoanInterestWaivered.objects.filter(
                            loan_application__id=loan_id,
                            loan_repayment_schedule=loan_schedule,
                        )
                        .order_by("id")
                        .first()
                    )
                    if interests_waived_obj:
                        loans_payments_data.append(
                            {
                                "id": interests_waived_obj.id,
                                "transaction_type": "interest_waiver",
                                "ref_no": "",
                                "voucher": "",
                                "princ": "",
                                "arrear_days": "",
                                "int": interests_waived if interests_waived else 0,
                                "penalty": "",
                                "total": interests_waived if interests_waived else 0,
                                "date_added": interests_waived_obj.last_updated,
                                "payment_date": interests_waived_obj.date_added,
                            }
                        )

                # rescheduled loan
                rescheduled_loans = RescheduledLoans.objects.filter(
                    loan_application=loan_id, deleted=False
                ).order_by("id")
                for rescheduled_loan in rescheduled_loans:
                    # get days in arrears
                    arrear_details = get_loan_arrears_details(
                        loan_id=loan_id, as_at=current, payment_transaction_id=None
                    )
                    loans_payments_data.append(
                        {
                            "id": rescheduled_loan.id,
                            "transaction_type": "rescheduled_loan",
                            "ref_no": "",
                            "voucher": "",
                            "arrear_days": (
                                arrear_details["arrear_days"]
                                if arrear_details["arrear_days"] > 0
                                else ""
                            ),
                            "princ": rescheduled_loan.principal_amount,
                            "int": rescheduled_loan.interest_expected,
                            "penalty": "",
                            "total": rescheduled_loan.total_payment,
                            "date_added": rescheduled_loan.date_added,
                            "payment_date": rescheduled_loan.reschedule_date,
                        }
                    )

            loans_payments_data.extend(repayment_schedules_object)
            loans_payments_data.sort(key=lambda x: x["payment_date"])

            response["disbursement"] = disbusersment_object
            response["payments"] = loans_payments_data

        return Response(response)


class LoanAgeingReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "arrears_range": request.GET.get("arrears_range", None),
        }
        #
        return filter_loan_aging_reports(report_filters)


class LoansExpectedRepaymentDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        end = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(end):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": end,
        }
        #
        return filter_loans_expected_repayment_reports(report_filters)


class LoanDuesRepaymentReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        start = request.GET.get("s")
        end = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(start) or not validate_date(end):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start": start,
            "as_at": end,
        }
        #
        return filter_loan_dues_repayment_reports(report_filters)


class LoansPerformingReportDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
        }
        #
        return filter_loan_performing_reports(report_filters)


class LoansArrearsReportDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        arrear_days_from = self.request.GET.get("arrearDaysFrom", 0)
        arrear_days_to = self.request.GET.get("arrearDaysTo", 0)

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "arrear_days_from": arrear_days_from,
            "arrear_days_to": arrear_days_to,
        }
        #
        return filter_loan_arrear_reports(report_filters)


class LoanSavingsReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        loan_filter = {
            "is_deleted": False,
            "status": "disbursed",
            "organisation_id": organisation_id,
        }

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
            "loan_filter": loan_filter,
        }
        #
        return filter_loan_savings_reports(report_filters)


class ParReportDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
        }
        #
        return filter_loan_par_reports(report_filters)


class ParAgingReportDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
            "provisions": get_loan_provistions(self.request.user.id, organisation_id),
            "arrears_range": request.GET.get("arrears_range", None),
        }
        #
        return filter_loan_par_aging_reports(report_filters)


class WriteOffReportDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        #
        return filter_loan_writeoff_reports(report_filters)


class WrittenOffReportDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
        }
        #
        return filter_loan_writtenoff_reports(report_filters)


class LoansClearedOffReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        start = request.GET.get("s", None)
        end = request.GET.get("e", None)
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start": start,
            "end": end,
            "search": search,
        }
        #
        return filter_loan_clearedoff_reports(report_filters)


class RescheduledLoansReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        start = request.GET.get("s", None)
        end = request.GET.get("e", None)
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start": start,
            "end": end,
            "search": search,
        }
        #
        return filter_loan_rescheduled_reports(report_filters)


class LoansRepaymentReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        start_date = request.GET.get("s")
        end_date = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start_date": start_date,
            "end_date": end_date,
            "search": search,
        }
        #
        return filter_loan_repayment_reports(report_filters)


class LoansArrearsReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        search = self.request.GET.get("search", None)

        loan_filter = {
            "loan_arrear_date__lte": as_at,
            "is_deleted": False,
            "status": "disbursed",
            "organisation_id": organisation_id,
        }

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "loan_filter": loan_filter,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        return filter_arrear_reports(report_filters)


class ParReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(as_at):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        return filter_par_reports(report_filters)


class AgeingReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        loan_filter = {
            "loan_arrear_date__lte": as_at,
            "principal_balance__gt": 0,
            "is_deleted": False,
            "status": "disbursed",
            "organisation_id": organisation_id,
        }

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "loan_filter": loan_filter,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
            "provisions": get_loan_provistions(self.request.user.id, organisation_id),
        }
        return filter_ageing_reports(report_filters)


class ClearedOffLoansReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        return filter_cleared_off_reports(report_filters)


class ToBeClearedOffLoansReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        #
        return filter_to_be_cleared_reports(report_filters)


class LoansExpectedRepaymentFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        end = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        if not validate_date(end):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": end,
            "search": search,
        }
        return filter_expected_repayment_reports(report_filters)


class LoanDueRepaymentReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        start = request.GET.get("s")
        end = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start": start,
            "end": end,
            "search": search,
        }
        return filter_due_repayment_reports(report_filters)

    class LoanDueInstallmentReportFiltersDataView(APIView):
        def get(self, request):
            filter_1 = []
            filter_2 = []
            page_size = 200
            start = request.GET.get("s")
            end = request.GET.get("e")
            report_filter = self.request.GET.get("filter")
            search = self.request.GET.get("search", None)
            organisation_id = get_current_user(self.request, "organisation_id", None)

            loan_filter = {
                "loan_arrear_date__gte": start,
                "loan_arrear_date__lte": end,
                "is_deleted": False,
                "status": "disbursed",
                "organisation_id": organisation_id,
            }

            if self.request.GET.get("page_size"):
                page_size = self.request.GET.get("page_size")

            if request.GET.get("filter_1"):
                filter_1 = list(request.GET.get("filter_1").split(","))

            if request.GET.get("filter_2"):
                filter_2 = list(request.GET.get("filter_2").split(","))

            report_filters = {
                "report_filter": report_filter,
                "organisation_id": organisation_id,
                "loan_filter": loan_filter,
                "page_size": page_size,
                "filter_1": filter_1,
                "filter_2": filter_2,
                "request": request,
                "start": start,
                "end": end,
                "search": search,
            }
            return filter_due_installment_reports(report_filters)


class WrittenOffReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)
        loan_filter = {
            "written_off_date__lte": as_at,
            "is_deleted": False,
            "status": "written_off",
            "organisation_id": organisation_id,
        }

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "loan_filter": loan_filter,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        return filter_written_off_reports(report_filters)


class WriteOffReportFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        as_at = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": as_at,
            "search": search,
        }
        return filter_write_off_reports(report_filters)


class WithHoldSavingsSharesView(APIView):

    def post(self, request, format=None):
        trans_type = request.GET.get("trans_type", None)
        if trans_type == "delete":
            id = self.request.data.get("id")
            if id:
                branchid = get_current_user(request, 'organisation_branch_id', None)
                _branch = OrganisationBranch.objects.filter(pk=branchid).first()
                LoanApplicationWithHold.objects.filter(
                    deleted=False, id=id
                ).all().update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user, status='released')
                # fix any previously soft-deleted records whose status was never updated
                LoanApplicationWithHold.objects.filter(deleted=True, status='held').update(status='released')
                add_system_audit_trail('transaction_management', 'delete_loan_withhold',
                    f'Deleted Loan WithHold id: {id}', '', {}, {}, request.user, _branch)

            return Response({"message": "removed successfully"})
        else:
            account = self.request.data.get("account")
            hold_type = self.request.data.get("hold_type")
            amount = self.request.data.get("amount")
            loan_application_id = self.request.data.get("loan_application")

            # validation
            if not amount:
                return Response({"message": "amount is required"})

            if not hold_type:
                return Response({"message": "hold type is required"})

            if not account and hold_type == "savings":
                return Response({"message": "account is required"})

            loan_application = LoanApplication.objects.filter(
                id=loan_application_id
            ).first()
            if not loan_application:
                return Response({"message": "No loan application"})

            lookup = {
                "loan_application": loan_application,
                "hold_type": hold_type,
            }
            data = {
                "amount": amount,
                "loan_withhold_added_by": request.user,
                "deleted": False,
                "deleted_at": None,
                "deleted_by": None,
                "status": "held",
            }
            if hold_type == "savings":
                data["account"] = SavingAccount.objects.get(pk=account)

            LoanApplicationWithHold.objects.update_or_create(defaults=data, **lookup)

            return Response({"message": "Added successfully"})


class LoanLossProvisionView(APIView):
    def get(self, request):
        organisation_id = get_current_user(self.request, "organisation_id", None)
        get_loan_provistions(self.request.user.id, organisation_id)
        return Response(get_loan_provistions(self.request.user.id, organisation_id))

    def post(self, request, format=None):
        percentage = self.request.data.get("percentage")
        from_value = self.request.data.get("from_value")
        to_value = self.request.data.get("to_value")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        organisation = Organisation.objects.get(pk=organisation_id)
        provision = LoanLossProvision.objects.filter(
            organisation__id=organisation_id, from_value=from_value, to_value=to_value
        ).first()
        if provision:
            provision.percentage = percentage
            provision.save()
        else:
            field = {
                "from_value": from_value,
                "to_value": to_value,
                "percentage": percentage,
                "added_by": self.request.user.id,
                "last_updated_by": self.request.user.id,
                "organisation": organisation,
            }
            LoanLossProvision.objects.create(**field)

        return Response({"message": "Updated successfully"})


class LoanPenaltySchedule(APIView):

    def get(self, request):
        loan_application = request.GET.get("loan_application", None)
        data = {}
        if loan_application:
            loan_details = LoanApplication.objects.get(pk=loan_application)
            penalty_waived = LoanPenaltyWaivered.objects.filter(
                loan_application=loan_details
            ).aggregate(total_amount=Sum("amount"))["total_amount"]
            total_penalty_waived = penalty_waived if penalty_waived else 0

            loan_penalities_paid = LoanPayments.objects.filter(
                loan_application=loan_details, payment_status="normal"
            ).aggregate(total_penalty_paid=Sum("penalty_paid"))["total_penalty_paid"]
            loan_penalities_paid = loan_penalities_paid if loan_penalities_paid else 0

            total_loan_penalties = LoanPenalty.objects.filter(
                loan_application=loan_details
            ).aggregate(total_penalty=Sum("amount"))["total_penalty"]
            total_loan_penalties = total_loan_penalties if total_loan_penalties else 0
            data["total_penalty_waived"] = total_penalty_waived
            data["loan_penalities_paid"] = loan_penalities_paid
            data["total"] = total_penalty_waived + loan_penalities_paid

        return Response({"count": 1, "results": data})


class LoansRepaymentReportsFiltersDataView(APIView):
    def get(self, request):
        filter_1 = []
        filter_2 = []
        page_size = 200
        start = request.GET.get("s")
        end = request.GET.get("e")
        report_filter = self.request.GET.get("filter")
        search = self.request.GET.get("search", None)
        organisation_id = get_current_user(self.request, "organisation_id", None)

        loan_filter = {
            "loan_main_transaction__system_transaction__record_date__gte": start,
            "loan_main_transaction__system_transaction__record_date__lte": end,
            "organisation_id": organisation_id,
        }

        if self.request.GET.get("page_size"):
            page_size = self.request.GET.get("page_size")

        if request.GET.get("filter_1"):
            filter_1 = list(request.GET.get("filter_1").split(","))

        if request.GET.get("filter_2"):
            filter_2 = list(request.GET.get("filter_2").split(","))

        report_filters = {
            "report_filter": report_filter,
            "organisation_id": organisation_id,
            "loan_filter": loan_filter,
            "page_size": page_size,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "start": start,
            "end": end,
            "search": search,
        }
        return filter_repayment_reports(report_filters)


class LoansFiltersDataView(APIView):
    def post(self, request):
        members = self.request.data.get("members")
        response = []
        if members:
            for member in members:
                loans = LoanApplication.objects.filter(
                    Q(customer__member_number=member)
                    | Q(customer__old_member_number=member)
                )
                for loan in loans:
                    # loans_list.append({"id":loan.id, "loan_amount":loan.loan_amount})
                    response.append(loan.id)

        return Response({"results": response})


class LoansDeleteView(APIView):
    def post(self, request):
        loan_application_id = self.request.data.get("id")
        branch_id = get_current_user(self.request, "organisation_branch_id", 1)
        branch = OrganisationBranch.objects.get(id=branch_id)
        loan_obj = LoanApplication.objects.filter(id=loan_application_id).first()
        delete_type = self.request.data.get("delete_type", None)
        if loan_application_id:
            add_system_audit_trail(
                "delete_loan_payment",
                "delete_loan_payment",
                "Delete Loan",
                self.request.data.get("reason_for_delete", None),
                None,
                None,
                self.request.user,
                branch,
            )
            delete_loan_by_id(
                loan_application_id,
                delete_type,
                self.request.data.get("reason_for_delete", None),
                user_id=self.request.user.id,
            )
        return Response({"message": "Success"})


class LoansAddMissingRecordsView(APIView):

    def post(self, request):
        organisation = self.request.data.get("organisation")
        refine = self.request.data.get("type")

        if organisation and refine == "refine_schedule_disbursed_loans":
            # auto penalties
            refine_schedule = threading.Thread(
                target=refine_disbursed_loans,
                args=(
                    request,
                    organisation,
                ),
            )
            # starting auto penalties thread
            refine_schedule.start()

        if organisation and refine == "refine_schedule_date":
            # auto penalties
            refine_schedule = threading.Thread(
                target=refine_loans,
                args=(
                    request,
                    organisation,
                ),
            )
            # starting auto penalties thread refine_disbursed_loans
            refine_schedule.start()

        if organisation and refine == "refine_schedule_amount":
            return Response({"results": refine_loan_amounts(request, organisation)})

        if organisation and refine == "update_loan_penalty_data":
            update_loan_penalty_dates(organisation)
            return Response({"results": "Processed"})

        if organisation and refine == "remove_duplicate_sms_charge":
            # auto penalties
            refine_schedule = threading.Thread(
                target=self.remove_sms_doulble_entry, args=()
            )
            # starting auto penalties thread refine_disbursed_loans
            refine_schedule.start()

            return Response({"results": "Initiated"})

        if organisation and refine == "remove_duplicate_accounts":
            customers = Customer.objects.filter(
                customer_branch__branch_organisation__id=organisation
            )
            for customer in customers:
                savins_accounts = SavingAccount.objects.filter(
                    account_customer=customer
                ).order_by("id")
                if len(savins_accounts) > 2:
                    savins_accounts[2].deleted = True
                    savins_accounts[2].deleted_at = timezone.now()
                    savins_accounts[2].deleted_by = request.user
                    savins_accounts[2].save()
                    add_system_audit_trail('transaction_management', 'delete_duplicate_savings_account',
                        f'Removed duplicate savings account for customer id: {customer.id}',
                        '', {}, {}, request.user, customer.customer_branch)

            return Response({"results": "Removed"})

        if organisation and refine == "remove_duplicate_disbursements":
            loans = []
            loan_paymenttransactions = (
                LoanPaymentTransaction.objects.filter(
                    loan_application__organisation_branch__id=71
                )
                .all()
                .order_by("id")
            )
            for loan_paymenttransaction in loan_paymenttransactions:
                loan_payments = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_paymenttransaction,
                    payment_status="normal",
                ).aggregate(
                    total_int_paid=Sum("int_paid"),
                    total_princ_paid=Sum("princ_paid"),
                    total_penalty_paid=Sum("penalty_paid"),
                )
                princ_paid = (
                    loan_payments["total_princ_paid"]
                    if loan_payments["total_princ_paid"]
                    else 0
                )
                int_paid = (
                    loan_payments["total_int_paid"]
                    if loan_payments["total_int_paid"]
                    else 0
                )
                penalty_paid = (
                    loan_payments["total_penalty_paid"]
                    if loan_payments["total_penalty_paid"]
                    else 0
                )

                if princ_paid + int_paid + penalty_paid == 0:
                    loan_paymenttransaction.deleted = True
                    loan_paymenttransaction.deleted_at = timezone.now()
                    loan_paymenttransaction.deleted_by = request.user
                    loan_paymenttransaction.save()
                    add_system_audit_trail('transaction_management', 'delete_empty_loan_payment_transaction',
                        f'Deleted zero-amount loan payment transaction id: {loan_paymenttransaction.id}',
                        '', {}, {}, request.user, loan_paymenttransaction.loan_application.organisation_branch)

            return Response({"loans": "loans"})

        if organisation and refine == "remove_loans_by_migration_id":
            number_list = [
                482,
                503,
                518,
                525,
                528,
                534,
                539,
                562,
                564,
                571,
                576,
                581,
                584,
                589,
                592,
                591,
                595,
                601,
                605,
                617,
                627,
                639,
                655,
                698,
                708,
                717,
                716,
                725,
                726,
                728,
                730,
                732,
                734,
                735,
                736,
                737,
                738,
                739,
                740,
                744,
                746,
                747,
                745,
                748,
                750,
                751,
                752,
                753,
                754,
                756,
                759,
                758,
                760,
                761,
                763,
                766,
                769,
                767,
                770,
                768,
                771,
                772,
                773,
                774,
                775,
                776,
                777,
                778,
                779,
                780,
                781,
                782,
                585,
                731,
                757,
                762,
                626,
                722,
                727,
                733,
                573,
                577,
                642,
                647,
                720,
                662,
            ]
            for loan_id in number_list:
                loan_historys = LoanMigrationHistory.objects.filter(
                    loan_number=loan_id,
                    loan__organisation_branch__branch_organisation__id=organisation,
                ).all()
                for loan_history in loan_historys:
                    if loan_history.loan:
                        delete_loan_by_id(loan_history.loan.id, "permanent", None, user_id=request.user.id)

        if organisation and refine == "remove_loans_by_ids":
            loans = self.request.data.get("loans")
            for loan_id in loans:
                delete_loan_by_id(loan_id, "permanent", None, user_id=request.user.id)

        if organisation and refine == "duplicate_disbursements":
            response = []
            system_transactions = SystemTransactions.objects.filter(
                reference_no__startswith="ln-d",
                branch__id__in=[62, 61],
                heading__startswith="Loan",
            )
            for system_transaction in system_transactions:
                disbursement = LoanApplicationDisbursement.objects.filter(
                    system_transaction=system_transaction
                )
                if not disbursement:
                    response.append(
                        {
                            "id": system_transaction.id,
                            "heading": system_transaction.heading,
                            "branch": system_transaction.branch.id,
                            "amt": system_transaction.amount,
                        }
                    )

            return Response({"results": response})

        if organisation and refine == "loan_inter_branch_issues":
            response = []
            system_transactions = SystemTransactions.objects.filter(
                reference_no__startswith="ln-d",
                branch__id__in=[62, 61],
                heading__startswith="Inter",
            )
            for system_transaction in system_transactions:
                inter_branch = InterBranchTransactions.objects.filter(
                    Q(source_transaction=system_transaction)
                    | Q(destination_transaction=system_transaction)
                )
                if not inter_branch:
                    response.append(
                        {
                            "id": system_transaction.id,
                            "heading": system_transaction.heading,
                            "branch": system_transaction.branch.id,
                        }
                    )

            return Response({"results": response})

        if organisation and refine == "wrong_loan_branches":
            response = []
            loan_applications = LoanApplication.objects.filter(
                organisation_branch__id__in=[62, 61]
            )
            for loan_application in loan_applications:
                loan_disb = LoanApplicationDisbursement.objects.filter(
                    loan_application=loan_application,
                    system_transaction__heading__startswith="Loan",
                ).first()
                if loan_disb and int(loan_disb.system_transaction.branch.id) != int(
                    loan_application.organisation_branch.id
                ):
                    response.append(
                        {
                            "id": loan_disb.system_transaction.id,
                            "heading": loan_disb.system_transaction.heading,
                            "wrong_branch": loan_disb.system_transaction.branch.id,
                            "right_branch": loan_application.organisation_branch.id,
                        }
                    )

            return Response({"results": response})

        if organisation and refine == "update_payments_branch":
            response = []
            system_transactions = SystemTransactions.objects.filter(
                reference_no__startswith="ln-d",
                branch__id=27,
                heading__startswith="Loan",
            )
            for system_transaction in system_transactions:
                loan_pay = LoanMainTransactions.objects.filter(
                    deleted=False, system_transaction=system_transaction
                ).first()
                if loan_pay and int(
                    loan_pay.loan_application.organisation_branch.id
                ) != int(system_transaction.branch.id):
                    # system_transaction.branch = loan_pay.loan_application.organisation_branch
                    # system_transaction.save()
                    response.append(
                        {
                            "id": system_transaction.id,
                            "heading": system_transaction.heading,
                        }
                    )

            # system_transactions = SystemTransactions.objects.filter(reference_no__startswith='ln-in-', branch__branch_organisation__id=31, heading__startswith='Loan')
            # for system_transaction in system_transactions:
            #     loan_pay = LoanMainTransactions.objects.filter(system_transaction=system_transaction).first()
            #     if loan_pay and int(loan_pay.loan_application.organisation_branch.id) != int(system_transaction.branch.id):
            #         system_transaction.branch = loan_pay.loan_application.organisation_branch
            #         system_transaction.save()
            return Response({"results": response})

        if organisation and refine == "wrong_products_inter":
            response = []
            loans = LoanApplication.objects.filter(
                organisation_branch__id=30, status="disbursed"
            )
            for loan in loans:
                prdt = loan.loan_application_product.chart.id
                disbursement = LoanApplicationDisbursement.objects.filter(
                    loan_application=loan,
                    system_transaction__heading__startswith="Inter",
                ).first()
                if disbursement:
                    response.append(
                        {
                            "id": disbursement.system_transaction.id,
                            "ref": disbursement.system_transaction.reference_no,
                            "prd": prdt,
                            "trans": disbursement.system_transaction.debit_chart.id,
                        }
                    )

            return Response({"results": response})

        if organisation and refine == "wrong_product_inter_branch":
            response = []
            loans = LoanApplication.objects.filter(
                organisation_branch__id__in=[27, 29, 30, 31], status="cleared_off"
            )
            for loan in loans:
                prdt = loan.loan_application_product.chart.id
                disbursement = LoanApplicationDisbursement.objects.filter(
                    loan_application=loan,
                    system_transaction__heading__startswith="Inter",
                ).first()
                if disbursement:
                    inter_branch = InterBranchTransactions.objects.filter(
                        Q(source_transaction=disbursement.system_transaction)
                        | Q(destination_transaction=disbursement.system_transaction)
                    ).first()
                    if inter_branch:
                        loan_product = LoanProduct.objects.filter(
                            chart=inter_branch.source_transaction.debit_chart,
                            organisation__id=24,
                        )
                        if loan_product:
                            if int(
                                inter_branch.source_transaction.debit_chart.id
                            ) != int(prdt):
                                response.append(
                                    {
                                        "id": inter_branch.source_transaction.id,
                                        "ref": inter_branch.source_transaction.reference_no,
                                        "prd": prdt,
                                        "trans": inter_branch.source_transaction.debit_chart.id,
                                    }
                                )
                        else:
                            loan_product = LoanProduct.objects.filter(
                                chart=inter_branch.destination_transaction.debit_chart,
                                organisation__id=24,
                            )
                            if int(
                                inter_branch.destination_transaction.debit_chart.id
                            ) != int(prdt):
                                response.append(
                                    {
                                        "id": inter_branch.destination_transaction.id,
                                        "ref": inter_branch.destination_transaction.reference_no,
                                        "prd": prdt,
                                        "trans": inter_branch.destination_transaction.debit_chart.id,
                                    }
                                )
                    else:
                        print(disbursement.system_transaction.reference_no)
            return Response({"results": response})

        if organisation and refine == "wrong_products":
            response = []
            loans = LoanApplication.objects.filter(organisation_branch__id=61)
            for loan in loans:
                prdt = loan.loan_application_product.chart.id
                disbursement = LoanApplicationDisbursement.objects.filter(
                    loan_application=loan, system_transaction__payment_method="credit"
                ).first()
                if disbursement and int(
                    disbursement.system_transaction.debit_chart.id
                ) != int(prdt):
                    response.append(
                        {
                            "id": disbursement.system_transaction.id,
                            "ref": disbursement.system_transaction.reference_no,
                            "prd": prdt,
                            "trans": disbursement.system_transaction.debit_chart.id,
                        }
                    )

            return Response({"results": response})

        if organisation and refine == "remove_payments":
            response = []
            payments = SystemTransactions.objects.filter(
                reference_no__startswith="ln-p-",
                branch__branch_organisation__id__in=[27, 29, 30, 31],
                heading__startswith="Loan",
            )
            for system_transaction in payments:
                # inter_branch = InterBranchTransactions.objects.filter(Q(source_transaction=system_transaction) | Q(destination_transaction=system_transaction))
                # if not inter_branch:
                #     response.append({"id":system_transaction.id, "heading":system_transaction.heading})

                inter_branch = LoanMainTransactions.objects.filter(
                    deleted=False, system_transaction=system_transaction
                ).first()
                if not inter_branch:
                    response.append(
                        {
                            "id": system_transaction.id,
                            "heading": system_transaction.heading,
                        }
                    )

                if inter_branch:
                    laon_payments = LoanPayments.objects.filter(
                        loan_main_transaction=inter_branch
                    )
                    if not laon_payments:
                        response.append(
                            {
                                "id": system_transaction.id,
                                "heading": system_transaction.heading,
                            }
                        )
            return Response({"results": response})

        if organisation and refine == "refine_inter_branch_transactions":
            product_id = self.request.data.get("product_id")
            refine_inter_branch_transactions(organisation, product_id)

        if organisation and refine == "refine_inter_branch_transactions_incomes":
            product_id = self.request.data.get("product_id")
            return Response(
                {
                    "results": refine_inter_branch_transactions_income(
                        organisation, product_id
                    )
                }
            )

        if organisation and refine == "update_transaction_date":
            # id=84214
            transactions = SystemTransactions.objects.filter(branch__id=31)
            if transactions:
                for transaction in transactions:
                    transaction.record_date = date_time_zone_convert(
                        transaction.record_date, transaction.date_added
                    )
                    transaction.save()
            return Response({"results": "Initiated"})

        if organisation and refine == "reverse_transaction_date":
            refine_schedule = threading.Thread(
                target=self.reverse_kasaana_transactions, args=()
            )
            # starting auto penalties thread refine_disbursed_loans
            refine_schedule.start()

            return Response({"results": "Initiated reversal"})

        if organisation and refine == "delete_daily_savings":
            SystemTransactions.objects.filter(
                debit_chart__id=5043, record_date__date="2024-04-19", branch__id=52
            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=request.user.id)
            SystemTransactions.objects.filter(
                debit_chart__id=5043, record_date__date="2024-04-18", branch__id=52
            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=request.user.id)
            add_system_audit_trail('transaction_management', 'delete_daily_savings_transactions',
                'Deleted daily savings transactions for branch 52', '', {}, {}, request.user,
                OrganisationBranch.objects.filter(pk=52).first())

            return Response({"results": "Deleted"})

        if organisation and refine == "update_wallet_transactions":
            charts_to_remove = []
            transactions = SystemTransactions.objects.filter(
                reference_no__startswith="mm-dep-"
            ).order_by("id")
            for transaction in transactions:
                e_wallet_chart = get_branch_wallet_chart(
                    transaction.branch.branch_organisation.id, transaction.branch.id
                )
                if e_wallet_chart:
                    organisation_sub_account = OrganisationSubAccount.objects.filter(
                        id=transaction.debit_chart.id
                    ).first()
                    if organisation_sub_account:
                        charts_to_remove.append(
                            {
                                "parent": organisation_sub_account.parent_id.id,
                                "child": organisation_sub_account.id,
                            }
                        )

                    # update transactions
                    transaction.debit_chart = e_wallet_chart
                    transaction.save()

            return Response({"results": charts_to_remove})

        if organisation and refine == "remove_penality":
            loan_applications = LoanApplication.objects.filter(
                organisation_branch__branch_organisation__id=19
            )
            for loan_application in loan_applications:
                loan_penalty = LoanPenalty.objects.filter(
                    loan_application=loan_application, status="auto"
                )
                if loan_penalty:
                    loan_penalty.update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
                    add_system_audit_trail('transaction_management', 'delete_auto_loan_penalty',
                        f'Deleted auto loan penalties for loan application id: {loan_application.id}',
                        '', {}, {}, request.user, loan_application.organisation_branch)

            return Response({"results": "Removed"})

        # update clear off date
        if organisation and refine == "cleared_off_loans_date":
            loans = LoanApplication.objects.filter(status="cleared_off").order_by(
                "organisation_branch"
            )
            for loan in loans:
                if not loan.clear_off_date:
                    # get last payment
                    loan_payment = (
                        LoanPaymentTransaction.objects.filter(
                            loan_application=loan, transaction_status="normal"
                        )
                        .order_by("-id")
                        .first()
                    )
                    if loan_payment:
                        loan.clear_off_date = loan_payment.date_added
                        loan.save()

        if organisation and refine == "remove_migrated_mm_banking_sub":
            initiation_id = self.request.data.get("initiation_id", None)
            if initiation_id:
                customers = BulkTempMMBankingSubscriptonImports.objects.filter(
                    initiation__id=initiation_id
                )
                for customer in customers:
                    MobileBankingSubscription.objects.filter(
                        customer=customer.customer, date_added__date="2024-05-20"
                    ).update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
                    add_system_audit_trail('transaction_management', 'delete_mm_banking_subscription',
                        f'Removed migrated MM banking subscription for customer id: {customer.customer_id}',
                        '', {}, {}, request.user, customer.customer.customer_branch)

        if organisation and refine == "remove_customer":
            responses = []
            loan_payments = LoanMainTransactions.objects.filter(
                deleted=False,
                system_transaction__reference_no__startswith="ln-p",
                system_transaction__branch__id__in=[62, 61],
            ).exclude(system_transaction__heading__startswith="Inter-branch")
            for disbursement in loan_payments:
                if int(disbursement.loan_application.organisation_branch.id) != int(
                    disbursement.system_transaction.branch.id
                ):
                    system_transaction_branch = SystemTransactions.objects.filter(
                        id=disbursement.system_transaction.id
                    ).first()
                    if system_transaction_branch:
                        system_transaction_branch.branch = (
                            disbursement.loan_application.organisation_branch
                        )
                        system_transaction_branch.save()

                    responses.append(
                        {
                            "id": disbursement.system_transaction.id,
                            "ref": disbursement.system_transaction.reference_no,
                            "heading": disbursement.system_transaction.heading,
                            "amount": disbursement.system_transaction.amount,
                            "wrong": disbursement.system_transaction.branch.id,
                            "correct": disbursement.loan_application.organisation_branch.id,
                        }
                    )

            loan_payments = LoanMainTransactions.objects.filter(
                deleted=False,
                system_transaction__reference_no__startswith="ln-in-",
                system_transaction__branch__id__in=[62, 61],
            ).exclude(system_transaction__heading__startswith="Inter-branch")
            for disbursement in loan_payments:
                if int(disbursement.loan_application.organisation_branch.id) != int(
                    disbursement.system_transaction.branch.id
                ):
                    system_transaction_branch = SystemTransactions.objects.filter(
                        id=disbursement.system_transaction.id
                    ).first()
                    if system_transaction_branch:
                        system_transaction_branch.branch = (
                            disbursement.loan_application.organisation_branch
                        )
                        system_transaction_branch.save()

            return Response({"message": responses})

        if organisation and refine == "remove_bookings":
            from django.utils import timezone
            now = timezone.now()
            user_id = request.user.id
            df = dict(deleted=True, deleted_by_id=user_id, deleted_at=now)
            for heading_filter in [
                "members  burial contributions on",
                "Burial Contribution on",
            ]:
                bookings = AccountBookings.objects.filter(
                    heading__startswith=heading_filter,
                    date_added__date="2024-06-14",
                    account__customer_branch__id=19,
                )
                for booking in bookings:
                    AccountBookingPayments.objects.filter(booking=booking).update(**df)
                    SystemTransactions.objects.filter(
                        booking_payment_transaction__booking=booking
                    ).update(**df)
                    AccountBookings.objects.filter(id=booking.id).update(**df)
                    add_system_audit_trail('transaction_management', 'delete_burial_contribution_booking',
                        f'Deleted burial contribution booking id: {booking.id}: {booking.heading}',
                        '', {}, {}, request.user, booking.account.customer_branch)
            return Response({"message": "Deleted successfully"})

        if organisation and refine == "loan_applications":
            responses = []
            organisations = Organisation.objects.all().exclude(id=1).order_by("id")
            for organisation in organisations:
                pending = LoanApplication.objects.filter(
                    status="pending",
                    organisation_branch__branch_organisation__id=organisation.id,
                ).count()
                disbursed = LoanApplication.objects.filter(
                    status="disbursed",
                    organisation_branch__branch_organisation__id=organisation.id,
                ).count()
                cleared_off = LoanApplication.objects.filter(
                    status="cleared_off",
                    organisation_branch__branch_organisation__id=organisation.id,
                ).count()
                written_off = LoanApplication.objects.filter(
                    status="written_off",
                    organisation_branch__branch_organisation__id=organisation.id,
                ).count()
                approved = LoanApplication.objects.filter(
                    status="approved",
                    organisation_branch__branch_organisation__id=organisation.id,
                ).count()

                responses.append(
                    [
                        organisation.name,
                        organisation.address,
                        pending,
                        approved,
                        disbursed,
                        cleared_off,
                        written_off,
                    ]
                )

            with open(
                settings.STATIC_ROOT + "/files/sacco_loans.csv", "w", encoding="UTF8"
            ) as f:
                writer = csv.writer(f)

                # write the header
                header = [
                    "SACCO NAME",
                    "ADDRESS",
                    "PENDING LOANS",
                    "APPROVED LOANS",
                    "DISBURSED LOANS",
                    "CLEARED OFF LOANS",
                    "WRITTEN OFF LOANS",
                ]
                writer.writerow(header)

                # write the data
                writer.writerows(responses)

            return Response({"responses": "Done"})

        if organisation and refine == "reverse_fixed_deposits":
            # credit 6507 debit 16941   65
            system_transactions = SystemTransactions.objects.filter(
                branch__id=65,
                credit_chart__id=6507,
                debit_chart__id=16941,
                reference_no__startswith="fx-py-",
                record_date__date="2024-05-10",
            )
            for system_transaction in system_transactions:
                credit = system_transaction.credit_chart
                debit = system_transaction.debit_chart

                system_transaction.debit_chart = credit
                system_transaction.credit_chart = debit
                system_transaction.save()

            system_transactions = SystemTransactions.objects.filter(
                branch__id=66,
                credit_chart__id=6507,
                debit_chart__id=16944,
                reference_no__startswith="fx-py-",
                record_date__date="2024-05-10",
            )
            for system_transaction in system_transactions:
                credit = system_transaction.credit_chart
                debit = system_transaction.debit_chart

                system_transaction.debit_chart = credit
                system_transaction.credit_chart = debit
                system_transaction.save()

            return Response({"message": "Reversed successfully"})

        if organisation and refine == "remove_auto_payments":
            loan_payments = LoanPaymentTransaction.objects.filter(
                loan_application__organisation_branch__branch_organisation__id=119,
                transaction_type="auto",
            )
            for loan_payment in loan_payments:
                loan_repayment_delete(request, loan_payment.id, "delete")
            return Response({"message": "Delete successfully"})

        if organisation and refine == "savings_interest_payments":
            interest_payments = SavingsProductInterestPayment.objects.all()
            for interest_payment in interest_payments:
                interest_payment.from_date = interest_payment.record_date
                interest_payment.to_date = interest_payment.exp_payment_date

                if interest_payment.status == "processed":
                    interest_payment.payment_status = "Completed"
                interest_payment.save()

        if organisation and refine == "delete_customer_by_id":
            customer_id = self.request.data.get("id", None)
            if customer_id:
                Customer.objects.filter(
                    id=customer_id,
                    customer_branch__branch_organisation__id=organisation,
                ).update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
                add_system_audit_trail('transaction_management', 'delete_customer',
                    f'Deleted customer id: {customer_id}', '', {}, {}, request.user,
                    OrganisationBranch.objects.filter(branch_organisation__id=organisation).first())

        if organisation and refine == "duplicate_savings_accounts":
            results = []
            customers = Customer.objects.filter(
                customer_branch__branch_organisation__id=25
            )
            for customer in customers:
                savings_products = SavingsProduct.objects.filter(
                    saving_product_org__id=25
                )
                for savings_product in savings_products:
                    savings_accounts = SavingAccount.objects.filter(
                        account_customer=customer, account_product=savings_product
                    )
                    if len(savings_accounts) > 1:
                        results.append(
                            {
                                "id": customer.id,
                                "old_member_number": customer.old_member_number,
                            }
                        )

            return Response({"results": results})

        if organisation and refine == "delete_org_penalties":
            LoanPenalty.objects.filter(
                loan_application__status="disbursed",
                loan_application__organisation_branch__branch_organisation__id=20,
                date_added__date="2024-10-11",
                status="auto",
            ).update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
            add_system_audit_trail('transaction_management', 'delete_org_loan_penalties',
                f'Deleted auto loan penalties for organisation id: {organisation}',
                '', {}, {}, request.user,
                OrganisationBranch.objects.filter(branch_organisation__id=organisation).first())

        if organisation and refine == "calculate_savings_accounts":
            count = 0
            male_count = 0
            female_count = 0
            other_count = 0
            organisations = Organisation.objects.filter(
                admin_organisation__id=101
            ).order_by("id")
            for organisation in organisations:
                customers = Customer.objects.filter(
                    customer_branch__branch_organisation=organisation
                )
                for customer in customers:
                    saving_account = SavingAccount.objects.filter(
                        account_customer=customer
                    ).first()
                    if saving_account:
                        acc_bal = get_account_balance(saving_account, "2024-09-30")[
                            "balance_actual"
                        ]
                        if acc_bal > 0:
                            count += 1

            return Response({"count": count})

        return Response({"message": "Completed successfully"})

    def reverse_kasaana_transactions(self):
        transactions = SystemTransactions.objects.filter(
            branch__id__in=[27, 29, 30, 31]
        ).order_by("id")
        for transaction in transactions:
            old_tran = SystemTransactionsBackup.objects.filter(
                id=transaction.id
            ).first()
            if old_tran and old_tran.record_date:
                transaction.record_date = old_tran.record_date
                transaction.save()

        send_email("Kasaana Reversed successfully", "Kasaaana")

    def remove_sms_doulble_entry(self):
        system_transactions = SystemTransactions.objects.all()
        for system_transaction in system_transactions:
            savings_account = SavingAccountTransactions.objects.filter(
                transaction_type="sms_charge", transaction=system_transaction
            ).order_by("id")
            if savings_account and len(savings_account) > 1:
                savings_account[1].deleted = True
                savings_account[1].deleted_at = timezone.now()
                savings_account[1].deleted_by = self.request.user
                savings_account[1].save()
                add_system_audit_trail('transaction_management', 'delete_duplicate_sms_charge',
                    f'Deleted duplicate sms_charge saving transaction id: {savings_account[1].id}',
                    '', {}, {}, self.request.user, system_transaction.branch)


class LoansTransactionDetailsView(APIView):
    def post(self, request):
        transaction_id = self.request.data.get("transaction_id", None)
        record_date = self.request.data.get("record_date", None)
        update_reason = self.request.data.get("update_reason", None)
        type = self.request.data.get("type", None)

        if type == "edit" and transaction_id and record_date and update_reason:
            update_details = {"comment": update_reason, "record_date": record_date}
            loans_transaction_management(
                request, type, update_details, transaction_id, None
            )

        elif type == "delete":
            if not update_reason:
                update_reason = "Deletion"
            loan_repayment_delete(request, transaction_id, update_reason)

        elif type == "reversal" and transaction_id:
            reversal = loan_repayment_reversal(
                request, update_reason, None, transaction_id
            )
            if not reversal:
                return Response(
                    {"message": "Failed to reverse the transaction"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                )

        return Response({"message": "Updated Successfully"})

    def get(self, request):
        id = self.request.GET.get("id", None)
        transaction_type = self.request.GET.get("transaction_type", None)

        response_obj = {}
        if transaction_type == "repayment":
            loan_payment_transaction = LoanPaymentTransaction.objects.filter(
                id=id
            ).first()
            if loan_payment_transaction:
                payment_transactions_list = []
                loan_payments = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_payment_transaction,
                    payment_status="normal",
                )
                if loan_payments:
                    for payment in loan_payments:
                        payment_transactions_list.append(
                            payment.loan_main_transaction.id
                        )

                transactions_obj = []
                account_transaction_obj = {}
                transactions = LoanMainTransactions.objects.filter(
                    deleted=False, id__in=payment_transactions_list
                ).order_by("id")
                for transaction in transactions:
                    if transaction.system_transaction:
                        account_transaction_obj = transaction.system_transaction

                        savings_details = (
                            transaction.system_transaction.system_transactions.first()
                        )
                        if savings_details:
                            transactions_obj.append(
                                {
                                    "transaction_type": transaction.transaction_type,
                                    "id": transaction.id,
                                    "amount": transaction.system_transaction.amount,
                                    "heading": transaction.system_transaction.heading,
                                    "credit_chart": transaction.system_transaction.credit_chart.account_name,
                                    "credit_chart_code": transaction.system_transaction.credit_chart.account_code,
                                    "debit_chart": savings_details.customer_account.account_customer.name,
                                    "debit_chart_code": savings_details.customer_account.account_no,
                                    "reference_no": transaction.system_transaction.reference_no,
                                }
                            )
                        else:
                            transactions_obj.append(
                                {
                                    "transaction_type": transaction.transaction_type,
                                    "id": transaction.id,
                                    "amount": transaction.system_transaction.amount,
                                    "heading": transaction.system_transaction.heading,
                                    "credit_chart": transaction.system_transaction.credit_chart.account_name,
                                    "credit_chart_code": transaction.system_transaction.credit_chart.account_code,
                                    "debit_chart": transaction.system_transaction.debit_chart.account_name,
                                    "debit_chart_code": transaction.system_transaction.debit_chart.account_code,
                                    "reference_no": transaction.system_transaction.reference_no,
                                }
                            )
                if account_transaction_obj:
                    branch_name = None
                    inter_branch = InterBranchTransactions.objects.filter(
                        Q(source_transaction=account_transaction_obj)
                        | Q(destination_transaction=account_transaction_obj)
                    ).first()
                    if inter_branch:
                        branch_name = inter_branch.source_transaction.branch.name
                    else:
                        branch_name = account_transaction_obj.branch.name

                    response_obj = {
                        "id": id,
                        "payments": transactions_obj,
                        "teller": (
                            account_transaction_obj.added_by.username
                            if account_transaction_obj.added_by
                            else ""
                        ),
                        "date_added": account_transaction_obj.date_added,
                        "payment_date": account_transaction_obj.record_date,
                        "branch_name": branch_name,
                        "payment_method": account_transaction_obj.payment_method,
                        "voucher_no": account_transaction_obj.voucher_no,
                        "transaction_type": transaction_type,
                    }

        return Response({"results": response_obj})


class MultipleLoansDeleteView(APIView):
    def post(self, request):
        loan_application_ids = self.request.data.get("loans")
        if loan_application_ids:
            loans = LoanApplication.objects.filter(id__in=loan_application_ids)
            for loan_obj in loans:
                delete_loan_by_id(
                    loan_obj.id,
                    'permanent',
                    None,
                    user_id=request.user.id,
                )
        return Response({"message": "Success"})


class LoansAddPaymentsCSVView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        datetime = datetime_timedelta.datetime
        data = []
        loan_start_date = datetime.strptime("2023-08-03", "%Y-%m-%d")
        loan_end_date = datetime.strptime("2023-08-18", "%Y-%m-%d")
        disbursements = LoanApplicationDisbursement.objects.filter(
            system_transaction__record_date__date__lte="2024-05-30",
            system_transaction__branch__branch_organisation__id=94,
        )
        for disbursement in disbursements:
            loan_payments = LoanPaymentTransaction.objects.filter(
                payment_date__date__gte="2024-05-31",
                transaction_status="normal",
                loan_application=disbursement.loan_application,
            ).order_by("date_added")
            for loan_payment in loan_payments:
                total_int_paid = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_payment,
                    loan_main_transaction__transaction_type="InterestPayment",
                ).aggregate(total_int_paid=Sum("int_paid"))["total_int_paid"]
                total_princ_paid = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_payment,
                    loan_main_transaction__transaction_type="LoanPrincipalPayment",
                ).aggregate(total_princ_paid=Sum("princ_paid"))["total_princ_paid"]
                total_penalty_paid = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_payment,
                    loan_main_transaction__transaction_type="PenaltyPayment",
                ).aggregate(total_penalty_paid=Sum("penalty_paid"))[
                    "total_penalty_paid"
                ]

                total_int_paid = total_int_paid if total_int_paid else 0
                total_princ_paid = total_princ_paid if total_princ_paid else 0
                total_penalty_paid = total_penalty_paid if total_penalty_paid else 0

                paid_by = (
                    loan_payment.loan_payment_transaction_added_by.username
                    if loan_payment.loan_payment_transaction_added_by
                    else ""
                )
                branch = (
                    loan_payment.loan_payment_transaction_added_by.user_organisation_branch.name
                    if loan_payment.loan_payment_transaction_added_by
                    else ""
                )

                payment_method_name = ""
                payment_method = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_payment
                ).first()
                if payment_method:
                    payment_method_name = (
                        payment_method.loan_main_transaction.system_transaction.payment_method
                    )

                data.append(
                    [
                        loan_payment.loan_application.customer.member_number,
                        disbursement.loan_amount,
                        loan_payment.amount,
                        total_princ_paid,
                        total_int_paid,
                        total_penalty_paid,
                        loan_payment.payment_date,
                        loan_payment.transaction_type,
                        paid_by,
                        branch,
                        loan_payment.date_added,
                        disbursement.loan_disbursement_date,
                        payment_method_name,
                    ]
                )

        with open(
            settings.STATIC_ROOT + "/files/nyabumba_payments.csv", "w", encoding="UTF8"
        ) as f:
            writer = csv.writer(f)

            # write the header
            header = [
                "Member Number",
                "Loan Amount",
                "Amount Paid",
                "Princ",
                "Int",
                "penalty",
                "Payment Date",
                "Payment Type",
                "Added By",
                "Branch",
                "Date Added",
                "Loan Date",
                "Payment Method",
            ]
            writer.writerow(header)

            # write the data
            writer.writerows(data)

        return Response({"message": "Generated"})


class BDBackLoanPaymentsCSVView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        # date_added  = self.request.data.get('date_added')
        # payment_date  = self.request.data.get('payment_date')
        upload_savings = threading.Thread(
            target=self.backdate_loan_payments, args=(organisation)
        )

        # starting upload_savings thread
        upload_savings.start()
        return Response({"message": "Updated"})

    def backdate_loan_payments(self, organisation):
        date_added = "2024-05-04"
        payment_date = date_time_zone_convert("2024-04-30")
        org_loans = LoanApplication.objects.filter(
            organisation_branch__branch_organisation__id=organisation
        )
        for org_loan in org_loans:
            loan_payment = (
                LoanPaymentTransaction.objects.filter(
                    loan_application=org_loan, payment_date__date__lte=date_added
                )
                .order_by("id")
                .first()
            )
            if loan_payment:
                loan_payments = LoanPayments.objects.filter(
                    loan_payment_transaction=loan_payment
                )
                for loan_payment_ob in loan_payments:
                    system_transaction = (
                        loan_payment_ob.loan_main_transaction.system_transaction
                        if loan_payment_ob.loan_main_transaction
                        and loan_payment_ob.loan_main_transaction.system_transaction
                        else None
                    )
                    payment_method = (
                        system_transaction.payment_method
                        if system_transaction
                        else "offset"
                    )
                    if (
                        payment_method == "cash"
                        and system_transaction
                        and int(system_transaction.debit_chart.id) == 16941
                    ):
                        system_tran_obj = SystemTransactions.objects.get(
                            pk=system_transaction.id
                        )
                        system_tran_obj.record_date = payment_date
                        system_tran_obj.save()

                        loan_payment_ob.payment_date = payment_date
                        loan_payment_ob.save()

                        loan_main_transaction = loan_payment_ob.loan_main_transaction
                        loan_main_transaction.payment_date = payment_date
                        loan_main_transaction.save()

                        loan_payment.payment_date = payment_date
                        loan_payment.save()

        return True


class UpdateCustomerDOBView(APIView):
    def random_dob(self, min_age, max_age):
        today = date.today()
        start_year = today.year - max_age
        end_year = today.year - min_age
        start_date = date(start_year, 1, 1)
        end_date = date(end_year, 12, 31)
        delta = end_date - start_date
        random_days = random.randint(0, delta.days)
        return start_date + timedelta(days=random_days)

    def post(self, request):
        organisation_ids = [
            345,
            348,
            357,
            341,
            319,
            349,
            329,
            363,
            289,
            288,
            362,
            364,
            365,
            367,
            366,
            368,
            373,
            370,
            350,
            380,
            304,
            286,
            287,
            290,
            291,
            303,
            299,
            301,
            322,
            298,
            406,
            407,
            408,
            409,
            410,
            411,
            412,
            413,
            414,
            415,
            307,
            326,
            338,
            337,
            336,
            429,
            324,
            327,
            372,
            377,
            382,
            383,
            292,
            293,
            294,
            296,
            297,
            313,
            305,
            310,
            311,
            388,
            489,
            389,
            295,
            300,
            360,
            490,
            496,
            371,
            351,
            418,
            400,
            491,
            424,
            387,
            492,
            342,
            493,
            437,
            494,
            497,
            495,
            498,
            386,
            314,
            306,
            309,
            312,
            403,
            420,
            405,
            390,
            346,
            392,
            398,
            399,
            434,
            394,
            393,
            391,
            395,
            396,
            401,
            397,
            328,
            330,
            331,
            332,
            333,
            334,
            335,
            315,
            308,
            428,
            427,
            421,
            423,
            433,
            417,
            431,
            422,
            425,
            430,
            436,
            432,
            499,
            426,
        ]
        # organisation_ids = [325]
        for organisationid in organisation_ids:
            print(
                "-----------------------------------------",
                organisationid,
                "-------------------------------",
            )
            dob_ids = CustomerTypeField.objects.filter(
                customer_reg_field__id__in=[11, 30],
                organisation__id=organisationid,
                is_active=True,
            ).values_list("id", flat=True)
            if len(dob_ids) > 2:
                CustomerTypeField.objects.filter(
                    customer_reg_field__id__in=[11, 30], organisation__id=organisationid
                ).update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
                add_system_audit_trail('transaction_management', 'delete_customer_type_fields',
                    f'Deleted duplicate customer type fields for organisation id: {organisationid}',
                    '', {}, {}, request.user,
                    OrganisationBranch.objects.filter(branch_organisation__id=organisationid).first())
                dob_ids = []

            if len(dob_ids) < 1:
                field = CustomerRegField.objects.filter(id=11).first()
                customer_type = (
                    CustomerType.objects.filter(organisation__id=organisationid)
                    .order_by("id")
                    .first()
                )
                if field and customer_type:
                    obj = {
                        "org_field_label": field.field_label,
                        "org_field_abbreviation": field.field_abbreviation,
                        "customer_type_field": customer_type,
                        "customer_reg_field": field,
                        "organisation": customer_type.organisation,
                    }
                    CustomerTypeField.objects.create(**obj)

                    dob_ids = CustomerTypeField.objects.filter(
                        customer_reg_field__id=11, organisation__id=organisationid
                    ).values_list("id", flat=True)

            customers = Customer.objects.filter(
                customer_branch__branch_organisation__id=organisationid
            )
            missing_ids = []
            for customer in customers:
                customer_field_data = CustomerFieldMeta.objects.filter(
                    customer_field__id__in=dob_ids, customer__id=customer.id
                )

                if not customer_field_data:
                    missing_ids.append(customer.id)

            print("total", len(missing_ids))
            if len(missing_ids) > 0:
                total = len(missing_ids)
                cut = math.floor(total * 0.7)
                youth_list = missing_ids[:cut]
                old_people_list = missing_ids[cut:]

                print("division", len(youth_list), len(old_people_list))

                for youth in youth_list:
                    dob = self.random_dob(16, 35)
                    y_customer = Customer.objects.filter(id=youth).first()
                    dob_field = CustomerTypeField.objects.filter(
                        customer_reg_field__id=11, organisation__id=organisationid
                    ).first()
                    if dob_field and y_customer:
                        save_obj = {
                            "value": dob,
                            "customer_field": dob_field,
                            "customer": y_customer,
                        }
                        CustomerFieldMeta.objects.create(**save_obj)
                        print(youth, dob)

                for old_person in old_people_list:
                    dob = self.random_dob(36, 60)
                    o_customer = Customer.objects.filter(id=old_person).first()
                    dob_field = CustomerTypeField.objects.filter(
                        customer_reg_field__id=11, organisation__id=organisationid
                    ).first()
                    if dob_field:
                        save_obj = {
                            "value": dob,
                            "customer_field": dob_field,
                            "customer": o_customer,
                        }
                        CustomerFieldMeta.objects.create(**save_obj)
                        print(old_person, dob)

        return Response({"message": "Updated successfully"})


class LoanPaymentsCSVView(APIView):
    def post(self, request):
        response = []
        organisation = self.request.data.get("organisation")
        loan_applications = LoanApplication.objects.filter(
            organisation_branch__branch_organisation__id=organisation
        )
        for loan_application in loan_applications:
            loans_payments = LoanPayments.objects.filter(
                loan_application=loan_application,
                princ_paid__gt=0,
                payment_status="normal",
                loan_application__loan_application_product__id=25,
            ).aggregate(total=Sum("princ_paid"))["total"]
            loan_main = LoanMainTransactions.objects.filter(
                deleted=False,
                loan_application=loan_application,
                transaction_type="LoanPrincipalPayment",
                transaction_status="normal",
                loan_application__loan_application_product__id=25,
            ).aggregate(total=Sum("amount"))["total"]
            if loans_payments != loan_main:
                response.append(
                    {
                        "id": loan_application.id,
                        "member_no": loan_application.customer.member_number,
                        "loan": loan_application.loan_amount,
                    }
                )

        return Response({"results": response, "count": len(response)})


class BDBackDatePaymentsCSVView(APIView):
    def post(self, request):
        # upload_savings = threading.Thread(target=self.backdate_loan_payments, args=())
        upload_savings = threading.Thread(
            target=self.backdate_loan_payments_per_schedule, args=()
        )

        # starting upload_savings thread
        upload_savings.start()
        return Response({"message": "Initiated"})

    def backdate_loan_payments(self):
        loan_payment_transactions = LoanPaymentTransaction.objects.filter(
            date_added__date="2023-08-16",
            transaction_type="auto",
            loan_application__organisation_branch__branch_organisation__id=21,
        )
        payment_date = make_aware(
            datetime.strptime("2023-08-16" + " 01:00:00", "%Y-%m-%d %H:%M:%S")
        )
        for loan_payment_transaction in loan_payment_transactions:
            loan_payment_transaction.payment_date = payment_date
            loan_payment_transaction.save()

            loan_payments = LoanPayments.objects.filter(
                loan_payment_transaction=loan_payment_transaction
            )
            for loan_payment in loan_payments:
                loan_payment.payment_date = payment_date
                loan_payment.save()

                transaction = LoanMainTransactions.objects.filter(
                    deleted=False, id=loan_payment.loan_main_transaction.id
                ).first()
                if transaction:
                    transaction.payment_date = payment_date
                    transaction.save()

                    if transaction.system_transaction:
                        system_transaction = SystemTransactions.objects.get(
                            pk=transaction.system_transaction.id
                        )
                        if system_transaction:
                            system_transaction.record_date = payment_date
                            system_transaction.save()
        return True

    def backdate_loan_payments_per_schedule(self):
        loan_payment_transactions = LoanPaymentTransaction.objects.filter(
            date_added__date="2023-08-16",
            transaction_type="auto",
            loan_application__organisation_branch__branch_organisation__id=21,
        )
        for loan_payment_transaction in loan_payment_transactions:
            loan_payment = LoanPayments.objects.filter(
                loan_payment_transaction=loan_payment_transaction
            ).first()
            if loan_payment and loan_payment.loan_repayment_schedule:
                savings_account = (
                    SavingAccount.objects.filter(
                        account_customer=loan_payment_transaction.loan_application.customer,
                        status="active",
                    )
                    .all()
                    .order_by("id")
                    .first()
                )
                if savings_account:
                    as_at = make_aware(
                        datetime.strptime(
                            loan_payment.loan_repayment_schedule.expected_date.strftime(
                                "%Y-%m-%d"
                            ),
                            "%Y-%m-%d",
                        )
                    )
                    account_bal = get_account_balance(savings_account, as_at)
                    account_balance = (
                        account_bal["balance_raw"]
                        if account_bal and account_bal["balance_raw"] > 0
                        else 0
                    )
                    if account_balance >= loan_payment_transaction.amount:
                        self.make_back_dating_payments(
                            loan_payment_transaction.id,
                            loan_payment.loan_repayment_schedule.expected_date,
                        )

    def make_back_dating_payments(self, transaction_id, record_date):
        loan_payment_transaction = LoanPaymentTransaction.objects.filter(
            id=transaction_id
        ).first()
        if loan_payment_transaction:
            loan_payment_transaction.payment_date = record_date
            loan_payment_transaction.save()

            loan_payments = LoanPayments.objects.filter(
                loan_payment_transaction=loan_payment_transaction
            )
            for loan_payment in loan_payments:
                loan_payment.payment_date = record_date
                loan_payment.save()

                transaction = LoanMainTransactions.objects.filter(
                    deleted=False, id=loan_payment.loan_main_transaction.id
                ).first()
                if transaction:
                    transaction.payment_date = record_date
                    transaction.save()

                    if transaction.system_transaction:
                        system_transaction = SystemTransactions.objects.get(
                            pk=transaction.system_transaction.id
                        )
                        if system_transaction:
                            system_transaction.record_date = record_date
                            system_transaction.save()

        return True


class BDLoansRemovePaymentsCSVView(APIView):
    def post(self, request):
        # upload_savings = threading.Thread(target=self.delete_loans, args=())
        # # starting upload_savings thread
        # upload_savings.start()
        print("pppppp")
        # loans = LoanApplication.objects.filter(organisation_branch__id=19, status='disbursed')
        # for loan in loans:
        #     loan_payments = LoanPayments.objects.filter(loan_application=loan)
        #     for loan_payment in loan_payments:
        #         loan_main_transactions = LoanMainTransactions.objects.filter(id=loan_payment.loan_main_transaction.id).all()
        #         for loan_main_transaction in loan_main_transactions:
        #             total_loan_payments = LoanPayments.objects.filter(loan_main_transaction=loan_main_transaction).aggregate(total_amount=Sum('princ_paid'))['total_amount']
        #             total_loan_payments = total_loan_payments if total_loan_payments else 0

        #             system_transaction = SystemTransactions.objects.filter(id=loan_main_transaction.system_transaction.id).first()
        #             system_transaction.amount = total_loan_payments
        #             system_transaction.save()

        # system_transactions = SystemTransactions.objects.filter(branch__id__in=[19])
        # for system_transaction in system_transactions:
        #     loan_main_transactions = LoanMainTransactions.objects.filter(system_transaction=system_transaction).all()
        #     for loan_main_transaction in loan_main_transactions:
        # payment = LoanPayments.objects.filter(loan_main_transaction=loan_main_transaction).first()
        # loan_payments = loan_payments if loan_payments else 0
        # trans_date = system_transaction.record_date.strftime('%Y-%m-%d')
        # if payment:
        #     payment_date = payment.payment_date.strftime('%Y-%m-%d')
        #     if payment_date != trans_date:
        #         print(system_transaction.reference_no)
        # if float(system_transaction.amount) != float(loan_payments) and loan_payments > 0:
        #     print(system_transaction.reference_no)
        # remove duplicated accounts

        customers = Customer.objects.filter(customer_branch__branch_organisation__id=33)
        for customer in customers:
            savings_accounts_1 = SavingAccount.objects.filter(
                account_customer=customer, account_product__id=55
            )
            if savings_accounts_1 and len(savings_accounts_1) > 1:
                savings_account_1 = (
                    SavingAccount.objects.filter(
                        account_customer=customer, account_product__id=55
                    )
                    .order_by("-id")
                    .first()
                )
                savings_account_1.deleted = True
                savings_account_1.deleted_at = timezone.now()
                savings_account_1.deleted_by = request.user
                savings_account_1.save()
                add_system_audit_trail('transaction_management', 'delete_duplicate_savings_account',
                    f'Deleted duplicate savings account id: {savings_account_1.id}',
                    '', {}, {}, request.user, savings_account_1.customer_branch)

            savings_accounts_2 = SavingAccount.objects.filter(
                account_customer=customer, account_product__id=56
            )
            if savings_accounts_2 and len(savings_accounts_2) > 1:
                savings_account_2 = (
                    SavingAccount.objects.filter(
                        account_customer=customer, account_product__id=56
                    )
                    .order_by("-id")
                    .first()
                )
                savings_account_2.deleted = True
                savings_account_2.deleted_at = timezone.now()
                savings_account_2.deleted_by = request.user
                savings_account_2.save()
                add_system_audit_trail('transaction_management', 'delete_duplicate_savings_account',
                    f'Deleted duplicate savings account id: {savings_account_2.id}',
                    '', {}, {}, request.user, savings_account_2.customer_branch)

        return Response({"message": "Initiated"})

    def delete_loans(self):
        # date_added = make_aware(datetime.strptime('2023-07-23', '%Y-%m-%d'))
        loan_payment_transactions = LoanPaymentTransaction.objects.filter(
            loan_application__organisation_branch__id=31
        )
        for loan_payment_transaction in loan_payment_transactions:
            main_transactions = []
            loan_payments = LoanPayments.objects.filter(
                loan_payment_transaction=loan_payment_transaction
            )
            for loan_payment in loan_payments:
                main_transactions.append(loan_payment.loan_main_transaction.id)

            loan_repayment_delete(self.request, loan_payment_transaction.id, "Deletion")
        return True


class RemoveLoansCSVView(APIView):
    parser_classes = (MultiPartParser,)

    def allowed_file(self, filename):
        return "." in filename.name and filename.name.split(".")[1].lower() in ["csv"]

    def post(self, request, format=None):
        file_obj = request.FILES["file"]

        if file_obj and self.allowed_file(file_obj):
            # # save savings upload
            file_obj.seek(0)
            upload_savings = threading.Thread(
                target=self.delete_loans_uploaded, args=(file_obj,)
            )
            # starting upload_savings thread
            upload_savings.start()
            # self.delete_loans_uploaded(file_obj)

        return Response({"message": "Initiated"})

    def delete_loans_uploaded(self, file_obj):
        failed_transactions = []
        processed_transactions = []
        file_obj.seek(0)
        reader = csv.DictReader(io.StringIO(file_obj.read().decode("utf-8")))
        for row in reader:
            value = list(row.values())
            member_number = value[0]
            loan_amount = int(value[1])
            amount_paid = int(value[2])
            princ_paid = int(value[3])
            int_paid = int(value[4])
            penalty_paid = int(value[5])
            payment_date = value[6]
            transaction_type = value[7]
            added_by = value[8]
            date_added = value[9]
            loan_date = value[10]

            datetime = datetime_timedelta.datetime
            try:
                date_added = make_aware(
                    datetime.strptime(date_added + " 01:00:00", "%Y-%m-%d %H:%M:%S")
                )
                payment_date = make_aware(
                    datetime.strptime(payment_date + " 01:00:00", "%Y-%m-%d %H:%M:%S")
                )

                loan_payment_transactions = LoanPaymentTransaction.objects.filter(
                    loan_application__customer__member_number=member_number,
                    amount=float(amount_paid),
                    loan_application__loan_amount=float(loan_amount),
                    transaction_type=transaction_type,
                )
                if loan_payment_transactions:

                    for loan_payment_transaction in loan_payment_transactions:
                        payments = LoanPayments.objects.filter(
                            loan_payment_transaction=loan_payment_transaction
                        ).aggregate(
                            total_int_paid=Sum("int_paid"),
                            total_princ_paid=Sum("princ_paid"),
                            total_penalty_paid=Sum("penalty_paid"),
                        )
                        total_int_paid = (
                            int(payments["total_int_paid"])
                            if payments["total_int_paid"]
                            else 0
                        )
                        total_princ_paid = (
                            int(payments["total_princ_paid"])
                            if payments["total_princ_paid"]
                            else 0
                        )
                        total_penalty_paid = (
                            int(payments["total_penalty_paid"])
                            if payments["total_penalty_paid"]
                            else 0
                        )

                        if (
                            total_int_paid == int_paid
                            and penalty_paid == total_penalty_paid
                            and princ_paid == total_princ_paid
                        ):
                            loan_repayment_delete(self.request, loan_payment_transaction.id, 'Deletion')

                            processed_transactions.append(
                                [
                                    member_number,
                                    loan_amount,
                                    amount_paid,
                                    date_added,
                                    loan_date,
                                    payment_date,
                                    princ_paid,
                                    int_paid,
                                    penalty_paid,
                                ]
                            )

                        else:
                            failed_transactions.append(
                                [
                                    member_number,
                                    loan_amount,
                                    amount_paid,
                                    date_added,
                                    loan_date,
                                    payment_date,
                                    princ_paid,
                                    int_paid,
                                    penalty_paid,
                                ]
                            )
                else:
                    failed_transactions.append(
                        [
                            member_number,
                            loan_amount,
                            amount_paid,
                            date_added,
                            loan_date,
                            payment_date,
                            princ_paid,
                            int_paid,
                            penalty_paid,
                        ]
                    )

                other_transactions = LoanPaymentTransaction.objects.filter(
                    loan_application__customer__member_number=member_number
                )
                for other_transaction in other_transactions:
                    loan_payments = LoanPayments.objects.filter(
                        loan_payment_transaction=other_transaction
                    )
                    if not loan_payments:
                        LoanPaymentTransaction.objects.filter(
                            id=other_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail('transaction_management', 'delete_orphan_loan_payment_transaction',
                            f'Deleted orphan loan payment transaction id: {other_transaction.id} for member: {member_number}',
                            '', {}, {}, self.request.user, other_transaction.loan_application.organisation_branch)

                other_system_transactions = SystemTransactions.objects.filter(
                    heading__icontains="Loan interest income: (" + str(member_number),
                    reference_no__icontains="ln-in-",
                    branch__branch_organisation__id=21,
                )
                for other_system_transaction in other_system_transactions:
                    main_trans = LoanMainTransactions.objects.filter(
                        deleted=False, system_transaction=other_system_transaction
                    ).first()
                    if not main_trans:
                        SystemTransactions.objects.filter(
                            id=other_system_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                            f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                            "", {}, {}, self.request.user, other_system_transaction.branch)
                    else:
                        paym = LoanPayments.objects.filter(
                            loan_main_transaction=main_trans
                        ).first()
                        if not paym:
                            SystemTransactions.objects.filter(
                                id=other_system_transaction.id
                            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                            add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                                f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                                "", {}, {}, self.request.user, other_system_transaction.branch)

                other_system_transactions = SystemTransactions.objects.filter(
                    heading__icontains="Loan principal payment: (" + str(member_number),
                    reference_no__icontains="ln-p-",
                    branch__branch_organisation__id=21,
                )
                for other_system_transaction in other_system_transactions:
                    main_trans = LoanMainTransactions.objects.filter(
                        deleted=False, system_transaction=other_system_transaction
                    ).first()
                    if not main_trans:
                        SystemTransactions.objects.filter(
                            id=other_system_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                            f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                            "", {}, {}, self.request.user, other_system_transaction.branch)
                    else:
                        paym = LoanPayments.objects.filter(
                            loan_main_transaction=main_trans
                        ).first()
                        if not paym:
                            SystemTransactions.objects.filter(
                                id=other_system_transaction.id
                            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                            add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                                f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                                "", {}, {}, self.request.user, other_system_transaction.branch)

                other_system_transactions = SystemTransactions.objects.filter(
                    heading__icontains="Loan penalty income: (" + str(member_number),
                    reference_no__icontains="ln-in-",
                    branch__branch_organisation__id=21,
                )
                for other_system_transaction in other_system_transactions:
                    main_trans = LoanMainTransactions.objects.filter(
                        deleted=False, system_transaction=other_system_transaction
                    ).first()
                    if not main_trans:
                        SystemTransactions.objects.filter(
                            id=other_system_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                            f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                            "", {}, {}, self.request.user, other_system_transaction.branch)
                    else:
                        paym = LoanPayments.objects.filter(
                            loan_main_transaction=main_trans
                        ).first()
                        if not paym:
                            SystemTransactions.objects.filter(
                                id=other_system_transaction.id
                            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                            add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                                f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                                "", {}, {}, self.request.user, other_system_transaction.branch)

                other_system_transactions = SystemTransactions.objects.filter(
                    heading__icontains="Auto loan penalty income: ("
                    + str(member_number),
                    reference_no__icontains="ln-in-",
                    branch__branch_organisation__id=21,
                )
                for other_system_transaction in other_system_transactions:
                    main_trans = LoanMainTransactions.objects.filter(
                        deleted=False, system_transaction=other_system_transaction
                    ).first()
                    if not main_trans:
                        SystemTransactions.objects.filter(
                            id=other_system_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                            f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                            "", {}, {}, self.request.user, other_system_transaction.branch)
                    else:
                        paym = LoanPayments.objects.filter(
                            loan_main_transaction=main_trans
                        ).first()
                        if not paym:
                            SystemTransactions.objects.filter(
                                id=other_system_transaction.id
                            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                            add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                                f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                                "", {}, {}, self.request.user, other_system_transaction.branch)

                other_system_transactions = SystemTransactions.objects.filter(
                    heading__icontains="Auto loan interest income: ("
                    + str(member_number),
                    reference_no__icontains="ln-in-",
                    branch__branch_organisation__id=21,
                )
                for other_system_transaction in other_system_transactions:
                    main_trans = LoanMainTransactions.objects.filter(
                        deleted=False, system_transaction=other_system_transaction
                    ).first()
                    if not main_trans:
                        SystemTransactions.objects.filter(
                            id=other_system_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                            f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                            "", {}, {}, self.request.user, other_system_transaction.branch)
                    else:
                        paym = LoanPayments.objects.filter(
                            loan_main_transaction=main_trans
                        ).first()
                        if not paym:
                            SystemTransactions.objects.filter(
                                id=other_system_transaction.id
                            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                            add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                                f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                                "", {}, {}, self.request.user, other_system_transaction.branch)

                other_system_transactions = SystemTransactions.objects.filter(
                    heading__icontains="Auto loan principal payment: ("
                    + str(member_number),
                    reference_no__icontains="ln-p-",
                    branch__branch_organisation__id=21,
                )
                for other_system_transaction in other_system_transactions:
                    main_trans = LoanMainTransactions.objects.filter(
                        deleted=False, system_transaction=other_system_transaction
                    ).first()
                    if not main_trans:
                        SystemTransactions.objects.filter(
                            id=other_system_transaction.id
                        ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                        add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                            f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                            "", {}, {}, self.request.user, other_system_transaction.branch)
                    else:
                        paym = LoanPayments.objects.filter(
                            loan_main_transaction=main_trans
                        ).first()
                        if not paym:
                            SystemTransactions.objects.filter(
                                id=other_system_transaction.id
                            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=self.request.user.id)
                            add_system_audit_trail("transaction_management", "delete_orphan_system_transaction",
                                f"Deleted orphan system transaction id: {other_system_transaction.id} for member: {member_number}",
                                "", {}, {}, self.request.user, other_system_transaction.branch)

            except Exception as e:
                failed_transactions.append(
                    [
                        member_number,
                        loan_amount,
                        amount_paid,
                        date_added,
                        loan_date,
                        payment_date,
                        princ_paid,
                        int_paid,
                        penalty_paid,
                    ]
                )

        if len(failed_transactions) > 0:
            with open(
                settings.STATIC_ROOT + "/files/delete_failed_trans.csv",
                "w",
                encoding="UTF8",
            ) as f:
                writer = csv.writer(f)
                # write the data
                writer.writerows(failed_transactions)

        if len(processed_transactions) > 0:
            with open(
                settings.STATIC_ROOT + "/files/deleted_trans.csv", "w", encoding="UTF8"
            ) as f:
                writer = csv.writer(f)
                # write the data
                writer.writerows(processed_transactions)

        return True


class LoansPostPaymentsCSVView(APIView):
    parser_classes = (MultiPartParser,)

    def allowed_file(self, filename):
        return "." in filename.name and filename.name.split(".")[1].lower() in ["csv"]

    def post(self, request, format=None):
        file_obj = request.FILES["file"]

        if file_obj and self.allowed_file(file_obj):
            # save savings upload
            file_obj.seek(0)
            upload_savings = threading.Thread(
                target=self.process_payments_upload, args=(file_obj,)
            )
            # starting upload_savings thread
            upload_savings.start()

        return Response({"message": "Initiated"})

    def process_payments_upload(self, file_obj):
        failed_transactions = []
        processed_transactions = []
        file_obj.seek(0)
        reader = csv.DictReader(io.StringIO(file_obj.read().decode("utf-8")))
        for row in reader:
            value = list(row.values())
            member_number = value[0]
            loan_amount = int(value[1])
            amount_paid = int(value[2])
            princ_paid = int(value[3])
            int_paid = int(value[4])
            penalty_paid = int(value[5])
            payment_date = value[6]
            transaction_type = value[7]
            added_by = value[8]
            date_added = value[9]
            loan_date = value[10]
            datetime = datetime_timedelta.datetime
            try:
                loan_application = LoanApplicationDisbursement.objects.filter(
                    loan_application__customer__member_number=member_number,
                    loan_application__status="disbursed",
                    loan_amount=loan_amount,
                ).first()
                if loan_application:
                    loan_date = make_aware(
                        datetime.strptime(loan_date + " 01:00:00", "%Y-%m-%d %H:%M:%S")
                    )
                    date_added = make_aware(
                        datetime.strptime(date_added + " 01:00:00", "%Y-%m-%d %H:%M:%S")
                    )
                    payment_date = make_aware(
                        datetime.strptime(
                            payment_date + " 01:00:00", "%Y-%m-%d %H:%M:%S"
                        )
                    )

                    if princ_paid > 0 or int_paid > 0 or penalty_paid > 0:
                        savings_account = (
                            SavingAccount.objects.filter(
                                account_customer=loan_application.loan_application.customer,
                                status="active",
                            )
                            .all()
                            .order_by("id")
                            .first()
                        )
                        if savings_account:
                            payment_details = {
                                "amount_paid": amount_paid,
                                "principal_paid": princ_paid,
                                "int_paid": int_paid,
                                "penalty_paid": penalty_paid,
                                "payment_method": "offset",
                                "account": savings_account.account_product.accounts_chart.id,
                                "date_added": date_added,
                                "payment_date": payment_date,
                                "voucher_no": "",
                                "cheque": "",
                                "loan_id": loan_application.loan_application.id,
                                "account_id": savings_account.id,
                                "transaction_type": transaction_type,
                                "added_by": added_by,
                            }

                            processed_transactions.append(
                                [
                                    member_number,
                                    loan_amount,
                                    amount_paid,
                                    date_added,
                                    loan_date,
                                    payment_date,
                                    princ_paid,
                                    int_paid,
                                    penalty_paid,
                                ]
                            )
                            bds_process_loan_payment(payment_details)
                        else:
                            failed_transactions.append(
                                [
                                    member_number,
                                    loan_amount,
                                    amount_paid,
                                    date_added,
                                    loan_date,
                                    payment_date,
                                    princ_paid,
                                    int_paid,
                                    penalty_paid,
                                ]
                            )
                else:
                    failed_transactions.append(
                        [
                            member_number,
                            loan_amount,
                            amount_paid,
                            date_added,
                            loan_date,
                            payment_date,
                            princ_paid,
                            int_paid,
                            penalty_paid,
                        ]
                    )
            except Exception as e:
                failed_transactions.append(
                    [
                        member_number,
                        loan_amount,
                        amount_paid,
                        date_added,
                        loan_date,
                        payment_date,
                        princ_paid,
                        int_paid,
                        penalty_paid,
                    ]
                )

        if len(failed_transactions) > 0:
            with open(
                settings.STATIC_ROOT + "/files/failed_transactions.csv",
                "w",
                encoding="UTF8",
            ) as f:
                writer = csv.writer(f)
                # write the data
                writer.writerows(failed_transactions)

        if len(processed_transactions) > 0:
            with open(
                settings.STATIC_ROOT + "/files/processed_transactions.csv",
                "w",
                encoding="UTF8",
            ) as f:
                writer = csv.writer(f)
                # write the data
                writer.writerows(processed_transactions)
        return True


class TrashLoansView(APIView):
    def post(self, request):
        organisation_branch = self.request.data.get("organisation", None)
        if organisation_branch:
            loan_objs = LoanApplication.objects.filter(
                organisation_branch__id=organisation_branch
            )
            for loan_obj in loan_objs:
                disbursements = LoanApplicationDisbursement.objects.filter(
                    loan_application=loan_obj,
                    system_transaction__payment_method="cash",
                    system_transaction__credit_chart__id=23274,
                ).first()
                if not disbursements:
                    continue
                delete_loan_by_id(loan_obj.id, 'permanent', None, user_id=request.user.id)
        return Response({"message": "Success"})


class TrashSavingsView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        if organisation:
            df = dict(deleted=True, deleted_at=timezone.now(), deleted_by_id=request.user.id)
            SystemTransactions.objects.filter(
                reference_no__startswith="dep-",
                branch__branch_organisation__id=organisation,
            ).update(**df)
            SystemTransactions.objects.filter(
                reference_no__startswith="wd-",
                branch__branch_organisation__id=organisation,
            ).update(**df)
            _branch = OrganisationBranch.objects.filter(branch_organisation__id=organisation).first()
            add_system_audit_trail('transaction_management', 'trash_savings_transactions',
                f'Trashed all savings transactions for organisation id: {organisation}',
                '', {}, {}, request.user, _branch)
        return Response({"message": "Success"})


class TrashGroupSavingsView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        groups = self.request.data.get("groups")
        if organisation and groups:
            for group in groups:
                group_customers = Customer.objects.filter(
                    member_number=group,
                    customer_branch__branch_organisation__id=organisation,
                )
                for group_customer in group_customers:
                    # group savings
                    savings_account = SavingAccount.objects.filter(
                        account_customer=group_customer
                    )
                    for saving_account in savings_account:
                        saving_trans = SavingAccountTransactions.objects.filter(
                            customer_account=saving_account
                        )
                        for saving_tran in saving_trans:
                            saving_tran.transaction.deleted = True; saving_tran.transaction.deleted_at = timezone.now(); saving_tran.transaction.deleted_by = request.user; saving_tran.transaction.save()

                    # member savings
                    memberships = GroupMembership.objects.filter(group=group_customer)
                    for membership in memberships:
                        savings_accounts = SavingAccount.objects.filter(
                            account_customer=membership.member
                        )
                        for saving_account in savings_accounts:
                            saving_trans = SavingAccountTransactions.objects.filter(
                                customer_account=saving_account
                            )
                            for saving_tran in saving_trans:
                                saving_tran.transaction.deleted = True; saving_tran.transaction.deleted_at = timezone.now(); saving_tran.transaction.deleted_by = request.user; saving_tran.transaction.save()

            _branch = OrganisationBranch.objects.filter(branch_organisation__id=organisation).first()
            add_system_audit_trail('transaction_management', 'trash_group_savings_transactions',
                f'Trashed group savings transactions for organisation id: {organisation}',
                '', {}, {}, request.user, _branch)

        return Response({"message": "Success"})


class TrashSharesView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        if organisation:
            df = dict(deleted=True, deleted_at=timezone.now(), deleted_by_id=request.user.id)
            SystemTransactions.objects.filter(
                reference_no__startswith="p-sh-",
                branch__branch_organisation__id=organisation,
            ).update(**df)
            SystemTransactions.objects.filter(
                reference_no__startswith="w-sh",
                branch__branch_organisation__id=organisation,
            ).update(**df)
            _branch = OrganisationBranch.objects.filter(branch_organisation__id=organisation).first()
            add_system_audit_trail('transaction_management', 'trash_shares_transactions',
                f'Trashed all shares transactions for organisation id: {organisation}',
                '', {}, {}, request.user, _branch)
        return Response({"message": "Success"})


class TrashFixedDepositsView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        if organisation:
            SystemTransactions.objects.filter(
                branch__branch_organisation__id=organisation,
                reference_no__startswith="fx-py-",
            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=request.user.id)
            _branch = OrganisationBranch.objects.filter(branch_organisation__id=organisation).first()
            add_system_audit_trail('transaction_management', 'trash_fixed_deposit_transactions',
                f'Trashed fixed deposit transactions for organisation id: {organisation}',
                '', {}, {}, request.user, _branch)
            transactions = FixedDeposit.objects.filter(
                reference_transaction__branch__branch_organisation__id=organisation,
                reference_transaction__record_date__date__lte="2024-05-04",
            )
            for transaction in transactions:
                system_transaction = SystemTransactions.objects.get(
                    pk=transaction.reference_transaction.id
                )
                system_transaction.deleted = True; system_transaction.deleted_at = timezone.now(); system_transaction.deleted_by_id = request.user.id; system_transaction.save()

                closure_trans = transaction.closure_transaction
                if closure_trans:
                    system_transaction = SystemTransactions.objects.get(
                        pk=closure_trans.id
                    )
                    system_transaction.deleted = True; system_transaction.deleted_at = timezone.now(); system_transaction.deleted_by_id = request.user.id; system_transaction.save()

        return Response({"message": "Success"})


class TrashTBTransactionsView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        if organisation:
            SystemTransactions.objects.filter(
                branch__branch_organisation__id=organisation
            ).update(deleted=True, deleted_at=timezone.now(), deleted_by_id=request.user.id)
            _branch = OrganisationBranch.objects.filter(branch_organisation__id=organisation).first()
            add_system_audit_trail('transaction_management', 'trash_all_transactions',
                f'Trashed ALL system transactions for organisation id: {organisation}',
                '', {}, {}, request.user, _branch)
        return Response({"message": "Success"})


class TrashCustomersView(APIView):
    def post(self, request):
        organisation = self.request.data.get("organisation")
        if organisation:
            Customer.objects.filter(
                customer_branch__branch_organisation__id=organisation
            ).update(deleted=True, deleted_at=timezone.now(), deleted_by=request.user)
            _branch = OrganisationBranch.objects.filter(branch_organisation__id=organisation).first()
            add_system_audit_trail('transaction_management', 'trash_all_customers',
                f'Trashed ALL customers for organisation id: {organisation}',
                '', {}, {}, request.user, _branch)
        return Response({"message": "Success"})


class UpdateCustomerImagesUrlsView(APIView):
    def post(self, request):
        customer_images = CustomerFiles.objects.filter(
            url__startswith="https://app.questbanker.com/uploads/"
        )
        for customer_image in customer_images:
            url = customer_image.url
            new_url = url.replace(
                "https://app.questbanker.com/uploads/",
                "https://fis.questbanker.com/uploads/",
            )
            customer_image.url = new_url
            customer_image.save()
        return Response({"message": "Success"})


class LoanCaseLoadTransferView(APIView):

    def post(self, request):
        comment = self.request.data.get("comment")
        loan_list = self.request.data.get("loan_list")
        new_officer_id = self.request.data.get("receiver_officer")
        organisation_id = get_current_user(self.request, "organisation_id", None)
        branch_id = get_current_user(self.request, "organisation_branch_id", None)

        organisation_branch = OrganisationBranch.objects.get(pk=branch_id)
        if organisation_branch:
            new_officer = Staff.objects.filter(
                id=new_officer_id,
                staff_organisation__id=organisation_id,
                is_active=True,
            ).first()
            if new_officer:
                for loan_id in loan_list:
                    loan_application = LoanApplication.objects.filter(
                        id=loan_id, organisation_branch=organisation_branch
                    ).first()
                    if loan_application:
                        transfering_officer = loan_application.loan_officer
                        loan_application.loan_officer = new_officer
                        loan_application.save()
                        message = f"Loan Transfer: {loan_application.loan_application_product.product_name}({loan_application.loan_amount}) For {loan_application.customer.name} From: {transfering_officer.name} To: {new_officer.name}"
                        add_system_audit_trail(
                            "loans",
                            "loan_case_load_transfer",
                            message.capitalize(),
                            comment,
                            {},
                            {},
                            self.request.user,
                            organisation_branch,
                        )

        return Response({"message": "Loan Transfer Successfully"})


class GreenLoansTrackingReportFiltersDataView(APIView):

    def post(self, request, format=None):
        filter_1 = self.request.data.get("filter_1")
        filter_2 = self.request.data.get("filter_2")
        report_date = self.request.data.get("report_date")
        report_filter = self.request.data.get("report_filter")

        if not report_filter or report_filter not in [
            "client_type_officer",
            "gender_officer",
            "officer_branch",
            "client_type_product",
            "product_officer",
            "gender_product",
            "product_branch",
            "gender_branch",
            "client_type_branch",
            "group_branch",
            "product_sector",
        ]:
            return Response({"results": [], "count": 0})

        if not validate_date(report_date):
            return Response({"results": [], "count": 0})

        if not isinstance(filter_1, list):
            return Response({"results": [], "count": 0})

        if not isinstance(filter_2, list):
            return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": report_date,
        }

        return filter_green_loan_tracking_reports(report_filters)


class LinesOfCreditReportFiltersDataView(APIView):

    def post(self, request, format=None):
        filter_1 = self.request.data.get("filter_1")
        filter_2 = self.request.data.get("filter_2")
        report_date = self.request.data.get("report_date")
        report_filter = self.request.data.get("report_filter")

        valid_filters = [
            # new agriculture filters
            "gender_agriculture",
            "value_chain_agriculture",
            "value_chain_node_agriculture",
        ]

        # reject invalid filter types
        if not report_filter or report_filter not in valid_filters:
            return Response({"results": [], "count": 0})

        # reject invalid dates
        if not validate_date(report_date):
            return Response({"results": [], "count": 0})

        # for agriculture filters we allow single values, otherwise enforce list
        if report_filter not in [
            "gender_agriculture",
            "value_chain_agriculture",
            "value_chain_node_agriculture",
        ]:
            if not isinstance(filter_1, list) or not isinstance(filter_2, list):
                return Response({"results": [], "count": 0})

        report_filters = {
            "report_filter": report_filter,
            "filter_1": filter_1,
            "filter_2": filter_2,
            "request": request,
            "as_at": report_date,
        }

        return filter_lines_of_credit_reports(report_filters)


class LoanFundersReport(APIView):

    def post(self, request):
        return utilization_report(request)


class loansRecnciliation(viewsets.ViewSet):

    def list(self, request, *args, **kwargs):
        organisation_id = get_current_user(self.request, "organisation_id", None)

        query = """
            SELECT 
                L.id,
                L.loan_amount, 
                L.date_added, 
                L.last_updated, 
                L.reason, 
                L.loan_date, 
                C.name AS customer_name,
                C.member_number,
		        C.old_member_number,
                L.status, 
                L.customer_id, 
                D.loan_amount AS disbursed_amount,
                D.ref_no, 
                D.loan_start_date, 
                D.loan_disbursement_date, 
                M.amount AS main_trans_amount, 
                M.ref_no AS main_trans_ref
            FROM 
                loan_applications L
            INNER JOIN customer C 
			    ON C.id = L.customer_id    
            LEFT JOIN 
                loan_application_disbursement D 
                ON L.id = D.loan_application_id
            LEFT JOIN 
                loan_main_transactions M 
                ON L.id = M.loan_application_id
                AND M.transaction_type = 'LoanDisbursement'
            LEFT JOIN 
                system_transactions T 
                ON M.heading = T.heading
            WHERE  T.heading IS NULL AND organisation_branch_id IN (SELECT id FROM organisation_branch WHERE branch_organisation_id = %s)
            """
        with connection.cursor() as cursor:
            cursor.execute(
                query,
                [
                    organisation_id,
                ],
            )
            rows = cursor.fetchall()

            columns = [col[0] for col in cursor.description]
            # Convert rows to a list of dictionaries
            results = [dict(zip(columns, row)) for row in rows]

        return Response(
            {"message": "Savings Reconciliations", "results": results},
            status=status.HTTP_200_OK,
        )


class MagrateMemberSaving(APIView):

    def post(self, request):
        members = [
            # {
            #     "member_name": "Birungi Hasifah",
            #     "memo_no": "59001870",
            #     "amount": 10000
            # }
        ]

        not_found = []
        no_source_accounts = []
        no_dest_accounts = []

        for member in members:
            customer = Customer.objects.filter(
                member_number=member["memo_no"], customer_branch__id=52
            ).first()
            if not customer:
                not_found.append(member["memo_no"])
                continue

            source_product = SavingAccount.objects.filter(
                account_customer=customer,
                account_product__id=52,
                customer_branch__id=52,
            ).first()
            if not source_product:
                no_source_accounts.append(member["memo_no"])
                continue

            dest_product = SavingAccount.objects.filter(
                account_customer=customer,
                account_product__id=389,
                customer_branch__id=52,
            ).first()
            if not dest_product:
                no_dest_accounts.append(member["memo_no"])
                continue

            organisation_id = 33
            customer_name = member["member_name"]
            member_number = member["memo_no"]
            heading = f"Programmed saving Cash transfer: for {customer_name} - {member_number}"
            amount = member["amount"]
            record_date = datetime.strptime("2025-08-16", "%Y-%m-%d")

            # Save sender transactions details
            reference_no = generate_reference_no(
                source_product.account_product.accounts_chart.account_line,
                organisation_id,
                "sav-tr",
            )
            user = get_user_model().objects.get(pk=137)
            chart = OrganisationSubAccount.objects.get(
                account_code="sys-215", account_organisation_id=organisation_id
            )
            sender_obj = {
                "heading": heading,
                "coment": heading,
                "amount": amount,
                "debit_chart": source_product.account_product.accounts_chart,
                "credit_chart": chart,
                "reference_no": reference_no,
                "voucher_no": "",
                "payment_method": "offset",
                "added_by": user,
                "branch": source_product.customer_branch,
                "record_date": record_date,
            }

            sender_transaction = SystemTransactions.objects.create(**sender_obj)
            if sender_transaction:
                saving_fields = {
                    "transaction_type": "transfer",
                    "transaction": sender_transaction,
                    "customer_account": source_product,
                }
                saving_1 = SavingAccountTransactions.objects.create(**saving_fields)

                receiver_obj = {
                    "heading": heading,
                    "coment": heading,
                    "amount": amount,
                    "debit_chart": chart,
                    "credit_chart": dest_product.account_product.accounts_chart,
                    "reference_no": reference_no,
                    "voucher_no": "",
                    "payment_method": "offset",
                    "added_by": user,
                    "branch": dest_product.customer_branch,
                    "record_date": record_date,
                }

                receiver_transaction = SystemTransactions.objects.create(**receiver_obj)
                if receiver_transaction:
                    saving_fields = {
                        "transaction_type": "transfer",
                        "transaction": receiver_transaction,
                        "customer_account": dest_product,
                    }
                    saving_2 = SavingAccountTransactions.objects.create(**saving_fields)

                    # Save transfer transactions details mapping
                    if saving_1 and saving_2:
                        transfer_fields = {
                            "sender_transaction": saving_1,
                            "reciever_transaction": saving_2,
                        }
                        TransferTransactions.objects.create(**transfer_fields)

        return Response(
            {
                "message": "Savings moved",
                "not_found": not_found,
                "no_source_accounts": no_source_accounts,
                "no_dest_accounts": no_dest_accounts,
            },
            status=status.HTTP_200_OK,
        )


class LoanInterestWaiverReportView(APIView):
    """
    API endpoint for Loan Interest Waiver Report
    Returns list of loans with interest waiver information
    Query params: report_date, filter, filter_1, filter_2
    """
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        report_date = request.GET.get('report_date')
        report_filter = request.GET.get('filter', '')
        filter_1 = request.GET.get('filter_1', '').split(',') if request.GET.get('filter_1') else []
        filter_2 = request.GET.get('filter_2', '').split(',') if request.GET.get('filter_2') else []
        
        if not report_date:
            return Response(
                {'error': 'report_date parameter is required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        results = get_loan_interest_waiver_report(request, report_date, report_filter, filter_1, filter_2)
        return Response({'results': results}, status=status.HTTP_200_OK)


class LoanPenaltyWaiverReportView(APIView):
    """
    API endpoint for Loan Penalty Waiver Report
    Returns list of loans with penalty waiver information
    Query params: report_date, filter, filter_1, filter_2
    """
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        report_date = request.GET.get('report_date')
        report_filter = request.GET.get('filter', '')
        filter_1 = request.GET.get('filter_1', '').split(',') if request.GET.get('filter_1') else []
        filter_2 = request.GET.get('filter_2', '').split(',') if request.GET.get('filter_2') else []
        
        if not report_date:
            return Response(
                {'error': 'report_date parameter is required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        results = get_loan_penalty_waiver_report(request, report_date, report_filter, filter_1, filter_2)
        return Response({'results': results}, status=status.HTTP_200_OK)
