from rest_framework import serializers, viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from datetime import datetime
from ledgers.serializers import *
from .models import *
from .ledgers_helper import *
from questbanker_api.utils import get_current_user
from savings.savings_helper import thread_multiple_booking_payments,get_account_balance
from savings.models import SavingAccount, SavingAccountTransactions
from loans.helper import loans_transaction_management
from ussdbanking.helpers import get_branch_wallet_chart
from django.utils.timezone import make_aware
from organisations.models import OrganisationBranch
from exservices.exservices_helper import send_customer_sms
from notifications.notifications_helper import *
from django.db.models import Case, When, Value, IntegerField
from inventory.helper import delete_or_reverse_transaction
from django.shortcuts import get_object_or_404
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from users.models import UserAssignedRole
from .transaction_edit_permissions import can_edit_transaction_amount_for_organisation
from django.db import transaction as db_transaction


class OrganisationSubAccountView(viewsets.ModelViewSet):
    serializer_class = OrganisationSubAccountSerializer

    def get_queryset(self):
        account_line = self.request.GET.get('line', 'assets')
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return OrganisationSubAccount.objects.filter(
            account_line=account_line,
            account_organisation=organisation_id,
            parent_id__isnull=True,
            deleted=False,
        ).order_by('id')

    def retrieve(self, request, pk=None):
        instance = OrganisationSubAccount.objects.get(pk=pk)
        return Response(self.serializer_class(instance).data, status=status.HTTP_200_OK)

    def update(self, request, pk=None):
        description = self.request.data.get('description')
        account_name = self.request.data.get('account_name')
        account = OrganisationSubAccount.objects.get(pk=pk)
        account.description = description
        account.account_name = account_name
        account.save()
        return Response(self.serializer_class(account).data, status=status.HTTP_200_OK)

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        #Generate account code
        parent_id = self.request.data.get('parent_id', 0)
        account_line = self.request.data.get('account_line')
        account_code = generate_chart_of_account_code(parent_id, account_line, organisation_id)

        #Fill session organisation account details
        serializer.save(account_organisation=organisation, account_code=account_code, account_type='user_defined', added_by=self.request.user.id)

class CurrenciesView(viewsets.ModelViewSet):
    serializer_class = CurrenciesSerializer
    queryset = Currencies.objects.all()

class BankAccountsView(viewsets.ModelViewSet):
    serializer_class = BankAccountsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation_branches = OrganisationBranch.objects.filter(branch_organisation_id=organisation_id).values_list('id', flat=True)
        return BankAccounts.objects.filter(branch_id__in=organisation_branches)

    def perform_create(self, serializer):
        parent_chart = 'sys-11411'
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        #Bank account details
        bank_name = self.request.data.get('bank_name')
        account_name = self.request.data.get('account_name')
        account_currency = self.request.data.get('account_currency')
        branch_id = self.request.data.get('branch_id')
        account_alias = account_currency + ' : ' + bank_name + ' - ' + account_name

        #Generate account code for bank account chart.
        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)

            #Save bank account chart.
            chart = OrganisationSubAccount(account_name=account_alias, account_line=account_line, account_organisation=organisation, account_code=account_code, parent_id=parent, added_by=self.request.user.id, allow_sub_accounts=False)
            chart.save()

            serializer.save(chart=chart, account_alias=account_alias, branch_id=branch_id, added_by=self.request.user.id)

    def update(self, request, pk=None):
        #New Bank account details
        bank_name = request.data.get('bank_name')
        account_name = request.data.get('account_name')
        account_currency = request.data.get('account_currency')
        account_number = request.data.get('account_number')
        branch_id = request.data.get('branch_id')
        status = request.data.get('status')
        account_alias = account_currency + ' : ' + bank_name + ' - ' + account_name

        #Update bank account details.
        bank_account = BankAccounts.objects.get(pk=pk)
        bank_account.bank_name = bank_name
        bank_account.branch_id = branch_id
        bank_account.status = status
        bank_account.account_alias = account_alias
        bank_account.account_name = account_name
        bank_account.account_number = account_number
        bank_account.account_currency = account_currency
        bank_account.save()

        chart = OrganisationSubAccount.objects.get(pk=bank_account.chart_id)
        chart.account_name = account_alias
        chart.save()
        return Response(self.serializer_class(bank_account).data, status=status.HTTP_200_OK)

class CashAccountsView(viewsets.ModelViewSet):
    serializer_class = CashAccountsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        filter_query = self.request.GET.get('filter', None)
        transaction_type = self.request.GET.get('transaction_type', None)
       
        if filter_query == 'list':
            filter_data = {"user_staff__staff_organisation_id":organisation_id}
            if not transaction_type:
                filter_data['user_organisation_branch__id'] = organisation_branch_id

            logged_in_user_id = self.request.user.id
            users = User.objects.filter(**filter_data).values_list('id', flat=True)
            cash_accounts = CashAccounts.objects.filter(teller_id__in=users).annotate(
                custom_order=Case(
                    When(teller_id=logged_in_user_id, then=Value(0)),
                    default=Value(1),
                    output_field=IntegerField(),
                )
            ).order_by('custom_order', '-id')
            # users = User.objects.filter(user_staff__staff_organisation_id=organisation_id).values_list('id', flat=True)
            # return CashAccounts.objects.filter(teller_id__in=users)
            return cash_accounts 
        
        return CashAccounts.objects.filter(teller__id=self.request.user.id, teller__user_organisation_branch__id=organisation_branch_id).order_by('-id')

    def perform_create(self, serializer):
        parent_chart = 'sys-11412'
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        #Cash account details
        account_name = self.request.data.get('account_name')
        teller_id = self.request.data.get('teller_id')

        #Generate account code for cash account chart.
        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)

            #Save Cash account chart.
            chart = OrganisationSubAccount(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)
            chart.save()
            serializer.save(chart=chart, teller_id=teller_id, added_by=self.request.user.id)

    def update(self, request, pk=None):
        #New Cash account details
        account_name = request.data.get('account_name')
        account_currency = request.data.get('account_currency')
        teller_id = request.data.get('teller_id')
        account_status = request.data.get('status')
        expenses_limit    = self.request.data.get('expenses_limit')
        withdrawals_limit = self.request.data.get('withdrawals_limit')
        #Update Cash account details.
        cash_account = CashAccounts.objects.get(pk=pk)
        cash_account.teller_id = teller_id
        cash_account.status = account_status
        cash_account.account_name = account_name
        cash_account.account_currency  = account_currency
        cash_account.expenses_limit    = expenses_limit
        cash_account.withdrawals_limit = withdrawals_limit
        cash_account.save()

        chart = OrganisationSubAccount.objects.get(pk=cash_account.chart_id)
        chart.account_name = account_name
        chart.save()
        return Response(self.serializer_class(cash_account).data, status=status.HTTP_200_OK)
    
class ReceivableChartsAPIView(APIView):
    def get(self, request):
        # Get current user's organisation ID safely
        organisation_id = get_current_user(request, 'organisation_id', None)
        if not organisation_id:
            return Response({"count": 0, "results": []}, status=200)
        
        organisation = Organisation.objects.filter(pk=organisation_id).first()
        if not organisation:
            return Response({"count": 0, "results": []}, status=200)

        # Step 1: Get the parent receivable account
        parent = OrganisationSubAccount.objects.filter(
            account_code="sys-113",
            deleted=False,
            account_organisation=organisation
        ).first()

        if not parent:
            return Response({"count": 0, "results": []}, status=200)

        # Step 2: Get child accounts
        children = OrganisationSubAccount.objects.filter(
            parent_id=parent,
            deleted=False,
            account_organisation=organisation
        )

        # Step 3: If children exist → return only children
        if children.exists():
            serializer = OrganisationSubAccountSerializer(children, many=True)
            return Response({"count": children.count(), "results": serializer.data})

        # Step 4: Else return the parent account
        serializer = OrganisationSubAccountSerializer(parent)
        return Response({"count": 1, "results": [serializer.data]})

class SafeAccountsView(viewsets.ModelViewSet):
    serializer_class = SafeAccountsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation_branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        transaction_type = self.request.GET.get('transaction_type', None)

        if transaction_type and transaction_type == 'transfer':
            organisation_branches = OrganisationBranch.objects.filter(branch_organisation_id=organisation_id).values_list('id', flat=True)
            return SafeAccounts.objects.filter(branch_id__in=organisation_branches)

        return SafeAccounts.objects.filter(branch_id=organisation_branch_id)

    def perform_create(self, serializer):
        parent_chart = 'sys-11412'
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        #Cash account details
        account_name = self.request.data.get('account_name')
        branch_id = self.request.data.get('branch_id')

        #Generate account code for cash account chart.
        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)

            #Save Cash account chart.
            chart = OrganisationSubAccount(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)
            chart.save()

            serializer.save(chart=chart, branch_id=branch_id, added_by=self.request.user.id)

    def update(self, request, pk=None):
        #New Cash account details
        account_name = request.data.get('account_name')
        account_currency = request.data.get('account_currency')
        branch_id = self.request.data.get('branch_id')
        account_status = request.data.get('status')

        #Update Cash account details.
        safe_account = SafeAccounts.objects.get(pk=pk)
        safe_account.branch_id = branch_id
        safe_account.status = account_status
        safe_account.account_name = account_name
        safe_account.account_currency = account_currency
        safe_account.save()

        chart = OrganisationSubAccount.objects.get(pk=safe_account.chart_id)
        chart.account_name = account_name
        chart.save()
        return Response(self.serializer_class(safe_account).data, status=status.HTTP_200_OK)

class MobileMoneyAccountsView(viewsets.ModelViewSet):
    serializer_class = MobileMoneyAccountsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation_branches = OrganisationBranch.objects.filter(branch_organisation_id=organisation_id).values_list('id', flat=True)
        return MobileMoneyAccounts.objects.filter(branch_id__in=organisation_branches)

    def perform_create(self, serializer):
        parent_chart = 'sys-11414'
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        organisation = Organisation.objects.get(pk=organisation_id)

        #Cash account details
        account_name = self.request.data.get('account_name')
        branch_id = self.request.data.get('branch_id')

        #Generate account code for cash account chart.
        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)

            #Save Cash account chart.
            chart = OrganisationSubAccount(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)
            chart.save()

            serializer.save(chart=chart, branch_id=branch_id, added_by=self.request.user.id)

    def update(self, request, pk=None):
        #New Cash account details
        account_name = request.data.get('account_name')
        telephone_number = request.data.get('telephone_number')
        account_currency = request.data.get('account_currency')
        branch_id = self.request.data.get('branch_id')
        account_status = request.data.get('status')

        #Update Cash account details.
        mobile_money_account = MobileMoneyAccounts.objects.get(pk=pk)
        mobile_money_account.telephone_number = telephone_number
        mobile_money_account.branch_id = branch_id
        mobile_money_account.status = account_status
        mobile_money_account.account_name = account_name
        mobile_money_account.account_currency = account_currency
        mobile_money_account.save()

        chart = OrganisationSubAccount.objects.get(pk=mobile_money_account.chart_id)
        chart.account_name = account_name
        chart.save()
        return Response(self.serializer_class(mobile_money_account).data, status=status.HTTP_200_OK)

class SystemTransactionsListView(APIView):

    def get(self, request, format=None):
        '''
        Get transactions
        '''
        end = request.GET.get('e', None)
        start = request.GET.get('s', None)
        account = request.GET.get('account', None)
        transaction_type = request.GET.get('type', None)

        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = self.request.GET.get('branch', None)
        if not branch_id:
            branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        if int(branch_id) == 0:
            branch_id = ',' . join(map(str, OrganisationBranch.objects.filter(branch_organisation_id=organisation_id).values_list('id', flat=True)))

        if int(account) > 0:
            transactional_charts = OrganisationSubAccount.objects.filter(parent_id_id=account)
            if len(transactional_charts) == 0:
                transactional_charts = OrganisationSubAccount.objects.filter(id=account)
        else:
            account_line = request.GET.get('line', None)
            transactional_charts = get_transactional_charts(organisation_id, account_line)
            
        list = OrganisationSubAccountSerializer(
            transactional_charts,
            many=True,
            context={
                'branch_id': branch_id,
                'start_date': start,
                'end_date': end,
                'type': transaction_type
            }
        )

        for account in list.data:
            transactions = account.get("account_transactions", [])
            for transaction in transactions:
                heading = transaction.get("heading", "")
                # print(heading)  # Print the heading field
                if heading == 'LoanDisbursement: (16025751) to Aryatuha  Rose':
                    print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
                    print(heading)
                    print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")


        # for transaction in list.data:
        #     heading = transaction.get('heading', '')  # Access 'heading' from the serialized data
        #     print(transaction)
        #     if heading == 'LoanDisbursement: (16025751) to Aryatuha  Rose':
        #         print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
        #         print(heading)
        #         print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")

        data = {
            'count' : len(list.data),
            'results': list.data
        }
        
        return Response(data, status=status.HTTP_200_OK)

class SystemTransactionalChartsView(APIView):

    def get(self, request, format=None):
        '''
        Get transactions
        '''
        filter = request.GET.get('filter', None)
        account_line = request.GET.get('line', None)
        organisation = request.GET.get('organisation_id', None)
        organisation_id = get_current_user(request, 'organisation_id')
        branch_id = get_current_user(request, 'organisation_branch_id', None)
        
        if organisation:
            organisation_id = organisation
        
        transactional_charts = get_transactional_charts(organisation_id, account_line)
        if filter == 'agregated':
            parent_accounts = []
            parent_account_ids = []
            for chart in transactional_charts:
                parent = chart.parent_id
                if parent and not parent.deleted and parent.id not in parent_account_ids:
                    parent_accounts.append(parent)
                    parent_account_ids.append(parent.id)

            parent_accounts = parent_accounts
            transactional_charts = parent_accounts + transactional_charts

        charts = OrganisationSubAccountSerializer(transactional_charts, many=True, context={'branch_id': branch_id})
        data = {
            'count' : len(charts.data),
            'results': charts.data
        }
        
        return Response(data, status=status.HTTP_200_OK)
    
class SystemTransactionsView(viewsets.ModelViewSet):
    serializer_class = SystemTransactionsSerializer
    queryset = SystemTransactions.objects.all()
    http_method_names = ['post', 'put']

    def _normalize_transaction_payload(self, payload, shared_record_date=None):
        normalized_payload = dict(payload)

        if shared_record_date is not None:
            normalized_payload['record_date'] = shared_record_date

        if 'comment' in normalized_payload and 'coment' not in normalized_payload:
            normalized_payload['coment'] = normalized_payload.pop('comment')
        else:
            normalized_payload.pop('comment', None)

        return normalized_payload

    def _validate_single_transaction_payload(self, payload):
        serializer = SystemTransactionCreateSerializer(data=payload)
        serializer.is_valid(raise_exception=True)
        return serializer.validated_data

    def _validate_bulk_transaction_payloads(self, payload):
        serializer = SystemTransactionsBulkCreateSerializer(data=payload)
        serializer.is_valid(raise_exception=True)

        shared_record_date = serializer.validated_data['record_date']
        validated_transactions = []
        validation_errors = []

        for index, transaction_payload in enumerate(
            serializer.validated_data['transactions']
        ):
            normalized_payload = self._normalize_transaction_payload(
                transaction_payload, shared_record_date
            )
            item_serializer = SystemTransactionCreateSerializer(
                data=normalized_payload
            )

            if item_serializer.is_valid():
                validated_transactions.append(item_serializer.validated_data)
            else:
                validation_errors.append(
                    {'index': index, 'errors': item_serializer.errors}
                )

        if validation_errors:
            raise serializers.ValidationError({'transactions': validation_errors})

        return validated_transactions

    def _create_system_transaction(self, validated_data):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #Cash account details
        debit_chart = validated_data['debit_chart']
        credit_chart = validated_data['credit_chart']
        debit_chart_id = debit_chart.id
        credit_chart_id = credit_chart.id
        heading = validated_data['heading']
        amount = validated_data['amount']
        record_date = validated_data['record_date']
        voucher_no = validated_data.get('voucher_no')
        comment = validated_data.get('coment')

        # Check if transaction interbranch
        source_branch = get_account_branch(credit_chart_id)
        destination_branch = get_account_branch(debit_chart_id)

        # check if branch is different
        account = CashAccounts.objects.filter(chart_id=credit_chart_id).first()
        if account:
            if int(account.teller.user_organisation_branch.id) != int(branch_id):
                branch_id = account.teller.user_organisation_branch.id

        account = CashAccounts.objects.filter(chart_id=debit_chart_id).first()
        if account:
            if int(account.teller.user_organisation_branch.id) != int(branch_id):
                branch_id = account.teller.user_organisation_branch.id

        # Post normally if non-money transaction.
        if (source_branch == 'non_cash_account' or destination_branch == 'non_cash_account'):

            payment_method = 'settlement'
            destination_money_account_type = get_moeny_account_type(debit_chart_id)
            if destination_money_account_type != 'non_money_account':
                payment_method = destination_money_account_type
            else:
                source_money_account_type = get_moeny_account_type(credit_chart_id)
                if source_money_account_type != 'non_money_account':
                    payment_method = source_money_account_type

            #Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id)

            return SystemTransactions.objects.create(
                amount=amount,
                heading=heading,
                record_date=record_date,
                payment_method=payment_method,
                voucher_no=voucher_no,
                coment=comment,
                reference_no=reference_no,
                debit_chart_id=debit_chart_id,
                credit_chart_id=credit_chart_id,
                branch_id=branch_id,
                added_by=self.request.user,
            )
        else:
            payment_method = 'settlement'

            #Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id)
            saved_transaction = SystemTransactions.objects.create(
                amount=amount,
                heading=heading,
                record_date=record_date,
                payment_method=payment_method,
                voucher_no=voucher_no,
                coment=comment,
                reference_no=reference_no,
                debit_chart_id=debit_chart_id,
                credit_chart_id=credit_chart_id,
                branch_id=branch_id,
                added_by=self.request.user,
            )

            #Inititate cash Transfer for money accounts.
            cash_transfer = CashTransfers(
                heading=heading,
                approval_status='approved',
                reference_transaction=saved_transaction,
                amount=amount,
                record_date=record_date,
                source_chart_id=credit_chart_id,
                destination_chart_id=debit_chart_id,
                branch_id=branch_id,
                added_by=self.request.user,
                comment=comment,
            )
            cash_transfer.save()
            return saved_transaction

    def create(self, request, *args, **kwargs):
        try:
            if isinstance(request.data, dict) and isinstance(
                request.data.get('transactions'), list
            ):
                validated_transactions = self._validate_bulk_transaction_payloads(
                    request.data
                )

                with db_transaction.atomic():
                    created_transactions = [
                        self._create_system_transaction(validated_data)
                        for validated_data in validated_transactions
                    ]

                response_serializer = self.get_serializer(
                    created_transactions, many=True
                )
                return Response(
                    {
                        'count': len(response_serializer.data),
                        'results': response_serializer.data,
                    },
                    status=status.HTTP_201_CREATED,
                )

            validated_data = self._validate_single_transaction_payload(
                self._normalize_transaction_payload(request.data)
            )
            created_transaction = self._create_system_transaction(validated_data)
            response_serializer = self.get_serializer(created_transaction)
            return Response(
                response_serializer.data, status=status.HTTP_201_CREATED
            )
        except serializers.ValidationError as exc:
            return Response(exc.detail, status=status.HTTP_400_BAD_REQUEST)

class BulkTransactionsView(APIView):
    """
    POST /api/bulk-transactions/
    Accepts { record_date, transactions: [{debit_chart, credit_chart, amount, heading, voucher_no, comment}] }
    All rows share the same record_date and are saved atomically.
    """

    def post(self, request, format=None):
        serializer = SystemTransactionsBulkCreateSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        shared_record_date = serializer.validated_data['record_date']
        raw_transactions = serializer.validated_data['transactions']

        # Validate each transaction row individually
        validated_rows = []
        row_errors = []
        for index, row in enumerate(raw_transactions):
            row_data = dict(row)
            row_data['record_date'] = shared_record_date
            if 'comment' in row_data and 'coment' not in row_data:
                row_data['coment'] = row_data.pop('comment')
            else:
                row_data.pop('comment', None)

            row_serializer = SystemTransactionCreateSerializer(data=row_data)
            if row_serializer.is_valid():
                validated_rows.append(row_serializer.validated_data)
            else:
                row_errors.append({'index': index, 'errors': row_serializer.errors})

        if row_errors:
            return Response(
                {'transactions': row_errors},
                status=status.HTTP_400_BAD_REQUEST
            )

        organisation_id = get_current_user(request, 'organisation_id', None)
        branch_id = get_current_user(request, 'organisation_branch_id', None)

        created = []
        try:
            with db_transaction.atomic():
                for validated_data in validated_rows:
                    debit_chart = validated_data['debit_chart']
                    credit_chart = validated_data['credit_chart']
                    reference_no = generate_reference_no(
                        credit_chart.account_line, organisation_id
                    )
                    transaction = SystemTransactions.objects.create(
                        amount=validated_data['amount'],
                        heading=validated_data['heading'],
                        record_date=validated_data['record_date'],
                        payment_method='settlement',
                        voucher_no=validated_data.get('voucher_no') or '',
                        coment=validated_data.get('coment') or '',
                        reference_no=reference_no,
                        debit_chart=debit_chart,
                        credit_chart=credit_chart,
                        branch_id=branch_id,
                        added_by=request.user,
                    )
                    created.append(transaction)
        except Exception as exc:
            return Response(
                {'detail': str(exc)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        return Response(
            {
                'count': len(created),
                'results': SystemTransactionsSerializer(created, many=True).data,
            },
            status=status.HTTP_201_CREATED,
        )


class TellerCashLedgersView(viewsets.ModelViewSet):
    serializer_class = OrganisationSubAccountSerializer
    http_method_names = ['get']

    def get_queryset(self):
        return OrganisationSubAccount.objects.filter(cash_account_chart__teller=self.request.user).order_by('account_name')

class CashTransfersView(viewsets.ModelViewSet):
    serializer_class = CashTransfersSerializer

    def get_queryset(self):
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        return CashTransfers.objects.filter(branch_id=branch_id).order_by('-id')

    def perform_create(self, serializer):
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        source_chart_id = self.request.data.get('source_chart')
        destination_chart_id = self.request.data.get('destination_chart')

        # update branch if transfering on behalf of other branch
        source_branch = get_account_branch(source_chart_id)
        if source_branch and source_branch != 'non_cash_account':
            branch_id = source_branch

        #Save Transfer details.
        cash_transfer_details = serializer.save(source_chart_id=source_chart_id, destination_chart_id=destination_chart_id, branch_id=branch_id, added_by=self.request.user)
        if cash_transfer_details:
            save_user_notification({
                "heading":  "Cash Transfers Request",
                "message": f"Cash Transfer Request of Amount: {cash_transfer_details.amount} from {cash_transfer_details.source_chart.account_name} to  {cash_transfer_details.destination_chart.account_name} has been approved by {cash_transfer_details.approved_by} as at {cash_transfer_details.record_date.date()}",
                "branch":OrganisationBranch.objects.get(pk=cash_transfer_details.branch.id),
                "branch_name":cash_transfer_details.branch.name,
                "added_by":cash_transfer_details.added_by,
                "last_updated_by":cash_transfer_details.added_by,
                "key":"ledger_notifications"
            })

    def update(self, request, pk=None):
        approval_date = datetime.now().strftime("%Y-%m-%d")
        approval_comment = request.data.get('approval_comment')
        approval_status = request.data.get('approval_status')
        voucher_no = request.data.get('voucher_no')

        #Update Transfer details.
        transfer = CashTransfers.objects.get(pk=pk)

        transaction = None
        if approval_status == 'approved':
            organisation_id = get_current_user(self.request, 'organisation_id', None)

            #Generate reference number
            reference_no = generate_reference_no(transfer.source_chart.account_line, organisation_id, 'ch-tr')

            # Get source and destination branches.
            source_branch = get_account_branch(transfer.source_chart.id)
            destination_branch = get_account_branch(transfer.destination_chart.id)
            
            # Same branch transfer.
            if source_branch != 'non_cash_account' and destination_branch != 'non_cash_account' and destination_branch == source_branch:
                transaction = SystemTransactions(heading=transfer.heading,amount=transfer.amount,voucher_no=voucher_no, record_date=transfer.record_date, reference_no=reference_no, debit_chart=transfer.destination_chart, credit_chart=transfer.source_chart, branch_id=transfer.branch_id, added_by=transfer.added_by)
                transaction.save()

            # Inter-Branch Transfer
            if source_branch != 'non_cash_account' and destination_branch != 'non_cash_account' and destination_branch != source_branch:
                branches = OrganisationBranch.objects.filter(Q(id=destination_branch) | Q(id=source_branch))
                inter_branch_ledger = get_inter_branch_chart(branches[0], branches[1])

                #Interbranch leder should not be null.
                if inter_branch_ledger:
                    transaction = SystemTransactions(heading=transfer.heading + '-initiated',amount=transfer.amount,voucher_no=voucher_no, record_date=transfer.record_date, reference_no=reference_no, debit_chart=inter_branch_ledger, credit_chart=transfer.source_chart, branch_id=source_branch, added_by=transfer.added_by)
                    transaction.save()

                    destination_transaction = SystemTransactions(heading=transfer.heading + '-received',amount=transfer.amount, voucher_no=voucher_no, record_date=transfer.record_date, reference_no=reference_no, debit_chart=transfer.destination_chart, credit_chart=inter_branch_ledger, branch_id=destination_branch, added_by=transfer.added_by)
                    destination_transaction.save()

                    interBranch = InterBranchTransactions(source_transaction=transaction, destination_transaction=destination_transaction, added_by=transfer.added_by)
                    interBranch.save()

        # Update transfer details
        transfer.comment = approval_comment
        transfer.approval_status = approval_status
        transfer.reference_transaction = transaction
        transfer.approved_by = self.request.user
        transfer.date_approved = approval_date
        transfer.save()

        save_user_notification({
            "heading":  "Cash Transfers Approval",
            "message": f"Cash Transfer of Amount: {transfer.amount} from {transfer.source_chart.account_name} to  {transfer.destination_chart.account_name} has been approved by {transfer.approved_by} as at {transfer.record_date.date()}",
            "branch":OrganisationBranch.objects.get(pk=transfer.branch.id),
            "branch_name":transfer.branch.name,
            "added_by":transfer.added_by,
            "last_updated_by":transfer.approved_by,
            "key":"ledger_notifications"
        })
        return Response(self.serializer_class(transfer).data, status=status.HTTP_200_OK)

class MyTransfersView(viewsets.ModelViewSet):
    serializer_class = CashTransfersSerializer
    http_method_names = ['get', 'put']

    def get_queryset(self):
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        return CashTransfers.objects.filter(branch_id=branch_id, added_by=self.request.user).order_by('-id')

    def update(self, request, pk=None):
        approval_date = datetime.now().strftime("%Y-%m-%d")
        approval_comment = request.data.get('closure_comment')

        #Update Transfer details.
        transfer = CashTransfers.objects.get(pk=pk)

        #Update transfer details
        transfer.comment = approval_comment
        transfer.approval_status = 'closed'
        transfer.approved_by = self.request.user
        transfer.date_approved = approval_date
        transfer.save()
        return Response(self.serializer_class(transfer).data, status=status.HTTP_200_OK)


class LedgerIncomesView(APIView):

    def post(self, request, format=None):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #Cash account details
        heading = self.request.data.get('heading')
        amount = self.request.data.get('amount')
        record_date =  self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = self.request.data.get('payent_method')
        voucher_no = self.request.data.get('voucher_no')

        valid = True
        account = None
        inter_branch_ledger = None
        if payment_method == 'offset':
            valid = False
            account = SavingAccount.objects.get(pk=debit_chart_id)
            account_balance = get_account_balance(account)
            
            if account_balance['balance_raw'] < float(amount):
                data = {
                    'message' : 'Insufficient balance on selected account. Book account instead.',
                    'status': 'failed'
                }
            else:
                valid = True
                debit_chart_id = account.account_product.accounts_chart_id

                # Check InterBranch
                if self.request.user.user_organisation_branch.id != account.customer_branch.id:
                    inter_branch_ledger = get_inter_branch_chart(self.request.user.user_organisation_branch, account.customer_branch)

        if valid:
            credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)

            # Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'inc')
            if inter_branch_ledger:
                # Save interbranch transaction
                interbranch_transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method='settlement', voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=inter_branch_ledger.id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
                interbranch_transaction.save()

                # Save main transaction
                transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=inter_branch_ledger.id, branch_id=account.customer_branch.id, added_by=self.request.user)
                transaction.save()

                # save interbranch relation
                interbranch_relation = InterBranchTransactions(source_transaction=transaction, destination_transaction=interbranch_transaction, added_by=self.request.user)
                interbranch_relation.save()
            else:
                # Save transaction
                transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
                transaction.save()
            
            # Save Account Charge if offset.
            if payment_method == 'offset':
                charge = SavingAccountTransactions(customer_account=account, transaction=transaction, transaction_type='offset')
                charge.save()
            
            data = {
                'message' : 'Transaction created.',
                'status': 'success'
            }
        
        return Response(data, status=status.HTTP_200_OK)

class LedgerExpensesView(APIView):

    def post(self, request, format=None):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #Cash account details
        heading = self.request.data.get('heading')
        amount = self.request.data.get('amount')
        record_date =  self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = self.request.data.get('payment_method')
        voucher_no = self.request.data.get('voucher_no')
        send_sms   = self.request.data.get('send_sms')

        inter_branch_ledger = None
        if payment_method == 'offset':
            account = SavingAccount.objects.get(pk=credit_chart_id)
            credit_chart_id = account.account_product.accounts_chart_id

            # Check InterBranch
            if self.request.user.user_organisation_branch.id != account.customer_branch.id:
                inter_branch_ledger = get_inter_branch_chart(self.request.user.user_organisation_branch, account.customer_branch)

        # Check InterBranch if posted by someone with POST_ALL_BRANCHES_EXPENSES rights
        if payment_method == 'cash':
            permission = UserPermissions.objects.filter(user_id=self.request.user.id,is_feature_active=True,is_org_comp_active=True,is_role_active=True,is_role_component_active=True,is_user_role_active=True,key='POST_ALL_BRANCHES_EXPENSES').first()
            if permission:
                cash_account = CashAccounts.objects.filter(chart__id=credit_chart_id).first()
                if cash_account:
                    branch_id = cash_account.teller.user_organisation_branch.id
       
        credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)
        # Generate reference number
        reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'exp')

        if inter_branch_ledger:
            # Save interbranch transaction
            interbranch_transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method='settlement',voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=inter_branch_ledger.id, branch_id=branch_id, added_by=self.request.user)
            interbranch_transaction.save()

            # Save main transaction
            transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=inter_branch_ledger.id, credit_chart_id=credit_chart_id, branch_id=account.customer_branch.id, added_by=self.request.user)
            transaction.save()

            # save interbranch relation
            interbranch_relation = InterBranchTransactions(source_transaction=transaction, destination_transaction=interbranch_transaction, added_by=self.request.user)
            interbranch_relation.save()
        else:
            # Save transaction
            transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
            transaction.save()
        
        # Save Account Charge if offset.
        if payment_method == 'offset':
            charge = SavingAccountTransactions(customer_account=account, transaction=transaction, transaction_type='offset')
            charge.save()
            # Process account booking payments
            thread_multiple_booking_payments(account, organisation_id, account.customer_branch.id, self.request.user.id)
            
            if send_sms:
                data = {"sms_key": "cash_deposit_sms", "customer_account": account, "user": self.request.user, "branch_id": branch_id, "save_trans": charge,"customer":account.account_customer}
                send_customer_sms(data)
        
        data = {
            'message' : 'Transaction created.',
            'status': 'success'
        }
        
        return Response(data, status=status.HTTP_200_OK)
    
class LedgerPendingExpensesViewset(viewsets.ModelViewSet):
    serializer_class = PendingExpensesSerializer
    search_fields    = ['branch',]

    def get_queryset(self):
        
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        start = self.request.GET.get('s', None)
        end = self.request.GET.get('e', None)
        status = self.request.GET.get('status', None)
        branch = self.request.GET.get('branch', None)

        filter_query = {"branch__branch_organisation__id":organisation_id}
        if branch:
            filter_query['branch__id'] = branch   
        if status =='Pending' or status =='Approved' or status =='Declined':
            filter_query['status'] = status
        if start:
            start = make_aware(datetime.strptime(start + ' 00:00', '%Y-%m-%d %H:%M'))
            filter_query['record_date__gte'] = start
        if end:
            end = make_aware(datetime.strptime(end + ' 23:59', '%Y-%m-%d %H:%M'))
            filter_query['record_date__lte'] = end
        return PendingExpenses.objects.filter(**filter_query).order_by('-id')

    def perform_create(self, serializer):
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        payment_method = self.request.data.get('payment_method')
        credit_chart_id = self.request.data.get('credit_chart')
        # Check InterBranch if posted by someone with POST_ALL_BRANCHES_EXPENSES rights
        if payment_method == 'cash':
            permission = UserPermissions.objects.filter(user_id=self.request.user.id,is_feature_active=True,is_org_comp_active=True,is_role_active=True,is_role_component_active=True,is_user_role_active=True,key='POST_ALL_BRANCHES_EXPENSES').first()
            if permission:
                cash_account = CashAccounts.objects.filter(chart__id=credit_chart_id).first()
                if cash_account:
                    branch_id = cash_account.teller.user_organisation_branch.id
        expense_request = serializer.save(branch=OrganisationBranch(pk=branch_id), added_by=self.request.user)
        save_user_notification({
            "heading":  "Ledger Expense Request",
            "message": f"Expense Request. Amount: {expense_request.amount} has been requested by {expense_request.added_by} as at {expense_request.record_date.date()}",
            "branch":OrganisationBranch.objects.get(pk=expense_request.branch.id),
            "branch_name":expense_request.branch.name,
            "added_by":expense_request.added_by,
            "last_updated_by":expense_request.last_updated_by,
            "key":"ledger_notifications"
        })
    
    def perform_update(self, serializer):
        # branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        organisation_id = get_current_user(self.request, 'organisation_id', None)

        approved_expense = serializer.save(last_updated_by=self.request.user, last_updated=datetime.now())
        if approved_expense.status == 'Approved':
            credit_chart = None
            debit_chart = OrganisationSubAccount.objects.get(pk=approved_expense.debit_chart.id)
            credit_chart = OrganisationSubAccount.objects.get(pk=approved_expense.credit_chart.id)

            # Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'exp')
            # Save transaction
            transaction = SystemTransactions(amount=approved_expense.amount, heading=approved_expense.heading, coment=approved_expense.comment, record_date=approved_expense.record_date, payment_method=approved_expense.payment_method,voucher_no=approved_expense.voucher_no, reference_no=reference_no, credit_chart=credit_chart,debit_chart=debit_chart, branch_id=approved_expense.branch_id, added_by=self.request.user)
            transaction.save()

            save_user_notification({
                "heading":  "Ledger Expense Approvals",
                "message": f"Expense Approvals. Amount: {approved_expense.amount} has been approved by {approved_expense.last_updated_by} as at {approved_expense.record_date.date()}",
                "branch":OrganisationBranch.objects.get(pk=approved_expense.branch.id),
                "branch_name":approved_expense.branch.name,
                "added_by":approved_expense.added_by,
                "last_updated_by":approved_expense.last_updated_by,
                "key":"ledger_notifications"
            })

class CrbActivationRequestsView(viewsets.ModelViewSet):
    serializer_class = CrbActivationRequestSerializer

    def get_queryset(self):
        # Only users assigned the 'ROOT_ADMIN' role can list requests
        is_admin = UserAssignedRole.objects.filter(user=self.request.user, assigned_role__role_name='ROOT_ADMIN', is_active=True).exists()
        if not is_admin:
            return CrbActivationRequest.objects.none()
        # ROOT_ADMIN users can see all CRB activation requests across all organisations
        return CrbActivationRequest.objects.all().order_by('-id')

    def perform_create(self, serializer):
        from rest_framework.exceptions import ValidationError
        from ledgers.ledgers_helper import get_chart_of_account_balance_at
        
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)
        
        # Get the credit_chart from request data
        credit_chart_id = self.request.data.get('credit_chart')
        amount = self.request.data.get('amount')
        
        # Validate that credit account has sufficient balance
        if credit_chart_id and amount:
            try:
                credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)
                account_balance_info = get_chart_of_account_balance_at(credit_chart, branch_id)
                # Use balance_raw (unformatted numeric value) instead of balance (formatted string)
                current_balance = account_balance_info.get('balance_raw', 0) if isinstance(account_balance_info, dict) else 0
                
                if float(current_balance) < float(amount):
                    raise ValidationError({
                        'credit_chart': f'Insufficient balance. Current balance: {current_balance}, but you are trying to debit: {amount}'
                    })
            except OrganisationSubAccount.DoesNotExist:
                raise ValidationError({'credit_chart': 'Credit chart account does not exist'})
        
        # Save request with current user's branch and added_by
        request_obj = serializer.save(organisation_branch=OrganisationBranch(pk=branch_id), added_by=self.request.user)
        save_user_notification({
            "heading":  "CRB Activation Request",
            "message": f"CRB Activation Request. Amount: {request_obj.amount} has been requested by {request_obj.added_by} as at {request_obj.record_date.date()}",
            "branch":OrganisationBranch.objects.get(pk=request_obj.organisation_branch.id),
            "branch_name":request_obj.organisation_branch.name,
            "added_by":request_obj.added_by,
            "last_updated_by":request_obj.added_by,
            "key":"ledger_notifications"
        })

    def perform_update(self, serializer):
        # Only users assigned the 'ROOT_ADMIN' role can approve
        is_admin = UserAssignedRole.objects.filter(user=self.request.user, assigned_role__role_name='ROOT_ADMIN', is_active=True).exists()
        if not is_admin:
            raise PermissionDenied(detail='Only Root Admins can approve CRB activation requests')
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        # Allow optional voucher_no and comment in the approval payload; serializer.save will persist provided fields
        # Explicitly set status to 'approved' when saving
        approved_request = serializer.save(status='approved', approved_by=self.request.user, date_approved=datetime.now())

        voucher_no = self.request.data.get('voucher_no')
        comment = self.request.data.get('comment')

        if approved_request.status == 'approved' or approved_request.status == 'Approved':
            # Create system transactions exactly like LedgerExpensesView does it
            heading = approved_request.heading or 'CRB Activation'
            amount = approved_request.amount
            record_date = approved_request.record_date
            debit_chart_id = approved_request.debit_chart.id
            credit_chart_id = approved_request.credit_chart.id
            branch_id = approved_request.organisation_branch.id

            credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)
            # Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'crb')

            # Determine interbranch
            source_branch = get_account_branch(credit_chart_id)
            destination_branch = get_account_branch(debit_chart_id)

            inter_branch_ledger = None
            if source_branch != 'non_cash_account' and destination_branch != 'non_cash_account' and destination_branch != source_branch:
                inter_branch_ledger = get_inter_branch_chart(OrganisationBranch.objects.get(pk=source_branch), OrganisationBranch.objects.get(pk=destination_branch))

            if inter_branch_ledger:
                # Save interbranch transaction (initiated from source branch)
                interbranch_transaction = SystemTransactions(amount=amount, heading=heading + ' - initiated', record_date=record_date, payment_method='settlement', voucher_no=voucher_no, coment=comment, reference_no=reference_no, debit_chart_id=inter_branch_ledger.id, credit_chart_id=credit_chart_id, branch_id=source_branch, added_by=self.request.user)
                interbranch_transaction.save()

                # Save main transaction (received in destination branch)
                transaction = SystemTransactions(amount=amount, heading=heading + ' - received', record_date=record_date, payment_method='settlement', voucher_no=voucher_no, coment=comment, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=inter_branch_ledger.id, branch_id=destination_branch, added_by=self.request.user)
                transaction.save()

                # Save interbranch relation
                interbranch_relation = InterBranchTransactions(source_transaction=interbranch_transaction, destination_transaction=transaction, added_by=self.request.user)
                interbranch_relation.save()
                
                approved_request.reference_transaction = interbranch_transaction
                approved_request.save()
            else:
                # Same branch: single transaction
                transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method='settlement', voucher_no=voucher_no, coment=comment, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
                transaction.save()
                
                approved_request.reference_transaction = transaction
                approved_request.save()

            save_user_notification({
                "heading":  "CRB Activation Approval",
                "message": f"CRB Activation. Amount: {approved_request.amount} has been approved by {approved_request.approved_by} as at {approved_request.record_date.date()}",
                "branch":OrganisationBranch.objects.get(pk=approved_request.organisation_branch.id),
                "branch_name":approved_request.organisation_branch.name,
                "added_by":approved_request.added_by,
                "last_updated_by":approved_request.approved_by,
                "key":"ledger_notifications"
            })

class CreditorsView(viewsets.ModelViewSet):
    serializer_class = CreditorAccountsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return CreditorAccounts.objects.filter(organisation_id=organisation_id).order_by('-id')

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)

        status = self.request.data.get('status')
        if status == 'create':
            organisation = Organisation.objects.get(pk=organisation_id)

            #Generate account code
            parent_chart = OrganisationSubAccount.objects.filter(account_code='sys-212')
            parent_id = parent_chart[0].id
            account_line = 'liabilities'
            account_code = generate_chart_of_account_code(parent_id, account_line, organisation_id)

            #Fill session organisation account details
            account_name = 'Creditor: ' + self.request.data.get('account_name')
            chart = OrganisationSubAccount(account_name=account_name,parent_id=parent_chart[0], account_line=account_line, allow_sub_accounts=False, account_organisation=organisation, account_code=account_code, account_type='user_defined', added_by=self.request.user.id)
            chart.save()
            chart_id = chart.id
        else:
            chart_id = self.request.data.get('account_chart')

        #Save Creditor details.
        serializer.save(organisation_id=organisation_id, chart_id=chart_id, added_by=self.request.user.id)

    def update(self, request, pk=None):
        description = self.request.data.get('description')
        account_name = self.request.data.get('account_name')
        telephone_number = self.request.data.get('telephone_number')
        chart_id = self.request.data.get('account_chart')

        #Update Creditor details.
        creditor = CreditorAccounts.objects.get(pk=pk)
        creditor.description = description
        creditor.account_name = account_name
        creditor.telephone_number = telephone_number
        creditor.chart_id = chart_id
        creditor.save()

        return Response(self.serializer_class(creditor).data, status=status.HTTP_200_OK)

class CreditorSuppliesView(viewsets.ModelViewSet):
    serializer_class = CreditorSuppliesSerializer
    http_method_names = ['post', 'get']

    def get_queryset(self):
        supplies = []
        supplier_id = self.request.GET.get('supplier_id', None)
        supplier = CreditorAccounts.objects.filter(id=supplier_id)
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        if supplier and supplier[0].organisation_id == organisation_id:
            supplies = CreditorSupplies.objects.filter(creditor=supplier[0]).order_by('maturity_date')
        
        return supplies

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #transaction details
        heading = self.request.data.get('heading')
        amount = self.request.data.get('amount')
        record_date =  self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = 'credit'
        voucher_no = self.request.data.get('voucher_no')

        credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)

        # Generate reference number
        reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'exp')

        # Save transaction
        transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
        transaction.save()

        #Save Creditor details.
        supplier_id = self.request.data.get('supplier_id')
        maturity_date = self.request.data.get('maturity_date')
        serializer.save(creditor_id=supplier_id, maturity_date=maturity_date, reference_transaction=transaction, added_by=self.request.user.id)


class CreditorPaymentsView(viewsets.ModelViewSet):
    serializer_class = CreditorPaymentsSerializer
    http_method_names = ['post']

    def get_queryset(self):
        payments = []
        supplies_id = self.request.GET.get('supplies_id', None)
        if supplies_id:
            payments = CreditorPayments.objects.filter(supply_id=supplies_id)
        
        return payments
    
    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #transaction details
        heading = self.request.data.get('heading')
        amount = self.request.data.get('amount')
        record_date =  self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = self.request.data.get('payment_method')
        voucher_no = self.request.data.get('voucher_no')

        credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)

        # Generate reference number
        reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'exp')

        # Save transaction
        transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
        transaction.save()

        #Save Creditor details.
        supplies_id = self.request.data.get('supplies_id')
        serializer.save(supply_id=supplies_id, reference_transaction=transaction, added_by=self.request.user.id)


class DebtorsView(viewsets.ModelViewSet):
    serializer_class = DebtorAccountsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        return DebtorAccounts.objects.filter(organisation_id=organisation_id).order_by('-id')

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)

        status = self.request.data.get('status')
        if status == 'create':
            organisation = Organisation.objects.get(pk=organisation_id)

            #Generate account code
            parent_chart = OrganisationSubAccount.objects.filter(account_code='sys-113')
            parent_id = parent_chart[0].id
            account_line = 'assets'
            account_code = generate_chart_of_account_code(parent_id, account_line, organisation_id)

            #Fill session organisation account details
            account_name = 'Debtor: ' + self.request.data.get('account_name')
            chart = OrganisationSubAccount(account_name=account_name,parent_id=parent_chart[0], account_line=account_line, allow_sub_accounts=False, account_organisation=organisation, account_code=account_code, account_type='user_defined', added_by=self.request.user.id)
            chart.save()
            chart_id = chart.id
        else:
            chart_id = self.request.data.get('account_chart')

        #Save Creditor details.
        serializer.save(organisation_id=organisation_id, chart_id=chart_id, added_by=self.request.user.id)

    def update(self, request, pk=None):
        description = self.request.data.get('description')
        account_name = self.request.data.get('account_name')
        telephone_number = self.request.data.get('telephone_number')
        chart_id = self.request.data.get('account_chart')

        #Update Creditor details.
        debtor = DebtorAccounts.objects.get(pk=pk)
        debtor.description = description
        debtor.account_name = account_name
        debtor.telephone_number = telephone_number
        debtor.chart_id = chart_id
        debtor.save()

        return Response(self.serializer_class(debtor).data, status=status.HTTP_200_OK)

class DebtorSuppliesView(viewsets.ModelViewSet):
    serializer_class = DebtorSuppliesSerializer
    http_method_names = ['get', 'post']

    def get_queryset(self):
        """
        Returns DebtorSupplies queryset.
        - For list: filtered by supplier_id if provided
        - For detail/custom actions: filtered by organisation
        """
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        supplier_id = self.request.GET.get('supplier_id', None)

        if supplier_id:
            debtor = DebtorAccounts.objects.filter(id=supplier_id)
            if debtor and debtor[0].organisation_id == organisation_id:
                return DebtorSupplies.objects.filter(debtor=debtor[0]).order_by('maturity_date')
            return DebtorSupplies.objects.none()
        
        # For detail actions: all supplies in the organisation
        return DebtorSupplies.objects.filter(debtor__organisation_id=organisation_id)

    def perform_create(self, serializer):
        """
        Save a new DebtorSupply along with its reference SystemTransaction.
        """
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        # Transaction details
        heading = self.request.data.get('heading')
        amount = self.request.data.get('amount')
        record_date = self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = 'credit'
        voucher_no = self.request.data.get('voucher_no')

        credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)

        # Generate reference number
        reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'inc')

        # Save transaction
        transaction = SystemTransactions.objects.create(
            amount=amount,
            heading=heading,
            record_date=record_date,
            payment_method=payment_method,
            voucher_no=voucher_no,
            reference_no=reference_no,
            debit_chart_id=debit_chart_id,
            credit_chart_id=credit_chart_id,
            branch_id=branch_id,
            added_by=self.request.user
        )

        # Save DebtorSupply
        debtor_id = self.request.data.get('debtor_id')
        maturity_date = self.request.data.get('maturity_date')
        serializer.save(
            debtor_id=debtor_id,
            maturity_date=maturity_date,
            reference_transaction=transaction,
            added_by=self.request.user.id
        )

    @action(detail=True, methods=['post'])
    def update_maturity(self, request, pk=None):
        """
        Custom action to update maturity_date of a DebtorSupply.
        """
        supply = get_object_or_404(self.get_queryset(), pk=pk)
        maturity_date = request.data.get('maturity_date')
        update_reason = request.data.get('update_reason')

        if not maturity_date:
            return Response({"error": "maturity_date is required"}, status=status.HTTP_400_BAD_REQUEST)

        # Update the supply
        supply.maturity_date = maturity_date
        supply.save()

        # Optional: log the update_reason somewhere, e.g., in a history table

        return Response(
            {"success": "Maturity date updated successfully", "maturity_date": supply.maturity_date},
            status=status.HTTP_200_OK
        )

class DebtorPaymentsView(viewsets.ModelViewSet):
    """
    ViewSet for managing debtor payments and recording receivables.
    Allows users to register payments against outstanding receivables.
    """
    serializer_class = DebtorPaymentsSerializer

    def get_queryset(self):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        debtor_id = self.request.GET.get('debtor_id', None)

        if debtor_id:
            return DebtorPayments.objects.filter(
                supply__debtor__organisation_id=organisation_id,
                supply__debtor_id=debtor_id,
                deleted=False
            ).order_by('-date_added')

        return DebtorPayments.objects.filter(
            supply__debtor__organisation_id=organisation_id,
            deleted=False
        ).order_by('-date_added')

    def perform_create(self, serializer):
        """
        New method to record debtor payment
        Creates payment transaction and updates debtor payment records
        """
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        debtor_supply_id = self.request.data.get('supply_id')
        payment_method = self.request.data.get('payment_method', 'settlement')
        amount = self.request.data.get('amount')
        reference_no = self.request.data.get('reference_no')

        debtor_supply = DebtorSupplies.objects.get(id=debtor_supply_id)
        debtor_account = debtor_supply.debtor

        # Create payment transaction in the ledger system
        payment_transaction = SystemTransactions.objects.create(
            amount=amount,
            heading=f"Debtor Payment: {debtor_account.account_name} - {amount}",
            reference_no=reference_no or generate_reference_no(debtor_account.chart.account_line, organisation_id, 'rcv'),
            debit_chart=debtor_account.chart,
            credit_chart=debtor_account.chart,  # Same chart for clearing
            payment_method=payment_method,
            branch_id=branch_id,
            added_by=self.request.user
        )

        # Record the payment
        serializer.save(
            supply=debtor_supply,
            reference_transaction=payment_transaction,
            added_by=self.request.user.id
        )


class DebtorPaymentsView(viewsets.ModelViewSet):
    serializer_class = DebtorPaymentsSerializer
    http_method_names = ['post']

    def get_queryset(self):
        payments = []
        supplies_id = self.request.GET.get('debtor_id', None)
        if supplies_id:
            payments = DebtorPayments.objects.filter(supply_id=supplies_id)

        return payments

    def perform_create(self, serializer):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #transaction details
        heading = self.request.data.get('heading')
        amount = self.request.data.get('amount')
        record_date =  self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = self.request.data.get('payment_method')
        voucher_no = self.request.data.get('voucher_no')

        credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)

        # Generate reference number
        reference_no = generate_reference_no(credit_chart.account_line, organisation_id, 'ast')

        # Save transaction
        transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
        transaction.save()

        #Save Debtor details.
        supplies_id = self.request.data.get('supplies_id')
        serializer.save(supply_id=supplies_id, reference_transaction=transaction, added_by=self.request.user.id)

class SystemTransactionDetailsView(viewsets.ModelViewSet):
    serializer_class = SystemTransactionDetailsSerializer
    http_method_names = ['get', 'put']

    def get_queryset(self):
        # Do not list.
        return []
    
    def retrieve(self, request, pk=None):
        transaction = {}
        organisation_id = get_current_user(request, 'organisation_id', None)
        instance = SystemTransactions.objects.filter(pk=pk, deleted=False).first()
        if instance and instance.branch.branch_organisation_id == organisation_id:
            transaction = self.serializer_class(
                instance,
                context={'request': request},
            ).data
        return Response(transaction, status=status.HTTP_200_OK)
    
    def update(self, request, pk=None):
        response_details = {}
        organisation_id  = get_current_user(self.request, 'organisation_id', None)
        branch_id        = get_current_user(self.request, 'organisation_branch_id', None)
        branch           = OrganisationBranch.objects.get(pk=branch_id)
        comment = request.data.get('update_reason')
        update_type = request.data.get('type')
        transaction = get_object_or_404(
            SystemTransactions,
            pk=pk,
            deleted=False,
            branch__branch_organisation_id=organisation_id,
        )
        transaction_data = SystemTransactionDetailsSerializer(transaction).data
        can_edit_amount = can_edit_transaction_amount_for_organisation(
            transaction,
            organisation_id,
        )
        
        reference_numbers = transaction.reference_no.split('-')
        reference_number = '-'.join(reference_numbers[:len(reference_numbers)-2])
       
        if reference_number in ['ln-d', 'ln-in', 'ln-p']:
            update_details = {
                "comment": comment,
                "amount": request.data.get('amount', None),
                "record_date": request.data.get('record_date', None),
                "reference_number":reference_number,
                "reference_no":transaction.reference_no
            }
            
            loans_transaction_management(self.request, update_type, update_details, None, transaction.id)                
            
            return Response(response_details, status=status.HTTP_200_OK)
        
        if update_type == 'reversal':
            # reverse inventory data
            trans_data = delete_or_reverse_transaction(transaction.id, 'reversal')
            if trans_data:
                response_details = 'reversal'

            elif transaction_data['transaction_details']['type'] in ['withdrawal','withdrawal_charge', 'deposit', 'deposit_charge', 'transfer_charge', 'sms_charge', 'offset', 'offset_revesal', 'offset_reversed']:
                response_details = reverse_savings_transaction(transaction_data, request.user.id, branch, comment)

            elif transaction_data['transaction_details']['type'] in ['transfer']:
                # Reverse sender transaction
                response_details = reverse_savings_transaction(transaction_data, self.request.user.id, branch, comment, False)
                # Reverse receiver transaction
                transaction_data['transaction_details']['id'] = transaction_data['transaction_details']['recipient']['id']
                reverse_savings_transaction(transaction_data, self.request.user.id, branch, comment, False)
                
            elif transaction_data['transaction_details']['type'] in ['fixed-deposit']:
                response_details = reverse_fixed_deposit_transaction(transaction_data, self.request.user.id, branch, comment)
                
            elif transaction_data['transaction_details']['type'] in ['fixed-deposit-payment']:
                response_details = reverse_fixed_deposit_payment_transaction(transaction_data, self.request.user.id, branch, comment)

            elif transaction_data['transaction_details']['type'] in ['share-purchase', 'share-withdrawal']:
                response_details = reverse_shares_transaction(transaction_data, self.request.user.id,branch,comment)

            elif transaction_data['transaction_details']['type'] in ['ledger_to_ledger']:
                details          = SystemTransactionsSerializer(transaction).data
                response_details = reverse_ledger_transaction(transaction,self.request.user.id,branch,comment, transaction_data)
                f_amount = f"{float(transaction.amount):,}"
                message  = f'Reversed Transaction of Amount: {f_amount} Ref No: {transaction.reference_no}'
                add_system_audit_trail('transaction_management','reverse_transaction',message.capitalize(),comment,details,{},self.request.user.id,branch)

            response_details = self.serializer_class(response_details).data
        elif update_type == 'deletion':
            print('****************** step 1 deleting transaction')

            # delete inventory data
            trans_data = delete_or_reverse_transaction(transaction.id, 'delete')

            print('****************** step 5 deleting transaction')
            print(trans_data)
            if trans_data:
                response_details = 'deleted'

            elif transaction_data['transaction_details']['type'] in ['withdrawal','withdrawal_charge', 'deposit', 'deposit_charge', 'transfer_charge', 'sms_charge', 'offset', 'offset_revesal', 'offset_reversed']:
                print('****************** step 6 deleting transaction')
                # delete InterBranch Related Transaction
                delete_interbranch_transaction(transaction_data['id'], user_id=self.request.user.id, branch=branch)
                response_details = delete_savings_transaction(transaction_data,self.request.user.id, comment,branch)

            elif transaction_data['transaction_details']['type'] in ['transfer']:
                response_details = delete_transfer_transaction(transaction_data,self.request.user.id, comment,branch)

            elif transaction_data['transaction_details']['type'] in ['fixed-deposit']:
                response_details = delete_fixed_deposit_transaction(transaction_data,self.request.user.id, comment,branch)
                
            elif transaction_data['transaction_details']['type'] in ['fixed-deposit-payment']:
                response_details = delete_fixed_deposit_payment_transaction(transaction_data,self.request.user.id, comment,branch)

            elif transaction_data['transaction_details']['type'] in ['share-purchase', 'share-withdrawal']:
                response_details = delete_shares_transaction(transaction_data,self.request.user.id, comment,branch)

            elif transaction_data['transaction_details']['type'] in ['ledger_to_ledger']:
                details = SystemTransactionsSerializer(transaction).data
                reverse_ledger_transaction(transaction,self.request.user.id,branch,comment, transaction_data)
                f_amount = f"{float(transaction.amount):,}"
                message = f'Reversed Transaction of Amount: {f_amount} Ref No: {transaction.reference_no}'
                add_system_audit_trail('transaction_management','reverse_transaction',message.capitalize(),comment,details,{},self.request.user.id,branch)
                response_details = 'deleted'

        elif update_type == 'edit':
            if (
                transaction_data['transaction_details']['type'] == 'share-purchase'
                and not can_edit_amount
            ):
                raise PermissionDenied(
                    'Only organisation 994 can edit share purchase transactions.'
                )

            update_details = {
                "coment": comment,
                "record_date": request.data.get('record_date', None),
                "user_id":self.request.user.id
            }
            if can_edit_amount:
                try:
                    amount = float(request.data.get('amount', 0))
                except (TypeError, ValueError):
                    return Response(
                        {"amount": "A valid transaction amount is required."},
                        status=status.HTTP_400_BAD_REQUEST,
                    )

                if amount <= 0:
                    return Response(
                        {"amount": "Transaction amount must be greater than zero."},
                        status=status.HTTP_400_BAD_REQUEST,
                    )

                update_details["amount"] = amount
            if transaction_data['transaction_details']['type'] in ['withdrawal','withdrawal_charge', 'deposit', 'deposit_charge', 'transfer_charge', 'sms_charge', 'fixed-deposit', 'fixed-deposit_payment', 'offset', 'offset_revesal', 'offset_reversed']:
                response_details = update_savings_transaction(transaction_data, update_details, branch)
                response_details = self.serializer_class(
                    response_details,
                    context={'request': request},
                ).data
                
                account_transaction = SavingAccountTransactions.objects.get(pk=transaction_data['transaction_details']['id'])
                if account_transaction.customer_account:
                    customer_account = account_transaction.customer_account
                    # Process account booking payments
                    thread_multiple_booking_payments(customer_account, customer_account.customer_branch.branch_organisation.id, customer_account.customer_branch.id,request.user.id)
            
            elif transaction_data['transaction_details']['type'] in ['transfer']:
                # Update Original Transaction
                response_details = update_savings_transaction(transaction_data, update_details,branch, False)
                response_details = self.serializer_class(response_details).data

                # Reverse receiver transaction
                transaction_data['transaction_details']['id'] = transaction_data['transaction_details']['recipient']['id']            
                update_savings_transaction(transaction_data, update_details, branch, False)
                
                account_transaction = SavingAccountTransactions.objects.get(pk=transaction_data['transaction_details']['id'])
                if account_transaction.customer_account:
                    customer_account = account_transaction.customer_account
                    # Process account booking payments
                    thread_multiple_booking_payments(customer_account, customer_account.customer_branch.branch_organisation.id, customer_account.customer_branch.id,request.user.id)
            
            
            elif transaction_data['transaction_details']['type'] in ['ledger_to_ledger']:
                # Update original account type
                old_details = SystemTransactionsSerializer(transaction).data
                response_details = update_transaction(transaction, update_details)
                f_amount    = f"{float(response_details.amount):,}"
                message     = f'Updated Transaction of Amount: {f_amount} Ref No: {response_details.reference_no}'
                new_details = SystemTransactionsSerializer(response_details).data
                add_system_audit_trail('transaction_management','update_transaction',message,update_details['coment'],old_details,new_details,update_details['user_id'],branch)
                # Update InterBranch Related Transaction
                update_interbranch_transaction(transaction.id, update_details)
        
                response_details = self.serializer_class(
                    response_details,
                    context={'request': request},
                ).data

            elif transaction_data['transaction_details']['type'] in ['share-purchase']:
                response_details = update_shares_transaction(transaction_data, update_details, branch)
                response_details = self.serializer_class(
                    response_details,
                    context={'request': request},
                ).data

                account_transaction = SavingAccountTransactions.objects.filter(
                    transaction_id=transaction.id
                ).first()
                if account_transaction and account_transaction.customer_account:
                    customer_account = account_transaction.customer_account
                    thread_multiple_booking_payments(
                        customer_account,
                        customer_account.customer_branch.branch_organisation.id,
                        customer_account.customer_branch.id,
                        request.user.id,
                    )
                
        return Response(response_details, status=status.HTTP_200_OK)
    
class LedgerAssetsLiabilitiesCapitalView(APIView):

    def post(self, request, format=None):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        #Cash account details
        heading = self.request.data.get('heading')
        type = self.request.data.get('type')
        amount = self.request.data.get('amount')
        record_date =  self.request.data.get('record_date')
        debit_chart_id = self.request.data.get('debit_chart')
        credit_chart_id = self.request.data.get('credit_chart')
        payment_method = self.request.data.get('payent_method')
        voucher_no = self.request.data.get('voucher_no')

        valid = True
        account = None
        inter_branch_ledger = None
        if payment_method == 'offset':
            account = SavingAccount.objects.get(pk=debit_chart_id)
            account_balance = get_account_balance(account)
            debit_chart_id = account.account_product.accounts_chart_id
            
            # Check InterBranch
            if self.request.user.user_organisation_branch.id != account.customer_branch.id:
                inter_branch_ledger = get_inter_branch_chart(self.request.user.user_organisation_branch, account.customer_branch)

            if type =='offset-r':
                if account_balance['balance_raw'] < float(amount):
                    data = {
                        'message' : 'Insufficient balance on selected account.',
                        'status': 'failed'
                    }
                    valid = False
        else:
            if type =='cash-r' or type =='bank-r':
                account_balance = get_chart_of_account_balance_at(debit_chart_id, branch_id)
                if account_balance['balance_raw'] < float(amount):
                    data = {
                        'message' : 'Insufficient balance on selected ' + payment_method + ' account.',
                        'status': 'failed'
                    }
                    valid = False
            
        if valid:
            if type in ['cash-r', 'bank-r', 'offset-i']:
                temp = debit_chart_id
                debit_chart_id = credit_chart_id
                credit_chart_id = temp
            
            credit_chart = OrganisationSubAccount.objects.get(pk=credit_chart_id)

            # Generate reference number
            reference_no = generate_reference_no(credit_chart.account_line, organisation_id)
            if inter_branch_ledger:
                if type == 'offset-r':
                    debit_credit_inter_branch = {
                        "debit_chart_id": inter_branch_ledger.id,
                        "credit_chart_id": credit_chart_id
                    }

                    debit_credit_main = {
                        "debit_chart_id": debit_chart_id,
                        "credit_chart_id": inter_branch_ledger.id
                    }
                else:
                    debit_credit_inter_branch = {
                        "debit_chart_id": debit_chart_id,
                        "credit_chart_id": inter_branch_ledger.id
                    }

                    debit_credit_main = {
                        "debit_chart_id": inter_branch_ledger.id,
                        "credit_chart_id": credit_chart_id
                    }

                # Save interbranch transaction
                interbranch_transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method='settlement',voucher_no=voucher_no, reference_no=reference_no, branch_id=branch_id, added_by=self.request.user, **debit_credit_inter_branch)
                interbranch_transaction.save()

                # Save main transaction
                transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method, voucher_no=voucher_no, reference_no=reference_no, branch_id=account.customer_branch.id, added_by=self.request.user, **debit_credit_main)
                transaction.save()

                # save interbranch relation
                interbranch_relation = InterBranchTransactions(source_transaction=transaction, destination_transaction=interbranch_transaction, added_by=self.request.user)
                interbranch_relation.save()
            else:
                # Save transaction
                transaction = SystemTransactions(amount=amount, heading=heading, record_date=record_date, payment_method=payment_method,voucher_no=voucher_no, reference_no=reference_no, debit_chart_id=debit_chart_id, credit_chart_id=credit_chart_id, branch_id=branch_id, added_by=self.request.user)
                transaction.save()
            
            # Save Account transaction if offset.
            if payment_method == 'offset':
                charge = SavingAccountTransactions(customer_account=account, transaction=transaction, transaction_type='offset')
                charge.save()
            
            data = {
                'message' : 'Transaction created.',
                'status': 'success'
            }
        
        return Response(data, status=status.HTTP_200_OK)

class MergeChartsAccountsView(APIView):
    def post(self,request,format=None):
        transfer_account =  self.request.data.get('transfering_account')
        receiving_account = self.request.data.get('receiving_account')

        organisation_id = get_current_user(self.request, 'organisation_id', None)

        if transfer_account and receiving_account:
            receiving_chart = OrganisationSubAccount.objects.filter(id=receiving_account).first()
            transfer_chart = OrganisationSubAccount.objects.filter(id=transfer_account).first()

            credit_transactions  = self.request.data.get('credits')   #SystemTransactions.objects.filter(credit_chart=transfer_chart, branch__branch_organisation__id=organisation_id).all()
            debit_transactions  = self.request.data.get('debits')  #SystemTransactions.objects.filter(debit_chart=transfer_chart, branch__branch_organisation__id=organisation_id).all()

            if credit_transactions:
                for tran in credit_transactions:
                    transaction = SystemTransactions.objects.filter(id=tran.get('id')).first()
                    transaction.credit_chart=receiving_chart
                    transaction.save()

            if debit_transactions:
                for tran in debit_transactions:
                    transactions = SystemTransactions.objects.filter(id=tran.get('id')).first()
                    transactions.debit_chart=receiving_chart
                    transactions.save()

        return Response({"message":"Chart Transaction Transfered Successfully"})


class OrganisationBranchWalletAccountsView(APIView):

    def get(self, request, format=None):
        results = []
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        branch_id = get_current_user(self.request, 'organisation_branch_id', None)

        branch_wallet_chart = get_branch_wallet_chart(organisation_id, branch_id)
        response = {}
        if branch_wallet_chart:
            account_totals = get_chart_of_account_balance_at(branch_wallet_chart, branch_id)
            response['id'] = branch_wallet_chart.id
            response['account_name'] = branch_wallet_chart.account_name
            response['account_code'] = branch_wallet_chart.account_code
            response['balance'] = account_totals['balance_raw'] if account_totals else 0
            response['chart'] = branch_wallet_chart.id
            results.append(response)
    
        return Response({"results":results, "count":len(results)})


class OrganisationSubAccountChartView(APIView):

    def charts_data(self, chart_line):
        organisation_id = get_current_user(self.request, 'organisation_id', None)
        assets = OrganisationSubAccount.objects.filter(
            account_line=chart_line,
            account_organisation=organisation_id,
            parent_id__isnull=True,
            deleted=False,
        ).order_by('id')
        serializer = OrganisationSubAccountSerializer(assets, many=True)

        return serializer.data

    def get(self, request, format=None):
        tb_data = [
            {'data': self.charts_data('assets'), "label":"1. ASSETS"},
            {'data': self.charts_data('liabilities'), "label":"2. LIABILITIES"},
            {'data': self.charts_data('capital'), "label":"3. CAPITAL"},
            {'data': self.charts_data('income'), "label":"4. INCOME"},
            {'data': self.charts_data('expenses'), "label":"5. COSTS AND EXPENSES"}
        ]
        return Response({"results":tb_data, "count":len(tb_data)})
