import { Response } from "express";
import moment from "moment";
import Razorpay from "razorpay";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import clientDB from "../models/client.model";
import duesDB from "../models/dues.model";
import occupancyDB from "../models/occupancy.model";
import propertyDB from "../models/property.model";
import roomDB from "../models/room.model";
import staffDB from "../models/staff.model";
import tenantDB from "../models/tenant.model";
import transactionLogsDB from "../models/transactionLogs.model";
import flatDB from "../models/flat.model";
import transactionDB from "../models/transaction.model";
import propertiesTypes from "../schemas/property.schema";
import CustomRequest from "../types/requestType";
import getMatchedTransactionFor from "../utils/client/getMatchedTransactionFor";
import createReceipt from "../utils/createReceipt";
import createReceiptMultipleDues from "../utils/createReceiptMultipleDues";
import generateTransId from "../utils/generateTransId";
import getDueName from "../utils/getDueName";
import getModeName from "../utils/getModeName";
import { cli } from "winston/lib/winston/config";
import ledgerDB from "../models/ledger.model";
import getDueDescription from "../utils/getDueDescription";
import { isUserFinanceAdmin, isUserPartner } from "../utils/isUserPartner";
import sendSMS from "../utils/sendSMS";
import {
  clientMonthlyPendingRent,
  propertyCurrentFYPendingRent,
  propertyMonthlyPendingRent,
} from "../utils/client/rentPendingStats";
import { releasePayment, splitPayment, getSplitPaymentDetails } from "../utils/client/clientPayout";
import jwt from "jsonwebtoken";
import axios from "axios";
import paymentLinkDB from "../models/paymentLinks.model";
import settingsDB from "../models/settings.model";
import { sendWhatsappPaymentConfirmationClient, sendWhatsappPaymentConfirmationTenant } from "../utils/sendWhatsappWithConfig";
import moveOutDB from "../models/moveOut.model";
import moveOutDuesDB from "../models/moveOutDues.model";
import { logActivity } from "../utils/logActivity";
import { isPrivilegedStaff } from "../utils/isPrivilegedStaff";
import fs from "fs";
import fsPromises from "fs/promises";
import path from "path";
import { addTransactionsOnline, addTransactionsOnlineMoveOut } from "../utils/addTransaction";
import clientConfigDB from "../models/clientConfig.model";
import generateLedgerReferenceId from "../utils/generateLedgerReferenceId";
import sendWhatsapp from "../utils/sendWhatsappChatMitra";
import createGstReceipt from "../utils/createGstReceipt";
import bankDB from "../models/bank.model";
import { setTransactionTallyStatusByGId } from "../utils/setTallyStatus";
import { cashfreeSplit } from "../utils/cashfreeSplit";
import sendNotification from "../utils/sendNotification";
import adjustExcessPayments from "../utils/adjustExcessPayment";
import { generateRentReciept } from "../utils/generateRentReciept";
import { getClientWhatsappCredentialsMessageCentral } from "../utils/getClientWhatsappCredentials";
import sendWhatsappMC from "../utils/sendWhatsappMessageCentral";
import cashfree from "./cashfree.controller";

const transactions: any = {};

transactions.ListForTenant = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "ListForTenant";

  try {
    const tenantId = req.id;
    const isEvicted = req.isEvicted;
    const clientId = req.clientId;
    const { pageNum } = req.query;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Page Number [${pageNum}]`
    );

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Page Number [${pageNum}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenantId,
      clientId: clientId,
    });

    if (isEvicted) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId: tenantId,
        clientId: clientId,
      });
    }

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Page Number [${pageNum}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const limit = 10;
    const transactions = await transactionDB.getByTenantId({
      tenantId,
      clientId: occupancy.clientId,
      pageNum: Number(pageNum),
      limit,
    });

    const { rent, security, others } = await transactionDB.getCountByTenantId({
      tenantId,
    });

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Page Number [${pageNum}], Transactions sent successfully`
    );

    return res.status(200).json({
      msg: "Transactions sent successfully",
      data: {
        counts: {
          rent: Number(rent) || 0,
          security: Number(security) || 0,
          others: Number(others) || 0,
        },
        transactions,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.ListForClient = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "ListForClient";

  try {
    const { pageNum, m, s, y, propId } = req.query;

    // let clientId = req.id;
    const userType = req.userType;

    let transactions = [];
    let curMonthTransAmount = 0;
    let prevMonthTransAmount = 0;
    let diff = 0;
    const limit = 10;

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], PropId [${propId}], Page Number [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}]`
    );

    const month = Number(m) || 0;
    const year = Number(y) || 0;

    if (!month || month > 12 || !year) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Not a Valid Month`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let date = moment()
      .month(month - 1)
      .year(year)
      .format("YYYY-MM-DD");

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }...`
      );

      const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (s && s !== "undefined" && s !== "null" && s !== " ") {
        const { transactionForValue } = getMatchedTransactionFor(s.toString());

        transactions = await transactionDB.getSearchResultByClientId({
          clientId,
          pageNum: Number(pageNum),
          limit,
          searchVal: s,
          transactionForValue,
        });
      } else if (propId) {
        transactions = await transactionDB.getByPropId({
          propId,
          pageNum: Number(pageNum),
          limit,
          month: date,
        });
        curMonthTransAmount = await transactionDB.getTotalByPropId({
          propId,
          month: date,
        });
        prevMonthTransAmount = await transactionDB.getTotalByPropId({
          propId,
          month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
        });
        if (prevMonthTransAmount) {
          diff =
            ((curMonthTransAmount - prevMonthTransAmount) /
              prevMonthTransAmount) *
            100;
        } else {
          if (!curMonthTransAmount) diff = 0;
          else diff = 100;
        }
      } else {
        transactions = await transactionDB.getByClientId({
          clientId,
          pageNum: Number(pageNum),
          limit,
          month: date,
        });

        curMonthTransAmount = await transactionDB.getTotalByClientId({
          clientId,
          month: date,
        });

        prevMonthTransAmount = await transactionDB.getTotalByClientId({
          clientId,
          month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
        });
        if (prevMonthTransAmount) {
          diff =
            ((curMonthTransAmount - prevMonthTransAmount) /
              prevMonthTransAmount) *
            100;
        } else {
          if (!curMonthTransAmount) diff = 0;
          else diff = 100;
        }
      }
    } else {
      const staffId = req.id;
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Staff Id [${staffId}], Staff Requested.....`
      );

      clientId = staff.clientId;

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          const { transactionForValue } = getMatchedTransactionFor(
            s.toString()
          );

          transactions = await transactionDB.getSearchResultForStaff({
            clientId,
            pageNum: Number(pageNum),
            limit,
            searchVal: s,
            transactionForValue,
            propertiesIds,
          });
        } else {
          transactions = await transactionDB.getForStaff({
            clientId,
            pageNum: Number(pageNum),
            limit,
            propertiesIds,
            month: date,
          });

          curMonthTransAmount = await transactionDB.getTotalByClientIdForStaff({
            clientId,
            month: date,
            propertiesIds,
          });

          prevMonthTransAmount = await transactionDB.getTotalByClientIdForStaff(
            {
              clientId,
              month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
              propertiesIds,
            }
          );
          if (prevMonthTransAmount) {
            diff =
              ((curMonthTransAmount - prevMonthTransAmount) /
                prevMonthTransAmount) *
              100;
          } else {
            if (!curMonthTransAmount) diff = 0;
            else diff = 100;
          }
        }
      }
    }

    if (transactions) {
      for (let trans of transactions) {
        trans.desc = `for the month of ${moment(trans.dueDate).format("MMM")}`;
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Transactions sent successfully`
    );

    return res.status(200).json({
      msg: "Transactions sent successfully",
      data: {
        transactions: transactions || [],
        curMonthTransAmount: Number(curMonthTransAmount) || 0,
        prevMonthTransAmount: Number(prevMonthTransAmount) || 0,
        diff: Number(diff.toFixed(2)) || 0,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.ListTenantTrans = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "ListTenantTrans";

  try {
    const { pageNum, m, s, y, e = 0 } = req.query;
    const { tenantId } = req.params;

    let isEvicted = e === "1"

    let clientId = req.id;
    const userType = req.userType;

    let transactions = [];
    let curMonthTransAmount = 0;
    let prevMonthTransAmount = 0;
    let diff = 0;
    const limit = 10;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Tenant Id [${tenantId}], User Type [${userType}], Is Evicted [${isEvicted}]`
    );

    const month = Number(m) || 0;
    const year = Number(y) || 0;

    if (!month || month > 12 || !year) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Not a Valid Month`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let date = moment()
      .month(month - 1)
      .year(year)
      .format("YYYY-MM-DD");

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staffId = req.id;
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Staff Id [${staffId}], Staff Requested.....`
      );

      clientId = staff.clientId;
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Client Requested.....`
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let occupancy = null;

    if (isEvicted) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId,
        clientId,
      });
    } else {
      occupancy = await occupancyDB.getByClientIdAndTenantId({
        clientId,
        tenantId: tenantId,
      });
      if (!occupancy) {
        occupancy = await moveOutDB.getByTenantIdAndClientId({
          tenantId,
          clientId,
        });
        isEvicted = true;
        if (!occupancy) {
          log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found In Either Occupancies or MoveOut`);

          return res.status(400).json({
            msg: CONSTANTS.MSG.INVALID_REQUEST,
            isSuccess: false,
          })
        }
      } else {
        occupancy = occupancy[0];
      }
    }


    if (s && s !== "undefined" && s !== "null" && s !== " ") {
      const { transactionForValue } = getMatchedTransactionFor(s.toString());

      transactions = await transactionDB.getSearchResultByTenantId({
        tenantId,
        clientId,
        pageNum: Number(pageNum),
        limit,
        searchVal: s,
        transactionForValue,
      });
    } else {
      transactions = await transactionDB.getByTenantId({
        tenantId,
        clientId,
        pageNum: Number(pageNum),
        limit,
        //month: date,
      });
    }

    if (transactions) {
      for (let trans of transactions) {
        trans.desc = `for the month of ${moment(trans.dueDate).format("MMM")}`;

        let canRestore = true;

        const ledgerRecordsByTxnId = await ledgerDB.getByTransactionId({
          transactionId: Number(trans.id),
        });

        let securityUsed = false;

        if (occupancy) {
          securityUsed = await ledgerDB.getUsedSecurityAmtByTenantIdAndClientId({
            tenantId: trans.tenantId,
            clientId: clientId,
            createdAt: occupancy.createdAt,
          });
        }

        if (isEvicted) {
          //Cannot restore transactions for moved out tenants
          canRestore = false;
        } else if (securityUsed && trans.transactionFor === CONSTANTS.TRANSACTION_FOR.SECURITY) {
          canRestore = false;
        } else if (!ledgerRecordsByTxnId || ledgerRecordsByTxnId.length === 0) {
          log.info(
            `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], No Ledger Records Found`
          );
          canRestore = false;
        } else if (ledgerRecordsByTxnId && ledgerRecordsByTxnId.length > 1) {
          //Multiple Dues Check using transactionId
          canRestore = false;
        } else if (Math.abs(ledgerRecordsByTxnId[0].amount) !== Math.abs(trans.amount)) {
          //Excess Amount Check using transaction amount and ledger record amount
          canRestore = false;
        } else if (ledgerRecordsByTxnId[0].type === CONSTANTS.DUES_TYPES.SETTLEMENT) {
          canRestore = false;
        }

        // else if(ledgerRecordsByTxnId[0].balance !== 0) {
        //   // log.info(
        //   //   `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], Due Has Been Partially Paid, Cannot Restore Transaction`
        //   // );
        //   canRestore = false;
        // }

        trans.canRestore = canRestore;
      }
    }

    const totalCollection = await transactionDB.getTotalByTenantId({
      tenantId,
      clientId,
      // year: date,
    });

    let totalSecurityReceived = await ledgerDB.getSecurityTransactionAmountX({
      tenantId,
      type: CONSTANTS.DUES_TYPES.SECURITY,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
    });
    let totalDuesPending: any;
    if (isEvicted) {
      totalDuesPending = await moveOutDuesDB.getTotalDuesByTenantId({
        tenantId: tenantId,
        propId: occupancy.propId,
      });
    } else {
      totalDuesPending = await duesDB.getTotalDuesByTenantId({
        tenantId: tenantId,
        propId: occupancy.propId,
      });
    }

    // let totalAdjustedFromSecurity = await ledgerDB.getUsedSecurityAmtByTenantIdAndClientId({
    //   clientId,
    //   tenantId,
    //   createdAt: moment("1976-02-18").format("YYYY-MM-DD HH:mm:ss"),
    // });

    let totalAdjustedFromSecurity = await ledgerDB.getAdjustedSecurityAmtByTenantIdAndClientId({
      clientId,
      tenantId
    });

    let refundedAmount = 0;

    let refundedEntry = await ledgerDB.getLastRefundEntry({
      clientId,
      tenantId,
      moveInDate: occupancy.moveInDate,
    })

    if (refundedEntry) {
      const match = refundedEntry.description.match(/₹\s*(\d+)/);
      refundedAmount = match ? Number(match[1]) : 0;
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Month [${month}], Year [${year}], Search Val [${s}], Transactions sent successfully`
    );

    return res.status(200).json({
      msg: "Transactions sent successfully",
      data: {
        transactions: transactions || [],
        totalCollection: totalCollection || 0,
        totalAdjustedFromSecurity: totalAdjustedFromSecurity || 0,
        totalSecurityReceived: Math.abs(totalSecurityReceived.amount) || 0,
        refundedAmount: refundedAmount || 0,
        totalDues: totalDuesPending.totalDues
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.GenerateOrderId = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "GenerateOrderId";

  try {
    const tenantId = req.id;
    const { amount, mode } = req.body;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}]`
    );

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let payableAmount: number | string = amount;
    let charges: number = 0;

    if (mode === CONSTANTS.TRANSACTION_MODES.CARD)
      charges = Number((amount * 0.005).toFixed(2));
    else if (mode === CONSTANTS.TRANSACTION_MODES.NET_BANKING)
      charges = Number((amount * 0.003).toFixed(2));

    payableAmount = ((amount + charges) * 100).toFixed(0);

    let orderId = null;

    const instance = new Razorpay({
      key_id: process.env.RZPAY_KEY || "",
      key_secret: process.env.RZPAY_SECRET,
    });

    await instance.orders
      .create({
        amount: payableAmount,
        currency: "INR",
        // receipt: "111",
        partial_payment: false,
        notes: {
          Title: "KIPINN PAYMENTS",
        },
      })
      .then((order: any) => {
        orderId = order.id;
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], Order Id [${order.id}], Order Id generated successfully`
        );
      })
      .catch((err: any) => {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], Error [${err?.error.description || err?.message || err
          }], Failed to generate Order Id, `
        );
      });

    return res.status(200).json({
      msg: "Order Id generated successfully",
      data: {
        orderId,
        originalAmount: amount,
        totalAmount: Number(payableAmount) / 100,
        payableAmount,
        charges,
        key: process.env.RZPAY_KEY,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.TransactionCharge = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "TransactionCharge";

  try {
    const tenantId = req.id;
    const clientId = req.clientId;
    let isEvicted = req.isEvicted;
    const { amount, mode } = req.body;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}]`
    );

    if (!clientId) {
      log.info(`[${C}], [${F}], No Client Id In Tenant Token, Expiring Session`);
      return res.status(401).json({
        msg: "Session Expired",
        isSuccess: false,
      });
    }

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let occupancy = await occupancyDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (Number(isEvicted) === 1) {
      occupancy = await moveOutDB.getAllByTenantIdAndClientId({
        tenantId,
        clientId,
      });
    }
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], No Occupancy Found`
      );
      return res.status(400).json({
        msg: "No Occupancy Found",
        isSuccess: false,
      });
    }

    occupancy = occupancy[0];

    const client = await clientDB.getById({ id: occupancy.clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], No Client Found`
      );
      return res.status(400).json({
        msg: "No Client Found",
        isSuccess: false,
      });
    }

    let payableAmount: number | string = amount;
    let charges: number = 0;
    let getChargesType = await clientConfigDB.getClientConfig({
      clientId: client.id,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.TRANSACTION_CHARGES,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.TRANSACTION_CHARGES.TYPE
    });
    let getChargesThrashHold = await clientConfigDB.getClientConfig({
      clientId: client.id,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.TRANSACTION_CHARGES,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.TRANSACTION_CHARGES.THRASHOLD
    });
    log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Total Amount [${amount}], Charge Type [${getChargesType?.value}], Thrashhold Amount [${getChargesThrashHold?.value}]`);
    if (false == getChargesType || getChargesType?.value == null || getChargesType?.value == "" || Number(getChargesType?.value) === CONSTANTS.CLIENT_CONFIG_VALUE.TRANSACTION_CHARGES.PERCENTAGE) {
      if (getChargesThrashHold && Number(amount) > Number(getChargesThrashHold?.value)) {
        //charges = 0;
        if (mode === CONSTANTS.TRANSACTION_MODES.CARD)
          charges = Number((amount * (client.creditCardCharges / 100)).toFixed(2));
        else if (mode === CONSTANTS.TRANSACTION_MODES.NET_BANKING)
          charges = 0;
        else if (mode === CONSTANTS.TRANSACTION_MODES.UPI)
          charges = 0;
      } else {
        if (mode === CONSTANTS.TRANSACTION_MODES.CARD)
          charges = Number((amount * (client.creditCardCharges / 100)).toFixed(2));
        else if (mode === CONSTANTS.TRANSACTION_MODES.NET_BANKING)
          charges = Number((amount * (client.otherCharges / 100)).toFixed(2));
        else if (mode === CONSTANTS.TRANSACTION_MODES.UPI)
          charges = Number(((amount * client.upiCharges) / 100).toFixed(2));
      }
    } else {
      if (getChargesThrashHold && Number(amount) > Number(getChargesThrashHold?.value)) {
        //charges = 0;
        if (mode === CONSTANTS.TRANSACTION_MODES.CARD)
          charges = Number((amount * (client.creditCardCharges / 100)).toFixed(2));
        else if (mode === CONSTANTS.TRANSACTION_MODES.NET_BANKING)
          charges = 0;
        else if (mode === CONSTANTS.TRANSACTION_MODES.UPI)
          charges = 0;
      } else {
        if (mode === CONSTANTS.TRANSACTION_MODES.CARD)
          charges = Number((amount * (client.creditCardCharges / 100)).toFixed(2));
        else if (mode === CONSTANTS.TRANSACTION_MODES.NET_BANKING)
          charges = Number(client.otherCharges);
        else if (mode === CONSTANTS.TRANSACTION_MODES.UPI)
          charges = Number(client.upiCharges);
      }
    }

    // charges = Math.floor(charges);
    payableAmount = amount + charges;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Mode [${mode}], Charges [${charges}],Payable Amount [${payableAmount}], Charges generated successfully`
    );

    return res.status(200).json({
      msg: "Transaction charge generated successfully",
      data: {
        originalAmount: amount,
        totalAmount: Number(payableAmount),
        charges,
        key: process.env.PAYU_KEY,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.Add = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "Add";

  try {
    const tenantId = req.id;
    const clientId = req.clientId;
    const isEvicted = req.isEvicted;
    if (isEvicted) {
      return transactions.AddMoveOut(req, res);
    } else {
      return transactions.AddOccupied(req, res);
    }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};
transactions.AddOccupied = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "AddOccupied";

  try {
    const tenantId = req.id;
    const clientId = req.clientId;
    //const { paymentId, amount, status, mode, key, ledgerReferenceId } =
    let {
      dueIds,
      ledgerReferenceIds,
      paymentId,
      amount,
      status,
      mode,
      key,
      charges,
      gstAmount = 0,
      isBookingPayment = false,
    } = req.body;
    let dueDescription = "";
    let isExcessPayment = false;
    /*
    if (mode !== CONSTANTS.TRANSACTION_MODES.OFFLINE && charges !== 0) {
      amount = Number(amount) - Number(charges);
    }
   */
    let splitTransId = null;
    let splitDetail;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Due Ids [${dueIds}], Payment Id [${paymentId}], ledgereferenceId [${ledgerReferenceIds}], Status [${status}], Mode [${mode}], Key [${key}], Amount [${amount}], Charges [${charges}], Gst Charges [${gstAmount}], Is Booking Payment [${isBookingPayment}], Is Excess Payment [${isExcessPayment}]`
    );

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Payment Id [${paymentId}], ledgereferenceId [${ledgerReferenceIds}], Status [${status}], Mode [${mode}], Key [${key}], Amount [${amount}], Charges [${charges}], Is Booking Payment [${isBookingPayment}], Is Excess Payment [${isExcessPayment}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let occupancy = await occupancyDB.getByClientIdAndTenantId({
      clientId: clientId,
      tenantId: tenantId,
    });
    occupancy = occupancy[0];
    const client = await clientDB.getById({ id: occupancy.clientId });
    const property = await propertyDB.getById({ id: occupancy.propId });
    const room = await roomDB.getById({ id: occupancy.roomId });
    let isTransactionRecorded = false;
    let amtPaid = amount;
    let duesStatsArr = [];
    let dueStats = {};
    let excessDueStats = {};
    let excessDueflag = false;

    let allDues = await duesDB.getAllDues({
      ids: dueIds,
    });

    log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Dues Fetched [${JSON.stringify(allDues)}]`);

    if (!allDues) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Payment Id [${paymentId}], No Dues Found`
      );
      return res.status(400).json({
        msg: "All dues paid",
        isSuccess: false,
      });
    }
    let count = 1;
    let lengthOfDues = allDues.length;
    let isSingleDuePayment = allDues.length === 1 ? true : false;
    let security = await allDues.find(
      (due: any) => due.type === CONSTANTS.DUES_TYPES.SECURITY
    );
    let receipts = [];
    let dueTotal = 0;

    let transTitle: string = "Payment for ";
    if (allDues.length === 1) {
      transTitle = "";
    }
    let isSecurityPayment = false;
    for (let i = 0; i < allDues.length; i++) {
      const due = allDues[i];
      if (isSingleDuePayment) {
        if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
          isSecurityPayment = true;
        }
      }
      if (due.title === null) {
        transTitle += await getDueDescription(due.type);
      } else {
        transTitle += `${due.title}`;
      }
      if (i !== allDues.length - 1) {
        transTitle += ", ";
      }
    }

    let transDescription = allDues.length === 1
      ? allDues[0].rentStartDate !== null
        ? `${transTitle} for ${moment(allDues[0].rentStartDate).format("DD MMM YY")} to ${moment(allDues[0].rentEndDate).format("DD MMM YY")}`
        : `${transTitle} for ${moment(allDues[0].dueDate).format("MMM YYYY")}`
      : transTitle;

    if (isBookingPayment) transDescription = 'Booking amount';

    const transGId: string = await generateTransId();
    let transId;
    if (status === CONSTANTS.TRANSACTION_STATUS.SUCCESS) {
      // transId = await transactionDB.addOnline({
      //   gId: paymentId || transGId,
      //   bankId: property.bankId,
      //   clientId: allDues[0].clientId,
      //   tenantId: allDues[0].tenantId,
      //   roomId: allDues[0].roomId,
      //   propId: allDues[0].propId,
      //   amount: amount,
      //   name:
      //     allDues.length === 1
      //       ? allDues[0].rentStartDate !== null
      //         ? `${transTitle} for ${moment(allDues[0].rentStartDate).format(
      //             "DD MMM YY"
      //           )} to ${moment(allDues[0].rentEndDate).format("DD MMM YY")}`
      //         : `${transTitle} for ${moment(allDues[0].dueDate).format(
      //             "MMM YYYY"
      //           )}`
      //       : transTitle,
      //   type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
      //   transactionFor: allDues[0].type,
      //   status,
      //   dueDate: allDues[0].dueDate,
      //   receipt: "",
      //   mode,
      //   collectionDate: moment().format("YYYY-MM-DD HH:mm:ss"),
      //   propName: property.name,
      //   roomNum: room.roomNum,
      //   ledgerReferenceId: "",
      //   recordedBy: "Kipinn App",
      //   charges: charges,
      //   gstCharges: gstAmount,
      //   title: allDues[0].title || null,
      // });
      if (security && security.balance > 0) {
        let due = security;
        const securityAdjustEntry = await ledgerDB.getLastEntryX({
          tenantId,
          clientId: due.clientId,
          createdAt: occupancy?.moveInDate,
        });
        dueTotal += due.balance;
        dueDescription = await getDueDescription(due.type);
        if (isBookingPayment) {
          dueDescription = "Booking amount"
        } else if (due.balance > amtPaid) {
          dueDescription = "Partial payment for " + dueDescription;
        } else if (due.balance < due.amount) {
          dueDescription = "Final payment for " + dueDescription;
        }
        if (security.balance <= amtPaid) {
          if (count == lengthOfDues) {
            transId = await addTransactionsOnline({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: amtPaid,
              description: dueDescription,
              mode,
              transGId: paymentId || transGId,
              charges: 0,
              gstAmount: 0,
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: -(amtPaid - due.balance),
              referenceId: due.ledgerReferenceId,
              transactionId: null,
              type: due.type,
              rentStartDate: null,
              rentEnddate: null,
              dueDate: due.dueDate,
              description: dueDescription,
              subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
            });
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            if (Number(amtPaid) > Number(due.balance)) isExcessPayment = true;
            amtPaid = 0;
          } else {
            transId = await addTransactionsOnline({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: due.balance,
              description: dueDescription,
              mode,
              transGId: paymentId || transGId,
              charges: 0,
              gstAmount: 0,
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: 0,
              referenceId: due.ledgerReferenceId,
              transactionId: null,
              type: due.type,
              rentStartDate: null,
              rentEnddate: null,
              dueDate: due.dueDate,
              description: dueDescription,
              subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
            });
            amtPaid -= due.balance;
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
          }
          await duesDB.removeDue({ id: due.id });

          isTransactionRecorded = true;
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceIds}], Due Id [${due.id}], Transaction has been recorded successfully`
          );
        } else if (security.balance > amtPaid) {
          transId = await addTransactionsOnline({
            due: due,
            clientId: clientId,
            tenantId: tenantId,
            amount: amtPaid,
            description: dueDescription,
            mode,
            transGId: paymentId || transGId,
            charges: 0,
            gstAmount: 0,
          });
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -amtPaid,
            balance: due.balance - amtPaid,
            referenceId: due.ledgerReferenceId,
            transactionId: null,
            type: due.type,
            rentStartDate: null,
            rentEnddate: null,
            dueDate: due.dueDate,
            description: dueDescription,
            subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
          });

          //Creating DueStats for Recipt
          dueStats = { title: dueDescription, amount: amtPaid };
          duesStatsArr.push(dueStats);

          await duesDB.updateBalance({
            id: due.id,
            balance: due.balance - amtPaid,
          });
          amtPaid = 0;
          isTransactionRecorded = true;
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceIds}], Due Id [${due.id}], Transaction has been recorded successfully`
          );
        }
        count++;
        if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
          let securityAmt = amount >= due.balance ? due.balance : amount;
          const refundEntry = await ledgerDB.getLastEntryX({
            tenantId,
            clientId: occupancy.clientId,
            createdAt: occupancy?.moveInDate,
          });

          if (refundEntry) {
            await ledgerDB.addBalance({
              tenantId,
              clientId: occupancy.clientId,
              balance: -securityAmt,
              type: refundEntry.type,
              referenceId: refundEntry.referenceId,
            });
          } else {
            const referenceId = await generateLedgerReferenceId({ clientId: occupancy.clientId });
            ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: 0,
              balance: -securityAmt,
              referenceId: referenceId,
              transactionId: null,
              type: CONSTANTS.DUES_TYPES.SECURITY,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: occupancy.moveOutDate,
              description:
                "Security deposit and any excess amounts paid by tenant. Refund after eviction.",
            });
          }
        }
        if (securityAdjustEntry !== false) {
          await ledgerDB.removeById({
            id: securityAdjustEntry.id,
          });

          //creating new security entry after deleting previous
          await ledgerDB.add({
            tenantId: securityAdjustEntry.tenantId,
            roomId: securityAdjustEntry.roomId,
            propId: securityAdjustEntry.propId,
            clientId: due.clientId,
            amount: securityAdjustEntry.amount,
            balance: securityAdjustEntry.balance,
            referenceId: securityAdjustEntry.referenceId,
            transactionId: securityAdjustEntry.transactionId,
            type: securityAdjustEntry.type,
            rentStartDate: securityAdjustEntry.rentStartDate,
            rentEndDate: securityAdjustEntry.rentEndDate,
            description: securityAdjustEntry.description,
            dueDate: securityAdjustEntry.dueDate,
          });
        }
      }

      let discountAmount = 0;

      if (amtPaid != 0) {
        for (const due of allDues) {
          if (amtPaid == 0) break;
          const securityAdjustEntry = await ledgerDB.getLastEntryX({
            tenantId,
            clientId: due.clientId,
            createdAt: occupancy?.moveInDate,
          });
          dueTotal += due.balance;
          if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
            continue;
          }
          let dueDescription = "";
          if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
            dueDescription = due.title;
            if (dueDescription === null) {
              dueDescription = await getDueDescription(allDues[0].type);
            }
          } else if (
            due.title &&
            due.title.toLowerCase().includes("advance rent")
          ) {
            dueDescription = "Advance Rent";
          } else {
            dueDescription = await getDueDescription(due.type);
          }

          let addDiscountInReceipt = true;

          if (due.balance > amtPaid) {
            dueDescription = "Partial payment for " + dueDescription;
            addDiscountInReceipt = false;
          } else if (due.balance < due.amount) {
            dueDescription = "Final payment for " + dueDescription;
          }

          if (due.type === CONSTANTS.DUES_TYPES.RENT) {
            dueDescription =
              dueDescription +
              " for " +
              moment(due.rentStartDate).format("DD MMM, YY") +
              " to " +
              moment(due.rentEndDate).format("DD MMM, YY");
          } else {
            dueDescription =
              dueDescription + " for " + moment(due.dueDate).format("DD MMM, YY");
          }
          // dueDescription = due.title || getDueDescription(due.type);
          // dueDescription = `${dueDescription} for ${moment(due.dueDate).format(
          //   "MMM YYYY"
          // )}`;
          if (status === CONSTANTS.TRANSACTION_STATUS.SUCCESS) {
            if (amtPaid <= due.balance) {
              transId = await addTransactionsOnline({
                due: due,
                clientId: clientId,
                tenantId: tenantId,
                amount: amtPaid,
                description: isBookingPayment ? transDescription : dueDescription,
                mode,
                transGId: paymentId || transGId,
                charges: 0,
                gstAmount: 0,
              });
              await ledgerDB.add({
                tenantId,
                roomId: occupancy.roomId,
                propId: occupancy.propId,
                clientId: occupancy.clientId,
                amount: -amtPaid,
                balance: due.balance - amtPaid,
                referenceId: due.ledgerReferenceId,
                transactionId: null,
                type: due.type,
                rentStartDate: null,
                rentEnddate: null,
                dueDate: due.dueDate,
                description: due?.discount > 0 ? Number(amtPaid) === Number(due.balance) ? `${dueDescription}, Discount of ₹${due?.discount} given for this due` : dueDescription : dueDescription,
                discount: due.discount,
                subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
              });
              if (amtPaid == due.balance) {
                await duesDB.removeDue({ id: due.id });

                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: due.balance + due.discount };
                duesStatsArr.push(dueStats);
              } else {
                await duesDB.updateBalance({
                  id: due.id,
                  balance: due.balance - amtPaid,
                });

                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: amtPaid };
                duesStatsArr.push(dueStats);
              }
              amtPaid = 0;
            } else {
              if (count == lengthOfDues) {
                // amtPaid -= due.balance;
                transId = await addTransactionsOnline({
                  due: due,
                  clientId: clientId,
                  tenantId: tenantId,
                  amount: amtPaid,
                  description: isBookingPayment ? transDescription : dueDescription,
                  mode,
                  transGId: paymentId || transGId,
                  charges: 0,
                  gstAmount: 0,
                });
                await ledgerDB.add({
                  tenantId,
                  roomId: occupancy.roomId,
                  propId: occupancy.propId,
                  clientId: occupancy.clientId,
                  amount: -due.balance,
                  balance: -(amtPaid - due.balance),
                  referenceId: due.ledgerReferenceId,
                  transactionId: null,
                  type: due.type,
                  rentStartDate: null,
                  rentEnddate: null,
                  dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
                  description: due.discount > 0 ? `${dueDescription}, Discount of ₹${due.discount} given for this due` : dueDescription,
                  discount: due.discount,
                  subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
                });

                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: due.balance + due.discount };
                duesStatsArr.push(dueStats);
                //Exesss Due
                if (Math.abs(amtPaid - due.balance) > 0) {
                  excessDueflag = true;
                  excessDueStats = {
                    title: "Excess Payment",
                    amount: Math.abs(amtPaid - due.balance),
                  };
                }
                isExcessPayment = true;
              } else {
                transId = await addTransactionsOnline({
                  due: due,
                  clientId: clientId,
                  tenantId: tenantId,
                  amount: due.balance,
                  description: isBookingPayment ? transDescription : dueDescription,
                  mode,
                  transGId: paymentId || transGId,
                  charges: 0,
                  gstAmount: 0,
                });
                await ledgerDB.add({
                  tenantId,
                  roomId: occupancy.roomId,
                  propId: occupancy.propId,
                  clientId: occupancy.clientId,
                  amount: -due.balance,
                  balance: 0,
                  referenceId: due.ledgerReferenceId,
                  transactionId: null,
                  type: due.type,
                  rentStartDate: null,
                  rentEnddate: null,
                  dueDate: due.dueDate,
                  description: due.discount > 0 ? `${dueDescription}, Discount of ₹${due.discount} given for this due` : dueDescription,
                  discount: due.discount,
                  subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
                });
                amtPaid -= due.balance;
                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: due.balance + due.discount };
                duesStatsArr.push(dueStats);
              }

              await duesDB.removeDue({ id: due.id });
            }
            isTransactionRecorded = true;
            if (addDiscountInReceipt) {
              discountAmount += due.discount;
            }
          }
          count++;
          if (securityAdjustEntry !== false) {
            await ledgerDB.removeById({
              id: securityAdjustEntry.id,
            });

            //creating new security entry after deleting previous
            await ledgerDB.add({
              tenantId: securityAdjustEntry.tenantId,
              roomId: securityAdjustEntry.roomId,
              propId: securityAdjustEntry.propId,
              clientId: due.clientId,
              amount: securityAdjustEntry.amount,
              balance: securityAdjustEntry.balance,
              referenceId: securityAdjustEntry.referenceId,
              transactionId: securityAdjustEntry.transactionId,
              type: securityAdjustEntry.type,
              rentStartDate: securityAdjustEntry.rentStartDate,
              rentEndDate: securityAdjustEntry.rentEndDate,
              description: securityAdjustEntry.description,
              dueDate: securityAdjustEntry.dueDate,
            });
          }
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceIds}], Due Id [${due.id}], Transaction has been recorded successfully`
          );
        }
        if (discountAmount > 0) {
          dueStats = { title: "Discount", amount: -discountAmount };
          duesStatsArr.push(dueStats);
        }

        await transactionDB.updateDiscount({
          id: transId,
          discount: discountAmount,
        });

        await transactionDB.updateCharges({
          id: transId,
          charges: charges,
        });

        await transactionDB.updateGstCharges({
          id: transId,
          gstCharges: gstAmount,
        });
      }

      if (isExcessPayment) {
        let allExsistingDues = await duesDB.getByTenantIdAndPropId({
          tenantId,
          propId: occupancy.propId,
        });

        let count = 1;
        let discountAmountForExcess = 0;

        if (allExsistingDues && allExsistingDues.length > 0) {
          for (let due of allExsistingDues) {
            if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
              dueDescription = due.title;
              if (dueDescription === null) {
                dueDescription = await getDueDescription(due.type);
              }
            } else if (
              due.title &&
              due.title.toLowerCase().includes("advance rent")
            ) {
              dueDescription = "Advance Rent";
            } else {
              dueDescription = await getDueDescription(due.type);
            }
            dueDescription =
              due.type === CONSTANTS.DUES_TYPES.RENT
                ? dueDescription +
                " for " +
                moment(due.rentStartDate).format("YYYY-MM-DD") +
                " to " +
                moment(due.rentEndDate).format("YYYY-MM-DD")
                : dueDescription +
                " for " +
                moment(due.dueDate).format("MMM YYYY");
            let prevBalance = await ledgerDB.getPreviousBalance({
              tenantId,
              clientId,
            });

            if (prevBalance && prevBalance < 0) {
              if (count == allExsistingDues.length) {
                if (Math.abs(prevBalance) - due.balance > 0) {
                  excessDueflag = true;
                  excessDueStats = {
                    title: "Excess Payment",
                    amount: Math.abs(prevBalance) - due.balance,
                  };
                }
              }
              if (due.balance > Math.abs(prevBalance)) {
                dueStats = {
                  title: dueDescription,
                  amount: Math.abs(prevBalance),
                };
                duesStatsArr.push(dueStats);
              } else if (due.balance <= Math.abs(prevBalance)) {
                dueStats = {
                  title: dueDescription,
                  amount: Math.abs(due.balance),
                };
                duesStatsArr.push(dueStats);
                discountAmountForExcess += due.discount;
              }
              await adjustExcessPayments({
                tenantId: tenantId,
                clientId: client.id,
                amountPaid: Math.abs(Number(prevBalance)),
                ledgerReferenceId: due.ledgerReferenceId,
              });
            } else if (!prevBalance || prevBalance >= 0) {
              break;
            }

            count++;
          }
        } else {
          let prevBalance = await ledgerDB.getPreviousBalance({
            tenantId,
            clientId,
          });
          if (prevBalance && prevBalance < 0) {
            excessDueflag = true;
            excessDueStats = {
              title: "Excess Payment",
              amount: Math.abs(Number(prevBalance)),
            };
          }
        }

        let dueStatsArrDiscountEntry: any = duesStatsArr.find((item: any) => item.title === "Discount");
        if (dueStatsArrDiscountEntry) {
          dueStatsArrDiscountEntry.amount -= discountAmountForExcess;
        }
      }

      let transLogData = await transactionLogsDB.getByGId({ gId: paymentId });
      let transGateway = client.paymentGateway;
      if (transLogData) {
        transGateway = transLogData.gateway;
      }

      if (Number(transGateway) === CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ && transLogData.udf3?.split("_")[3]) {
        const bank = await bankDB.getBankAccountByEazebuzzCreds({
          clientId,
          easebuzzSubMerchantId: transLogData.udf3.split("_")[3],
        });

        if (bank) {
          await transactionDB.updateAccountDetailsByGId({
            gId: paymentId,
            bankId: bank.id,
            paymentAccountNo: bank.accountNum,
            paymentAccountName: bank.holderName,
          });
        }
      }

      await transactionDB.updateGatewayByGId({ gId: paymentId, gateway: transGateway });

      if (Number(transGateway) === CONSTANTS.PAYMENT_GATEWAY.CASHFREE) {
        let requestData = transLogData?.requestData;
        splitTransId = requestData?.data?.payment?.cf_payment_id || null;

        try {
          const parsedData = typeof requestData === "string" ? JSON.parse(requestData) : requestData;
          splitTransId = parsedData?.data?.payment?.cf_payment_id || null;
        } catch (error: any) {
          log.info(`[${C}], [${F}], Client Id [${clientId}], Payment Id [${paymentId}], Error while parsing requestData JSON [${error.message}]`);
          splitTransId = null;
        }

        const splitResponse = await cashfreeSplit(
          occupancy.clientId, //clientId
          occupancy.propId, //propId
          amount,
          charges,
          gstAmount,
          paymentId, //order id in this case
          mode,
          Number(tenantId),
          transDescription, //description
          isSecurityPayment,
          transId,
        );

        if (!splitResponse) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${paymentId}], Failed to split the transaction`
          );
        } else {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${paymentId}], Transaction has been split successfully`
          );
        }
        await transactionDB.updateSplitId({
          gId: paymentId,
          splitTnxId: splitTransId,
        });
      }

      if (Number(transGateway) === CONSTANTS.PAYMENT_GATEWAY.PAYU && process.env.PAYU_ENV === "PROD") {
        const isPaymentAgreementRule = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.PAYMENT_AGREEMENT_RULE
        });
        if (client.holdOutBalance > 0) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Client Hold Out Balance [${client.holdOutBalance}], Amount [${amount}], Charges [${charges}], Gst Charges [${gstAmount}], Substracting The Amount and Gst Amount From Hold Out Amount`
          );
          const amountDiff = Number(client.holdOutBalance) - (Number(amount) + Number(gstAmount));

          await clientDB.updateHoldOutBalance({
            id: client.id,
            holdOutBalance: amountDiff > 0 ? amountDiff : 0,
          });

          let childKey = property.payuKey;
          let childMid = property.payuMid;

          if (!childKey || !childMid) {
            childKey = client.payuKey || null;
            childMid = client.payuMid || null;
          }
          if (isPaymentAgreementRule && Number(occupancy?.agreementPeriod) <= Number(isPaymentAgreementRule?.value)) {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Matched Agreement Rule`
            );
            let getChildKey = await clientConfigDB.getClientConfig({
              clientId,
              provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.PAYU,
              type: CONSTANTS.CLIENT_CONFIG_TYPE.AGREEMENT_RULE_ACCOUNT_PAYU.ACCOUNT_KEY
            });
            let getChildMid = await clientConfigDB.getClientConfig({
              clientId,
              provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.PAYU,
              type: CONSTANTS.CLIENT_CONFIG_TYPE.AGREEMENT_RULE_ACCOUNT_PAYU.ACCOUNT_MID
            });
            if (getChildKey && getChildMid) {
              childKey = getChildKey?.value || null;
              childMid = getChildMid?.value || null;
            }
          }
          let holdMid = client.payuHoldMid;
          let holdKey = client.payuHoldKey;

          if (childKey && childMid) {
            let totalAmount = Number(amount) + Number(charges) + Number(gstAmount);

            let splitObj = null;

            // const amountDiff = Number(client.holdOutBalance) - amount;
            if (amountDiff > 0) {
              //keep full payment

              // log.info(
              //   `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Amount Diff [${amountDiff}], Total Amount To Kipinn With Charges [${totalAmount}], Hold Key [${holdKey}], Hold MID [${holdMid}], Splitting The Whole Payment to Kipinn!`
              // );

              if (holdMid && holdKey) {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Amount Diff [${amountDiff}], Charges To Kipinn [${charges}], Amount To Hold Party [${amount}], Total Amount [${totalAmount}], Hold Key [${holdKey}], Hold MID [${holdMid}], Splitting The Payment to Kipinn And Hold Party!`
                );
                splitObj = {
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${charges}`,
                  },
                  [holdKey]: {
                    aggregatorSubTxnId: `${paymentId}_${holdKey}`,
                    aggregatorSubAmt: `${Number(amount) + Number(gstAmount)}`,
                  },
                };
              } else {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Amount Diff [${amountDiff}], Total Amount To Kipinn With Charges [${totalAmount}], Hold Key [${holdKey}], Hold MID [${holdMid}], Splitting The Whole Payment to Kipinn!`
                );
                splitObj = {
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${totalAmount}`,
                  },
                };
              }
            } else {
              //update holdOutBalance and keep remaining holdOutBalance and return the amount diff

              totalAmount = Number(client.holdOutBalance) + Number(charges);

              // log.info(
              //   `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Total Amount To Kipinn With Charges [${totalAmount}], Amount Paid To Client [${Math.abs(amountDiff)}], Payment Split Between Client and Kipinn.`
              // );

              if (holdMid && holdKey) {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Charges To Kipinn [${charges}], Amount Paid to Hold Party [${client.holdOutBalance}], Amount Paid To Client [${Math.abs(amountDiff)}], Hold Key [${holdKey}], Hold MID [${holdMid}], Payment Split Between Client, Kipinn And Hold Party.`
                );
                splitObj = {
                  [childKey]: {
                    aggregatorSubTxnId: `${paymentId}_${childKey}`,
                    aggregatorSubAmt: `${Math.abs(amountDiff)}`,
                  },
                  [holdKey]: {
                    aggregatorSubTxnId: `${paymentId}_${holdKey}`,
                    aggregatorSubAmt: `${client.holdOutBalance}`,
                  },
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${charges}`,
                  },
                };
              } else {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Total Amount To Kipinn With Charges [${totalAmount}], Amount Paid To Client [${Math.abs(amountDiff)}], Hold Key [${holdKey}], Hold MID [${holdMid}], Payment Split Between Client and Kipinn.`
                );
                splitObj = {
                  [childKey]: {
                    aggregatorSubTxnId: `${paymentId}_${childKey}`,
                    aggregatorSubAmt: `${Math.abs(amountDiff)}`,
                  },
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${totalAmount}`,
                  },
                };
              }
            }

            const splitResponse = await splitPayment(
              "absolute",
              paymentId,
              splitObj
            );

            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
              }], Transaction Id [${paymentId}], Split Payment Response [${JSON.stringify(
                splitResponse
              )}]`
            );

            if (
              splitResponse?.splitStatus &&
              splitResponse?.splitStatus === "success"
            ) {

                splitDetail = await getSplitPaymentDetails(paymentId);
                splitTransId = splitDetail && splitDetail?.splitItems?.[childKey]?.capture?.mihpayid ? splitDetail?.splitItems[childKey]?.capture?.mihpayid : null;
              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Split Transaction Id [${splitTransId}]`
              );
              // await transactionDB.updateSettleDetails({
              //   id: transId,
              //   settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
              //   settleDescription: JSON.stringify(splitResponse),
              //   splitTnxId: splitTransId,
              // });

              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              }

              await transactionDB.updateHoldAmount({
                id: transId,
                holdAmount:
                  amountDiff > 0 ? Number(amount) : client.holdOutBalance,
              });
            } else {
              // await transactionDB.updateSettleDetails({
              //   id: transId,
              //   settleStatus: CONSTANTS.PAYOUT_STATUS.PENDING,
              //   settleDescription: JSON.stringify(splitResponse),
              //   splitTnxId: splitTransId,
              // });

              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              }

              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split ERROR Response [${JSON.stringify(
                  splitResponse
                )}]`
              );
            }
          } else {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split API can't be initiated due to absence of Account Details`
            );
          }
        } else {
          let childKey = property.payuKey;
          let childMid = property.payuMid;

          if (!childKey || !childMid) {
            childKey = client.payuKey || null;
            childMid = client.payuMid || null;
          }
          if (isPaymentAgreementRule && Number(occupancy?.agreementPeriod) <= Number(isPaymentAgreementRule?.value)) {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Matched Agreement Rule`
            );
            let getChildKey = await clientConfigDB.getClientConfig({
              clientId,
              provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.PAYU,
              type: CONSTANTS.CLIENT_CONFIG_TYPE.AGREEMENT_RULE_ACCOUNT_PAYU.ACCOUNT_KEY
            });
            let getChildMid = await clientConfigDB.getClientConfig({
              clientId,
              provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.PAYU,
              type: CONSTANTS.CLIENT_CONFIG_TYPE.AGREEMENT_RULE_ACCOUNT_PAYU.ACCOUNT_MID
            });
            if (getChildKey && getChildMid) {
              childKey = getChildKey?.value || null;
              childMid = getChildMid?.value || null;
            }
          }
          // const childKey = client.payuKey;
          // const childMid = client.payuMid;

          //Payment gateway check payu, null check....
          if (childKey && childMid) {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], No Hold Out Balance Remaining, Initiating Split Payment API`
            );

            let splitObj = {
              [childKey]: {
                aggregatorSubTxnId: `${paymentId}_${childKey}`,
                aggregatorSubAmt: `${Number(amount) + Number(gstAmount)}`,
              },
            };

            if (
              mode !== CONSTANTS.TRANSACTION_MODES.UPI &&
              mode !== CONSTANTS.TRANSACTION_MODES.OFFLINE &&
              Number(charges) !== 0
            ) {
              splitObj = {
                [childKey]: {
                  aggregatorSubTxnId: `${paymentId}_${childKey}`,
                  aggregatorSubAmt: `${Number(amount) + Number(gstAmount)}`,
                },
                [key]: {
                  aggregatorSubTxnId: `${paymentId}_${key}`,
                  aggregatorSubAmt: `${charges}`,
                },
              };
            }

            const splitResponse = await splitPayment(
              "absolute",
              paymentId,
              splitObj
            );

            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
              }], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split Payment Response [${JSON.stringify(
                splitResponse
              )}]`
            );

            if (
              splitResponse?.splitStatus &&
              splitResponse?.splitStatus === "success"
            ) {
              splitDetail = await getSplitPaymentDetails(paymentId);
              // splitTransId = splitDetail?.splitItems[childKey]?.capture?.mihpayid || null;
              splitTransId = splitDetail && splitDetail?.splitItems?.[childKey]?.capture?.mihpayid ? splitDetail?.splitItems[childKey]?.capture?.mihpayid : null;
              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Payu Key [${childKey}] Split Transaction Id [${splitTransId}]`
              );
              // await transactionDB.updateSettleDetails({
              //   id: transId,
              //   settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
              //   settleDescription: JSON.stringify(splitResponse),
              //   splitTnxId: splitTransId
              // });

              let isBank = false;

              const bank = await bankDB.getBankAccountByPayuCreds({
                clientId,
                payuKey: childKey,
                payuMid: childMid
              });

              if (bank) isBank = true;

              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });

                if (isBank) {
                  await transactionDB.updateAccountDetailsByGId({
                    gId: paymentId,
                    bankId: bank.id,
                    paymentAccountNo: bank.accountNum,
                    paymentAccountName: bank.holderName,
                  });
                }

              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });

                if (isBank) {
                  await transactionDB.updateAccountDetailsByGId({
                    gId: transGId,
                    bankId: bank.id,
                    paymentAccountNo: bank.accountNum,
                    paymentAccountName: bank.holderName,
                  });
                }
              }

              await transactionDB.updateHoldAmount({
                id: transId,
                holdAmount: 0,
              });
              // log.info(`[${C}], [${F}], Client ID [${client.id}], Initiating Release Payment API`);

              // const releaseResponse = await releasePayment(paymentId, childMid);

              // log.info(
              //   `[${C}], [${F}], Release Payment API, ClientId [${
              //     client.id
              //   }], Transaction ID [${paymentId}], PayU MID [${childMid}], Payu Key [${childKey}], Response [${JSON.stringify(
              //     releaseResponse
              //   )}]`
              // );
            } else {
              // await transactionDB.updateSettleDetails({
              //   id: transId,
              //   settleStatus: CONSTANTS.PAYOUT_STATUS.PENDING,
              //   settleDescription: JSON.stringify(splitResponse),
              //   splitTnxId:splitTransId,
              // });

              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              }

              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split ERROR Response [${JSON.stringify(
                  splitResponse
                )}]`
              );
            }
          } else {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split API can't be initiated due to absence of Account Details`
            );
          }
        }
      } else {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Payment Gateway Not Set To PayU Or Test Environment Is Enabled`
        );
      }
    }

    let receipt: any = "";
    //const transGId: string = await generateTransId();
    //const transTitle: string = "Payment";
    let settings = await settingsDB.getByClientIdAndPropId({
      clientId: client.id,
      propId: occupancy.propId,
    });

    let logo = settings?.logo
      ? process.env.RS_LOGO_URI + client?.id + "/" + occupancy?.propId + "/" + settings?.logo
      : client?.logo
        ? process.env.RS_LOGO_URI + client?.id + "/" + client?.logo
        : String(process.env.RS_DEFAULT_LOGO_URI);
    log.info(`[${C}], [${F}], Logo [${logo}]`);
    if (status === CONSTANTS.TRANSACTION_STATUS.SUCCESS) {
      if (excessDueflag) {
        duesStatsArr.push(excessDueStats);
      }
      let flatName = "";

      if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
        const { name } = await flatDB.getById({ id: occupancy.flatId });
        flatName = name;
      } else {
        flatName =
          occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
      }

      let isGstEnabled = property?.isGstEnabled || 0;
      // let isGstEnabled = property?.isGstEnabled & occupancy?.isGstEnabled || 0;
      let invoiceNo = property?.invoiceNo || null;
      let invoiceNoPrefix = property?.invoiceNoPrefix || null;
      let gstNo = property?.gstNo || null;
      let businessName = property?.ownerName || ""
      let transactionInvoiceNo = ""
      let prefix = "";
      if (0 != isGstEnabled && 0 != invoiceNo) {
        let getGstTransactions = await transactionDB.getByGstNo({ gstNo });
        if (false == getGstTransactions) {
          if (null != invoiceNoPrefix) {
            prefix = invoiceNoPrefix;
          }
          transactionInvoiceNo = `${prefix}${invoiceNo}`;
        } else {
          invoiceNo = getGstTransactions?.invoiceNo;
          if (null != invoiceNoPrefix) {
            prefix = invoiceNoPrefix;
            invoiceNo = invoiceNo.replace(prefix, "");
            transactionInvoiceNo = `${prefix}${String(Number(invoiceNo) + 1)}`;
          } else {
            transactionInvoiceNo = String(Number(invoiceNo) + 1);
          }
        }
        businessName = property?.businessName || "";
      } else {
        isGstEnabled = 0;
      }

      if (Number(gstAmount) > 0) {
        receipt = await createGstReceipt({
          // title: receiptDescription,
          title: `${transId}_${paymentId || transGId}`,
          roomNum: room.roomNum,
          propName: property.name,
          dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
          mode: getModeName(Number(mode)),
          transGId: splitTransId || paymentId || transGId,
          paidDate: moment().format("DD MMM, YYYY"),
          month: moment(allDues[0].dueDate).format("MMM, YYYY"),
          tenantName: tenant?.name || "",
          amount: Number(amount) + Number(gstAmount),
          address: `${property?.address}`,
          landlord: businessName || "",
          landlordNumber: property?.ownerMobile || "",
          logo: logo,
          dueStats: duesStatsArr,
          "landlord-pan": "",
          occupancy,
          flatName,
          propType: property.type,
          isGstEnabled,
          transactionInvoiceNo,
          gstNo,
          gstCharges: gstAmount,
        });
      } else {
        if (Number(gstAmount) > 0) {
          dueStats = { title: `Gst charges (${parseFloat(property?.gstCharges)}%)`, amount: gstAmount };
          duesStatsArr.push(dueStats);
        }
        // receipt = await createReceiptMultipleDues({
        //   // title: transTitle,
        //   title: `${transId}_${paymentId || transGId}`,
        //   roomNum: room.roomNum,
        //   propName: property.name,
        //   dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
        //   mode: getModeName(Number(mode)),
        //   transGId: splitTransId || paymentId || transGId,
        //   paidDate: moment().format("DD MMM, YYYY"),
        //   month: moment(allDues[0].dueDate).format("MMM, YYYY"),
        //   tenantName: tenant?.name || "",
        //   amount: Number(amount) + Number(gstAmount),
        //   address: `${property?.address}`,
        //   landlord: businessName || "",
        //   landlordNumber: property?.ownerMobile || "",
        //   logo: logo,
        //   "landlord-pan": "",
        //   occupancy,
        //   dueStats: duesStatsArr,
        //   flatName,
        //   propType: property.type,
        //   isGstEnabled,
        //   transactionInvoiceNo,
        //   gstNo,
        // });
        receipt = await generateRentReciept({
          // title: transTitle,
          title: `${transId}_${paymentId || transGId}`,
          roomNum: room.roomNum,
          propName: property.name,
          dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
          mode: getModeName(Number(mode)),
          transGId: splitTransId || paymentId || transGId,
          paidDate: moment().format("DD MMM, YYYY HH:mm:ss"),
          month: moment(allDues[0].dueDate).format("MMM, YYYY"),
          tenantName: tenant?.name || "",
          amount: Number(amount) + Number(gstAmount),
          address: `${property?.address}`,
          landlord: businessName || "",
          landlordNumber: property?.ownerMobile || "",
          logo: logo,
          "landlord-pan": "",
          occupancy,
          dueStats: duesStatsArr,
          flatName,
          propType: property.type,
          isGstEnabled,
          transactionInvoiceNo,
          gstNo,
          recordedBy: "Kipinn App"
        });
      }

      log.info(`[${C}], [${F}], RECIPT [${receipt}]`);
      if (0 != isGstEnabled && 0 != invoiceNo) {
        // await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });

        if (paymentId) {
          await transactionDB.updateReceiptWithInvoiceByGId({
            gId: paymentId,
            receipt,
            invoiceNo: transactionInvoiceNo,
            gstNo
          });
        } else {
          await transactionDB.updateReceiptWithInvoiceByGId({
            gId: transGId,
            receipt,
            invoiceNo: transactionInvoiceNo,
            gstNo
          });
        }
      } else {
        // await transactionDB.updateReceipt({
        //   id: transId,
        //   receipt,
        // });

        if (paymentId) {
          await transactionDB.updateReceiptByGId({
            gId: paymentId,
            receipt,
          });
        } else {
          await transactionDB.updateReceiptByGId({
            gId: transGId,
            receipt,
          });
        }
      }

      if (paymentId) {
        await setTransactionTallyStatusByGId(
          Number(clientId),
          paymentId,
          CONSTANTS.TALLY_STATUS.PENDING,
        );
      } else {
        await setTransactionTallyStatusByGId(
          Number(clientId),
          transGId,
          CONSTANTS.TALLY_STATUS.PENDING,
        );
      }

      const tenantDues = await duesDB.getByTenantIdAndClientId({
        tenantId,
        clientId,
      });
      if (!tenantDues) {
        await tenantDB.updateLastReminded({
          id: tenantId,
          lastRemindedOn: null,
        });
        await occupancyDB.updateLastReminded({
          clientId,
          tenantId,
          lastRemindedOn: null,
        });
      }

      const dueName = allDues.length === 1
        ? allDues[0].rentStartDate !== null
          ? `${transTitle} for ${moment(allDues[0].rentStartDate).format("DD MMM YY")} to ${moment(allDues[0].rentEndDate).format("DD MMM YY")}`
          : `${transTitle} for ${moment(allDues[0].dueDate).format("MMM YYYY")}`
        : transTitle

      const notiSettings = await settingsDB.getByClientIdAndPropId({
        clientId: occupancy.clientId,
        propId: occupancy.propId,
      });

      let footerText = notiSettings.footer || "The Kipinn Team";

      let modeVal = getModeName(Number(mode));

      if (client.regId) {
        await sendNotification({
          title: `Due amount received`,
          message: `Tenant Name: ${tenant.name}\nAmount: ₹${Number(amount) + Number(gstAmount)}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment().format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: Kipinn App`,
          regId: client.regId,
          userId: client.id,
          userType: CONSTANTS.USER_TYPE.CLIENT,
          notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ONLINE_PAYMENT_CONFIRM,
          clientId: client.id,
        });
      }
      const notifyPartner = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.NOTIFY_PARTNER,
      });
      if (notifyPartner && Number(notifyPartner?.value) === 1) {
        const partner = await staffDB.getByClientIdAndRole({
          clientId,
          role: CONSTANTS.STAFF_ROLES.PARTNER,
        });
        if (partner.regId) {
          await sendNotification({
            title: `Due amount received`,
            message: `Tenant Name: ${tenant.name}\nAmount: ₹${Number(amount) + Number(gstAmount)}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment().format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: Kipinn App`,
            regId: partner.regId,
            userId: partner.id,
            userType: CONSTANTS.USER_TYPE.CLIENT,
            notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ONLINE_PAYMENT_CONFIRM,
            clientId: client.id,
          });
        }
      }
      if (notiSettings && Number(notiSettings.rentReminder) === 1) {
        let whatsappAgreegrator = await clientConfigDB.getClientConfig({
            clientId,
            provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
            type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.WHATSAPP_AGGREGATOR
        });
        let confirmPaymentToClientTemp = await getClientWhatsappCredentialsMessageCentral(Number(clientId), property?.id, CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION, CONSTANTS.USER_TYPE.CLIENT);
        let clientAmount = Number(amount) + Number(gstAmount);
        let whatsAppProvider = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY,
        });
        if (whatsAppProvider && whatsAppProvider?.value != '') {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Chat Mitra`
          );
          let bodyValues = [
            { type: "text", text: `${tenant?.name}` },
            { type: "text", text: `${clientAmount}` },
            { type: "text", text: `${property.name}` },
            { type: "text", text: `${flatName} (${room.roomNum})` },
            { type: "text", text: `${dueName}` },
            { type: "text", text: `${moment().format("DD MMM YY HH:mm:ss")}` },
            { type: "text", text: `${modeVal}` },
            { type: "text", text: `Kipinn App` },
          ];
          sendWhatsapp(
            client.mobile,
            client.name,
            Number(clientId),
            Number(occupancy.propId),
            bodyValues,
            CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
            CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
            CONSTANTS.USER_TYPE.CLIENT,
          );
        } else {
          if(whatsappAgreegrator && Number(whatsappAgreegrator?.value)  === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL && confirmPaymentToClientTemp) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through message central to client`
            );
            const bodyValues: Record<string, string> = {
              body_1: `${tenant?.name}`,
              body_2: `${clientAmount}`,
              body_3: `${property.name}`,
              body_4: `${flatName} (${room.roomNum})}`,
              body_5: `${dueName}`,
              body_6: `${moment().format("DD MMM YY HH:mm:ss")}`,
              body_7: `${modeVal}`,
              body_8: `Kipinn App`
            };
            sendWhatsappMC(
              client.mobile,
              Number(clientId),
              Number(occupancy.propId),
              bodyValues,
              CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
              CONSTANTS.USER_TYPE.CLIENT,
              CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL
            );
          } else {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through interakt to client`
            );
            sendWhatsappPaymentConfirmationClient(
              client.mobile,
              client.name,
              tenant.name,
              clientAmount,
              property.name,
              `${flatName} (${room.roomNum})`,
              dueName,
              moment().format("DD MMM YY HH:mm:ss"),
              modeVal,
              "Kipinn App",
              Number(clientId)
            );
          }
        }
        const notifyPartner = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.NOTIFY_PARTNER,
        });
        if (notifyPartner && Number(notifyPartner?.value) === 1) {
          const partner = await staffDB.getByClientIdAndRole({
            clientId,
            role: CONSTANTS.STAFF_ROLES.PARTNER,
          });
          let partnerMobile = partner[0]?.mobile;
          let partnerName = partner[0]?.name;
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Notify to partner`
          );
          // let whatsAppProvider = await clientConfigDB.getClientConfig({
          //   clientId,
          //   provider : CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
          //   type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY,
          // });
          if (whatsAppProvider && whatsAppProvider?.value != '') {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Chat Mitra to partner`
            );
            let bodyValues = [
              { type: "text", text: `${tenant?.name}` },
              { type: "text", text: `${clientAmount}` },
              { type: "text", text: `${property.name}` },
              { type: "text", text: `${flatName} (${room.roomNum})` },
              { type: "text", text: `${dueName}` },
              { type: "text", text: `${moment().format("DD MMM YY HH:mm:ss")}` },
              { type: "text", text: `${modeVal}` },
              { type: "text", text: `Kipinn App` },
            ];
            sendWhatsapp(
              partnerMobile,
              partnerName,
              Number(clientId),
              Number(occupancy.propId),
              bodyValues,
              CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
              CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
              CONSTANTS.USER_TYPE.CLIENT,
            );
          } else {
              if(whatsappAgreegrator && Number(whatsappAgreegrator?.value)  === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL && confirmPaymentToClientTemp) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through message central to partner`
              );
              const bodyValues: Record<string, string> = {
                body_1: `${tenant?.name}`,
                body_2: `${clientAmount}`,
                body_3: `${property.name}`,
                body_4: `${flatName} (${room.roomNum})}`,
                body_5: `${dueName}`,
                body_6: `${moment().format("DD MMM YY HH:mm:ss")}`,
                body_7: `${modeVal}`,
                body_8: `Kipinn App`
              };
              sendWhatsappMC(
                partnerMobile,
                Number(clientId),
                Number(occupancy.propId),
                bodyValues,
                CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
                CONSTANTS.USER_TYPE.CLIENT,
                CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL
              );
            } else {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through interakt to partner`
              );
              sendWhatsappPaymentConfirmationClient(
                partnerMobile,
                partnerName,
                tenant.name,
                clientAmount,
                property.name,
                `${flatName} (${room.roomNum})`,
                dueName,
                moment().format("DD MMM YY HH:mm:ss"),
                modeVal,
                "Kipinn App",
                Number(clientId)
              );
            }
          }

        }
        if (whatsAppProvider && whatsAppProvider?.value != '') {
          // log.info(
          // `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending to Tenant Through Chat Mitra`
          // );
          let bodyValues = [
            { type: "text", text: `${amount}` },
            { type: "text", text: `${property.name}` },
            { type: "text", text: `${flatName} (${room.roomNum})` },
            { type: "text", text: `${footerText}` },
          ];
          let headerValues = [
            {
              type: "document",
              document: {
                link: receipt,
                filename: `Receipt_${paymentId || transGId}.pdf`,
              }
            },
          ];
          sendWhatsapp(
            tenant.mobile,
            tenant.name,
            Number(clientId),
            Number(occupancy.propId),
            bodyValues,
            CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.PAYMENT_CONFIRMATION,
            CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
            CONSTANTS.USER_TYPE.TENANT,
            headerValues
          );
        } else {
          let paymentAckToTenant = await getClientWhatsappCredentialsMessageCentral(Number(clientId), property?.id, CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.PAYMENT_CONFIRMATION, CONSTANTS.USER_TYPE.TENANT);
          if(whatsappAgreegrator && Number(whatsappAgreegrator?.value)  === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL && paymentAckToTenant) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through message central to tenant`
              );
              const bodyValues: Record<string, string> = {
                body_1: `${amount}`,
                body_2: `${property.name}`,
                body_3: `${flatName} (${room.roomNum})}`,
                body_4: `${footerText.replace("Team", "").trim()}`
              };
              const folderName = `tenant_${tenantId}`;
              const currentYear = moment().year();
              const fileName = path.basename(receipt);
              const folderPath = `uploads/documents/${currentYear}/receipt/${clientId}/${occupancy?.propId}/${occupancy?.roomId}_${occupancy?.bedId}/${folderName}/${fileName}`;
              sendWhatsappMC(
                tenant?.mobile,
                Number(clientId),
                Number(occupancy.propId),
                bodyValues,
                CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.PAYMENT_CONFIRMATION,
                CONSTANTS.USER_TYPE.TENANT,
                CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL,
                folderPath,
              );
            } else {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through interakt to tenant`
              );
              sendWhatsappPaymentConfirmationTenant(
                tenant.mobile,
                tenant.name,
                amount,
                property.name,
                `${flatName} (${room.roomNum})`,
                receipt,
                footerText,
                Number(clientId)
              );
            }
        }
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Payment Id [${paymentId}], Receipt [${receipt}], Whatsapp messages sent successfully`
        );
      }


      // sending sms to client if payment is success
      // const msg = CONSTANTS.MSG.COMPLAINT_ASSIGNED
      //   .replace("{#var1#}", client.name)
      //   .replace("{#var2#}", amount)
      //   .replace("{#var3#}", `Room ${room.roomNum}, ${property.type === CONSTANTS.PROPERTY_TYPE.FLAT ? "Flat" : "Floor"} ${flatName}, Property ${property.name}`)
      //   .replace("{#var4#}", tenant.name)
      //   .replace("{#var5#}", getModeName(Number(mode)))
      //   .replace("{#var6#}", paymentId || transGId);

      // const isSent = await sendSMS(
      //   client.mobile,
      //   msg,
      //   CONSTANTS.SMS_TEMPLATE_IDS.COMPLAINT_ASSIGNED
      // );
    }

    //const isFullPayment = dueTotal <= Number(amount || 0);

    return res.status(200).json({
      msg: "Transactions recorded successfully",
      paymentId,
      amount,
      mode,
      isSuccess: true,
    });
    /*
    return res.status(200).json({
      msg: "Transactions recorded successfully",
      paymentId,
      amount,
      mode,
      isSuccess: true,
    });*/
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.AddMoveOut = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "AddMoveOut";

  try {
    const tenantId = req.id;
    const clientId = req.clientId;
    let {
      dueIds,
      ledgerReferenceIds,
      paymentId,
      amount,
      status,
      mode,
      key,
      charges,
      gstAmount = 0,
      isBookingPayment = false,
    } = req.body;
    let dueDescription = "";
    let splitTransId = null;
    let splitDetail;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Due Ids [${dueIds}], Payment Id [${paymentId}], ledgereferenceId [${ledgerReferenceIds}], Status [${status}], Mode [${mode}], Key [${key}], Amount [${amount}], Charges [${charges}], Gst Charges [${gstAmount}], Is Booking Payment [${isBookingPayment}],`
    );

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Payment Id [${paymentId}], ledgereferenceId [${ledgerReferenceIds}], Status [${status}], Mode [${mode}], Key [${key}], Amount [${amount}], Charges [${charges}], Is Booking Payment [${isBookingPayment}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let occupancy = await moveOutDB.getByClientIdAndTenantId({
      clientId: clientId,
      tenantId: tenantId,
    });
    occupancy = occupancy[0];
    const client = await clientDB.getById({ id: occupancy.clientId });
    const property = await propertyDB.getById({ id: occupancy.propId });
    const room = await roomDB.getById({ id: occupancy.roomId });
    let isTransactionRecorded = false;
    let amtPaid = amount;
    let duesStatsArr = [];
    let dueStats = {};
    let excessDueStats = {};
    let excessDueflag = false;

    let allDues = await moveOutDuesDB.getAllDues({
      ids: dueIds,
    });

    log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Dues Fetched [${JSON.stringify(allDues)}]`);

    if (!allDues) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Payment Id [${paymentId}], No Dues Found`
      );
      return res.status(400).json({
        msg: "All dues paid",
        isSuccess: false,
      });
    }

    let count = 1;
    let lengthOfDues = allDues.length;
    let isSingleDuePayment = allDues.length === 1 ? true : false;
    let security = await allDues.find(
      (due: any) => due.type === CONSTANTS.DUES_TYPES.SECURITY
    );
    let receipts = [];
    let dueTotal = 0;

    let transTitle: string = "Payment for ";
    if (allDues.length === 1) {
      transTitle = "";
    }
    let isSecurityPayment = false;
    for (let i = 0; i < allDues.length; i++) {
      const due = allDues[i];
      if (isSingleDuePayment) {
        if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
          isSecurityPayment = true;
        }
      }
      if (due.title === null) {
        transTitle += await getDueDescription(due.type);
      } else {
        transTitle += `${due.title}`;
      }
      if (i !== allDues.length - 1) {
        transTitle += ", ";
      }
    }

    let transDescription = allDues.length === 1
      ? allDues[0].rentStartDate !== null
        ? `${transTitle} for ${moment(allDues[0].rentStartDate).format("DD MMM YY")} to ${moment(allDues[0].rentEndDate).format("DD MMM YY")}`
        : `${transTitle} for ${moment(allDues[0].dueDate).format("MMM YYYY")}`
      : transTitle;

    if (isBookingPayment) transDescription = 'Booking amount';

    const transGId: string = await generateTransId();
    let transId;
    if (status === CONSTANTS.TRANSACTION_STATUS.SUCCESS) {
      if (security && security.balance > 0) {
        let due = security;
        dueTotal += due.balance;
        dueDescription = await getDueDescription(due.type);
        if (isBookingPayment) {
          dueDescription = "Booking amount"
        } else if (due.balance > amtPaid) {
          dueDescription = "Partial payment for " + dueDescription;
        } else if (due.balance < due.amount) {
          dueDescription = "Final payment for " + dueDescription;
        }
        if (security.balance <= amtPaid) {
          if (count == lengthOfDues) {
            transId = await addTransactionsOnlineMoveOut({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: amtPaid,
              description: dueDescription,
              mode,
              transGId: paymentId || transGId,
              charges: 0,
              gstAmount: 0,
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: -(amtPaid - due.balance),
              referenceId: due.ledgerReferenceId,
              transactionId: null,
              type: due.type,
              rentStartDate: null,
              rentEnddate: null,
              dueDate: due.dueDate,
              description: dueDescription,
              subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
            });
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            amtPaid = 0;
          } else {
            transId = await addTransactionsOnlineMoveOut({
              due: due,
              clientId: clientId,
              tenantId: tenantId,
              amount: due.balance,
              description: dueDescription,
              mode,
              transGId: paymentId || transGId,
              charges: 0,
              gstAmount: 0,
            });
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: 0,
              referenceId: due.ledgerReferenceId,
              transactionId: null,
              type: due.type,
              rentStartDate: null,
              rentEnddate: null,
              dueDate: due.dueDate,
              description: dueDescription,
              subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
            });
            amtPaid -= due.balance;
            //Creating DueStats for Recipt
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
          }
          await moveOutDuesDB.removeDue({ id: due.id });

          isTransactionRecorded = true;
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceIds}], Due Id [${due.id}], Transaction has been recorded successfully`
          );
        } else if (security.balance > amtPaid) {
          transId = await addTransactionsOnlineMoveOut({
            due: due,
            clientId: clientId,
            tenantId: tenantId,
            amount: amtPaid,
            description: dueDescription,
            mode,
            transGId: paymentId || transGId,
            charges: 0,
            gstAmount: 0,
          });
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -amtPaid,
            balance: due.balance - amtPaid,
            referenceId: due.ledgerReferenceId,
            transactionId: null,
            type: due.type,
            rentStartDate: null,
            rentEnddate: null,
            dueDate: due.dueDate,
            description: dueDescription,
            subType: isBookingPayment ? CONSTANTS.LEDGER_SUBTYPES.BOOKING_AMT : 0,
          });

          //Creating DueStats for Recipt
          dueStats = { title: dueDescription, amount: amtPaid };
          duesStatsArr.push(dueStats);

          await moveOutDuesDB.updateBalance({
            id: due.id,
            balance: due.balance - amtPaid,
          });
          amtPaid = 0;
          isTransactionRecorded = true;
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceIds}], Due Id [${due.id}], Transaction has been recorded successfully`
          );
        }
        count++;
      }

      let discountAmount = 0;

      if (amtPaid != 0) {
        for (const due of allDues) {
          if (amtPaid == 0) break;
          const securityAdjustEntry = await ledgerDB.getLastEntryX({
            tenantId,
            clientId: due.clientId,
            createdAt: occupancy?.moveInDate,
          });
          dueTotal += due.balance;
          if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
            continue;
          }
          let dueDescription = "";
          if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
            dueDescription = due.title;
            if (dueDescription === null) {
              dueDescription = await getDueDescription(allDues[0].type);
            }
          } else if (
            due.title &&
            due.title.toLowerCase().includes("advance rent")
          ) {
            dueDescription = "Advance Rent";
          } else {
            dueDescription = await getDueDescription(due.type);
          }

          let addDiscountInReceipt = true;

          if (due.balance > amtPaid) {
            dueDescription = "Partial payment for " + dueDescription;
            addDiscountInReceipt = false;
          } else if (due.balance < due.amount) {
            dueDescription = "Final payment for " + dueDescription;
          }

          if (due.type === CONSTANTS.DUES_TYPES.RENT) {
            dueDescription =
              dueDescription +
              " for " +
              moment(due.rentStartDate).format("DD MMM, YY") +
              " to " +
              moment(due.rentEndDate).format("DD MMM, YY");
          } else {
            dueDescription =
              dueDescription + " for " + moment(due.dueDate).format("DD MMM, YY");
          }
          if (status === CONSTANTS.TRANSACTION_STATUS.SUCCESS) {
            if (amtPaid <= due.balance) {
              transId = await addTransactionsOnlineMoveOut({
                due: due,
                clientId: clientId,
                tenantId: tenantId,
                amount: amtPaid,
                description: isBookingPayment ? transDescription : dueDescription,
                mode,
                transGId: paymentId || transGId,
                charges: 0,
                gstAmount: 0,
              });
              await ledgerDB.add({
                tenantId,
                roomId: occupancy.roomId,
                propId: occupancy.propId,
                clientId: occupancy.clientId,
                amount: -amtPaid,
                balance: due.balance - amtPaid,
                referenceId: due.ledgerReferenceId,
                transactionId: null,
                type: due.type,
                rentStartDate: null,
                rentEnddate: null,
                dueDate: due.dueDate,
                description: due?.discount > 0 ? Number(amtPaid) === Number(due.balance) ? `${dueDescription}, Discount of ₹${due?.discount} given for this due` : dueDescription : dueDescription,
                discount: due.discount,
              });
              if (amtPaid == due.balance) {
                await moveOutDuesDB.removeDue({ id: due.id });

                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: due.balance + due.discount };
                duesStatsArr.push(dueStats);
              } else {
                await moveOutDuesDB.updateBalance({
                  id: due.id,
                  balance: due.balance - amtPaid,
                });

                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: amtPaid };
                duesStatsArr.push(dueStats);
              }
              amtPaid = 0;
            } else {
              if (count == lengthOfDues) {
                // amtPaid -= due.balance;
                transId = await addTransactionsOnlineMoveOut({
                  due: due,
                  clientId: clientId,
                  tenantId: tenantId,
                  amount: amtPaid,
                  description: isBookingPayment ? transDescription : dueDescription,
                  mode,
                  transGId: paymentId || transGId,
                  charges: 0,
                  gstAmount: 0,
                });
                await ledgerDB.add({
                  tenantId,
                  roomId: occupancy.roomId,
                  propId: occupancy.propId,
                  clientId: occupancy.clientId,
                  amount: -due.balance,
                  balance: -(amtPaid - due.balance),
                  referenceId: due.ledgerReferenceId,
                  transactionId: null,
                  type: due.type,
                  rentStartDate: null,
                  rentEnddate: null,
                  dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
                  description: due.discount > 0 ? `${dueDescription}, Discount of ₹${due.discount} given for this due` : dueDescription,
                  discount: due.discount,
                });

                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: due.balance + due.discount };
                duesStatsArr.push(dueStats);
                //Exesss Due
                if (Math.abs(amtPaid - due.balance) > 0) {
                  excessDueflag = true;
                  excessDueStats = {
                    title: "Excess Payment",
                    amount: Math.abs(amtPaid - due.balance),
                  };
                }
              } else {
                transId = await addTransactionsOnlineMoveOut({
                  due: due,
                  clientId: clientId,
                  tenantId: tenantId,
                  amount: due.balance,
                  description: isBookingPayment ? transDescription : dueDescription,
                  mode,
                  transGId: paymentId || transGId,
                  charges: 0,
                  gstAmount: 0,
                });
                await ledgerDB.add({
                  tenantId,
                  roomId: occupancy.roomId,
                  propId: occupancy.propId,
                  clientId: occupancy.clientId,
                  amount: -due.balance,
                  balance: 0,
                  referenceId: due.ledgerReferenceId,
                  transactionId: null,
                  type: due.type,
                  rentStartDate: null,
                  rentEnddate: null,
                  dueDate: due.dueDate,
                  description: due.discount > 0 ? `${dueDescription}, Discount of ₹${due.discount} given for this due` : dueDescription,
                  discount: due.discount,
                });
                amtPaid -= due.balance;
                //Creating DueStats for Recipt
                dueStats = { title: dueDescription, amount: due.balance + due.discount };
                duesStatsArr.push(dueStats);
              }

              await moveOutDuesDB.removeDue({ id: due.id });
            }
            isTransactionRecorded = true;
            if (addDiscountInReceipt) {
              discountAmount += due.discount;
            }
          }
          count++;
          if (securityAdjustEntry !== false) {
            await ledgerDB.removeById({
              id: securityAdjustEntry.id,
            });

            //creating new security entry after deleting previous
            await ledgerDB.add({
              tenantId: securityAdjustEntry.tenantId,
              roomId: securityAdjustEntry.roomId,
              propId: securityAdjustEntry.propId,
              clientId: due.clientId,
              amount: securityAdjustEntry.amount,
              balance: securityAdjustEntry.balance,
              referenceId: securityAdjustEntry.referenceId,
              transactionId: securityAdjustEntry.transactionId,
              type: securityAdjustEntry.type,
              rentStartDate: securityAdjustEntry.rentStartDate,
              rentEndDate: securityAdjustEntry.rentEndDate,
              description: securityAdjustEntry.description,
              dueDate: securityAdjustEntry.dueDate,
            });
          }
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceIds}], Due Id [${due.id}], Transaction has been recorded successfully`
          );
        }

        if (discountAmount > 0) {
          dueStats = { title: "Discount", amount: -discountAmount };
          duesStatsArr.push(dueStats);
        }


        await transactionDB.updateDiscount({
          id: transId,
          discount: discountAmount,
        });

        await transactionDB.updateCharges({
          id: transId,
          charges: charges,
        });

        await transactionDB.updateGstCharges({
          id: transId,
          gstCharges: gstAmount,
        });
      }

      let transLogData = await transactionLogsDB.getByGId({ gId: paymentId });
      let transGateway = client.paymentGateway;
      if (transLogData) {
        transGateway = transLogData.gateway;
      }

      if (Number(transGateway) === CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ && transLogData.udf3?.split("_")[3]) {
        const bank = await bankDB.getBankAccountByEazebuzzCreds({
          clientId,
          easebuzzSubMerchantId: transLogData.udf3.split("_")[3],
        });

        if (bank) {
          await transactionDB.updateAccountDetailsByGId({
            gId: paymentId,
            bankId: bank.id,
            paymentAccountNo: bank.accountNum,
            paymentAccountName: bank.holderName,
          });
        }
      }

      await transactionDB.updateGatewayByGId({ gId: paymentId, gateway: transGateway });

      if (Number(transGateway) === CONSTANTS.PAYMENT_GATEWAY.CASHFREE) {
        let requestData = transLogData?.requestData;
        splitTransId = requestData?.data?.payment?.cf_payment_id || null;

        try {
          const parsedData = typeof requestData === "string" ? JSON.parse(requestData) : requestData;
          splitTransId = parsedData?.data?.payment?.cf_payment_id || null;
        } catch (error: any) {
          log.info(`[${C}], [${F}], Client Id [${clientId}], Payment Id [${paymentId}], Error while parsing requestData JSON [${error.message}]`);
          splitTransId = null;
        }

        const splitResponse = await cashfreeSplit(
          occupancy.clientId, //clientId
          occupancy.propId, //propId
          amount,
          charges,
          gstAmount,
          paymentId, //order id in this case
          mode,
          Number(tenantId),
          transDescription, //description
          isSecurityPayment,
          transId
        );

        if (!splitResponse) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${paymentId}], Failed to split the transaction`
          );
        } else {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Order Id [${paymentId}], Transaction has been split successfully`
          );
        }
        await transactionDB.updateSplitId({
          gId: paymentId,
          splitTnxId: splitTransId,
        });
      }

      if (
        Number(transGateway) === CONSTANTS.PAYMENT_GATEWAY.PAYU &&
        process.env.PAYU_ENV === "PROD"
      ) {
        if (client.holdOutBalance > 0) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Client Hold Out Balance [${client.holdOutBalance}], Amount [${amount}], Charges [${charges}], Gst Charges [${gstAmount}], Substracting The Amount and Gst Amount From Hold Out Amount`
          );
          const amountDiff = Number(client.holdOutBalance) - (Number(amount) + Number(gstAmount));

          await clientDB.updateHoldOutBalance({
            id: client.id,
            holdOutBalance: amountDiff > 0 ? amountDiff : 0,
          });

          let childKey = property.payuKey;
          let childMid = property.payuMid;

          if (!childKey || !childMid) {
            childKey = client.payuKey || null;
            childMid = client.payuMid || null;
          }

          let holdMid = client.payuHoldMid;
          let holdKey = client.payuHoldKey;

          if (childKey && childMid) {
            let totalAmount = Number(amount) + Number(charges) + Number(gstAmount);

            let splitObj = null;

            if (amountDiff > 0) {
              //keep full payment

              if (holdMid && holdKey) {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Amount Diff [${amountDiff}], Charges To Kipinn [${charges}], Amount To Hold Party [${amount}], Total Amount [${totalAmount}], Hold Key [${holdKey}], Hold MID [${holdMid}], Splitting The Payment to Kipinn And Hold Party!`
                );
                splitObj = {
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${charges}`,
                  },
                  [holdKey]: {
                    aggregatorSubTxnId: `${paymentId}_${holdKey}`,
                    aggregatorSubAmt: `${Number(amount) + Number(gstAmount)}`,
                  },
                };
              } else {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Amount Diff [${amountDiff}], Total Amount To Kipinn With Charges [${totalAmount}], Hold Key [${holdKey}], Hold MID [${holdMid}], Splitting The Whole Payment to Kipinn!`
                );
                splitObj = {
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${totalAmount}`,
                  },
                };
              }
            } else {
              //update holdOutBalance and keep remaining holdOutBalance and return the amount diff

              totalAmount = Number(client.holdOutBalance) + Number(charges);

              if (holdMid && holdKey) {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Charges To Kipinn [${charges}], Amount Paid to Hold Party [${client.holdOutBalance}], Amount Paid To Client [${Math.abs(amountDiff)}], Hold Key [${holdKey}], Hold MID [${holdMid}], Payment Split Between Client, Kipinn And Hold Party.`
                );
                splitObj = {
                  [childKey]: {
                    aggregatorSubTxnId: `${paymentId}_${childKey}`,
                    aggregatorSubAmt: `${Math.abs(amountDiff)}`,
                  },
                  [holdKey]: {
                    aggregatorSubTxnId: `${paymentId}_${holdKey}`,
                    aggregatorSubAmt: `${client.holdOutBalance}`,
                  },
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${charges}`,
                  },
                };
              } else {
                log.info(
                  `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Total Amount To Kipinn With Charges [${totalAmount}], Amount Paid To Client [${Math.abs(amountDiff)}], Hold Key [${holdKey}], Hold MID [${holdMid}], Payment Split Between Client and Kipinn.`
                );
                splitObj = {
                  [childKey]: {
                    aggregatorSubTxnId: `${paymentId}_${childKey}`,
                    aggregatorSubAmt: `${Math.abs(amountDiff)}`,
                  },
                  [key]: {
                    aggregatorSubTxnId: `${paymentId}_${key}`,
                    aggregatorSubAmt: `${totalAmount}`,
                  },
                };
              }
            }

            const splitResponse = await splitPayment(
              "absolute",
              paymentId,
              splitObj
            );

            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
              }], Transaction Id [${paymentId}], Split Payment Response [${JSON.stringify(
                splitResponse
              )}]`
            );

            if (
              splitResponse?.splitStatus &&
              splitResponse?.splitStatus === "success"
            ) {

              splitDetail = await getSplitPaymentDetails(paymentId);
              // splitTransId = splitDetail?.splitItems[childKey]?.capture?.mihpayid || null;
              splitTransId = splitDetail && splitDetail?.splitItems?.[childKey]?.capture?.mihpayid ? splitDetail?.splitItems[childKey]?.capture?.mihpayid : null;
              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Split Transaction Id [${splitTransId}]`
              );
              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              }

              await transactionDB.updateHoldAmount({
                id: transId,
                holdAmount:
                  amountDiff > 0 ? Number(amount) : client.holdOutBalance,
              });
            } else {
              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              }

              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split ERROR Response [${JSON.stringify(
                  splitResponse
                )}]`
              );
            }
          } else {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split API can't be initiated due to absence of Account Details`
            );
          }
        } else {
          let childKey = property.payuKey;
          let childMid = property.payuMid;

          if (!childKey || !childMid) {
            childKey = client.payuKey || null;
            childMid = client.payuMid || null;
          }

          //Payment gateway check payu, null check....
          if (childKey && childMid) {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], No Hold Out Balance Remaining, Initiating Split Payment API`
            );

            let splitObj = {
              [childKey]: {
                aggregatorSubTxnId: `${paymentId}_${childKey}`,
                aggregatorSubAmt: `${Number(amount) + Number(gstAmount)}`,
              },
            };

            if (
              mode !== CONSTANTS.TRANSACTION_MODES.UPI &&
              mode !== CONSTANTS.TRANSACTION_MODES.OFFLINE &&
              Number(charges) !== 0
            ) {
              splitObj = {
                [childKey]: {
                  aggregatorSubTxnId: `${paymentId}_${childKey}`,
                  aggregatorSubAmt: `${Number(amount) + Number(gstAmount)}`,
                },
                [key]: {
                  aggregatorSubTxnId: `${paymentId}_${key}`,
                  aggregatorSubAmt: `${charges}`,
                },
              };
            }

            const splitResponse = await splitPayment(
              "absolute",
              paymentId,
              splitObj
            );

            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
              }], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split Payment Response [${JSON.stringify(
                splitResponse
              )}]`
            );

            if (
              splitResponse?.splitStatus &&
              splitResponse?.splitStatus === "success"
            ) {
              splitDetail = await getSplitPaymentDetails(paymentId);
              // splitTransId = splitDetail?.splitItems[childKey]?.capture?.mihpayid || null;
              splitTransId = splitDetail && splitDetail?.splitItems?.[childKey]?.capture?.mihpayid ? splitDetail?.splitItems[childKey]?.capture?.mihpayid : null;
              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Payu Key [${childKey}] Split Transaction Id [${splitTransId}]`
              );

              let isBank = false;

              const bank = await bankDB.getBankAccountByPayuCreds({
                clientId,
                payuKey: childKey,
                payuMid: childMid
              });

              if (bank) isBank = true;

              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });

                if (isBank) {
                  await transactionDB.updateAccountDetailsByGId({
                    gId: paymentId,
                    bankId: bank.id,
                    paymentAccountNo: bank.accountNum,
                    paymentAccountName: bank.holderName,
                  });
                }
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });

                if (isBank) {
                  await transactionDB.updateAccountDetailsByGId({
                    gId: transGId,
                    bankId: bank.id,
                    paymentAccountNo: bank.accountNum,
                    paymentAccountName: bank.holderName,
                  });
                }
              }

              await transactionDB.updateHoldAmount({
                id: transId,
                holdAmount: 0,
              });
            } else {
              if (paymentId) {
                await transactionDB.updateSettleDetailsByGId({
                  gId: paymentId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              } else {
                await transactionDB.updateSettleDetailsByGId({
                  gId: transGId,
                  settleStatus: CONSTANTS.PAYOUT_STATUS.INITITATED,
                  settleDescription: JSON.stringify(splitResponse),
                  splitTnxId: splitTransId,
                });
              }

              log.info(
                `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id
                }], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split ERROR Response [${JSON.stringify(
                  splitResponse
                )}]`
              );
            }
          } else {
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], PayU Mid [${childMid}], Payu Key [${childKey}], Split API can't be initiated due to absence of Account Details`
            );
          }
        }
      } else {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Payment Gateway Not Set To PayU Or Test Environment Is Enabled`
        );
      }
    }

    let receipt: any = "";
    let settings = await settingsDB.getByClientIdAndPropId({
      clientId: client.id,
      propId: occupancy.propId,
    });


    let logo = settings?.logo
      ? process.env.RS_LOGO_URI + client?.id + "/" + occupancy?.propId + "/" + settings?.logo
      : client?.logo
        ? process.env.RS_LOGO_URI + client?.id + "/" + client?.logo
        : String(process.env.RS_DEFAULT_LOGO_URI);
    log.info(`[${C}], [${F}], Logo [${logo}]`);
    if (status === CONSTANTS.TRANSACTION_STATUS.SUCCESS) {
      if (excessDueflag) {
        duesStatsArr.push(excessDueStats);
      }
      let flatName = "";

      if (property.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
        const { name } = await flatDB.getById({ id: occupancy.flatId });
        flatName = name;
      } else {
        flatName =
          occupancy.floor === "G" ? "Ground Floor" : "Floor " + occupancy.floor;
      }

      let isGstEnabled = property?.isGstEnabled || 0;
      let invoiceNo = property?.invoiceNo || null;
      let invoiceNoPrefix = property?.invoiceNoPrefix || null;
      let gstNo = property?.gstNo || null;
      let businessName = property?.ownerName || ""
      let transactionInvoiceNo = ""
      let prefix = "";
      if (0 != isGstEnabled && 0 != invoiceNo) {
        let getGstTransactions = await transactionDB.getByGstNo({ gstNo });
        if (false == getGstTransactions) {
          if (null != invoiceNoPrefix) {
            prefix = invoiceNoPrefix;
          }
          transactionInvoiceNo = `${prefix}${invoiceNo}`;
        } else {
          invoiceNo = getGstTransactions?.invoiceNo;
          if (null != invoiceNoPrefix) {
            prefix = invoiceNoPrefix;
            invoiceNo = invoiceNo.replace(prefix, "");
            transactionInvoiceNo = `${prefix}${String(Number(invoiceNo) + 1)}`;
          } else {
            transactionInvoiceNo = String(Number(invoiceNo) + 1);
          }
        }
        businessName = property?.businessName || "";
      } else {
        isGstEnabled = 0;
      }

      if (Number(gstAmount) > 0) {
        receipt = await createGstReceipt({
          // title: receiptDescription,
          title: `${transId}_${paymentId || transGId}`,
          roomNum: room.roomNum,
          propName: property.name,
          dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
          mode: getModeName(Number(mode)),
          transGId: splitTransId || paymentId || transGId,
          paidDate: moment().format("DD MMM, YYYY"),
          month: moment(allDues[0].dueDate).format("MMM, YYYY"),
          tenantName: tenant?.name || "",
          amount: Number(amount) + Number(gstAmount),
          address: `${property?.address}`,
          landlord: businessName || "",
          landlordNumber: property?.ownerMobile || "",
          logo: logo,
          dueStats: duesStatsArr,
          "landlord-pan": "",
          occupancy,
          flatName,
          propType: property.type,
          isGstEnabled,
          transactionInvoiceNo,
          gstNo,
          gstCharges: gstAmount,
        });
      } else {
        if (Number(gstAmount) > 0) {
          dueStats = { title: "Gst charges", amount: gstAmount };
          duesStatsArr.push(dueStats);
        }

        // receipt = await createReceiptMultipleDues({
        //   // title: transTitle,
        //   title: `${transId}_${paymentId || transGId}`,
        //   roomNum: room.roomNum,
        //   propName: property.name,
        //   dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
        //   mode: getModeName(Number(mode)),
        //   transGId: splitTransId || paymentId || transGId,
        //   paidDate: moment().format("DD MMM, YYYY"),
        //   month: moment(allDues[0].dueDate).format("MMM, YYYY"),
        //   tenantName: tenant?.name || "",
        //   amount: Number(amount) + Number(gstAmount),
        //   address: `${property?.address}`,
        //   landlord: businessName || "",
        //   landlordNumber: property?.ownerMobile || "",
        //   logo: logo,
        //   "landlord-pan": "",
        //   occupancy,
        //   dueStats: duesStatsArr,
        //   flatName,
        //   propType: property.type,
        //   isGstEnabled,
        //   transactionInvoiceNo,
        //   gstNo,
        // });
        receipt = await generateRentReciept({
          // title: transTitle,
          title: `${transId}_${paymentId || transGId}`,
          roomNum: room.roomNum,
          propName: property.name,
          dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
          mode: getModeName(Number(mode)),
          transGId: splitTransId || paymentId || transGId,
          paidDate: moment().format("DD MMM, YYYY"),
          month: moment(allDues[0].dueDate).format("MMM, YYYY"),
          tenantName: tenant?.name || "",
          amount: Number(amount) + Number(gstAmount),
          address: `${property?.address}`,
          landlord: businessName || "",
          landlordNumber: property?.ownerMobile || "",
          logo: logo,
          "landlord-pan": "",
          occupancy,
          dueStats: duesStatsArr,
          flatName,
          propType: property.type,
          isGstEnabled,
          transactionInvoiceNo,
          gstNo,
          recordedBy: "Kipinn App"
        });
      }

      log.info(`[${C}], [${F}], RECIPT [${receipt}]`);
      if (0 != isGstEnabled && 0 != invoiceNo) {
        if (paymentId) {
          await transactionDB.updateReceiptWithInvoiceByGId({
            gId: paymentId,
            receipt,
            invoiceNo: transactionInvoiceNo,
            gstNo
          });
        } else {
          await transactionDB.updateReceiptWithInvoiceByGId({
            gId: transGId,
            receipt,
            invoiceNo: transactionInvoiceNo,
            gstNo
          });
        }
      } else {
        if (paymentId) {
          await transactionDB.updateReceiptByGId({
            gId: paymentId,
            receipt,
          });
        } else {
          await transactionDB.updateReceiptByGId({
            gId: transGId,
            receipt,
          });
        }
      }

      if (paymentId) {
        await setTransactionTallyStatusByGId(
          Number(clientId),
          paymentId,
          CONSTANTS.TALLY_STATUS.PENDING,
        );
      } else {
        await setTransactionTallyStatusByGId(
          Number(clientId),
          transGId,
          CONSTANTS.TALLY_STATUS.PENDING,
        );
      }

      const tenantDues = await moveOutDuesDB.getByTenantIdAndClientId({
        tenantId,
        clientId,
      });
      if (!tenantDues) {
        await tenantDB.updateLastReminded({
          id: tenantId,
          lastRemindedOn: null,
        });
        await moveOutDB.updateLastReminded({
          clientId,
          tenantId,
          lastRemindedOn: null,
        });
      }

      const dueName = allDues.length === 1
        ? allDues[0].rentStartDate !== null
          ? `${transTitle} for ${moment(allDues[0].rentStartDate).format("DD MMM YY")} to ${moment(allDues[0].rentEndDate).format("DD MMM YY")}`
          : `${transTitle} for ${moment(allDues[0].dueDate).format("MMM YYYY")}`
        : transTitle

      const notiSettings = await settingsDB.getByClientIdAndPropId({
        clientId: occupancy.clientId,
        propId: occupancy.propId,
      });

      let footerText = notiSettings.footer || "The Kipinn Team";

      let modeVal = getModeName(Number(mode));
      //In App Notification 
      if (client.regId) {
        await sendNotification({
          title: `Due amount received`,
          message: `Tenant Name: ${tenant.name}\nAmount: ₹${Number(amount) + Number(gstAmount)}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment().format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: Kipinn App`,
          regId: client.regId,
          userId: client.id,
          userType: CONSTANTS.USER_TYPE.CLIENT,
          notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ONLINE_PAYMENT_CONFIRM,
          clientId: client.id,
        });
      }
      const notifyPartner = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.NOTIFY_PARTNER,
      });
      if (notifyPartner && Number(notifyPartner?.value) === 1) {
        const partner = await staffDB.getByClientIdAndRole({
          clientId,
          role: CONSTANTS.STAFF_ROLES.PARTNER,
        });
        if (partner.regId) {
          await sendNotification({
            title: `Due amount received`,
            message: `Tenant Name: ${tenant.name}\nAmount: ₹${Number(amount) + Number(gstAmount)}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment().format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: Kipinn App`,
            regId: partner.regId,
            userId: partner.id,
            userType: CONSTANTS.USER_TYPE.CLIENT,
            notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.ONLINE_PAYMENT_CONFIRM,
            clientId: client.id,
          });
        }
      }

      if (notiSettings && Number(notiSettings.rentReminder) === 1) {
        let clientAmount = Number(amount) + Number(gstAmount);
        let whatsappAgreegrator = await clientConfigDB.getClientConfig({
            clientId,
            provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
            type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.WHATSAPP_AGGREGATOR
        });
        let confirmPaymentToClientTemp = await getClientWhatsappCredentialsMessageCentral(Number(clientId), property?.id, CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION, CONSTANTS.USER_TYPE.CLIENT);
        let whatsAppProvider = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY,
        });
        if (whatsAppProvider && whatsAppProvider?.value != '') {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Chat Mitra to client`
          );
          let bodyValues = [
            { type: "text", text: `${tenant?.name}` },
            { type: "text", text: `${clientAmount}` },
            { type: "text", text: `${property.name}` },
            { type: "text", text: `${flatName} (${room.roomNum})` },
            { type: "text", text: `${dueName}` },
            { type: "text", text: `${moment().format("DD MMM YY HH:mm:ss")}` },
            { type: "text", text: `${modeVal}` },
            { type: "text", text: `Kipinn App` },
          ];
          sendWhatsapp(
            client.mobile,
            client.name,
            Number(clientId),
            Number(occupancy.propId),
            bodyValues,
            CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
            CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
            CONSTANTS.USER_TYPE.CLIENT,
          );
        } else {
          if(whatsappAgreegrator && Number(whatsappAgreegrator?.value)  === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL && confirmPaymentToClientTemp) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through message central to client`
            );
            const bodyValues: Record<string, string> = {
              body_1: `${tenant?.name}`,
              body_2: `${clientAmount}`,
              body_3: `${property.name}`,
              body_4: `${flatName} (${room.roomNum})}`,
              body_5: `${dueName}`,
              body_6: `${moment().format("DD MMM YY HH:mm:ss")}`,
              body_7: `${modeVal}`,
              body_8: `Kipinn App`
            };
            sendWhatsappMC(
              client.mobile,
              Number(clientId),
              Number(occupancy.propId),
              bodyValues,
              CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
              CONSTANTS.USER_TYPE.CLIENT,
              CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL
            );
          } else {
            
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through interakt to client`
            );
            sendWhatsappPaymentConfirmationClient(
              client.mobile,
              client.name,
              tenant.name,
              clientAmount,
              property.name,
              `${flatName} (${room.roomNum})`,
              dueName,
              moment().format("DD MMM YY HH:mm:ss"),
              modeVal,
              "Kipinn App",
              Number(clientId)
            );
          }
        }
        const notifyPartner = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.NOTIFY_PARTNER,
        });
        if (notifyPartner && Number(notifyPartner?.value) === 1) {
          const partner = await staffDB.getByClientIdAndRole({
            clientId,
            role: CONSTANTS.STAFF_ROLES.PARTNER,
          });
          let partnerMobile = partner[0]?.mobile;
          let partnerName = partner[0]?.name;
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${client.id}], Transaction Id [${paymentId}], Notify to partner`
          );
          if (whatsAppProvider && whatsAppProvider?.value != '') {
            // log.info(
            // `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending Through Chat Mitra`
            // );
            let bodyValues = [
              { type: "text", text: `${tenant?.name}` },
              { type: "text", text: `${clientAmount}` },
              { type: "text", text: `${property.name}` },
              { type: "text", text: `${flatName} (${room.roomNum})` },
              { type: "text", text: `${dueName}` },
              { type: "text", text: `${moment().format("DD MMM YY HH:mm:ss")}` },
              { type: "text", text: `${modeVal}` },
              { type: "text", text: `Kipinn App` },
            ];
            sendWhatsapp(
              partnerMobile,
              partnerName,
              Number(clientId),
              Number(occupancy.propId),
              bodyValues,
              CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
              CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
              CONSTANTS.USER_TYPE.CLIENT,
            );
          } else {
            if(whatsappAgreegrator && Number(whatsappAgreegrator?.value)  === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL && confirmPaymentToClientTemp) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through message central to partner`
              );
              const bodyValues: Record<string, string> = {
                body_1: `${tenant?.name}`,
                body_2: `${clientAmount}`,
                body_3: `${property.name}`,
                body_4: `${flatName} (${room.roomNum})}`,
                body_5: `${dueName}`,
                body_6: `${moment().format("DD MMM YY HH:mm:ss")}`,
                body_7: `${modeVal}`,
                body_8: `Kipinn App`
              };
              sendWhatsappMC(
                partnerMobile,
                Number(clientId),
                Number(occupancy.propId),
                bodyValues,
                CONSTANTS.WHATSAPP_TEMPLATE_TYPES.CLIENT.PAYMENT_CONFIRMATION,
                CONSTANTS.USER_TYPE.CLIENT,
                CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL
              );
            } else {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through interakt to partner`
              );
              sendWhatsappPaymentConfirmationClient(
                partnerMobile,
                partnerName,
                tenant.name,
                clientAmount,
                property.name,
                `${flatName} (${room.roomNum})`,
                dueName,
                moment().format("DD MMM YY HH:mm:ss"),
                modeVal,
                "Kipinn App",
                Number(clientId)
              );
            }
          }
        }
        if (whatsAppProvider && whatsAppProvider?.value != '') {
          let bodyValues = [
            { type: "text", text: `${amount}` },
            { type: "text", text: `${property.name}` },
            { type: "text", text: `${flatName} (${room.roomNum})` },
            { type: "text", text: `${footerText}` },
          ];
          let headerValues = [
            {
              type: "document",
              document: {
                link: receipt,
                filename: `Receipt_${paymentId || transGId}.pdf`,
              }
            },
          ];
          sendWhatsapp(
            tenant.mobile,
            tenant.name,
            Number(clientId),
            Number(occupancy.propId),
            bodyValues,
            CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.PAYMENT_CONFIRMATION,
            CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
            CONSTANTS.USER_TYPE.TENANT,
            headerValues
          );
        } else {
          let paymentAckToTenant = await getClientWhatsappCredentialsMessageCentral(Number(clientId), property?.id, CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.PAYMENT_CONFIRMATION, CONSTANTS.USER_TYPE.TENANT);
          if(whatsappAgreegrator && Number(whatsappAgreegrator?.value)  === CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL && paymentAckToTenant) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through message central to tenant`
              );
              const bodyValues: Record<string, string> = {
                body_1: `${amount}`,
                body_2: `${property.name}`,
                body_3: `${flatName} (${room.roomNum})}`,
                body_4: `${footerText.replace("Team", "").trim()}`
              };
              const folderName = `tenant_${tenantId}`;
              const currentYear = moment().year();
              const fileName = path.basename(receipt);
              const folderPath = `uploads/documents/${currentYear}/receipt/${clientId}/${occupancy?.propId}/${occupancy?.roomId}_${occupancy?.bedId}/${folderName}/${fileName}`;
              sendWhatsappMC(
                tenant?.mobile,
                Number(clientId),
                Number(occupancy.propId),
                bodyValues,
                CONSTANTS.WHATSAPP_TEMPLATE_TYPES.TENANT.PAYMENT_CONFIRMATION,
                CONSTANTS.USER_TYPE.TENANT,
                CONSTANTS.CLIENT_CONFIG_PROVIDER.MESSAGE_CENTRAL,
                folderPath,
              );
            } else {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending through interakt to tenant`
              );
              sendWhatsappPaymentConfirmationTenant(
                tenant.mobile,
                tenant.name,
                amount,
                property.name,
                `${flatName} (${room.roomNum})`,
                receipt,
                footerText,
                Number(clientId)
              );
            }
        }
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Payment Id [${paymentId}], Receipt [${receipt}], Whatsapp messages sent successfully`
        );
      }
    }

    return res.status(200).json({
      msg: "Transactions recorded successfully",
      paymentId,
      amount,
      mode,
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.ListForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "ListForWeb";

  try {
    let { startDate, endDate, s, t, propId, filter, recordedByFilters, modeFilters, transactionForFilters, propFilters, settlementFilters = null, locationFilters = null } = req.query;
    // let clientId = req.id;
    const userType = req.userType;
    let transactions = [];
    let diff = 0;
    const limit = 10e9;
    const pageNum = 1;
    let monthlyData: any = null;
    let yearlyData: any = null;
    let totalRent: any = null;
    let totalRentCollection: any = null;
    let totalFine: any = null;
    let totalUtilities: any = null;
    let totalSecurity: any = null;
    let totalOffline: any = null;
    let totalOnline: any = null;
    let totalViaKipinn: any = null;
    let todayCollection: any = null;
    let selfOwnedGateway = false;
    let totalCharges: any = null;

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], Start Date [${startDate}], End Date [${endDate}], Prop Id [${propId}], Type [${t}], Search Val [${s}], Filter [${filter}], Recorded By Filter [${recordedByFilters}], Mode Filter [${modeFilters}], Trans For Filter [${transactionForFilters}], Prop Filter [${propFilters}], Settlement Filter [${settlementFilters}], Location Filter [${locationFilters}]`
    );

    if (recordedByFilters && typeof recordedByFilters === 'string') {
      // recordedByFilterList = (recordedByFilters).split(',').map(v => v.trim()).filter(Boolean);
      recordedByFilters = (recordedByFilters).split(',').map(v => v.trim()).filter(Boolean);
    }
    let isKipinnAppFilterApplied = false;

    if (modeFilters && typeof modeFilters === 'string') {
      modeFilters = modeFilters.split(',').map(v => v.trim()).filter(Boolean);
      if (Array.isArray(modeFilters) && modeFilters.includes('0')) {
        isKipinnAppFilterApplied = true;
      }
    }

    if (transactionForFilters && typeof transactionForFilters === 'string') {
      transactionForFilters = transactionForFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (propFilters && typeof propFilters === 'string') {
      propFilters = propFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (settlementFilters && typeof settlementFilters === 'string') {
      settlementFilters = settlementFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (locationFilters && typeof locationFilters === 'string') {
      locationFilters = locationFilters.split(',').map(v => v.trim()).filter(Boolean);
    }

    let date = moment().format("YYYY-MM-DD");

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Number [${pageNum}], Prop Id [${propId}], Filter [${filter}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      const client = await clientDB.getById({ id: clientId });

      const selfGateway = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.GATEWAY
      });

      if (!selfGateway || Number(selfGateway?.value) === CONSTANTS.CLIENT_CONFIG_VALUE.GATEWAY_OWNERSHIP.OWNED) {
        selfOwnedGateway = true;
      }

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Prop Id [${propId}], Search Val [${s}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      //filter is not being used can be removed!!! --Abhinav 17-09-2025

      if (Number(propId) && Number(propId) !== 0) {
        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          transactions = await transactionDB.getByClientIdAndPropIdAndSearch({
            clientId: clientId,
            propId: propId,
            searchVal: s,
            searchType: t,
          });
          // totalRent = await transactionDB.getRentByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalSecurity = await transactionDB.getSecurityByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalRentCollection = await transactionDB.getRentCollectionByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalFine = await transactionDB.getFineCollectionByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalUtilities = await transactionDB.getUtilitiesCollectionByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalOffline = await transactionDB.getOfflineTransByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalOnline = await transactionDB.getOnlineTransByTenantNameOrMobile({
          //   clientId: clientId,
          //   propId: propId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // todayCollection = await transactionDB.getTodaysCollectionForPropBySearch({
          //   propId,
          //   searchVal: s,
          //   searchType: t,
          // });
        } else if (filter === "CMS") {
          // Cur Month Security
          transactions = await transactionDB.curMonthSecurityForProp({
            propId,
          });
        } else if (filter === "CMT") {
          // Cur Month Transaction
          transactions = await transactionDB.curMonthTransactionForProp({
            propId,
          });
        } else if (filter === "CMC") {
          // Cur Month Cash
          transactions = await transactionDB.curMonthOfflineTransactionsForProp({
            propId,
          });
        } else if (filter === "CMO") {
          // Cur Month Online
          transactions = await transactionDB.curMonthOnlineTransactionsForProp({
            propId,
          });
        } else {
          // transactions = await transactionDB.getByPropIdandDateRange({
          //   propId,
          //   startDate,
          //   endDate,
          // });
          transactions = await transactionDB.getByPropIdandDateRangeAndFilterForApp({
            propId,
            startDate,
            endDate,
            limit,
            pageNum: Number(pageNum),
            mode: modeFilters ? modeFilters : null,
            transactionFor: transactionForFilters ? transactionForFilters : null,
            recordedBy: recordedByFilters ? recordedByFilters : null,
            settlement: settlementFilters ? settlementFilters : null,
            locations: locationFilters ? locationFilters : null,
          });
          // totalRent = await transactionDB.getRentByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalSecurity = await transactionDB.getSecurityByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalRentCollection = await transactionDB.getRentCollectionByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalFine = await transactionDB.getFineCollectionByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalUtilities = await transactionDB.getUtilitiesCollectionByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalOffline = await transactionDB.getOfflineTransByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalOnline = await transactionDB.getOnlineTransByPropIdandDateRange({
          //   propId: propId,
          //   startDate,
          //   endDate,
          // });
          // totalViaKipinn =
          //   await transactionDB.getViaKipinnTransByPropIdandDateRange({
          //     propId: propId,
          //     startDate,
          //     endDate,
          //   });
          // if(selfOwnedGateway) {
          //   totalCharges =
          //   await transactionDB.getViaKipinnTransChargesByPropIdandDateRange({
          //     propId: propId,
          //     startDate,
          //     endDate,
          //   });
          // }

          // todayCollection = await transactionDB.getTodaysCollectionForProp({
          //   propId,
          // });
        }
        // monthlyData = await propertyMonthlyPendingRent({
        //   propId: Number(propId),
        // });
        // yearlyData = await propertyCurrentFYPendingRent({
        //   propId: Number(propId),
        // });
      } else {
        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          transactions = await transactionDB.getByClientIdAndTenantNameMobile({
            clientId: clientId,
            searchVal: s,
            searchType: t,
          });
          // totalRent = await transactionDB.getRentByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalSecurity = await transactionDB.getSecurityByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalRentCollection = await transactionDB.getRentCollectionByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalFine = await transactionDB.getFineCollectionByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalUtilities = await transactionDB.getUtilitiesCollectionByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalOffline = await transactionDB.getOfflineTransByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // totalOnline = await transactionDB.getOnlineTransByClientIdAndTenantNameOrMobile({
          //   clientId: clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
          // todayCollection = await transactionDB.getTodaysCollectionByClientIdAndSearch({
          //   clientId,
          //   searchVal: s,
          //   searchType: t,
          // });
        } else {
          // transactions = await transactionDB.getByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          //   limit,
          //   pageNum,
          // });
          transactions = await transactionDB.getByClientIdandDateRangeAndFilter({
            clientId,
            startDate,
            endDate,
            limit,
            pageNum: Number(pageNum),
            mode: modeFilters ? modeFilters : null,
            transactionFor: transactionForFilters ? transactionForFilters : null,
            recordedBy: recordedByFilters ? recordedByFilters : null,
            propId: propFilters ? propFilters : null,
            settlement: settlementFilters ? settlementFilters : null,
            locations: locationFilters ? locationFilters : null,
          });
          // totalRent = await transactionDB.getRentByClientIdandDateRange({
          //   clientId: clientId,
          //   startDate,
          //   endDate,
          // });
          // totalSecurity = await transactionDB.getSecurityByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          // totalRentCollection = await transactionDB.getRentCollectionByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          // totalFine = await transactionDB.getFineCollectionByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          // totalUtilities = await transactionDB.getUtilitiesCollectionByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          // totalOffline = await transactionDB.getOfflineTransByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          // totalOnline = await transactionDB.getOnlineTransByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          // totalViaKipinn =
          //   await transactionDB.getViaKipinnTransByClientIdandDateRange({
          //     clientId,
          //     startDate,
          //     endDate,
          //   });
          // if(selfOwnedGateway) {
          //   totalCharges =
          //   await transactionDB.getViaKipinnTransChargesByClientIdandDateRange({
          //     clientId,
          //     startDate,
          //     endDate,
          //   });
          // }
          // todayCollection = await transactionDB.getTodaysCollection({
          //   clientId,
          // });
        }
        // const client = await clientDB.getById({
        //   id: clientId,
        // });
        // monthlyData = await clientMonthlyPendingRent(
        //   client,
        // );
        // yearlyData = await propertyCurrentFYPendingRent({
        //   propId: Number(propId),
        // });
      }
    } else {
      const staffId = req.id;
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Prop Id [${propId}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      let isStaffAllowed = await isPrivilegedStaff(staff.role, 3);
      if (!isStaffAllowed) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Staff Role [${staff.role}], Unauthorized Access`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Prop Id [${propId}], Search Val [${s}], Staff Id [${staffId}], Staff Requested....`
      );

      clientId = staff.clientId;
      const selfGateway = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.GATEWAY
      });

      if (!selfGateway || Number(selfGateway?.value) === CONSTANTS.CLIENT_CONFIG_VALUE.GATEWAY_OWNERSHIP.OWNED) {
        selfOwnedGateway = true;
      }
      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        if (Number(propId) && Number(propId) !== 0) {
          if (s && s !== "undefined" && s !== "null" && s !== " ") {
            transactions = await transactionDB.getByClientIdAndPropIdAndSearch({
              clientId: clientId,
              propId: propId,
              searchVal: s,
              searchType: t,
            });
            // totalRent = await transactionDB.getRentByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // totalSecurity = await transactionDB.getSecurityByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // totalRentCollection = await transactionDB.getRentCollectionByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // totalFine = await transactionDB.getFineCollectionByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // totalUtilities = await transactionDB.getUtilitiesCollectionByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // totalOffline = await transactionDB.getOfflineTransByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // totalOnline = await transactionDB.getOnlineTransByTenantNameOrMobile({
            //   clientId: clientId,
            //   propId: propId,
            //   searchVal: s,
            //   searchType: t,
            // });
            // todayCollection = await transactionDB.getTodaysCollectionForPropBySearch({
            //   propId,
            //   searchVal: s,
            //   searchType: t,
            // });
          } else {
            // transactions = await transactionDB.getByPropIdandDateRange({
            //   propId,
            //   startDate,
            //   endDate,
            // });
            transactions = await transactionDB.getByPropIdandDateRangeAndFilterForApp({
              clientId,
              pageNum: Number(pageNum),
              limit,
              startDate,
              endDate,
              propId,
              mode: modeFilters ? modeFilters : null,
              transactionFor: transactionForFilters ? transactionForFilters : null,
              recordedBy: recordedByFilters ? recordedByFilters : null,
              settlement: settlementFilters ? settlementFilters : null,
              locations: locationFilters ? locationFilters : null,
            });
            // totalRent = await transactionDB.getRentByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalSecurity = await transactionDB.getSecurityByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalRentCollection = await transactionDB.getRentCollectionByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalFine = await transactionDB.getFineCollectionByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalUtilities = await transactionDB.getUtilitiesCollectionByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalOffline = await transactionDB.getOfflineTransByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalOnline = await transactionDB.getOnlineTransByPropIdandDateRange({
            //   propId: propId,
            //   startDate,
            //   endDate,
            // });
            // totalViaKipinn =
            //   await transactionDB.getViaKipinnTransByPropIdandDateRange({
            //     propId: propId,
            //     startDate,
            //     endDate,
            //   });
            // if(selfOwnedGateway) {
            //   totalCharges =
            //   await transactionDB.getViaKipinnTransChargesByPropIdandDateRange({
            //     propId: propId,
            //     startDate,
            //     endDate,
            //   });
            // }
            // todayCollection = await transactionDB.getTodaysCollectionForProp({
            //   propId,
            // });
          }
          // monthlyData = await propertyMonthlyPendingRent({
          //   propId: Number(propId),
          // });
          // yearlyData = await propertyCurrentFYPendingRent({
          //   propId: Number(propId),
          // });
        } else {
          if (s && s !== "undefined" && s !== "null" && s !== " ") {
            transactions = await transactionDB.getByClientIdAndTenantNameMobileForStaff({
              clientId: clientId,
              searchVal: s,
              searchType: t,
              propertiesIds,
            });
            // totalRent = await transactionDB.getRentByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // totalSecurity = await transactionDB.getSecurityByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // totalRentCollection = await transactionDB.getRentCollectionByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // totalFine = await transactionDB.getFineCollectionByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // totalUtilities = await transactionDB.getUtilitiesCollectionByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // totalOffline = await transactionDB.getOfflineTransByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // totalOnline = await transactionDB.getOnlineTransByClientIdAndTenantNameOrMobileForStaff({
            //   clientId: clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
            // todayCollection = await transactionDB.getTodaysCollectionByClientIdAndSearchForStaff({
            //   clientId,
            //   searchVal: s,
            //   searchType: t,
            //   propertiesIds,
            // });
          } else {
            // transactions = await transactionDB.getForStaffByDateRange({
            //   clientId,
            //   pageNum,
            //   limit,
            //   startDate,
            //   endDate,
            //   propertiesIds: propertiesIds,
            // });
            transactions = await transactionDB.getForStaffByDateRangeAndFilter({
              clientId,
              pageNum: Number(pageNum),
              limit,
              startDate,
              endDate,
              propertiesIds,
              mode: modeFilters ? modeFilters : null,
              transactionFor: transactionForFilters ? transactionForFilters : null,
              recordedBy: recordedByFilters ? recordedByFilters : null,
              propId: propFilters ? propFilters : null,
              settlement: settlementFilters ? settlementFilters : null,
              locations: locationFilters ? locationFilters : null,
            });
            // totalRent = await transactionDB.getRentByClientIdandDateRangeForStaff({
            //   clientId: clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds,
            // });
            // totalSecurity = await transactionDB.getSecurityByClientIdandDateRangeForStaff({
            //   clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds,
            // });
            // totalRentCollection = await transactionDB.getRentCollectionByClientIdandDateRangeForStaff({
            //   clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds,
            // });
            // totalFine = await transactionDB.getFineCollectionByClientIdandDateRangeForStaff({
            //   clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds,
            // });
            // totalUtilities = await transactionDB.getUtilitiesCollectionByClientIdandDateRangeForStaff({
            //   clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds,
            // });
            // totalOffline = await transactionDB.getOfflineTransByClientIdandDateRangeForStaff({
            //   clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds
            // });
            // totalOnline = await transactionDB.getOnlineTransByClientIdandDateRangeForStaff({
            //   clientId,
            //   startDate,
            //   endDate,
            //   propertiesIds
            // });
            // totalViaKipinn =
            //   await transactionDB.getViaKipinnTransByClientIdandDateRangeForStaff({
            //     clientId,
            //     startDate,
            //     endDate,
            //     propertiesIds
            //   });
            // if(selfOwnedGateway) {
            //   totalCharges =
            //   await transactionDB.getViaKipinnTransChargesByClientIdandDateRangeForStaff({
            //     clientId,
            //     startDate,
            //     endDate,
            //     propertiesIds
            //   });
            // }
            // todayCollection = await transactionDB.getTodaysCollectionForStaff({
            //   clientId,
            //   propertiesIds,
            // });
          }
        }
      }
    }

    let totalCollectionX = 0;
    let totalTodayCollectionX = 0;
    let totalRentCollectionX = 0;
    let totalSecurityCollectionX = 0;
    let totalOtherCollectionX = 0;
    let totalKipinnCollectionX = 0;
    let totalFineCollectionX = 0;
    let totalOnlineCollectionX = 0;
    let totalOfflineCollectionX = 0;
    let totalChargesX = 0;

    //let banks = await bankDB.getActivePaymentAccountsByClientId({clientId});
    const accountSummary: Record<string, { bankName: string; accountNum: string; holderName: string; totalAmount: number; kipinnAmount: number; markedOnlineAmount: number; }> = {};
    let isBankOnline = false;
    if (isKipinnAppFilterApplied) {
      let banks = await bankDB.getActivePaymentAccountsByClientId({ clientId });
      if (banks && banks.length > 0) {
        for (const bank of banks) {
          isBankOnline = true;
          if (!accountSummary[bank?.accountNum]) {
            accountSummary[bank?.accountNum] = {
              bankName: bank?.bankName,
              accountNum: bank?.accountNum,
              holderName: bank?.holderName,
              totalAmount: 0,
              kipinnAmount: 0,
              markedOnlineAmount: 0,
            };
          }
        }
      }
    }

    let monthlyDueDateCollection: Record<string, number> = {};

    if (transactions && transactions.length > 0) {
      const transactionIds = transactions.map((t: any) => Number(t.id));
      const tenantIds = [...new Set(
        transactions
          .map((t: any) => Number(t.tenantId))
          .filter((id: any) => Number.isInteger(id) && id > 0)
      )];

      const ledgers = await ledgerDB.getByMultipleTransactionIds({
        transactionId: transactionIds,
      });

      const ledgerByTxnId = new Map<number, any[]>();
      if (ledgers && ledgers.length > 0) {
        for (const l of ledgers) {
          if (!ledgerByTxnId.has(l.transactionId)) {
            ledgerByTxnId.set(l.transactionId, []);
          }
          ledgerByTxnId.get(l.transactionId)!.push(l);
        }
      }

      const occupancies = await occupancyDB.getByMultipleTenantIdAndClientId({
        tenantIds,
        clientId,
      });
      const occupancyByTenantId = new Map<number, any>();
      if (occupancies && occupancies.length > 0) {
        occupancies.forEach((o: any) => occupancyByTenantId.set(o.tenantId, o));
      }
      for (let transaction of transactions) {
        // const tenantOccupancy = await occupancyDB.getByTenantId({
        //   tenantId: transaction.tenantId,
        // });
        // if (!tenantOccupancy) {
        //   transaction.isMovedOut = true;
        // } else {
        //   transaction.isMovedOut = false;
        // }
        if (isKipinnAppFilterApplied) {
          if (isBankOnline) {
            if (transaction?.recordedBy === 'Kipinn App') {
              if (accountSummary[transaction?.paymentAccountNo]) {
                accountSummary[transaction?.paymentAccountNo].totalAmount += Number(transaction?.amount) || 0;
                accountSummary[transaction?.paymentAccountNo].kipinnAmount += Number(transaction?.kipinnAmount) || 0;
              }
            } else {
              if (accountSummary[transaction?.paymentAccountNo]) {
                accountSummary[transaction?.paymentAccountNo].totalAmount += Number(transaction?.amount) || 0;
                accountSummary[transaction?.paymentAccountNo].markedOnlineAmount += Number(transaction?.amount) || 0;
              }
            }
          }
        }

        if (transaction.type !== CONSTANTS.TRANSACTION_TYPES.SECURITY_ADJUSTED && transaction.amount > 0) {
          if (transaction.isFinanciallyApplicable === 1) {
            totalCollectionX += Number(transaction.amount);
            if (moment(transaction.collectionDate).isSame(moment(), 'day')) {
              totalTodayCollectionX += Number(transaction.amount);
            }
          }

          const monthKey = moment(transaction.dueDate).format('YYYY-MM');
          // log.info(`Trans Id - ${transaction.id}, transaction.dueDate - ${transaction.dueDate}, monthKey - ${monthKey}`)
          if (!monthlyDueDateCollection[monthKey]) {
            monthlyDueDateCollection[monthKey] = 0;
          }
          monthlyDueDateCollection[monthKey] += Number(transaction.amount);
          

          if (transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.RENT) {
            totalRentCollectionX += Number(transaction.amount);
          } else if (transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.SECURITY) {
            totalSecurityCollectionX += Number(transaction.amount);
          } else if (transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.ELECTRICITY ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.GAS ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.KEY_CHARGES ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.LAUNDRY_CHARGES ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.MAID_CHARGES ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.MAINTENANCE ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.WIFI ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.TECH_CHARGES ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.AGREEMENT_CHARGE ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.FOOD ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.OTHER ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.DAMAGE ||
            transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.REGISTRATION
          ) {
            totalOtherCollectionX += Number(transaction.amount);
          } else if (transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.FINE) {
            totalFineCollectionX += Number(transaction.amount);
          }

          if (transaction.recordedBy === "Kipinn App") {
            totalKipinnCollectionX += Number(transaction.amount);

            if (selfOwnedGateway && (transaction.gateway === CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ || transaction.gateway === CONSTANTS.PAYMENT_GATEWAY.CASHFREE)) {
              totalChargesX += Number(transaction.charges);
            }
          }

          if (transaction.mode === CONSTANTS.TRANSACTION_MODES.OFFLINE) {
            totalOfflineCollectionX += Number(transaction.amount);
          } else if (transaction.mode !== CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY) {
            totalOnlineCollectionX += Number(transaction.amount);
          }
        }

        let canRestore = true;

        // const ledgerRecordsByTxnId = await ledgerDB.getByTransactionId({
        //   transactionId: Number(transaction.id),
        // });

        // let occupancy = await occupancyDB.getByTenantIdAndClientId({
        //   tenantId: transaction.tenantId,
        //   clientId,
        // });

        const occupancy = occupancyByTenantId.get(transaction.tenantId);
        const ledgerRecordsByTxnId = ledgerByTxnId.get(Number(transaction.id)) || [];

        transaction.isMovedOut = !occupancy;

        let securityUsed = false;

        if (occupancy) {
          securityUsed = await ledgerDB.getUsedSecurityAmtByTenantIdAndClientId({
            tenantId: transaction.tenantId,
            clientId: clientId,
            createdAt: occupancy.createdAt,
          });
        }

        transaction.isMovedOut = false;
        if (!occupancy) {
          transaction.isMovedOut = true;
          canRestore = false;
        } else if (securityUsed && transaction.transactionFor === CONSTANTS.TRANSACTION_FOR.SECURITY) {
          canRestore = false;
        } else if (!ledgerRecordsByTxnId || ledgerRecordsByTxnId.length === 0) {
          canRestore = false;
        } else if (ledgerRecordsByTxnId && ledgerRecordsByTxnId.length > 1) {
          canRestore = false;
        } else if (Math.abs(ledgerRecordsByTxnId[0].amount) !== Math.abs(transaction.amount)) {
          canRestore = false;
        } else if (ledgerRecordsByTxnId[0].type === CONSTANTS.DUES_TYPES.SETTLEMENT) {
          canRestore = false;
        } else if (ledgerRecordsByTxnId[0].balance !== 0) {
          canRestore = false;
        }

        transaction.canRestore = canRestore;
      }
    }

    let staffs = await staffDB.getActiveByClientId({ clientId });
    // log.info(
    //   `[${C}], [${F}], Client Id [${clientId}], ${JSON.stringify(monthlyDueDateCollection)}`
    // );
    if (Object.keys(monthlyDueDateCollection).length > 0) {
        monthlyDueDateCollection  = Object.fromEntries(
        Object.entries(monthlyDueDateCollection).sort(([a], [b]) => b.localeCompare(a))
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Prop Id [${propId}], Search Val [${s}], Transactions sent successfully`
    );

    return res.status(200).json({
      msg: "Transactions sent successfully",
      data: {
        transactions: transactions || [],
        // summary: {
        //   totalOffline: totalOffline?.amount || 0,
        //   totalOnline: totalOnline?.amount || 0,
        //   totalViaKipinn: totalViaKipinn?.amount || 0,
        //   totalRent: totalRent?.amount || 0,
        //   totalRentOnly: totalRentCollection?.amount || 0,
        //   totalFine: totalFine?.amount || 0,
        //   totalUtilities: totalUtilities?.amount || 0,
        //   todayCollection: todayCollection || 0,
        //   totalSecurity: totalSecurity?.amount || 0,
        //   monthlyData: monthlyData?.data[0] || [],
        //   totalYearlyRent: yearlyData?.totalYearlyRent || 0,
        //   totalYearlySecurity: yearlyData?.totalYearlySecurity || 0,
        //   totalCharges: totalCharges?.charges || 0,
        // },
        summary: {
          totalOffline: totalOfflineCollectionX || 0,
          totalOnline: totalOnlineCollectionX || 0,
          totalViaKipinn: totalKipinnCollectionX || 0,
          totalRent: totalCollectionX || 0,
          totalRentOnly: totalRentCollectionX || 0,
          totalFine: totalFineCollectionX || 0,
          totalUtilities: totalOtherCollectionX || 0,
          todayCollection: totalTodayCollectionX || 0,
          totalSecurity: totalSecurityCollectionX || 0,
          totalCharges: Number(Number(totalChargesX ?? 0).toFixed(2)),
          monthlyDueDateCollection,
        },
        staffs: staffs || [],
        accountSummary: isKipinnAppFilterApplied === true ? accountSummary : []
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

//To handle Date Range
transactions.ListForClientX = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "ListForClientX";

  try {
    let { pageNum, m, s, y, propId, startDate, endDate, modeFilters, transactionForFilters, recordedByFilters, propFilters, accountFilters, locationFilters, settlementFilters=null } = req.query;

    // let clientId = req.id;
    const userType = req.userType;

    let transactions = [];
    let curMonthTransAmount = 0;
    // let prevMonthTransAmount = 0;
    // let diff = 0;
    let totalCollection = 0;
    let totalSecurityCollection = 0;
    const limit = 10;
    let staffs = [];
    let propList = [];
    let recordedByFilterList: string[] = [];

    if (recordedByFilters && typeof recordedByFilters === 'string') {
      recordedByFilterList = (recordedByFilters).split(',').map(v => v.trim()).filter(Boolean);
      recordedByFilters = (recordedByFilters).split(',').map(v => v.trim()).filter(Boolean);
    }
    // if (modeFilters && typeof modeFilters === 'string') {
    //   modeFilters = modeFilters.split(',').map(v => v.trim()).filter(Boolean);
    //   // if (modeFilters.includes("0")) {
    //   //   if (!Array.isArray(recordedByFilterList)) recordedByFilterList = [];
    //   //   recordedByFilterList.push("Kipinn App");
    //   //   recordedByFilters = recordedByFilterList;
    //   //   modeFilters = modeFilters.filter((v: any) => v !== "0");
    //   // }
    // }

    // let isKipinnAppFilterApplied = false;
    let isKipinnAppFilterApplied = true;

    if (modeFilters && typeof modeFilters === 'string') {
      modeFilters = modeFilters.split(',').map(v => v.trim()).filter(Boolean);
      if (Array.isArray(modeFilters) && modeFilters.includes('0')) {
        isKipinnAppFilterApplied = true;
      }
    }
    if (transactionForFilters && typeof transactionForFilters === 'string') {
      transactionForFilters = transactionForFilters.split(',').map(v => v.trim()).filter(Boolean);
    }
    if (propFilters && typeof propFilters === 'string') {
      propFilters = propFilters.split(',').map(v => v.trim()).filter(Boolean);
    }

    if (accountFilters && typeof accountFilters === 'string') {
      accountFilters = accountFilters.split(',').map(v => v.trim()).filter(Boolean);
    }

    if (locationFilters && typeof locationFilters === 'string') {
      locationFilters = locationFilters.split(',').map(v => v.trim()).filter(Boolean);
    }

    if (settlementFilters && typeof settlementFilters === 'string') {
      settlementFilters = settlementFilters.split(',').map(v => v.trim()).filter(Boolean);
    }

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], PropId [${propId}], Page Number [${pageNum}], Search Val [${s}], Month [${m}], Year [${y}], Start Date [${startDate}], End Date [${endDate}], Mode Filters [${modeFilters}], Transaction-For Filters [${transactionForFilters}], Recorded-By Filters [${recordedByFilters}], Property Filters [${propFilters}], Account Filters [${accountFilters}], Location Filters [${locationFilters}], Settlement Filters [${settlementFilters}]`
    );

    const month = Number(m) || 0;
    const year = Number(y) || 0;

    if (!month || month > 12 || !year) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Page Number [${pageNum}], Search Val [${s}], Start Date [${startDate}], End Date [${endDate}], Not a Valid Month`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let date = moment()
      .month(month - 1)
      .year(year)
      .format("YYYY-MM-DD");

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    let { isFinanceAdmin } = await isUserFinanceAdmin(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner || isFinanceAdmin) {
      log.info(
        `[${C}], [${F}], ${isFinanceAdmin ? `Finance Admin Id [${req.id}]` : isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Number [${pageNum}], Search Val [${s}], ${isFinanceAdmin ? `Finance Admin Requesting` : isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      propList = await propertyDB.getPropIdsByClientId({
        clientId,
        status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
      });

      const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Start Date [${startDate}], End Date [${endDate}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (s && s !== "undefined" && s !== "null" && s !== " ") {
        const { transactionForValue } = getMatchedTransactionFor(s.toString());

        transactions = await transactionDB.getSearchResultByClientId({
          clientId,
          pageNum: Number(pageNum),
          limit,
          searchVal: s,
          transactionForValue,
        });
      } else if (propId) {
        if (startDate && endDate) {
          transactions = await transactionDB.getByPropIdandDateRangeAndFilterForApp({
            propId,
            startDate,
            endDate,
            limit,
            pageNum: Number(pageNum),
            mode: modeFilters ? modeFilters : null,
            transactionFor: transactionForFilters ? transactionForFilters : null,
            settlement: settlementFilters ? settlementFilters : null,
            recordedBy: recordedByFilters ? recordedByFilters : null,
            accounts: accountFilters ? accountFilters : null
          });
          // transactions = await transactionDB.getByPropIdandDateRangeForApp({
          //   propId,
          //   startDate,
          //   endDate,
          //   limit,
          //   pageNum: Number(pageNum),
          // });
        } else {
          transactions = await transactionDB.getByPropId({
            propId,
            pageNum: Number(pageNum),
            limit,
            month: date,
          });
        }
        // curMonthTransAmount = await transactionDB.getTotalByPropId({
        //   propId,
        //   month: date,
        // });
        // prevMonthTransAmount = await transactionDB.getTotalByPropId({
        //   propId,
        //   month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
        // });
        // if (prevMonthTransAmount) {
        //   diff =
        //     ((curMonthTransAmount - prevMonthTransAmount) /
        //       prevMonthTransAmount) *
        //     100;
        // } else {
        //   if (!curMonthTransAmount) diff = 0;
        //   else diff = 100;
        // }
      } else {
        if (startDate && endDate) {
          //log.info('I am here');
          transactions = await transactionDB.getByClientIdandDateRangeAndFilter({
            clientId,
            startDate,
            endDate,
            limit,
            pageNum: Number(pageNum),
            mode: modeFilters ? modeFilters : null,
            transactionFor: transactionForFilters ? transactionForFilters : null,
            recordedBy: recordedByFilters ? recordedByFilters : null,
            propId: propFilters ? propFilters : null,
            accounts: accountFilters ? accountFilters : null,
            settlement: settlementFilters ? settlementFilters : null,
            locations: locationFilters ? locationFilters : null,
          });
          // transactions = await transactionDB.getByClientIdandDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          //   limit,
          //   pageNum: Number(pageNum),
          // });
        } else {
          transactions = await transactionDB.getByClientId({
            clientId,
            pageNum: Number(pageNum),
            limit,
            month: date,
          });
        }

        curMonthTransAmount = await transactionDB.getTotalByClientId({
          clientId,
          month: date,
        });

        // prevMonthTransAmount = await transactionDB.getTotalByClientId({
        //   clientId,
        //   month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
        // });
        // if (prevMonthTransAmount) {
        //   diff =
        //     ((curMonthTransAmount - prevMonthTransAmount) /
        //       prevMonthTransAmount) *
        //     100;
        // } else {
        //   if (!curMonthTransAmount) diff = 0;
        //   else diff = 100;
        // }
      }

      if (startDate && endDate) {
        if (propId) {
          // totalCollection = await transactionDB.getTotalByDateRangeAndPropId({
          //   clientId,
          //   propId,
          //   startDate,
          //   endDate,
          // });
          totalCollection = await transactionDB.getTotalByPropIdandDateRangeAndFilterForApp({
            propId,
            startDate,
            endDate,
            mode: modeFilters ? modeFilters : null,
            transactionFor: transactionForFilters ? transactionForFilters : null,
            recordedBy: recordedByFilters ? recordedByFilters : null,
            accounts: accountFilters ? accountFilters : null
          });
        } else {
          // totalCollection = await transactionDB.getTotalByDateRange({
          //   clientId,
          //   startDate,
          //   endDate,
          // });
          totalCollection = await transactionDB.getTotalByClientIdandDateRangeAndFilter({
            clientId,
            startDate,
            endDate,
            mode: modeFilters ? modeFilters : null,
            transactionFor: transactionForFilters ? transactionForFilters : null,
            recordedBy: recordedByFilters ? recordedByFilters : null,
            propId: propFilters ? propFilters : null,
            accounts: accountFilters ? accountFilters : null,
            locations: locationFilters ? locationFilters : null,
          });
        }
      } else {
        totalCollection = curMonthTransAmount;
      }
    } else {
      const staffId = req.id;
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Staff Id [${staffId}], Staff Requested....`
      );

      clientId = staff.clientId;

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId,
      });

      propList = staffLinkedProps;

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        if (s && s !== "undefined" && s !== "null" && s !== " ") {
          const { transactionForValue } = getMatchedTransactionFor(
            s.toString()
          );

          transactions = await transactionDB.getSearchResultForStaff({
            clientId,
            pageNum: Number(pageNum),
            limit,
            searchVal: s,
            transactionForValue,
            propertiesIds,
          });
        } else if (propId) {
          if (startDate && endDate) {
            transactions = await transactionDB.getByPropIdandDateRangeAndFilterForApp({
              clientId,
              pageNum: Number(pageNum),
              limit,
              startDate,
              endDate,
              propId,
              mode: modeFilters ? modeFilters : null,
              transactionFor: transactionForFilters ? transactionForFilters : null,
              settlement: settlementFilters ? settlementFilters : null,
              recordedBy: recordedByFilters ? recordedByFilters : null,
              accounts: accountFilters ? accountFilters : null
            });
            // transactions = await transactionDB.getForStaffByDateRange({
            //   clientId,
            //   pageNum: Number(pageNum),
            //   limit,
            //   startDate,
            //   endDate,
            //   propertiesIds:propId,
            // });
          } else {
            transactions = await transactionDB.getForStaff({
              clientId,
              pageNum: Number(pageNum),
              limit,
              propertiesIds: propId,
              month: date,
            });
          }
          // curMonthTransAmount = await transactionDB.getTotalByClientIdForStaff({
          //   clientId,
          //   month: date,
          //   propertiesIds: propId,
          // });

          // prevMonthTransAmount = await transactionDB.getTotalByClientIdForStaff(
          //   {
          //     clientId,
          //     month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
          //     propertiesIds: propId,
          //   }
          // );
          // if (prevMonthTransAmount) {
          //   diff =
          //     ((curMonthTransAmount - prevMonthTransAmount) /
          //       prevMonthTransAmount) *
          //     100;
          // } else {
          //   if (!curMonthTransAmount) diff = 0;
          //   else diff = 100;
          // }
        } else {
          if (startDate && endDate) {
            transactions = await transactionDB.getForStaffByDateRangeAndFilter({
              clientId,
              pageNum: Number(pageNum),
              limit,
              startDate,
              endDate,
              propertiesIds,
              mode: modeFilters ? modeFilters : null,
              transactionFor: transactionForFilters ? transactionForFilters : null,
              recordedBy: recordedByFilters ? recordedByFilters : null,
              propId: propFilters ? propFilters : null,
              accounts: accountFilters ? accountFilters : null,
              settlement: settlementFilters ? settlementFilters : null,
              locations: locationFilters ? locationFilters : null,
            });
            // transactions = await transactionDB.getForStaffByDateRange({
            //   clientId,
            //   pageNum: Number(pageNum),
            //   limit,
            //   startDate,
            //   endDate,
            //   propertiesIds,
            // });
          } else {
            transactions = await transactionDB.getForStaff({
              clientId,
              pageNum: Number(pageNum),
              limit,
              propertiesIds,
              month: date,
            });
          }
          curMonthTransAmount = await transactionDB.getTotalByClientIdForStaff({
            clientId,
            month: date,
            propertiesIds,
          });

          // prevMonthTransAmount = await transactionDB.getTotalByClientIdForStaff(
          //   {
          //     clientId,
          //     month: moment(date).subtract(1, "month").format("YYYY-MM-DD"),
          //     propertiesIds,
          //   }
          // );
          // if (prevMonthTransAmount) {
          //   diff =
          //     ((curMonthTransAmount - prevMonthTransAmount) /
          //       prevMonthTransAmount) *
          //     100;
          // } else {
          //   if (!curMonthTransAmount) diff = 0;
          //   else diff = 100;
          // }
        }

        if (startDate && endDate) {
          if (propId) {
            totalCollection = await transactionDB.getTotalByPropIdandDateRangeAndFilterForApp({
              propId,
              startDate,
              endDate,
              mode: modeFilters ? modeFilters : null,
              transactionFor: transactionForFilters ? transactionForFilters : null,
              recordedBy: recordedByFilters ? recordedByFilters : null,
            });
          } else {
            totalCollection = await transactionDB.getTotalByClientIdandDateRangeAndFilterForStaff({
              clientId,
              startDate,
              endDate,
              propertiesIds,
              mode: modeFilters ? modeFilters : null,
              transactionFor: transactionForFilters ? transactionForFilters : null,
              recordedBy: recordedByFilters ? recordedByFilters : null,
              propId: propFilters ? propFilters : null,
            });
          }
        } else {
          totalCollection = curMonthTransAmount;
        }
      }
    }

    //const accountSummary: Record<string,{ bankName: string; accountNum: string; totalAmount: number;}> = {};
    let accountSummary: {
      bankName: string;
      accountNum: string;
      holderName: string;
      totalAmount: number;
    }[] = [];

    let isBankOnline = false;
    if (isKipinnAppFilterApplied) {
      let banks = await bankDB.getActivePaymentAccountsByClientId({ clientId });
      if (banks && banks.length > 0) {
        for (const bank of banks) {
          let totalSettled = await transactionDB.getOnlineAccountTransactions({
            clientId,
            startDate,
            endDate,
            paymentAccountNo: bank?.accountNum,
            propIds: propFilters ? propFilters : null,
          });
          isBankOnline = true;
          // if (!accountSummary[bank?.accountNum]) {
          //       accountSummary[bank?.accountNum] = {
          //         bankName: bank?.bankName,
          //         accountNum: bank?.accountNum,
          //         totalAmount: totalSettled?.amount || 0,
          //       };
          //     }
          accountSummary.push({
            bankName: bank?.bankName,
            accountNum: bank?.accountNum,
            holderName: bank?.holderName,
            totalAmount: totalSettled?.amount || 0,
          });
        }
      }
    }
    if (transactions) {
      for (let trans of transactions) {
        if (isBankOnline) {
          if (trans?.recordedBy === 'Kipinn App') {
            const acc = accountSummary.find(
              (a) => a.accountNum === trans.paymentAccountNo
            );
            trans.bankName = acc?.bankName || "";
            trans.paymentAccountName = acc?.holderName || "";
          } else {
            trans.bankName = "";
            trans.paymentAccountNo = "";
            trans.paymentAccountName = "";
          }
        } else {
          trans.bankName = "";
          trans.paymentAccountNo = "";
          trans.paymentAccountName = "";
        }
        trans.desc = `for the month of ${moment(trans.dueDate).format("MMM")}`;
        let canRestore = true;

        const ledgerRecordsByTxnId = await ledgerDB.getByTransactionId({
          transactionId: Number(trans.id),
        });

        let occupancy = await occupancyDB.getByTenantIdAndClientId({
          tenantId: trans.tenantId,
          clientId,
        });

        let securityUsed = false;

        if (occupancy) {
          securityUsed = await ledgerDB.getUsedSecurityAmtByTenantIdAndClientId({
            tenantId: trans.tenantId,
            clientId: clientId,
            createdAt: occupancy.createdAt,
          });
        }

        trans.isEvicted = false;
        if (!occupancy) {
          canRestore = false;
          trans.isEvicted = true;
        } else if (securityUsed && trans.transactionFor === CONSTANTS.TRANSACTION_FOR.SECURITY) {
          //Security used to adjust another due
          canRestore = false;
        } else if (!ledgerRecordsByTxnId || ledgerRecordsByTxnId.length === 0) {
          // log.info(
          //   `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], No Ledger Records Found`
          // );
          canRestore = false;
        } else if (ledgerRecordsByTxnId && ledgerRecordsByTxnId.length > 1) {
          //Multiple Dues Check using transactionId
          // log.info(
          //   `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], Multiple Ledger Records Found, Cannot Restore Transaction`
          // );
          canRestore = false;
        } else if (Math.abs(ledgerRecordsByTxnId[0].amount) !== Math.abs(trans.amount)) {
          //Excess Amount Check using transaction amount and ledger record amount
          // log.info(
          //   `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], Transaction has excess amount, Cannot Restore Transaction`
          // );
          canRestore = false;
        } else if (ledgerRecordsByTxnId[0].type === CONSTANTS.DUES_TYPES.SETTLEMENT) {
          // log.info(
          //   `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], Settlement Transaction, Cannot Restore Transaction`
          // );
          canRestore = false;
        }
        // else if(ledgerRecordsByTxnId[0].balance !== 0) {
        //   // log.info(
        //   //   `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${trans.id}], Due Has Been Partially Paid, Cannot Restore Transaction`
        //   // );
        //   canRestore = false;
        // }

        trans.canRestore = canRestore;
      }
    }

    // if (Number(clientId) === 5) log.info(`Transactions [${JSON.stringify(transactions)}]`);

    staffs = await staffDB.getActiveByClientId({ clientId });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Page Number [${pageNum}], Search Val [${s}], Start Date [${startDate}], End Date [${endDate}], Mode Filters [${modeFilters}], Transaction-For Filters [${transactionForFilters}], Recorded-By Filters [${recordedByFilters}], Property Filters [${propFilters}], Transactions sent successfully`
    );
    //log.info(`${JSON.stringify(accountSummary)}`);
    return res.status(200).json({
      msg: "Transactions sent successfully",
      data: {
        transactions: transactions || [],
        // curMonthTransAmount: Number(curMonthTransAmount) || 0,
        // prevMonthTransAmount: Number(prevMonthTransAmount) || 0,
        // diff: Number(diff.toFixed(2)) || 0,
        totalCollection: totalCollection || 0,
        staffs: staffs || [],
        propList: propList || [],
        accountSummary: isKipinnAppFilterApplied ? accountSummary : null,
      },
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.SettlePayment = async (req: Request, res: Response) => {
  const C = "Transaction Controller";
  const F = "SettlePayment";

  try {
    log.info(`[${C}], [${F}], Initiating Split Payment API`);

    const childKey = "wOJXei";
    const childMid = "12993258";
    const key = "7TJgi7";
    const paymentId = "23046379339";
    const amount = 100;
    const charges = 10;

    const splitObj = {
      [childKey]: {
        aggregatorSubTxnId: `${paymentId}_${childKey}`,
        aggregatorSubAmt: amount,
      },
      [key]: {
        aggregatorSubTxnId: `${paymentId}_${key}`,
        aggregatorSubAmt: charges,
      },
    };

    const splitResponse = await splitPayment("absolute", paymentId, splitObj);

    log.info(
      `[${C}], [${F}], Split Payment API, Transaction ID [${paymentId}], PayU MID [${childMid}], Payu Key [${childKey}], Response [${JSON.stringify(
        splitResponse
      )}]`
    );

    if (
      splitResponse?.splitStatus &&
      splitResponse?.splitStatus === "success"
    ) {
      log.info(`[${C}], [${F}], Initiating Release Payment API`);

      // const releaseResponse = await releasePayment(paymentId, childMid);

      // log.info(
      //   `[${C}], [${F}], Release Payment API, Transaction ID [${paymentId}], PayU MID [${childMid}], Payu Key [${childKey}], Response [${JSON.stringify(
      //     releaseResponse
      //   )}]`
      // );
    } else {
      log.info(
        `[${C}], [${F}], Split Payment API ERROR, Transaction ID [${paymentId}], PayU MID [${childMid}], Payu Key [${childKey}], Response [${JSON.stringify(
          splitResponse
        )}]`
      );
    }

    return res.status(200).json({
      msg: "Testing",
      isSUccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};


transactions.PaymentLinkCallBack = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Transaction Controller";
  const F = "PaymentLinkCallBack";

  try {
    const {
      mihpayid,
      paymentMode,
      status,
      key,
      amount,
      udf1,
      udf2,
      udf3,
      udf4,
      udf5,
      payment_source,
      paymentId,
      addTransLog = true,
      mode,
    } = req.body;

    let requestData = JSON.stringify(req.body);
    log.info(`[${C}], [${F}], Request body [${requestData}]`);

    log.info(
      `[${C}], [${F}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], Add Transaction Log [${addTransLog}], Callback API Request`
    );

    if (status === "failed" || status === "failure") {
      log.info(`
        [${C}], [${F}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], Payment Failed`);
      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }

    if (!udf3 || udf3.trim() === "") {
      log.info(`
        [${C}], [${F}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], Unknown payment source exiting`);
      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }
    const transSource = udf5.split("_")[0];
    if (transSource === "BOND") {
      log.info(`[${C}], [${F}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}],  Payment for rental bond`);
      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }
    const tenantId = Number(udf3.split("_")[1]);
    const isEvicted = String(udf3.split("_")[2]).toLowerCase() === "true";
    const propGid = udf3.split("_")[0];
    const property = await propertyDB.getByGId({
      gId: propGid,
    });
    let tenantDetail = await occupancyDB.getByTenantIdAndClientId({
      clientId: property.clientId,
      tenantId: tenantId,
    });

    if (isEvicted) {
      tenantDetail = await moveOutDB.getByTenantIdAndClientId({
        clientId: property.clientId,
        tenantId: tenantId,
      });
    }

    if (!tenantDetail) {
      log.info(
        `[${C}], [${F}], Client Id [${property?.clientId}], Tenant Id [${tenantId}], No Occupancy Found, Blocking Request`
      );

      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }

    const clientId = tenantDetail?.clientId;
    const roomId = tenantDetail?.roomId;
    const propId = tenantDetail?.propId;
    let transMode = 0;
    if (paymentId) {
      if (paymentMode === 'CC' || paymentMode === 'DC') {
        transMode = CONSTANTS.TRANSACTION_MODES.CARD;
      } else if (paymentMode === 'NB') {
        transMode = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
      } else {
        transMode = CONSTANTS.TRANSACTION_MODES.UPI;
      }

      log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], ${addTransLog ? 'Payment Is Through Web' : 'Payment marked via backend'}`);
      if (addTransLog === true) {
        if (status === "Success" || status === "success") {
          await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: paymentId, paymentMode: transMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body })
        }
      }
    } else if (payment_source === "apiIntInvoice") {
      //PayU Generated Link
      log.info(`
        [${C}], [${F}], Tenant Id [${tenantId}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], ${addTransLog ? 'Payment Is Through Link' : 'Payment marked via backend'}`);
      if (addTransLog === true) {
        if (status === "Success" || status === "success") {
          await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: mihpayid, paymentMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body })
        }
      }
    } else {
      if (mode === 'CC' || mode === 'DC') {
        transMode = CONSTANTS.TRANSACTION_MODES.CARD;
      } else if (mode === 'NB') {
        transMode = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
      } else {
        transMode = CONSTANTS.TRANSACTION_MODES.UPI;
      }
      log.info(`
        [${C}], [${F}], Tenant Id [${tenantId}], Mihpayid [${mihpayid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], Suspicious Source Blocking It`);
      if (addTransLog === true) {
        if (status === "Success" || status === "success") {
          await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: mihpayid, paymentMode: transMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body })
        }
      }

      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }
    const ledgerReferenceId = udf5 ? [udf5.split("_")[0]] : [];
    let isBookingPayment = false;
    if (transSource === "BOOKINGLINK") {
      isBookingPayment = true;
    }
    const charges = udf5
      ? udf5.split("_")[1]
        ? Number(udf5.split("_")[1])
        : 0
      : 0;
    const gstCharges = udf5
      ? udf5.split("_")[2]
        ? Number(udf5.split("_")[2])
        : 0
      : 0;

    log.info(`[${C}], [${F}], Tenant Id [${tenantId}]`);

    const token = jwt.sign(
      {
        id: tenantId,
        type: CONSTANTS.USER_TYPE.TENANT,
        clientId: clientId,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    let dueIds: any = [];
    if (udf4) {
      dueIds = udf4
        .split(",")
        .map((id: any) => Number(id))
        .filter((id: any) => !isNaN(id));
    }
    const payload = {
      amount: (amount - charges - gstCharges).toString(),
      dueIds: dueIds ? dueIds : [],
      mode: paymentMode
        ? paymentMode.toLowerCase() === "upi"
          ? 2
          : paymentMode.toLowerCase() === "nb"
            ? 3
            : 4
        : 2,
      status: 2,
      key: key || "xyz123",
      paymentId:
        mihpayid === undefined ? paymentId.toString() : mihpayid.toString(),
      // ledgerReferenceIds: udf5 ? [udf5.toString()] : [],
      ledgerReferenceIds: ledgerReferenceId || [],
      charges,
      gstAmount: gstCharges,
      isBookingPayment,
    };

    if (isEvicted) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Evicted [${isEvicted}], Tenant Is MoveOut Calling AddMoveOut Fucntion`
      );
      const response = await axios.post(
        process.env.PAYMENT_LINK_RECORDING_MOVEOUT_URL!,
        payload,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json",
          },
        }
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Evicted [${isEvicted}], Tenant Is Active Calling Add Fucntion`
      );
      const response = await axios.post(
        process.env.PAYMENT_LINK_RECORDING_URL!,
        payload,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json",
          },
        }
      );
    }

    log.info(
      `[${C}], [${F}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Done Through Payment Link`
    );

    return res.status(200).json({
      msg: "Payment Link Callback API Success",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.RecordOnlineTransactionManually = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "RecordOnlineTransactionManually";
  try {
    const {
      mihpayid,
      paymentMode,
      status,
      key,
      amount,
      udf3,
      udf4,
      udf5,
      payment_source,
      paymentId,
    } = req.body;
    log.info(
      `[${C}], [${F}], Mihpayid [${mihpayid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], Recording unmarked payments`
    );
    return transactions.PaymentLinkCallBack(req, res);
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};


transactions.EaseBuzzCallback = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "EaseBuzzCallback";
  try {
    const {
      easepayid,
      status,
      key,
      amount,
      udf1,
      udf2,
      udf3,
      udf4,
      udf5,
      payment_source,
      txnid,
      addTransLog = true,
      mode,
      bank_ref_num: bankRefNum,
    } = req.body;

    let requestData = JSON.stringify(req.body);
    log.info(`[${C}], [${F}], Request body [${requestData}]`);

    log.info(
      `[${C}], [${F}], Easepayid [${easepayid}], Txn Id [${txnid}], Bank Ref Num [${bankRefNum}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Add Transaction Log [${addTransLog}], Callback Request`
    );

    if (status === "failed" || status === "failure" || status === "userCancelled") {
      log.info(`
        [${C}], [${F}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Failed`);
      return res.status(200).json({
        msg: "Success!",
        isSuccess: true,
      });
    }
    if (!udf3 || udf3.trim() === "") {
      log.info(`
        [${C}], [${F}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Unknown payment source exiting`);
      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }
    const transSource = udf5.split("_")[0];
    if (transSource === "LANDLORDPAYMENT") {
      log.info(`
        [${C}], [${F}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Bank Ref Num [${bankRefNum}], Status [${status}], Key [${key}], Amount [${amount}], UDF3(propertyGid_PayerId(clientId)) [${udf3}], UDF4(Remark) [${udf4}], UDF5(TRANSDESC_Rent/Security Type) [${udf5}], Payment Source [${payment_source}], Landlord Payment Recieved`);
      const landlordId = Number(udf3.split("_")[1]);
      const propGid = udf3.split("_")[0];
      const property = await propertyDB.getByGId({
        gId: propGid,
      });

      const token = jwt.sign(
        {
          id: property.clientId,
          type: CONSTANTS.USER_TYPE.CLIENT,
          platform: CONSTANTS.TENANT_DEVICE_TYPE.WEB
        },
        process.env.TOKEN_SECRET!,
        { expiresIn: process.env.TOKEN_EXPIRY }
      );
      const payload = {
        amount,
        gId: txnid,
        propId: property.id,
        mode: mode
          ? mode.toLowerCase() === "upi"
            ? 2
            : mode.toLowerCase() === "nb"
              ? 3
              : 4
          : 2,
        status: 2,
        key: key || "ease123",
        landlordId: landlordId,
        gateway: CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ,
        remark: udf4,
        bankRefNum,
        type: Number(udf5.split("_")[1]),
        expenseId: Number(udf5.split("_")[2]),
      };

      const response = await axios.post(
        process.env.LANDLORD_PAYMENT_LINK_RECORDING_URL!,
        payload,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json",
          },
        }
      );

      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }
    const tenantId = Number(udf3.split("_")[1]);
    const isEvicted = String(udf3.split("_")[2]).toLowerCase() === "true";
    const propGid = udf3.split("_")[0];
    const property = await propertyDB.getByGId({
      gId: propGid,
    });
    let tenantDetail = await occupancyDB.getByTenantIdAndClientId({
      clientId: property.clientId,
      tenantId: tenantId,
    });

    if (isEvicted) {
      tenantDetail = await moveOutDB.getByTenantIdAndClientId({
        clientId: property.clientId,
        tenantId: tenantId,
      });
    }

    if (!tenantDetail) {
      log.info(
        `[${C}], [${F}], Client Id [${property?.clientId}], Tenant Id [${tenantId}], No Occupancy Found, Blocking Request`
      );

      return res.status(200).json({
        msg: "Success",
        isSuccess: true,
      });
    }

    const clientId = tenantDetail?.clientId;
    const roomId = tenantDetail?.roomId;
    const propId = tenantDetail?.propId;
    let transMode = 0;
    let isBookingPayment = false;
    if (transSource === "PAYVIALINK" || transSource === "BOOKINGLINK") {
      if (transSource === "BOOKINGLINK") {
        isBookingPayment = true;
      }
      if (mode === 'CC' || mode === 'DC') {
        transMode = CONSTANTS.TRANSACTION_MODES.CARD;
      } else if (mode === 'NB') {
        transMode = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
      } else {
        transMode = CONSTANTS.TRANSACTION_MODES.UPI;
      }

      log.info(`
        [${C}], [${F}], Tenant Id [${tenantId}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], ${addTransLog ? 'Payment Is Through Web' : 'Payment marked via backend'}`);
      if (addTransLog === true) {
        if (status === "Success" || status === "success") {
          await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: txnid, paymentMode: transMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body, gateway: CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ })
        }
      }
    } else if (udf3 == "") {
      log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment not from link and app`);
      return res.status(200).json({
        msg: "Success!",
        isSuccess: true,
      });
    }
    else {
      if (mode === 'CC' || mode === 'DC') {
        transMode = CONSTANTS.TRANSACTION_MODES.CARD;
      } else if (mode === 'NB') {
        transMode = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
      } else {
        transMode = CONSTANTS.TRANSACTION_MODES.UPI;
      }
      log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Suspicious Source Blocking It`);
      if (addTransLog === true) {
        if (status === "Success" || status === "success") {
          await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: txnid, paymentMode: transMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body, gateway: CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ })
        }
      }

      return res.status(200).json({
        msg: "Success!",
        isSuccess: true,
      });
    }
    const ledgerReferenceId = udf5 ? [udf5.split("_")[0]] : [];
    const charges = udf5
      ? udf5.split("_")[1]
        ? Number(udf5.split("_")[1])
        : 0
      : 0;
    const gstCharges = udf5
      ? udf5.split("_")[2]
        ? Number(udf5.split("_")[2])
        : 0
      : 0;

    log.info(`[${C}], [${F}], Tenant Id [${tenantId}]`);

    const token = jwt.sign(
      {
        id: tenantId,
        type: CONSTANTS.USER_TYPE.TENANT,
        clientId: clientId,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    let dueIds: any = [];
    if (udf4) {
      dueIds = udf4
        .split(",")
        .map((id: any) => Number(id))
        .filter((id: any) => !isNaN(id));
    }
    const payload = {
      amount: (amount - charges - gstCharges).toString(),
      dueIds: dueIds ? dueIds : [],
      mode: mode
        ? mode.toLowerCase() === "upi"
          ? 2
          : mode.toLowerCase() === "nb"
            ? 3
            : 4
        : 2,
      status: 2,
      key: key || "xyz123",
      paymentId: txnid.toString(),
      // ledgerReferenceIds: udf5 ? [udf5.toString()] : [],
      ledgerReferenceIds: ledgerReferenceId || [],
      charges,
      gstAmount: gstCharges,
      isBookingPayment,
    };

    if (isEvicted) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Evicted [${isEvicted}], Tenant Is MoveOut Calling AddMoveOut Fucntion`
      );
      const response = await axios.post(
        process.env.PAYMENT_LINK_RECORDING_MOVEOUT_URL!,
        payload,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json",
          },
        }
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Evicted [${isEvicted}], Tenant Is Active Calling Add Fucntion`
      );
      const response = await axios.post(
        process.env.PAYMENT_LINK_RECORDING_URL!,
        payload,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            "Content-Type": "application/json",
          },
        }
      );
    }


    log.info(
      `[${C}], [${F}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Done Through Payment Link`
    );

    return res.status(200).json({
      msg: "Payment Link Callback API Success",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }

};


transactions.RecordOnlineEaseBuzzTransManually = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "RecordOnlineEaseBuzzTransManually";
  try {
    const {
      easepayid,
      paymentMode,
      status,
      key,
      amount,
      udf3,
      udf4,
      udf5,
      txnid,
      payment_source,
      paymentId,
    } = req.body;
    log.info(
      `[${C}], [${F}], Easepayid [${easepayid}], Txn Id [${txnid}], Mode [${paymentMode}], Status [${status}], Key [${key}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Id [${paymentId}], Recording unmarked payments`
    );
    return transactions.EaseBuzzCallback(req, res);
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.CashfreeCallback = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "CashfreeCallback";
  try {
    const {
      data,
      addTransLog = true
    } = req.body;

    let udf1: any;
    let udf2: any;
    let udf3: any;
    let udf4: any;
    let udf5: any;

    let requestData = JSON.stringify(req.body);
    log.info(`[${C}], [${F}], Request body [${requestData}]`);
    let orderId = data?.order?.order_id;
    let status = data?.payment?.payment_status;
    let txnid = data?.payment?.cf_payment_id;
    let amount = data?.payment?.payment_amount;
    let bankRefNum = data?.payment?.bank_reference;
    let mode = data?.payment?.payment_group;
    status = status.toLowerCase();
    let payment_source = "";
    if (data?.order?.order_tags) {
      udf1 = data?.order?.order_tags.udf1;
      udf2 = data?.order?.order_tags.udf2;
      udf3 = data?.order?.order_tags.udf3;
      udf4 = data?.order?.order_tags.udf4;
      udf5 = data?.order?.order_tags.udf5;
      payment_source = "APP";
    } else if (data?.payment?.link_notes) {
      udf1 = data?.payment?.link_notes.udf1;
      udf2 = data?.payment?.link_notes.udf2;
      udf3 = data?.payment?.link_notes.udf3;
      udf4 = data?.payment?.link_notes.udf4;
      udf5 = data?.payment?.link_notes.udf5;
      payment_source = "link";
    } else {
      return res.status(200).json({
        msg: "Success!",
        isSuccess: true,
      });
    }

    log.info(
      `[${C}], [${F}], Order Id  [${orderId}], Payment Id [${txnid}], Bank Ref Num [${bankRefNum}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Add Transaction Log [${addTransLog}], Callback Request`
    );

    if (status === "success") {
      if (!udf3 || udf3.trim() === "") {
        log.info(`
          [${C}], [${F}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Unknown payment source exiting`);
        return res.status(200).json({
          msg: "Success",
          isSuccess: true,
        });
      }
      const transSource = udf5.split("_")[0];
      if (transSource === "LANDLORDPAYMENT") {
        log.info(`
          [${C}], [${F}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Bank Ref Num [${bankRefNum}], Status [${status}], Amount [${amount}], UDF3(propertyGid_PayerId(clientId)) [${udf3}], UDF4(Remark) [${udf4}], UDF5(TRANSDESC_Rent/Security Type) [${udf5}], Payment Source [${payment_source}], Landlord Payment Recieved`);
        const landlordId = Number(udf3.split("_")[1]);
        const propGid = udf3.split("_")[0];
        const property = await propertyDB.getByGId({
          gId: propGid,
        });

        const token = jwt.sign(
          {
            id: property.clientId,
            type: CONSTANTS.USER_TYPE.CLIENT,
            platform: CONSTANTS.TENANT_DEVICE_TYPE.WEB
          },
          process.env.TOKEN_SECRET!,
          { expiresIn: process.env.TOKEN_EXPIRY }
        );
        const payload = {
          amount,
          gId: orderId,
          propId: property.id,
          mode: mode
            ? mode.toLowerCase() === "upi"
              ? 2
              : mode.toLowerCase() === "net_banking"
                ? 3
                : 4
            : 2,
          status: 2,
          landlordId: landlordId,
          gateway: CONSTANTS.PAYMENT_GATEWAY.CASHFREE,
          remark: udf4,
          bankRefNum,
          key: "",
          type: Number(udf5.split("_")[1]),
          expenseId: Number(udf5.split("_")[2]),
        };

        const response = await axios.post(
          process.env.LANDLORD_PAYMENT_LINK_RECORDING_URL!,
          payload,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              "Content-Type": "application/json",
            },
          }
        );

        return res.status(200).json({
          msg: "Success",
          isSuccess: true,
        });
      }
      const tenantId = Number(udf3.split("_")[1]);
      const isEvicted = String(udf3.split("_")[2]).toLowerCase() === "true";
      const propGid = udf3.split("_")[0];
      const property = await propertyDB.getByGId({
        gId: propGid,
      });
      let tenantDetail = await occupancyDB.getByTenantIdAndClientId({
        clientId: property.clientId,
        tenantId: tenantId,
      });

      if (isEvicted) {
        tenantDetail = await moveOutDB.getByTenantIdAndClientId({
          clientId: property.clientId,
          tenantId: tenantId,
        });
      }

      if (!tenantDetail) {
        log.info(
          `[${C}], [${F}], Client Id [${property?.clientId}], Tenant Id [${tenantId}], No Occupancy Found, Blocking Request`
        );

        return res.status(200).json({
          msg: "Success",
          isSuccess: true,
        });
      }

      const clientId = tenantDetail?.clientId;
      const roomId = tenantDetail?.roomId;
      const propId = tenantDetail?.propId;
      let transMode = 0;
      let isBookingPayment = false;
      if (transSource === "PAYVIALINK" || transSource === "BOOKINGLINK" || transSource === "PAYVIAAPPLINK") {
        if (transSource === "BOOKINGLINK") {
          isBookingPayment = true;
        }
        if (mode === 'CC' || mode === 'DC') {
          transMode = CONSTANTS.TRANSACTION_MODES.CARD;
        } else if (mode === 'net_banking') {
          transMode = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
        } else {
          transMode = CONSTANTS.TRANSACTION_MODES.UPI;
        }

        log.info(`
          [${C}], [${F}], Tenant Id [${tenantId}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], ${addTransLog ? 'Payment Is Through Web' : 'Payment marked via backend'}`);
        if (addTransLog === true) {
          if (status === "Success" || status === "success") {
            await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: orderId, paymentMode: transMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body, gateway: CONSTANTS.PAYMENT_GATEWAY.CASHFREE })
          }
        }
      } else if (udf3 == "") {
        log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment not from link and app`);
        return res.status(200).json({
          msg: "Success!",
          isSuccess: true,
        });
      }
      else {
        if (mode === 'CC' || mode === 'DC') {
          transMode = CONSTANTS.TRANSACTION_MODES.CARD;
        } else if (mode === 'net_banking') {
          transMode = CONSTANTS.TRANSACTION_MODES.NET_BANKING;
        } else {
          transMode = CONSTANTS.TRANSACTION_MODES.UPI;
        }
        log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Suspicious Source Blocking It`);
        if (addTransLog === true) {
          if (status === "Success" || status === "success") {
            await transactionLogsDB.add({ tenantId, clientId, roomId, propId, amount, gId: orderId, paymentMode: transMode, udf1, udf2, udf3, udf4, udf5, requestData: req.body, gateway: CONSTANTS.PAYMENT_GATEWAY.CASHFREE })
          }
        }

        return res.status(200).json({
          msg: "Success!",
          isSuccess: true,
        });
      }
      const ledgerReferenceId = udf5 ? [udf5.split("_")[0]] : [];
      const charges = udf5
        ? udf5.split("_")[1]
          ? Number(udf5.split("_")[1])
          : 0
        : 0;
      const gstCharges = udf5
        ? udf5.split("_")[2]
          ? Number(udf5.split("_")[2])
          : 0
        : 0;

      log.info(`[${C}], [${F}], Tenant Id [${tenantId}]`);

      const token = jwt.sign(
        {
          id: tenantId,
          type: CONSTANTS.USER_TYPE.TENANT,
          clientId: clientId,
        },
        process.env.TOKEN_SECRET!,
        { expiresIn: process.env.TOKEN_EXPIRY }
      );

      let dueIds: any = [];
      if (udf4) {
        dueIds = udf4
          .split(",")
          .map((id: any) => Number(id))
          .filter((id: any) => !isNaN(id));
      }
      const payload = {
        amount: (amount - charges - gstCharges).toString(),
        dueIds: dueIds ? dueIds : [],
        mode: mode
          ? mode.toLowerCase() === "upi"
            ? 2
            : mode.toLowerCase() === "net_banking"
              ? 3
              : 4
          : 2,
        status: 2,
        paymentId: orderId.toString(),
        key: "",
        ledgerReferenceIds: ledgerReferenceId || [],
        charges,
        gstAmount: gstCharges,
        isBookingPayment,
      };

      if (isEvicted) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Evicted [${isEvicted}], Tenant Is MoveOut Calling AddMoveOut Fucntion`
        );
        const response = await axios.post(
          process.env.PAYMENT_LINK_RECORDING_MOVEOUT_URL!,
          payload,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              "Content-Type": "application/json",
            },
          }
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Is Evicted [${isEvicted}], Tenant Is Active Calling Add Fucntion`
        );
        const response = await axios.post(
          process.env.PAYMENT_LINK_RECORDING_URL!,
          payload,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              "Content-Type": "application/json",
            },
          }
        );
      }


      log.info(
        `[${C}], [${F}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Done Through Payment Link`
      );

      return res.status(200).json({
        msg: "Payment Link Callback API Success",
        isSuccess: true,
      });
    } else {
      log.info(`
        [${C}], [${F}], Order Id  [${orderId}], Payment Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propIdGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Payment Failed`);
      return res.status(200).json({
        msg: "Success!",
        isSuccess: true,
      });
    }

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }

};

transactions.RecordOnlineCashfreeTransManually = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "RecordOnlineCashfreeTransManually";
  try {
    const {
      data,
    } = req.body;

    let udf1: any;
    let udf2: any;
    let udf3: any;
    let udf4: any;
    let udf5: any;

    let requestData = JSON.stringify(req.body);
    log.info(`[${C}], [${F}], Request body [${requestData}]`);
    let orderId = data?.order?.order_id;
    let status = data?.payment?.payment_status;
    let txnid = data?.payment?.cf_payment_id;
    let amount = data?.payment?.payment_amount;
    let bankRefNum = data?.payment?.bank_reference;
    let mode = data?.payment?.payment_group;
    status = status.toLowerCase();
    let payment_source = "";
    if (data?.order?.order_tags) {
      udf1 = data?.order?.order_tags.udf1;
      udf2 = data?.order?.order_tags.udf2;
      udf3 = data?.order?.order_tags.udf3;
      udf4 = data?.order?.order_tags.udf4;
      udf5 = data?.order?.order_tags.udf5;
      payment_source = "APP";
    } else if (data?.payment?.link_notes) {
      udf1 = data?.payment?.link_notes.udf1;
      udf2 = data?.payment?.link_notes.udf2;
      udf3 = data?.payment?.link_notes.udf3;
      udf4 = data?.payment?.link_notes.udf4;
      udf5 = data?.payment?.link_notes.udf5;
      payment_source = "link";
    } else {
      return res.status(200).json({
        msg: "Success!",
        isSuccess: true,
      });
    }
    log.info(
      `[${C}], [${F}], OrderId [${orderId}], Txn Id [${txnid}], Mode [${mode}], Status [${status}], Amount [${amount}], udf3(propGid_tenantId) [${udf3}], udf4(Due Id) [${udf4}], udf5(Ledger Id) [${udf5}], Payment Source [${payment_source}], Recording unmarked payments`
    );
    return transactions.CashfreeCallback(req, res);
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.Restore = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "Restore";

  try {
    const { id } = req.body;

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}]`
    );

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Transaction Id [${id}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

      let isStaffAuthorized = await isPrivilegedStaff(
        staff.role,
        1,
      );

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && !isStaffAuthorized && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}] Transaction Id [${id}], Only Admin, Warden, Finance-Admin, Back Office Authorized....`
        );
        return res.status(401).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}] Transaction Id [${id}], Admin/Warden/Finance-Admin Requesting....`
        );
      }
    }

    if (!id || isNaN(Number(id))) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Invalid Transaction Id`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const transaction = await transactionDB.getById({ id: Number(id) });
    if (!transaction) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], No Transaction Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    let isEvicted = false;

    let occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: transaction.tenantId,
      clientId: clientId,
    });

    if (!occupancy) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId: transaction.tenantId,
        clientId: clientId,
      });
      isEvicted = true;
      if (!occupancy) {
        log.info(
          `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], No Occupancy Found`
        );
        return res.status(400).json({
          msg: "No occupancy found for this transaction",
          isSuccess: false,
        });
      }
    }

    const ledgerRecordsByTxnId = await ledgerDB.getByTransactionId({
      transactionId: Number(id),
    });

    log.info(`[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Ledger Records By Transaction Id [${JSON.stringify(ledgerRecordsByTxnId)}], Transaction [${JSON.stringify(transaction)}], Occupancy [${JSON.stringify(occupancy)}]`);

    if (!ledgerRecordsByTxnId || ledgerRecordsByTxnId.length === 0) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], No Ledger Records Found`
      );
      return res.status(400).json({
        msg: "No ledger records found for this transaction",
        isSuccess: false,
      });
    }

    //Multiple Dues Check using transactionId
    if (ledgerRecordsByTxnId && ledgerRecordsByTxnId.length > 1) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Multiple Ledger Records Found, Cannot Restore Transaction`
      );
      return res.status(400).json({
        msg: "Transactions with multiple dues cannot be restored",
        isSuccess: false,
      });
    }

    //Excess Amount / partial Check using transaction amount and ledger record amount
    if (Math.abs(ledgerRecordsByTxnId[0].amount) !== Math.abs(transaction.amount)) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Transaction has excess amount, Cannot Restore Transaction`
      );
      return res.status(400).json({
        msg: "Transaction has excess amount, cannot restore transaction",
        isSuccess: false,
      });
    }

    if (ledgerRecordsByTxnId[0].type === CONSTANTS.DUES_TYPES.SETTLEMENT) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Settlement Transaction, Cannot Restore Transaction`
      );
      return res.status(400).json({
        msg: "Settlement transaction cannot be restored",
        isSuccess: false,
      });
    }

    // if (ledgerRecordsByTxnId[0].balance !== 0) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Due Has Been Partially Paid, Cannot Restore Transaction`
    //   );
    //   return res.status(400).json({
    //     msg: "Due has been partially paid, cannot restore transaction",
    //     isSuccess: false,
    //   });
    // }

    const dueName = await getDueName(
      transaction.transactionFor,
    );

    if (!isEvicted) {

      const ledgerRecords = await ledgerDB.getDueByLedgerReferenceId({
        referenceId: ledgerRecordsByTxnId[0].referenceId,
      });

      if (ledgerRecordsByTxnId[0].balance !== 0) {
        const due = await duesDB.getByTenantIdAndLedgerReferenceId({
          tenantId: ledgerRecordsByTxnId[0].tenantId,
          ledgerReferenceId: ledgerRecordsByTxnId[0].referenceId,
        });

        if (!due) {
          await duesDB.addWithStartEndDateX({
            tenantId: transaction.tenantId,
            amount: Math.abs(ledgerRecords[0].amount),
            occupancyId: occupancy.id,
            roomId: transaction.roomId,
            propId: transaction.propId,
            clientId: clientId,
            rentStartDate: ledgerRecordsByTxnId[0].rentStartDate,
            rentEndDate: ledgerRecordsByTxnId[0].rentEndDate,
            dueDate: transaction.dueDate,
            type: transaction.transactionFor,
            balance: Math.abs(transaction.amount),
            ledgerReferenceId: ledgerRecordsByTxnId[0].referenceId,
            description: ledgerRecordsByTxnId[0].description,
            title: ledgerRecordsByTxnId[0].title ? ledgerRecordsByTxnId[0].title : dueName,
          });
        } else {
          await duesDB.updateBalance({
            id: due[0].id,
            balance: due[0].balance + Math.abs(ledgerRecordsByTxnId[0].amount),
          });
        }

        let i = 1;

        for (let record of ledgerRecords) {
          if (Number(ledgerRecordsByTxnId[0].id) < Number(record.id)) {
            await ledgerDB.updateBalanceById({
              id: record.id,
              balance: Number(record.balance) + Number(transaction.amount),
            });
          }
          if (i === ledgerRecords.length) {
            record.description = record.description.replace(/final/i, "").trim();
            await ledgerDB.updateDescription({
              id: record.id,
              description: `Partial ${record.description}`
            });
          }
          i += 1;
        }
      } else {
        await duesDB.addWithStartEndDateX({
          tenantId: transaction.tenantId,
          // amount: Math.abs(transaction.amount),
          amount: Math.abs(ledgerRecords[0].amount),
          occupancyId: occupancy.id,
          roomId: transaction.roomId,
          propId: transaction.propId,
          clientId: clientId,
          rentStartDate: ledgerRecordsByTxnId[0].rentStartDate,
          rentEndDate: ledgerRecordsByTxnId[0].rentEndDate,
          dueDate: transaction.dueDate,
          type: transaction.transactionFor,
          balance: Math.abs(transaction.amount),
          ledgerReferenceId: ledgerRecordsByTxnId[0].referenceId,
          description: ledgerRecordsByTxnId[0].description,
          title: ledgerRecordsByTxnId[0].title ? ledgerRecordsByTxnId[0].title : dueName,
        });
      }
    } else {
      if (ledgerRecordsByTxnId[0].balance !== 0) {
        const due = await moveOutDuesDB.getByTenantIdAndLedgerReferenceId({
          tenantId: ledgerRecordsByTxnId[0].tenantId,
          ledgerReferenceId: ledgerRecordsByTxnId[0].referenceId,
        });

        const ledgerRecords = await ledgerDB.getDueByLedgerReferenceId({
          referenceId: ledgerRecordsByTxnId[0].referenceId,
        });

        if (!due) {
          await moveOutDuesDB.add({
            tenantId: transaction.tenantId,
            amount: Math.abs(ledgerRecords[0].amount),
            moveOutId: occupancy.id,
            roomId: transaction.roomId,
            propId: transaction.propId,
            clientId: clientId,
            rentStartDate: ledgerRecordsByTxnId[0].rentStartDate,
            rentEndDate: ledgerRecordsByTxnId[0].rentEndDate,
            dueDate: transaction.dueDate,
            type: transaction.transactionFor,
            balance: Math.abs(transaction.amount),
            ledgerReferenceId: ledgerRecordsByTxnId[0].referenceId,
            description: ledgerRecordsByTxnId[0].description,
            title: ledgerRecordsByTxnId[0].title ? ledgerRecordsByTxnId[0].title : dueName,
            discount: ledgerRecordsByTxnId[0].discount,
          });

        } else {
          await moveOutDuesDB.updateBalance({
            id: due[0].id,
            balance: due[0].balance + Math.abs(ledgerRecordsByTxnId[0].amount),
          });
        }

        let i = 1

        for (let record of ledgerRecords) {
          if (ledgerRecordsByTxnId[0].id < record.id) {
            await ledgerDB.updateBalanceById({
              id: record.id,
              balance: Number(record.balance) + Number(transaction.amount),
            });
          }
          if (i === ledgerRecords.length) {
            record.description = record.description.replace(/final/i, "").trim();
            await ledgerDB.updateDescription({
              id: record.id,
              description: `Partial ${record.description}`
            });
          }
          i += 1;
        }
      } else {
        await moveOutDuesDB.add({
          moveOutId: occupancy.id,
          tenantId: transaction.tenantId,
          roomId: transaction.roomId,
          propId: transaction.propId,
          clientId: clientId,
          amount: Math.abs(transaction.amount),
          balance: Math.abs(transaction.amount),
          ledgerReferenceId: ledgerRecordsByTxnId[0].referenceId,
          dueDate: transaction.dueDate,
          type: transaction.transactionFor,
          rentStartDate: ledgerRecordsByTxnId[0].rentStartDate,
          rentEndDate: ledgerRecordsByTxnId[0].rentEndDate,
          description: ledgerRecordsByTxnId[0].description,
          title: ledgerRecordsByTxnId[0].title ? ledgerRecordsByTxnId[0].title : dueName,
          discount: ledgerRecordsByTxnId[0].discount,
        });
      }
    }

    if (transaction.docs !== null && transaction.docs !== "") {
      const folderPath = `uploads/documents/client_${clientId}/transaction_${transaction.id}`;

      const files = await fsPromises.readdir(folderPath);

      for (const file of files) {
        const filePath = path.join(folderPath, file);
        await fsPromises.unlink(filePath);
      }
    }

    await transactionDB.removeById({
      id: Number(id),
    });

    await ledgerDB.removeById({
      id: ledgerRecordsByTxnId[0].id,
    });

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      transaction.tenantId,
      CONSTANTS.ACTIVITY_TYPES.RESTORE_TRANS,
      Math.abs(transaction.amount),
      ledgerRecordsByTxnId[0].description,
      transaction.collectionDate,
      null,
      null
    );

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], Transaction Id [${id}], Tenant Id [${transaction.tenantId}], Ledger Id [${ledgerRecordsByTxnId[0].referenceId}], Amount [${transaction.amount}], Due Type [${transaction.transactionFor}], Transaction restored successfully`
    );

    return res.status(200).json({
      msg: "Transaction restored successfully",
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.RefundList = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "RefundList";

  try {
    const { startDate, endDate } = req.query;

    log.info(
      `[${C}], [${F}], Start Date [${startDate}], End Date [${endDate}]`
    );

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    let refundList: any = [];

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      refundList = await transactionDB.getRefundedRecordsByClientIdAndDateRange({
        clientId,
        startDate,
        endDate,
      });
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

      let isStaffAuthorized = await isPrivilegedStaff(
        staff.role,
        1,
      );

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && !isStaffAuthorized && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Finance-Admin, Back Office Authorized....`
        );
        return res.status(401).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden/Finance-Admin Requesting....`
        );
      }

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId: staff.id,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        refundList = await transactionDB.getRefundedRecordsByClientIdAndDateRangeForStaff({
          clientId,
          startDate,
          endDate,
          propertiesIds,
        });
      }
    }

    let refundedAmount = 0;

    if (refundList && refundList.length > 0) {
      for (let record of refundList) {
        refundedAmount += Math.abs(record.amount);
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Start Date [${startDate}], End Date [${endDate}], Refund List Sent Successfully`
    );

    return res.status(200).json({
      msg: "Refund list fetched successfully",
      list: refundList || [],
      refundedAmount: refundedAmount || 0,
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.ListPaymentStats = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "ListPaymentStats";

  try {
    const { startDate, endDate, propId } = req.query;

    log.info(
      `[${C}], [${F}], Start Date [${startDate}], End Date [${endDate}], Prop Id [${propId}]`
    );

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    let transactions = [];

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
      if (Number(propId) && Number(propId) > 0) {
        transactions = await transactionDB.getByPropIdsandDateRange({
          propId: [propId],
          startDate,
          endDate,
        });
      } else {
        transactions = await transactionDB.getByClientIdandDateRangeForWeb({
          clientId,
          startDate,
          endDate,
        });
      }
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

      let isStaffAuthorized = await isPrivilegedStaff(
        staff.role,
        1,
      );

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && !isStaffAuthorized && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Only Admin, Warden, Finance-Admin, Back Office Authorized....`
        );
        return res.status(401).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin/Warden/Finance-Admin Requesting....`
        );
      }

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId: staff.id,
      });

      if (staffLinkedProps) {
        let propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");
        if (Number(propId) && Number(propId) > 0) {
          const exists = propertiesIds.split(",").includes(String(propId));
          if (exists) {
            propertiesIds = String(propId);
          } else {
            propertiesIds = '0';
          }
        }

        transactions = await transactionDB.getByClientIdandDateRangeForWebStaff({
          clientId,
          startDate,
          endDate,
          propertiesIds,
        });
      }
    }

    let totalCollection = 0;
    let offlineCollection = 0;
    let onlineCollection = 0;
    let cashfreeCollection = 0;
    let cashfreeCollectionCharges = 0;
    let cashfreeCollectionSettled = 0;
    let cashfreeCollectionPending = 0;
    let payuCollection = 0;
    let payuCollectionCharges = 0;
    let payuCollectionSettled = 0;
    let payuCollectionPending = 0;
    let easebuzzCollection = 0;
    let easebuzzCollectionCharges = 0;
    let easebuzzCollectionSettled = 0;
    let easebuzzCollectionPending = 0;
    let onlineCollectionByBank: any = {};
    let offlineCollectionByBank: any = {};
    let collectionByPaymentMode: any = {};
    let paymentGatewaySummary: any = [];

    let selfOwnedGateway = false;
    const selfGateway = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.GATEWAY
    });

    if (!selfGateway || Number(selfGateway?.value) === CONSTANTS.CLIENT_CONFIG_VALUE.GATEWAY_OWNERSHIP.OWNED) {
      selfOwnedGateway = true;
    }
    

    if (transactions && transactions.length > 0) {
      for (let transaction of transactions) {
        if (transaction.mode === CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY) {
          continue;
        }
        if (Number(transaction.amount) < 0) {
          continue;
        }
        totalCollection += transaction.amount;
        if (transaction.mode === CONSTANTS.TRANSACTION_MODES.OFFLINE) {
          offlineCollection += transaction.amount;
        } else {
          if (transaction.recordedBy === 'Kipinn App') {
            onlineCollection += transaction.amount;
          } else {
            offlineCollection += transaction.amount;
          }
        }

        if (transaction.paymentAccountNo) {
          const bank = await bankDB.getByAccNum({
            accountNum: transaction.paymentAccountNo,
            clientId,
          });

          if (bank) {
            let targetArr = offlineCollectionByBank;
            if (transaction.recordedBy === 'Kipinn App') {
              targetArr = onlineCollectionByBank;
            }

            if (!targetArr[bank.id]) {
              targetArr[bank.id] = {
                bankName: bank.bankName,
                accountNum: transaction.paymentAccountNo,
                amount: 0
              };
            }

            //log.info(`[${C}], [${F}], Target Array: ${JSON.stringify(offlineCollectionByBank)}`);

            targetArr[bank.id].amount += transaction.amount;
          }
        }

        let modeName = getModeName(transaction.mode);

        if (!collectionByPaymentMode[modeName]) {
          collectionByPaymentMode[modeName] = {
            mode: modeName,
            amount: 0
          };
        }

        collectionByPaymentMode[modeName].amount += transaction.amount;

        if (Number(transaction.gateway) === CONSTANTS.PAYMENT_GATEWAY.CASHFREE) {
          cashfreeCollection += transaction.amount;
          if (Number(transaction.settleStatus) === CONSTANTS.PAYOUT_STATUS.COMPLETED) {
            cashfreeCollectionSettled += transaction.amount;
          } else {
            cashfreeCollectionPending += transaction.amount;
          }

          if(selfOwnedGateway) {
            cashfreeCollectionCharges += transaction.charges;
          }
        } else if (Number(transaction.gateway) === CONSTANTS.PAYMENT_GATEWAY.PAYU) {
          payuCollection += transaction.amount;
          if (Number(transaction.settleStatus) === CONSTANTS.PAYOUT_STATUS.COMPLETED) {
            payuCollectionSettled += transaction.amount;
          } else {
            payuCollectionPending += transaction.amount;
          }

          //Currently no one has self owned payu gateway 2026-06-17
          // if(selfOwnedGateway) {
          //   payuCollectionCharges += transaction.charges;
          // }
        } else if (Number(transaction.gateway) === CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ) {
          easebuzzCollection += transaction.amount;
          if (Number(transaction.settleStatus) === CONSTANTS.PAYOUT_STATUS.COMPLETED) {
            easebuzzCollectionSettled += transaction.amount;
          } else {
            easebuzzCollectionPending += transaction.amount;
          }

          if(selfOwnedGateway) {
            easebuzzCollectionCharges += transaction.charges;
          }
        }
      }
    }

    const onlineCollectionByBankArray = Object.values(onlineCollectionByBank);
    const offlineCollectionByBankArray = Object.values(offlineCollectionByBank);
    const collectionByPaymentModeArray = Object.values(collectionByPaymentMode);

    if(cashfreeCollection > 0){
      paymentGatewaySummary.push({
        gateway: "Cashfree",
        collected: cashfreeCollection,
        charges: cashfreeCollectionCharges,
        settled: cashfreeCollectionSettled,
        pending: cashfreeCollectionPending
      });
    }

    if(payuCollection > 0){
      paymentGatewaySummary.push({
        gateway: "PayU",
        collected: payuCollection,
        charges: payuCollectionCharges,
        settled: payuCollectionSettled,
        pending: payuCollectionPending
      });
    }

    if(easebuzzCollection > 0){
      paymentGatewaySummary.push({
        gateway: "Easebuzz",
        collected: easebuzzCollection,
        charges: easebuzzCollectionCharges,
        settled: easebuzzCollectionSettled,
        pending: easebuzzCollectionPending
      });
    }

    log.info(`[${C}], [${F}], Total Collection [${totalCollection}], Offline Collection [${offlineCollection}], Online Collection [${onlineCollection}], Online Collection By Bank [${JSON.stringify(onlineCollectionByBankArray)}], Offline Collection By Bank [${JSON.stringify(offlineCollectionByBankArray)}], Collection By Payment Mode [${JSON.stringify(collectionByPaymentModeArray)}]`)

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Start Date [${startDate}], End Date [${endDate}], Payment Stats Sent Successfully`
    );

    return res.status(200).json({
      msg: "Payment stats fetched successfully",
      totalCollection,
      offlineCollection,
      onlineCollection,
      onlineCollectionByBank: onlineCollectionByBankArray,
      offlineCollectionByBank: offlineCollectionByBankArray,
      collectionByPaymentMode: collectionByPaymentModeArray,
      paymentGatewaySummary: paymentGatewaySummary || [],
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.UpdateDueDate = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "UpdateDueDate";
  try {
    const { id, dueDate } = req.body;

    log.info(
      `[${C}], [${F}], Transaction Id [${id}], New Due Date [${dueDate}]`
    );

    const userType = req.userType;

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE && staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}] Transaction Id [${id}], Only Admin, Warden, Finance-Admin, Back Office Authorized....`
        );
        return res.status(401).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}] Transaction Id [${id}], Admin/Warden/Finance-Admin Requesting....`
        );
      }
    }

    const transaction = await transactionDB.getById({
      id,
    });

    if (!transaction) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Transaction Id [${id}], No Record Found`
      );
      return res.status(400).json({
        msg: "Transaction not found",
        isSuccess: false,
      });
    }

    const ledgerRecords = await ledgerDB.getDueByLedgerReferenceId ({
      referenceId: transaction.ledgerReferenceId,
    });

    if (!ledgerRecords.length) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Transaction Id [${id}], Ledger Records Not Found`
      );
      return res.status(400).json({
        msg: "Transaction not found",
        isSuccess: false,
      });
    }

    await transactionDB.updateDueDate({
      id: id,
      dueDate: dueDate,
    });

    await ledgerDB.updateDueDateByReferenceId({
      referenceId: transaction.ledgerReferenceId,
      dueDate: dueDate,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Transaction Id [${id}], New Due Date [${dueDate}], Due Date Updated Successfully`);

    return res.status(200).json({
      msg: "Due date updated successfully",
      isSuccess: true,
    });
    
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

transactions.InitiatePayment = async (req: CustomRequest, res: Response) => {
  const C = "Transaction Controller";
  const F = "InitiatePayment";

  try {
    const tenantId = req.id;
    const isEvicted = req.isEvicted;
    const clientId = req.clientId;
    let { amount, customerDetails, dueId, charges = 0, mode = 2, gstAmount = 0, payingFor = CONSTANTS.PAYING_FOR.APP } = req.body;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], initiate payment`
    );

    log.info(
      `[${C}], [${F}], Amount [${amount}], Customer Id [${customerDetails.customer_id}], Customer Name [${customerDetails.customer_name}], Customer Mobile [${customerDetails.customer_phone}], Payment Device ${payingFor === CONSTANTS.PAYING_FOR.APP ? "[APP]" : "[WEB LINK]"},  Paying for [${payingFor === CONSTANTS.PAYING_FOR.WEB_BOOKING_LINK ? "Booking" : "Due Payment"}]`
    );

    const tenant = await tenantDB.getById({ id: tenantId });

    if (!tenant) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenantId,
      clientId: clientId,
    });

    if (isEvicted) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId: tenantId,
        clientId: clientId,
      });
    }

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let client = await clientDB.getById({id: clientId});

    if (!client) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    
    let property = await propertyDB.getById({id: occupancy?.propId});

    if (!property) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Prop Id [${occupancy?.propId}], No Property Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    let gateway = client?.paymentGateway;
    if(property && property?.paymentGateway) {
      gateway = property?.paymentGateway;
    }
    if(Number(gateway) === CONSTANTS.PAYMENT_GATEWAY.CASHFREE) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Initiating payment via Cashfree`
      );
      await cashfree.CreateOrder(req, res);
    } else {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Initiating payment via PayU`
      );
      //await payu.AppPayment(req, res);  
      let redirectLink = `${process.env.PAYU_APP_WEB_PAYMENT_BASE_URL}?encryptedId=${occupancy?.gId}&amount=${amount}&dueId=${dueId}&charges=${charges}&gstAmount=${gstAmount}&payingFor=${payingFor}&mode=${mode}`;
      let respData: any = {
        paymentLink: null,
      };
      respData.paymentLink = redirectLink;
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], Pay U Resp : ${JSON.stringify(respData)}`
      );
      return res.status(200).json({
        msg: `Order created successfull`,
        data: respData,
        isSuccess: true,
      });
    }

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

export default transactions;
