import { Response } from "express";
// import moment from "moment";
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 tenantDB from "../models/tenant.model";
import transactionDB from "../models/transaction.model";
import duesTypes from "../schemas/dues.schema";
import CustomRequest from "../types/requestType";
import createReceipt from "../utils/createReceipt";
import generateTransId from "../utils/generateTransId";
import getDueName from "../utils/getDueName";
import staffDB from "../models/staff.model";
import flatDB from "../models/flat.model";
import moment from 'moment-timezone';
import {
  duesForClient,
  duesForStaff,
  duesForWeb,
} from "../utils/client/duesByUserType";
import roomDB from "../models/room.model";
import {
  calculateMonthlyRent,
  calculateRentAsPerRentalType,
  calculateRentAsPerRentalTypeForReserved,
  calculateRentPerDayForMoveOut,
} from "../utils/calculateRentPerDay";
import getModeName from "../utils/getModeName";
import ledgerDB from "../models/ledger.model";
import generateLedgerReferenceId from "../utils/generateLedgerReferenceId";
import adjustExcessPayments, {
  adjustExcessPaymentsForMovedOutX,
} from "../utils/adjustExcessPayment";
import getDueDescription from "../utils/getDueDescription";
import createReceiptMultipleDues from "../utils/createReceiptMultipleDues";
import moveOutDB from "../models/moveOut.model";
import propertiesTypes from "../schemas/property.schema";
import { isUserPartner } from "../utils/isUserPartner";
import { logActivity } from "../utils/logActivity";
import {
  AddDues,
  AddMovedOutDues,
  AddMovedOutDuesWithoutAdjust,
  MarkPaid,
  MarkPaidForMovedOut,
} from "../utils/dueHandler";
import moveOutDuesDB from "../models/moveOutDues.model";
import documentDB from "../models/document.model";
import editLogsDB from "../models/editLogs.model";
import ledger from "./ledger.controller";
import settingsDB from "../models/settings.model";
import { sendWhatsappPaymentConfirmationClient, sendWhatsappPaymentConfirmationTenant } from "../utils/sendWhatsappWithConfig";
import fsPromises from "fs/promises";
import fs from "fs";
import path from "path";
import bankDB from "../models/bank.model";
import requestDB from "../models/request.model";
import clientConfigDB from "../models/clientConfig.model";
import generateQRCode from "../utils/generateQRCode";
import extraChargeDB from "../models/extraCharges.model";
import jwt from "jsonwebtoken";
import axios from "axios";
import hiddenDuesDB from "../models/hiddenDues.model";
import tenantNotesDB from "../models/tenantNotes.model";
import { rentalMonthsToBeDeleted } from "../utils/rentalMonthHandler";
import sendWhatsapp from "../utils/sendWhatsappChatMitra";
import createGstReceipt from "../utils/createGstReceipt";
import { setTransactionTallyBillRef, setTransactionTallyStatus } from "../utils/setTallyStatus";
import sendNotification from "../utils/sendNotification";
import { generateRentReciept } from "../utils/generateRentReciept";
import { getClientWhatsappCredentialsMessageCentral } from "../utils/getClientWhatsappCredentials";
import sendWhatsappMC from "../utils/sendWhatsappMessageCentral";

const dues: any = {};

dues.ClientDuesList = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "ClientDuesList";

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

    const userType = req.userType;

    // let clientId = req.id;

    const limit = 10;

    let finalDuesAmt = 0;
    let finalDueTenantCount = 0;
    let finalDuesList: any = [];
    let finalOverDuesAmt = 0;
    let finalCurrentMonthDues = 0;
    let finalRentDues = 0;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${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}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Not a Valid Month or Year`
      );
      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) {
      const client = await clientDB.getById({ id: clientId });

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

      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Date [${date}], ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      const {
        totalDuesAmt,
        totalDueTenantCount,
        duesList,
        overDuesAmt,
        totalDuesForCurrentMonthAmt,
        totalRentDuesAmt,
      } = await duesForClient(
        Number(clientId),
        Number(propId),
        Number(tenantId),
        Number(pageNum),
        limit,
        date,
        String(s)
      );

      finalDuesAmt = totalDuesAmt;
      finalDueTenantCount = totalDueTenantCount;
      finalDuesList = duesList;
      finalOverDuesAmt = overDuesAmt;
      finalCurrentMonthDues = totalDuesForCurrentMonthAmt;
      finalRentDues = totalRentDuesAmt;
    } else {
      const staffId = req.id;

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Prop Id [${propId}], Tenant Id [${tenantId}], Month [${m}], Year [${y}], Search Val [${s}], Page Num [${pageNum}], Staff Requested....`
      );

      const {
        totalDuesAmt,
        totalDueTenantCount,
        duesList,
        overDuesAmt,
        totalDuesForCurrentMonthAmt,
        totalRentDuesAmt,
      } = await duesForStaff(
        Number(clientId),
        Number(staffId),
        Number(propId),
        Number(tenantId),
        Number(pageNum),
        limit,
        date,
        String(s)
      );

      finalDuesAmt = totalDuesAmt;
      finalDueTenantCount = totalDueTenantCount;
      finalDuesList = duesList;
      finalOverDuesAmt = overDuesAmt;
      finalCurrentMonthDues = totalDuesForCurrentMonthAmt;
      finalRentDues = totalRentDuesAmt;
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Tenant Dues list sent successfully`
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Total Dues [${finalDuesAmt}], Total Due Tenant Count [${finalDueTenantCount}], Total Over Dues [${finalOverDuesAmt}], Total Current Month Dues [${finalCurrentMonthDues}], Total Rent Dues [${finalRentDues}]`
    );

    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      totalDues: Number(finalDuesAmt) || 0,
      overDuesAmt: Number(finalOverDuesAmt) || 0,
      currentMonthDues: Number(finalCurrentMonthDues) || 0,
      rentDues: Number(finalRentDues) || 0,
      dueTenantCount: Number(finalDueTenantCount) || 0,
      dues: finalDuesList || [],
      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,
    });
  }
};

dues.ClientDuesListForSingleTenant = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Dues Controller";
  const F = "ClientDuesListForSingle";

  try {
    let { tenantId, date } = req.body;

    //To handle cases where first Move 0ut date option is selected then it is changed to dont calculate.
    await duesDB.removeTempByTenantId({ tenantId });

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Date [${date}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Moveout Date [${date}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Moveout Date [${date}], Client Requested....`
      );
    }

    date = date + " 23:23:59";

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

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

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

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

    const now = moment();
    const currentMonthStart = moment().startOf("month");

    let rentalCycle = Number(occupancy.rentalCycle);
    const rentCollentionDate = moment(currentMonthStart).add(
      rentalCycle - 1,
      "days"
    );

    const isRentDeductedForCurMonth = moment(now).isAfter(rentCollentionDate);

    let monthCount: number = moment(date).diff(rentCollentionDate, "months");
    monthCount = Math.ceil(monthCount);

    if (!isRentDeductedForCurMonth) monthCount = monthCount + 1;

    if (monthCount > 0) {
      for (let i = 1; i <= monthCount; i++) {
        const dueDate = moment(rentCollentionDate)
          .add(isRentDeductedForCurMonth ? i : i - 1, "months")
          .format("YYYY-MM-DD HH:mm:ss");

        const dateForCalculatingEndDate = moment(rentCollentionDate).add(
          isRentDeductedForCurMonth ? i : i - 1,
          "months"
        );

        const isExists = await duesDB.getFromTemp({
          tenantId,
          clientId,
          occupancyId: occupancy.id,
          dueDate,
          type: CONSTANTS.DUES_TYPES.RENT,
        });

        let actualRent = Number(occupancy.rent);

        // only for last iteration
        if (i === monthCount) {
          actualRent = calculateRentPerDayForMoveOut({
            dueDate,
            monthlyRent: Number(occupancy.rent),
            moveOutDate: date,
          });
        }

        if (actualRent === 0) {
          if (isExists) {
            await duesDB.removeTempDue({
              id: isExists.id,
            });
          }

          continue;
        }

        if (!isExists) {
          await duesDB.addToTemp({
            tenantId,
            amount: actualRent,
            balance: actualRent,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            dueDate,
            rentStartDate: dueDate,
            rentEndDate: dateForCalculatingEndDate
              .clone()
              .add(dateForCalculatingEndDate.daysInMonth() - 1, "days")
              .format("YYYY-MM-DD HH:mm:ss"),
            type: CONSTANTS.DUES_TYPES.RENT,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], DueDate [${dueDate}], Amount [${actualRent}], Temp Due Added Successfully`
          );
        } else {
          await duesDB.updateTempAmount({
            amount: actualRent,
            balance: actualRent,
            id: isExists.id,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], DueDate [${dueDate}], Amount [${actualRent}], Temp Due Updated Successfully`
          );
        }
      }
    }

    let { totalDues } = await duesDB.getTotalDuesByDate({ tenantId });
    let dues = await duesDB.getDuesByDate({ tenantId });

    let { totalTempDues } = await duesDB.getTotalTempDuesByDate({
      tenantId,
    });
    let tempDues = await duesDB.getTempDuesByDate({
      tenantId,
      propId: occupancy.propId,
    });

    let alteredDues = [];

    if (dues) {
      alteredDues =
        dues.map((due: duesTypes) => ({
          ...due,
          isTemp: false,
        })) || [];
    }

    let alteredTempDues =
      tempDues.map((due: duesTypes) => ({
        ...due,
        isTemp: true,
      })) || [];

    totalDues = Number(totalDues) + Number(totalTempDues);
    dues = [...alteredDues, ...alteredTempDues];

    dues = dues.sort((a: any, b: any) => a.dueDate - b.dueDate);

    // const securityTransanction = await transactionDB.getSecurityAmount({
    //   tenantId,
    //   clientId,
    //   roomId: occupancy.roomId,
    //   propId: occupancy.propId,
    //   status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
    //   transactionFor: CONSTANTS.TRANSACTION_FOR.SECURITY,
    // });

    const securityTransanction = await ledgerDB.getSecurityTransactionAmountX({
      tenantId,
      type: CONSTANTS.DUES_TYPES.SECURITY,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
    });

    const securityAmount = securityTransanction
      ? Math.abs(securityTransanction.amount)
      : 0;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Security Amount [${securityAmount}] Tenant Dues list sent successfully`
    );

    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      totalDues: Number(totalDues) || 0,
      dues,
      securityAmount,
      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,
    });
  }
};

dues.ClientDuesListForSingleTenantX = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Dues Controller";
  const F = "ClientDuesListForSingleTenantX";
  try {
    let { tenantId, date, calculateDues = 1, moveOutReason = 'end_of_lease' } = req.body;
    log.info (`[${C}], [${F}], Tenant Id [${tenantId}], Moveout Date [${date}], Calculate Dues [${calculateDues}], Moveout Reason [${moveOutReason}]`)
    /*
      calculateDues : 1/0 (1: Current Temp Dues Will be cleared and New Temp Dues Will be created and list will be sent, 0: No calculation will not be done only Temp Dues list will be sent )
      To handle Edit Temp Dues
    */
    if (calculateDues === 1) {
      //To handle cases where first Move 0ut date option is selected then it is changed to dont calculate.
      await duesDB.removeTempByTenantId({ tenantId });
    }
    const userType = req.userType;
    let clientId = req.id;
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {      
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Moveout Date [${date}], Calculate Dues [${calculateDues}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff.clientId;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Moveout Date [${date}], Calculate Dues [${calculateDues}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Moveout Date [${date}], Calculate Dues [${calculateDues}], Client Requested....`
      );
    }
    date = date + " 23:23:59";
    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    const tenant = await tenantDB.getById({ id: tenantId });
    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    const now = moment();
    // const currentMonthStart = moment().startOf("month");
    let rentalCycle = Number(occupancy.rentalCycle);
    let lastRentDue = await ledgerDB.getLastRent({
      tenantId,
      clientId,
    });

    log.info(`Last Rent Due [${JSON.stringify(lastRentDue)}]`);

    // let lastRentEndMonth = moment(lastRentDue.rentEndDate).add(1, "days");
    let lastRentEndMonth = moment(lastRentDue.rentEndDate);
    lastRentEndMonth = moment(lastRentEndMonth).startOf("month");
    const rentCollentionDate = moment(lastRentEndMonth).add(
      rentalCycle - 1,
      "days"
    );
    // const rentDueDate = moment(lastRentDue.rentStartDate);
    let rentDueDate = moment()
      .month(moment(lastRentDue.rentStartDate).month())
      .year(moment(lastRentDue.rentStartDate).year())
      .date(rentalCycle)
      .startOf("day");
    if (!moment(rentDueDate).isValid()) {
      rentDueDate = moment()
        .month(moment(lastRentDue.rentStartDate).month() + 1)
        .year(moment(lastRentDue.rentStartDate).year())
        .date(1)
        .startOf("day");
    }

    log.info(`Rent Due Date [${rentDueDate}]`);

    const lastRentDueEndDate = moment(lastRentDue.rentEndDate).startOf("day");
    const lastRentDueStartDate = moment(lastRentDue.rentStartDate).startOf("day");
    // const isRentDeductedForCurMonth = moment(now).isAfter(rentDueDate);
    let isRentDeductedForCurMonth = moment(now).isSameOrAfter(lastRentDueStartDate);
    // log.info(`isRentDeductedForCurMonth [${isRentDeductedForCurMonth}].............`);
    let monthCount: number = moment(date).diff(rentCollentionDate, "months");
    // log.info(`Month Count [${monthCount}], Last Rent Due Start Date [${lastRentDueStartDate}], Rental Cycle [${rentalCycle}], Check [${moment(lastRentDueStartDate).date() < rentalCycle}] isRentDeductedForCurMonth [${isRentDeductedForCurMonth}].............`);
    // if(isRentDeductedForCurMonth && moment(lastRentDueStartDate).date() < rentalCycle) {
    //   isRentDeductedForCurMonth = false;
    //   monthCount -= 1;
    // }
    
    // log.info(`Month Count [${monthCount}], isRentDeductedForCurMonth [${isRentDeductedForCurMonth}].............`);
    // const tempdate = rentalCycle === 1 ? 1 : rentalCycle - 1;
    if (moment(date).isAfter(moment(date).date(rentalCycle - 1)) && Number(rentalCycle) !== 1) {
      monthCount += 1;
    }
    // log.info(`Month Count [${monthCount}], isRentDeductedForCurMonth [${isRentDeductedForCurMonth}].............`);
    //Fix Code if Month we Get 0 in cae of one or 2 days
    //let rentStartDay = rentalCycle - 1;
    
    if(Number(moment(date).format("D")) <= Number(rentalCycle - 1) && moment(date).isAfter(lastRentDue.rentEndDate) && Number(rentalCycle) !== 1)
    {
      
       monthCount += 1;

    }
    // log.info(`Month Count [${monthCount}], isRentDeductedForCurMonth [${isRentDeductedForCurMonth}].............`);

    if(moment(lastRentDue.rentEndDate).isAfter(moment(date).format("YYYY-MM-DD")) || moment(lastRentDue.rentEndDate).isSame(moment(date).format("YYYY-MM-DD")))
    {
      monthCount = 0;
    }
    log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Month Count [${monthCount}]`
      );
    monthCount = monthCount / Number(occupancy.rentalType);
    monthCount = Math.ceil(monthCount);
    if (!isRentDeductedForCurMonth) monthCount = monthCount + 1;
    if (moment(lastRentDue.rentStartDate).startOf("day").isAfter(moment(date).startOf("day"))) monthCount = 0;
    if (monthCount > 0 && calculateDues === 1 && Number(occupancy.stayType) !== CONSTANTS.STAY_TYPE.SHORT) {
      for (let i = 1; i <= monthCount; i++) {
        const dueDate = moment(rentDueDate)
          .add(
            isRentDeductedForCurMonth
              ? i * Number(occupancy.rentalType)
              : (i - 1) * Number(occupancy.rentalType),
            "months"
          )
          .format("YYYY-MM-DD HH:mm:ss");
        const dateForCalculatingEndDate = moment(rentDueDate).add(
          isRentDeductedForCurMonth
            ? i * Number(occupancy.rentalType)
            : (i - 1) * Number(occupancy.rentalType),
          "months"
        );
        const isExists = await duesDB.getFromTemp({
          tenantId,
          clientId,
          occupancyId: occupancy.id,
          dueDate,
          type: CONSTANTS.DUES_TYPES.RENT,
        });
        let actualRent = Number(occupancy.rent);
        let monthlyRent = Math.ceil(
          Number(occupancy.rent) / Number(occupancy.rentalType)
        );
        // only for last iteration
        if (i === monthCount) {
          if (moment(date).add(1, "day").date() === Number(occupancy.rentalCycle)) {
            log.info(`[${C}], [${F}], Last Iteration, Move Out Date is Same as Rent Cycle`);
            actualRent = monthlyRent;
          } 
          // else if (moment(date).date() === 31 && moment(date).add(1, "day").date()) {} 
          else {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Date [${dueDate}], Monthly Rent [${monthlyRent}], Move Out Date [${date}] Last Iteration, Move Out Date is Different from Rent Cycle`
            );
            actualRent = calculateRentPerDayForMoveOut({
              dueDate,
              monthlyRent: monthlyRent,
              moveOutDate: date,
            });
          }
        }
        if (actualRent === 0) {
          if (isExists) {
            await duesDB.removeTempDue({
              id: isExists.id,
            });
          }
          continue;
        }
        let rentEndDate = dateForCalculatingEndDate
          .clone()
          .add(occupancy.rentalType, "months")
          .subtract(1, "days")
          .format("YYYY-MM-DD HH:mm:ss");
        if (!isExists) {
          await duesDB.addToTempX({
            tenantId,
            amount: actualRent,
            balance: actualRent,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            dueDate,
            rentStartDate: dueDate,
            rentEndDate:
              moment(date).startOf("day") < moment(rentEndDate).startOf("day")
                ? date
                : rentEndDate,
            type: CONSTANTS.DUES_TYPES.RENT,
            title: "Rent",
            description: "Rent calculated automatically while moving out",
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], DueDate [${dueDate}], Amount [${actualRent}], Temp Due Added Successfully`
          );
        } else {
          await duesDB.updateTempAmount({
            amount: actualRent,
            balance: actualRent,
            id: isExists.id,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], DueDate [${dueDate}], Amount [${actualRent}], Temp Due Updated Successfully`
          );
        }
      }
    } else if (calculateDues === 1 && Number(occupancy.stayType) === CONSTANTS.STAY_TYPE.SHORT) {
      if (moment(occupancy.moveOutDate).format("YYYY-MM-DD") < moment(date).format("YYYY-MM-DD")) {
        let rentToBePaid = occupancy.rent * (moment(date).diff(moment(occupancy.moveOutDate), "days"));
        let dueDate = moment(occupancy.moveOutDate).add(1, "day").format("YYYY-MM-DD");

        const isExists = await duesDB.getFromTemp({
          tenantId,
          clientId,
          occupancyId: occupancy.id,
          dueDate,
          type: CONSTANTS.DUES_TYPES.RENT,
        });

        if (!isExists) {
          await duesDB.addToTempX({
            tenantId,
            amount: rentToBePaid,
            balance: rentToBePaid,
            occupancyId: occupancy.id,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId,
            dueDate,
            rentStartDate: dueDate,
            rentEndDate: moment(date).format("YYYY-MM-DD"),
            type: CONSTANTS.DUES_TYPES.RENT,
            title: "Rent",
            description: "Rent calculated automatically while moving out",
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], DueDate [${dueDate}], Amount [${rentToBePaid}], Temp Due Added Successfully`
          );
        } else {
          await duesDB.updateTempAmount({
            amount: rentToBePaid,
            balance: rentToBePaid,
            id: isExists.id,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], DueDate [${dueDate}], Amount [${rentToBePaid}], Temp Due Updated Successfully`
          );
        }
      }

    }

    const moveOutCharge = await extraChargeDB.getByPropAndType({
      clientId,
      propId: occupancy.propId,
      type: CONSTANTS.EXTRA_CHARGE_TYPES.MOVE_OUT_CHARGES,
    });

    if(moveOutCharge && calculateDues === 1) {
      const referenceId = await generateLedgerReferenceId({clientId});
      // await AddDues(
      //   tenantId,
      //   moveOutCharge?.amount || 0, //amount
      //   CONSTANTS.DUES_TYPES.MOVE_OUT_CHARGES, //type
      //   occupancy,
      //   clientId!,
      //   moment(date).format("YYYY-MM-DD"), //startDate
      //   moment(date).format("YYYY-MM-DD"), //endDate
      //   moment(date).format("YYYY-MM-DD"), //dueDate
      //   0, //rent duration
      //   "Move Out Charges", // description
      //   "Move Out Charges", //title
      //   "Move Out Charges", // dueDescription
      //   referenceId
      // );
      await duesDB.addToTempX({
        tenantId,
        amount: moveOutCharge?.amount || 0,
        balance: moveOutCharge?.amount || 0,
        occupancyId: occupancy.id,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        clientId,
        dueDate: moment(date).format("YYYY-MM-DD"),
        rentStartDate: moment(date).format("YYYY-MM-DD"),
        rentEndDate: moment(date).format("YYYY-MM-DD"),
        type: CONSTANTS.DUES_TYPES.MOVE_OUT_CHARGES,
        title: "Move Out Charges",
        description: "Move Out Charges automatically added while moving out",
      });
    }
    // Pallav Multi Tenant Senerio
    //let { totalDues } = await duesDB.getTotalDuesByDate({ tenantId });
    let { totalDues } = await duesDB.getTotalTenantDuesByClientId({ tenantId, clientId });
    //let dues = await duesDB.getDuesByDate({ tenantId });
    let dues = await duesDB.getTenantDuesByClient({ tenantId, clientId });
    // let { totalTempDues } = await duesDB.getTotalTempDuesByDate({
    //   tenantId,
    // });
    let { totalTempDues } = await duesDB.getTotalTempDuesByDateWithClient({
      tenantId,
      clientId,
    });
    let tempDues = await duesDB.getTempDuesByDate({
      tenantId,
      propId: occupancy.propId,
    });
    let alteredDues = [];
    if (dues) {
      alteredDues =
        dues.map((due: duesTypes) => ({
          ...due,
          isTemp: false,
        })) || [];
    }
    let alteredTempDues =
      tempDues.map((due: duesTypes) => ({
        ...due,
        isTemp: true,
      })) || [];
    totalDues = Number(totalDues) + Number(totalTempDues);
    dues = [...alteredDues, ...alteredTempDues];
    dues = dues.sort((a: any, b: any) => a.dueDate - b.dueDate);
    const securityTransanction = await ledgerDB.getSecurityTransactionAmountX({
      tenantId,
      type: CONSTANTS.DUES_TYPES.SECURITY,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
    });
    let securityAmount = securityTransanction
      ? Math.abs(securityTransanction.amount)
      : 0;

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

    securityAmount = Number(securityAmount) - (Number(securityUsed) || 0);

    await occupancyDB.updateMoveOutReasonByTenantId({
      tenantId: tenantId,
      clientId: clientId,
      moveOutReason: moveOutReason
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Date [${date}], Calculate Dues [${calculateDues}], Security Amount [${securityAmount}] Tenant Dues list sent successfully`
    );
    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      totalDues: Number(totalDues) || 0,
      dues,
      securityAmount,
      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,
    });
  }
};

dues.TenantDuesList = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "TenantDuesList";

  try {
    let isEvicted = req.isEvicted;
    const { tenantId } = req.params;
    //Pallav Multi-Tenant Handling
    let clientId = req.id || 0;
    const userType = req.userType;
    if(userType === CONSTANTS.USER_TYPE.TENANT) {
      clientId = req.clientId || 0;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Tenant Requested....`
      );
      if(!req.clientId){
          log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], No Client Id Found in tenant Token`);
          return res.status(401).json({
            msg: "Session Expired",
            isSuccess: false,
          });
      }
    } else {
      if(userType === CONSTANTS.USER_TYPE.CLIENT) {
        clientId = req.id || 0;
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Client Requested....`
        );
      } else if(userType === CONSTANTS.USER_TYPE.STAFF) {
        const staff = await staffDB.getById({ id: req.id });
        if (!staff) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Staff Id [${req.id}], No Staff Found`
          );
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }
        clientId = staff.clientId;
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Staff Requested....`
        );
      }
    }

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

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

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

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Client Found`);
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }
    // const occupancy = await occupancyDB.getByTenantId({
    //   tenantId,
    // });
    let occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenantId,
      clientId: clientId,
    });
    if (Number(isEvicted) === 1) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId: tenant.id,
        clientId: clientId,
      });
    }

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

    const property = await propertyDB.getById({
      id: occupancy.propId,
    });

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

    //const dues = await duesDB.getByTenantId({ tenantId });
    //const dues = await duesDB.getByTenantIdAndClientId({ tenantId, clientId });
    // const { totalDues } = await duesDB.getTotalDuesByTenantId({
    //   tenantId: tenant.id,
    //   propId: occupancy.propId,
    // });
    //let totalDues =0;
    let dues = await duesDB.getByTenantIdAndClientId({ tenantId, clientId });
    let allDues = await duesDB.getTotalDuesByTenantId({
        tenantId: tenant.id,
        propId: occupancy.propId,
      });
    let totalDues = allDues?.totalDues || 0;
    if (Number(isEvicted) === 1) {
      dues = await moveOutDuesDB.getByTenantIdAndClientId({ tenantId, clientId });
      allDues = await moveOutDuesDB.getTotalDuesByTenantId({
        tenantId: tenant.id,
        propId: occupancy.propId,
      });
      totalDues = allDues?.totalDues || 0;
    }

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Tenant Dues list sent successfully`
    );
    const mode = {
      upi: client.upiEnabled,
      card: client.creditCardEnabled,
      netbanking: client.netBankingEnabled,
    };
    //let isGstEnabled = property?.isGstEnabled & occupancy?.isGstEnabled || 0;
    let isGstEnabled = property?.isGstEnabled || 0;
    let gstCharges = 0.00;
    if(1 == isGstEnabled) {
      gstCharges = Number((totalDues * (property?.gstCharges / 100)).toFixed(2));
    }
    const combinedDues = true;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], totalDues [${totalDues}], gstCharges [${gstCharges}], Tenant Dues list sent successfully`
    );
    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      data: {
        mode,
        isGSTInvoice: isGstEnabled,
        gstAmount : gstCharges,
        totalDues: totalDues || 0,
        list: dues || [],
        combinedDues,
      },
      property,
      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,
    });
  }
};

dues.EditDues = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "EditDues";

  try {
    const { dueId, newAmount, isTemp } = req.body;

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Due Id [${dueId}], New Amount [${newAmount}], Is Temp [${isTemp}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], New Amount [${newAmount}], Is Temp [${isTemp}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], New Amount [${newAmount}], Is Temp [${isTemp}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], New Amount [${newAmount}], Is Temp [${isTemp}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const due = await duesDB.getById({ id: dueId });
    const dueType = await getDueName(due.type);
    let updatedAmountDiff = due.balance - newAmount;

    if (newAmount === 0) {
      if (isTemp) await duesDB.removeTempDue({ id: dueId });
      else {
        await duesDB.removeDue({ id: dueId });
        // await ledgerDB.add({
        //   tenantId: due.tenantId,
        //   roomId: due.roomId,
        //   propId: due.propId,
        //   clientId,
        //   amount: -due.balance,
        //   balance: 0,
        //   referenceId: due.ledgereferenceId,
        //   transactionId: null,
        //   type: due.type,
        //   rentStartDate: null,
        //   rentEndDate: null,
        //   description: "Due adjusted",
        // });
        if (due.amount === due.balance) {
          await ledgerDB.remove({
            referenceId: due.ledgerReferenceId,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], Ledger Reference Id [${due.ledgerReferenceId}], Ledger Entry Deleted Successfully`
          );
        } else {
          await ledgerDB.updateBalance({
            tenantId: due.tenantId,
            clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            balance: 0,
          });
          await ledgerDB.updateAmount({
            tenantId: due.tenantId,
            clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            amount: due.amount - updatedAmountDiff,
          });
        }
      }

      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.DELETE_DUES,
        due.amount,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        null
      );

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], New Amount [${newAmount}], Is Temp [${isTemp}], Dues Deleted Successfully`
      );
    } else {
      if (isTemp)
        await duesDB.updateTempAmount({
          id: dueId,
          amount: newAmount,
          balance: newAmount,
        });
      else {
        if (updatedAmountDiff > 0) {
          await duesDB.updateAmount({
            id: dueId,
            amount: due.amount - updatedAmountDiff,
          });
          await ledgerDB.updateAmount({
            tenantId: due.tenantId,
            clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            amount: due.amount - updatedAmountDiff,
          });
        } else if (updatedAmountDiff < 0) {
          await duesDB.updateAmount({
            id: dueId,
            amount: due.amount + Math.abs(updatedAmountDiff),
          });
          await ledgerDB.updateAmount({
            tenantId: due.tenantId,
            clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            amount: due.amount + Math.abs(updatedAmountDiff),
          });
        }
        await duesDB.updateBalance({ id: dueId, balance: newAmount });
        await ledgerDB.updateBalance({
          tenantId: due.tenantId,
          clientId,
          referenceId: due.ledgerReferenceId,
          type: due.type,
          balance: newAmount,
        });
      }

      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.EDIT_DUES,
        due.amount,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        newAmount
      );

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], New Amount [${newAmount}], Is Temp [${isTemp}], Dues Updated Successfully`
      );
    }

    return res.status(200).json({
      msg: "Dues 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,
    });
  }
};

dues.EditDuesX = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "EditDuesX";

  try {
    let { dueId, newAmount, isTemp, tenantId, isEvicted } = req.body;

    const userType = req.userType;

    let clientId = req.id;
    // let isEvicted = false;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Due Id [${dueId}], New Balance [${newAmount}], Is Temp [${isTemp}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], New Balance [${newAmount}], Is Temp [${isTemp}], Staff Id [${req.id}], Is Evicted [${isEvicted}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], New Balance [${newAmount}], Is Temp [${isTemp}], Is Evicted [${isEvicted}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], New Balance [${newAmount}], Is Temp [${isTemp}], No Client 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) {
      isEvicted = true;
      occupancy = await moveOutDB.getByTenantIdAndClientId({
        tenantId: tenantId,
        clientId: clientId,
      });
    }

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

    let due = await duesDB.getById({ id: dueId });
    ///Need to check if due exist or not
    if (isTemp) {
      due = await duesDB.getTempByIdAndTenantId({
        id: dueId,
        tenantId: tenantId,
        propId: occupancy.propId,
      })
    } else if (isEvicted) {
      due = await moveOutDuesDB.getById({ id: dueId });
    }

    if (!due) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], Is Evicted [${isEvicted}], Is Temp [${isTemp}], Due Does Not Exists`
      );
      
      return res.status(200).json({
        msg: "Due does not exists.",
        isSuccess: false,
      });
    }


    const dueType = await getDueName(due.type);
    let updatedAmountDiff = due.balance - newAmount;

    if (newAmount === 0) {
      if (isTemp) await duesDB.removeTempDue({ id: dueId });
      else {
        if (isEvicted) {
          await moveOutDuesDB.removeDue({ id: dueId });
        } else {
          await duesDB.removeDue({ id: dueId });
        }
        if (due.amount === due.balance) {
          await ledgerDB.remove({
            referenceId: due.ledgerReferenceId,
          });
          if (due.type === CONSTANTS.DUES_TYPES.SECURITY && !isEvicted) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], New Security [0], Updating Security in Occupancy DB`
            );
            await occupancyDB.updateSecurity({
              tenantId: due.tenantId,
              clientId: clientId,
              security: 0,
            });
          }
          await editLogsDB.add({
            tenantId: due.tenantId,
            clientId: clientId,
            propId: due.propId,
            roomId: due.roomId,
            oldAmount: due.amount,
            newAmount: 0,
            oldBalance: due.balance,
            newBalance: 0,
            rentStartDate: due.rentStartDate,
            rentEndDate: due.rentEndDate,
            dueId: due.id,
            ledgerReferenceId: due.ledgerReferenceId,
            dueDate: due.dueDate,
            dueType: due.type,
            doneByUserType: req.userType,
            doneBy: req.id,
          });

          // if (due.type === CONSTANTS.DUES_TYPES.RENT && moment().startOf("day").isBefore(moment(due.rentStartDate).startOf("day"))) {
          //   const occupancy = await occupancyDB.getByTenantId({
          //     tenantId: due.tenantId,
          //   });

          //   if (Number(occupancy.rentalMonths) > 0) {
          //     await occupancyDB.updateRentalMonths({
          //       tenantId: due.tenantId,
          //       clientId: due.clientId,
          //       rentalMonths: (Number(moment(due.rentStartDate).startOf("day").diff(moment(due.rentEndDate).endOf("day"), "months"))) - 1,
          //     });
          //   }
          // }

          if (due.type === CONSTANTS.DUES_TYPES.RENT) {
            const occupancy = await occupancyDB.getByTenantIdAndClientId({
              clientId: due.clientId,
              tenantId: due.tenantId,
            });

            const rentalMonths = await rentalMonthsToBeDeleted(
              occupancy,
              due.rentStartDate,//startDate
              due.rentEndDate//endDate
            );

            if (Number(occupancy.rentalMonths) > 0) {
              let finalRentalMonths = Number(occupancy.rentalMonths) > Number(rentalMonths) 
                ? -rentalMonths
                : -Number(occupancy.rentalMonths);
                
              await occupancyDB.updateRentalMonths({
                tenantId: due.tenantId,
                clientId: due.clientId,
                rentalMonths: finalRentalMonths,
              });
            }
          }

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], Ledger Reference Id [${due.ledgerReferenceId}], Is Evicted [${isEvicted}], Ledger Entry Deleted Successfully`
          );
        } else {
          await ledgerDB.addBalance({
            tenantId: due.tenantId,
            clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            balance: -due.balance,
          });
          await ledgerDB.updateAmount({
            tenantId: due.tenantId,
            clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            amount: due.amount - updatedAmountDiff,
          });
          if (due.type === CONSTANTS.DUES_TYPES.SECURITY && !isEvicted) {
            log.info(
              `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], New Security [${Math.abs(due.amount - updatedAmountDiff)}], Updating Security in Occupancy DB`
            );
            await occupancyDB.updateSecurity({
              tenantId: due.tenantId,
              clientId: clientId,
              security: Math.abs(due.amount - updatedAmountDiff),
            });
          }
          await editLogsDB.add({
            tenantId: due.tenantId,
            clientId: clientId,
            propId: due.propId,
            roomId: due.roomId,
            oldAmount: due.amount,
            newAmount: due.amount - updatedAmountDiff,
            oldBalance: due.balance,
            newBalance: newAmount,
            dueId: due.id,
            ledgerReferenceId: due.ledgerReferenceId,
            rentStartDate: due.rentStartDate,
            rentEndDate: due.rentEndDate,
            dueDate: due.dueDate,
            dueType: due.type,
            doneByUserType: req.userType,
            doneBy: req.id,
          });
        }
      }

      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.DELETE_DUES,
        due.amount,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        null
      );

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], Old Amount [${due.amount}], Old Balance [${due.balance}], New Balance [${newAmount}], Is Temp [${isTemp}], Is Evicted [${isEvicted}], Dues Deleted Successfully`
      );
    } else {
      if (isTemp)
        await duesDB.updateTempAmount({
          id: dueId,
          amount: newAmount,
          balance: newAmount,
        });
      else {
        if (updatedAmountDiff > 0) {
          if (isEvicted) {
            await moveOutDuesDB.updateAmount({
              id: dueId,
              amount: due.amount - updatedAmountDiff,
            });
          } else {
            await duesDB.updateAmount({
              id: dueId,
              amount: due.amount - updatedAmountDiff,
            });
            if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], New Security [${Math.abs(due.amount - updatedAmountDiff)}], Updating Security in Occupancy DB`
              );
              await occupancyDB.updateSecurity({
                tenantId: due.tenantId,
                clientId: clientId,
                security: Math.abs(due.amount - updatedAmountDiff),
              });
            }
            await editLogsDB.add({
              tenantId: due.tenantId,
              clientId: clientId,
              propId: due.propId,
              roomId: due.roomId,
              oldAmount: due.amount,
              newAmount: due.amount - updatedAmountDiff,
              oldBalance: due.balance,
              newBalance: newAmount,
              dueId: due.id,
              ledgerReferenceId: due.ledgerReferenceId,
              rentStartDate: due.rentStartDate,
              rentEndDate: due.rentEndDate,
              dueDate: due.dueDate,
              dueType: due.type,
              doneByUserType: req.userType,
              doneBy: req.id,
            });
          }
          await ledgerDB.updateAmount({
            tenantId: tenantId,
            clientId: clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            amount: due.amount - updatedAmountDiff,
          });
        } else if (updatedAmountDiff < 0) {
          if (isEvicted) {
            await moveOutDuesDB.updateAmount({
              id: dueId,
              amount: due.amount + Math.abs(updatedAmountDiff),
            });
          } else {
            await duesDB.updateAmount({
              id: dueId,
              amount: due.amount + Math.abs(updatedAmountDiff),
            });
            if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
              log.info(
                `[${C}], [${F}], Client Id [${clientId}], New Security [${due.amount + Math.abs(updatedAmountDiff)}], Updating Security in Occupancy DB`
              );
              await occupancyDB.updateSecurity({
                tenantId: due.tenantId,
                clientId: clientId,
                security: due.amount + Math.abs(updatedAmountDiff),
              });
            }
            await editLogsDB.add({
              tenantId: due.tenantId,
              clientId: clientId,
              propId: due.propId,
              roomId: due.roomId,
              oldAmount: due.amount,
              newAmount: due.amount + Math.abs(updatedAmountDiff),
              oldBalance: due.balance,
              newBalance: newAmount,
              dueId: due.id,
              ledgerReferenceId: due.ledgerReferenceId,
              rentStartDate: due.rentStartDate,
              rentEndDate: due.rentEndDate,
              dueDate: due.dueDate,
              dueType: due.type,
              doneByUserType: req.userType,
              doneBy: req.id,
            });
          }
          await ledgerDB.updateAmount({
            tenantId: due.tenantId,
            clientId: clientId,
            referenceId: due.ledgerReferenceId,
            type: due.type,
            amount: due.amount + Math.abs(updatedAmountDiff),
          });
        }

        if (isEvicted) {
          await moveOutDuesDB.updateBalance({ id: dueId, balance: newAmount });
        } else {
          await duesDB.updateBalance({ id: dueId, balance: newAmount });
        }
        await ledgerDB.addBalance({
          tenantId: tenantId,
          clientId: clientId,
          referenceId: due.ledgerReferenceId,
          type: due.type,
          // balance: newAmount,
          balance: updatedAmountDiff * (-1),
        });
      }

      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.EDIT_DUES,
        due.amount,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        newAmount
      );

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], New Balance [${newAmount}], Is Temp [${isTemp}], Is Evicted [${isEvicted}], Dues Updated Successfully`
      );
    }

    if (isEvicted) {
      if (updatedAmountDiff < 0) {
        //add to tenantDues
        await moveOutDB.updateTenantDue({
          tenantId: tenantId,
          tenantDues: Math.abs(updatedAmountDiff),
          propId: due.propId,
          roomId: due.roomId,
        });
      } else {
        // sub from tenantDues
        await moveOutDB.subtractTenantDue({
          clientId: clientId,
          tenantId: tenantId,
          tenantDues: Math.abs(updatedAmountDiff),
          propId: due.propId,
          roomId: due.roomId,
        });
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${due.id}], New Amount [${due.amount - updatedAmountDiff}], New Balance [${newAmount}], Old Balance [${due.balance}], Old Amount [${due.amount}], Amount Diff [${updatedAmountDiff}], ${newAmount === 0 ? "Due Removed Successfully" : "Due Edited Successfully"}`
    );

    return res.status(200).json({
      msg: "Dues 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,
    });
  }
};

dues.AddDues = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "AddDues";

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

    const userType = req.userType;

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], Staff Id [${req.id}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], Client Requested....`
      );
    }

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

    const tenant = await tenantDB.getById({ id: tenantId });
    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], No Occupancy Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }
    const referenceId = await generateLedgerReferenceId({ clientId });
    const dueType = await getDueName(type);
    const dueDescription = await getDueDescription(type);

    let prevBalance = await ledgerDB.getPreviousBalance({
      tenantId,
      clientId,
    });

    await duesDB.add({
      tenantId,
      amount,
      occupancyId: occupancy.id,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
      clientId,
      type,
      dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
      balance: amount,
      ledgerReferenceId: referenceId,
    });
    await ledgerDB.add({
      tenantId,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
      clientId,
      amount,
      balance: amount,
      referenceId: referenceId,
      transactionId: null,
      type,
      rentStartDate: null,
      rentEndDate: null,
      dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
      description: `${dueDescription}`,
    });

    if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      await moveOutDB.updateTenantDue({
        tenantId,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        tenantDues: amount,
      });
    }

    if (prevBalance && prevBalance < 0) {
      await adjustExcessPayments({
        tenantId: tenantId,
        clientId: clientId,
        amountPaid: Math.abs(Number(prevBalance)),
        ledgerReferenceId: referenceId,
      });
    }

    // log.info(
    //   `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], Other Dues Added Successfully`
    // );

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.ADD_DUES,
      amount,
      dueType,
      null,
      null,
      null
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Type [${type}], Other Dues Added Successfully`
    );

    return res.status(200).json({
      msg: "Dues added 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,
    });
  }
};

dues.AddDuesX = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "AddDuesX";

  try {
    const {
      tenantId,
      amount,
      rentDuration,
      type,
      startDate,
      endDate,
      dueDate,
      description = null,
      title,
      adjustFromRefund = 1,
    } = req.body;

    const userType = req.userType;
    let recordedBy = "";

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;
      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], Staff Id [${req.id}], Adjust From Refund [${adjustFromRefund}], Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], Adjust From Refund [${adjustFromRefund}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    const tenant = await tenantDB.getById({ id: tenantId });
    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

    let isAutoAdjustEnabled = isAutoAdjustConfig && isAutoAdjustConfig?.value ? isAutoAdjustConfig?.value : 1;

    const referenceId = await generateLedgerReferenceId({ clientId });
    const dueType = await getDueName(type);
    let dueDescription = await getDueDescription(type);

    if (type === CONSTANTS.DUES_TYPES.RENT) {
      dueDescription = `Advance Rent`;
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenantId,
      clientId: clientId,
    });
    if (!occupancy) {
      // log.info(
      //   `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], No Occupancy Found`
      // );
      // return res
      //   .status(400)
      //   .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      const moveOut = await moveOutDB.getByTenantIdAndClientId({
        tenantId: tenantId,
        clientId: clientId,
      });

      if (!moveOut) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found In Occupancies Or MoveOut`
        );

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      // if (Number(adjustFromRefund) === 1) {
      if (Number(isAutoAdjustEnabled) === 1) {
        await AddMovedOutDues(
          tenantId,
          amount,
          type,
          moveOut,
          clientId!,
          startDate,
          endDate,
          dueDate,
          rentDuration,
          description,
          title,
          dueDescription,
          referenceId,
          recordedBy,
        );
      } else {
        await AddMovedOutDuesWithoutAdjust(
          tenantId,
          amount,
          type,
          moveOut,
          clientId!,
          startDate,
          endDate,
          dueDate,
          rentDuration,
          description,
          title,
          dueDescription,
          referenceId
        );
      }
    } else {
      await AddDues(
        tenantId,
        amount,
        type,
        occupancy,
        clientId!,
        startDate,
        endDate,
        dueDate,
        rentDuration,
        description,
        title,
        dueDescription,
        referenceId,
        isAutoAdjustEnabled,
      );
    }

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.ADD_DUES,
      amount,
      title,
      startDate,
      endDate,
      null
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], Due Added Successfully`
    );

    return res.status(200).json({
      msg: "Dues added 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,
    });
  }
};

dues.AddDuesWithImages = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "AddDuesWithImages";

  const files = req.files as Express.Multer.File[];
  const removeTmpImages = async () => {
    for (const file of files) {
      try {
        await fsPromises.unlink(file.path);
      } catch (err) { }
    }
  };

  try {
    let {
      tenantId,
      amount,
      rentDuration,
      type,
      startDate,
      endDate,
      dueDate,
      description = null,
      title,
      adjustFromRefund = 1,
    } = req.body;

    const userType = req.userType;
    let recordedBy = "";
    let isMovedOut = false;

    if (String(startDate).trim().toLowerCase() === "null" || String(startDate).trim().toLowerCase() === "undefined" || String(startDate).trim().toLowerCase() === "") {
      startDate = null;
    }

    if (String(endDate).trim().toLowerCase() === "null" || String(endDate).trim().toLowerCase() === "undefined" || String(endDate).trim().toLowerCase() === "") {
      endDate = null;
    }

    if (String(dueDate).trim().toLowerCase() === "null" || String(dueDate).trim().toLowerCase() === "undefined" || String(dueDate).trim().toLowerCase() === "") {
      dueDate = null;
    }

    let clientId = req.id;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Amount [${amount}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;
      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], Staff Id [${req.id}], Adjust From Refund [${adjustFromRefund}], Is File Uploaded ${files ? `[Yes], ${files.length} Files` : "[No]"}, Staff Requested....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], Adjust From Refund [${adjustFromRefund}], Is File Uploaded ${files ? `[Yes], ${files.length} Files` : "[No]"}, Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    const tenant = await tenantDB.getById({ id: tenantId });
    if (!tenant) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], No Tenant Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

    let isAutoAdjustEnabled = isAutoAdjustConfig && isAutoAdjustConfig?.value ? isAutoAdjustConfig?.value : 1;

    let referenceId = await generateLedgerReferenceId({ clientId });
    const dueType = await getDueName(type);
    let dueDescription = await getDueDescription(type);

    if (type === CONSTANTS.DUES_TYPES.RENT) {
      dueDescription = `Advance Rent`;
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId: tenantId,
      clientId: clientId,
    });
    if (!occupancy) {
      // log.info(
      //   `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], No Occupancy Found`
      // );
      // return res
      //   .status(400)
      //   .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      const moveOut = await moveOutDB.getByTenantIdAndClientId({
        tenantId: tenantId,
        clientId: clientId,
      });

      if (!moveOut) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found In Occupancies Or MoveOut`
        );

        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      // if (Number(adjustFromRefund) === 1) {
      if (Number(isAutoAdjustEnabled) === 1) {
        await AddMovedOutDues(
          Number(tenantId),
          Number(amount),
          Number(type),
          moveOut,
          clientId!,
          startDate,
          endDate,
          dueDate,
          rentDuration,
          description,
          title,
          dueDescription,
          referenceId,
          recordedBy,
        );
      } else {
        await AddMovedOutDuesWithoutAdjust(
          Number(tenantId),
          Number(amount),
          Number(type),
          moveOut,
          clientId!,
          startDate,
          endDate,
          dueDate,
          rentDuration,
          description,
          title,
          dueDescription,
          referenceId
        );
      }
      isMovedOut = true;
    } else {
      await AddDues(
        Number(tenantId),
        Number(amount),
        Number(type),
        occupancy,
        clientId!,
        startDate,
        endDate,
        dueDate,
        rentDuration,
        description,
        title,
        dueDescription,
        referenceId,
        isAutoAdjustEnabled,
      );
    }

    if (files) {
      let i = 1; //for image indexing
      if (Number(type) === CONSTANTS.DUES_TYPES.SECURITY) {
        const isSecurityAdded = await ledgerDB.getByTenantIdAndDueType({
          tenantId,
          clientId,
          type: CONSTANTS.DUES_TYPES.SECURITY,
        });

        if (isSecurityAdded) {
          referenceId = isSecurityAdded.referenceId;
        }
      }
      for (let file of files) {
        const folderName = `Due_Images`;
        const folderPath = `uploads/documents/${clientId}/${tenantId}/${folderName}`;
        const fileExtension = file.mimetype.split("/")[1];
        const filename = `Image${i}_${referenceId}.${fileExtension}`;

        if (!fs.existsSync(folderPath)) {
          await fsPromises.mkdir(folderPath, { recursive: true });
        }
  
        const oldPath = `uploads/tmp/${file.filename}`;
        const newPath = `${folderPath}/${filename}`;
        let url = `${process.env.UPLOAD_PATH}/documents/${clientId}/${tenantId}/${folderName}/${filename}`;

        await fsPromises.copyFile(oldPath, newPath);

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
        );

        const docId = await documentDB.addDoc({
          tenantId,
          clientId,
          propId: occupancy?.propId,
          roomId: occupancy?.roomId,
          type: CONSTANTS.DOCUMENT_TYPES.DUE_IMAGE,
          value: url,
          ledgerReferenceId: referenceId,
        });

        if (isMovedOut) {
          await documentDB.updateMoveOutById({
            id: docId,
            moveOut: 1,
          });
        }

        i += 1;
      }
    }

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.ADD_DUES,
      amount,
      title,
      startDate,
      endDate,
      null
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Amount [${amount}], Rent Duration [${rentDuration}], Type [${type}], Start Date [${startDate}], End Date [${endDate}], Due Date [${dueDate}], Description [${description}], Title [${title}], Due Added Successfully`
    );

    return res.status(200).json({
      msg: "Dues added 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,
    });
  }
};

//Not Being Use -- 2026-01-14
dues.MarkPaid = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "MarkPaid";

  try {
    let {
      id,
      mode,
      collectionDate,
      amount,
      ledgerReferenceId,
      paymentType,
      transactionId,
    } = req.body;

    const userType = req.userType;
    // let excessPayment = 0;
    let clientId = req.id;
    let dueDescription = "";
    let receiptDescription = "";
    let recordedBy = "";
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], Amount [${amount}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], Amount [${amount}], Staff Id [${req.id}], Payment Type [${paymentType}], Transaction Id [${transactionId}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], Amount [${amount}], Payment Type [${paymentType}], Transaction Id [${transactionId}], Client Requested....`
      );
    }

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

    if (req.userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    const dues = await duesDB.getByIdForTransaction({ id });
    if (!dues) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Amount [${amount}], No Due Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const tenant = await tenantDB.getById({ id: dues[0].tenantId });
    const tenantId = tenant.id;
    const property = await propertyDB.getById({ id: dues[0].propId });
    const room = await roomDB.getById({ id: dues[0].roomId });
    const occupancy = await occupancyDB.getById({ id: dues[0].occupancyId });

    let allDues = await duesDB.getByIdForTransaction({ id });
    if (!allDues) {
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], User Type [${userType}], User Id [${req.id}], No Dues Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    let duesStatsArr = [];
    let dueStats = {};
    let excessDueStats = {};
    let excessDueflag = false;

    if (paymentType == 2) {
      // excessPayment = amount;
      amount = amount + allDues[0].balance;
    }
    let amtPaid = amount;
    let count = 1;
    let lengthOfDues = allDues.length;
    let security = await allDues.find(
      (due: any) => due.type === CONSTANTS.DUES_TYPES.SECURITY
    );
    let receipts = [];

    const transGId: string = await generateTransId();
    let transTitle: string = "";
    if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
      transTitle = allDues[0].title;
      if (transTitle === null) {
        transTitle = await getDueDescription(allDues[0].type);
      }
    } else if (
      allDues[0].title &&
      allDues[0].title.toLowerCase().includes("advance rent")
    ) {
      transTitle = "Advance Rent";
    } else {
      transTitle = await getDueDescription(allDues[0].type);
    }
    const transId = await transactionDB.add({
      gId: transactionId || transGId,
      clientId: client.id,
      tenantId: tenantId,
      roomId: allDues[0].roomId,
      propId: allDues[0].propId,
      amount: amount,
      name:
        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"
            )}`,
      type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
      transactionFor: allDues[0].type,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      dueDate: allDues[0].dueDate, //lengthOfDues - 1
      receipt: "",
      mode: Number(mode),
      collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
      propName: property.name,
      roomNum: room.roomNum,
      ledgerReferenceId: ledgerReferenceId,
      recordedBy: recordedBy,
    });

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

    if (security && security.balance > 0) {
      let due = security;
      dueDescription = await getDueDescription(due.type);
      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) {
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -due.balance,
            balance: -(amtPaid - due.balance),
            referenceId: ledgerReferenceId,
            transactionId: transId,
            type: due.type,
            rentStartDate: null,
            rentEndDate: null,
            dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
            description: dueDescription,
          });
          dueStats = { title: dueDescription, amount: due.balance };
          duesStatsArr.push(dueStats);
          receiptDescription = dueDescription;
          amtPaid = 0;
        } else {
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -due.balance,
            balance: 0,
            referenceId: ledgerReferenceId,
            transactionId: transId,
            type: due.type,
            rentStartDate: null,
            rentEndDate: null,
            dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
            description: dueDescription,
          });
          dueStats = { title: dueDescription, amount: due.balance };
          duesStatsArr.push(dueStats);
          receiptDescription = dueDescription;
          amtPaid -= due.balance;
        }
        await duesDB.removeDue({ id: due.id });
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Reference Id [${ledgerReferenceId}], Due Id [${due.id}], [Security Adjust], [Due Deleted], Transaction has been recorded successfully`
        );
      } else if (security.balance > amtPaid) {
        await ledgerDB.add({
          tenantId,
          roomId: occupancy.roomId,
          propId: occupancy.propId,
          clientId: occupancy.clientId,
          amount: -amtPaid,
          balance: due.balance - amtPaid,
          referenceId: ledgerReferenceId,
          transactionId: transId,
          type: due.type,
          rentStartDate: null,
          rentEndDate: null,
          dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
          description: dueDescription,
        });
        dueStats = { title: dueDescription, amount: amtPaid };
        duesStatsArr.push(dueStats);
        receiptDescription = dueDescription;
        await duesDB.updateBalance({
          id: due.id,
          balance: due.balance - amtPaid,
        });
        amtPaid = 0;
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], Due Id [${due.id}], [Security Adjust], [Due Updated], Transaction has been recorded successfully`
        );
      }
      count++;
    }

    if (amtPaid != 0) {
      for (const due of allDues) {
        // let dueTypeName = await getDueName(due.type);
        if (amtPaid == 0) break;
        if (due.type === CONSTANTS.DUES_TYPES.SECURITY) {
          continue;
        }

        let dueDescription = "";
        // if(due.type == CONSTANTS.DUES_TYPES.RENT){
        //   if(due.amount != amtPaid){
        //     dueDescription = "Partial Rent";
        //   }
        //   else{
        //     dueDescription = await getDueDescription(due.type)
        //   }
        // }
        // else{
        //   dueDescription = await getDueDescription(due.type)
        // }
        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);
        }

        if (due.balance > amtPaid) {
          dueDescription = "Partial payment for " + dueDescription;
        } 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 (!due) {
        //   log.info(
        //     `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], Due Id [${due.id}], User Type [${userType}], User Id [${userId}], No Due Found`
        //   );
        //   continue;
        // }
        if (amtPaid <= due.balance) {
          await ledgerDB.add({
            tenantId,
            roomId: occupancy.roomId,
            propId: occupancy.propId,
            clientId: occupancy.clientId,
            amount: -amtPaid,
            balance: due.balance - amtPaid,
            referenceId: ledgerReferenceId,
            transactionId: transId,
            type: due.type,
            rentStartDate: due.rentStartDate,
            rentEndDate: due.rentEndDate,
            dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
            description: dueDescription,
          });
          receiptDescription = dueDescription;
          // receiptDescription =
          //   dueDescription + " for " + moment(due.dueDate).format("MMM YYYY");
          if (amtPaid == due.balance) {
            await duesDB.removeDue({ id: due.id });
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], Due Id [${due.id}], Due Type [${due.type}], [Due Deleted]`
            );
          } else {
            await duesDB.updateBalance({
              id: due.id,
              balance: due.balance - amtPaid,
            });
            dueStats = { title: dueDescription, amount: amtPaid };
            duesStatsArr.push(dueStats);
            log.info(
              `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], Due Id [${due.id}], Due Type [${due.type}], [Due Updated]`
            );
          }
          amtPaid = 0;
        } else {
          if (count == lengthOfDues) {
            amtPaid -= due.balance;
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: -amtPaid,
              referenceId: ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription,
            });
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            receiptDescription = dueDescription;
            // receiptDescription =
            //   dueDescription + " for " + moment(due.dueDate).format("MMM YYYY");
            //break;
          } else {
            await ledgerDB.add({
              tenantId,
              roomId: occupancy.roomId,
              propId: occupancy.propId,
              clientId: occupancy.clientId,
              amount: -due.balance,
              balance: 0,
              referenceId: ledgerReferenceId,
              transactionId: transId,
              type: due.type,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription,
            });
            dueStats = { title: dueDescription, amount: due.balance };
            duesStatsArr.push(dueStats);
            receiptDescription = dueDescription;
            // receiptDescription =
            //   dueDescription + " for " + moment(due.dueDate).format("MMM YYYY");
            amtPaid -= due.balance;
          }
          await duesDB.removeDue({ id: due.id });
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], Due Id [${due.id}], Due Type [${due.type}], [Due Deleted]`
          );
        }
        count++;
        log.info(
          `[${C}], [${F}], Tenant Id [${tenantId}], Reference Id [${ledgerReferenceId}], Due Id [${due.id}], Due Type [${due.type}], Transaction has been recorded successfully`
        );
      }
    }

    // Too settle the excess payment from the pre exsisting dues of that tenant
    if (paymentType == 2) {
      let allExsistingDues = await duesDB.getByTenantIdAndPropId({
        tenantId,
        propId: occupancy.propId,
      });

      let count = 1;

      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);
            }
            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 settings = await settingsDB.getByClientIdAndPropId({
      clientId,
      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);

    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;
    }
    log.info(`[${C}], [${F}], Logo [${logo}], Flat Name [${flatName}]`);

    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;
    }

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

    if(0 != isGstEnabled && 0 != invoiceNo) {
      await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
    } else {
      await transactionDB.updateReceipt({
        id: transId,
        receipt,
      });
    }
    // const receipt = await createReceipt({
    //   title: receiptDescription,
    //   roomNum: room.roomNum,
    //   propName: property.name,
    //   dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
    //   mode: getModeName(Number(mode)),
    //   transGId,
    //   paidDate: moment(collectionDate).format("DD MMM, YYYY"),
    //   month: moment(allDues[0].dueDate).format("MMM, YYYY"),
    //   tenantName: tenant?.name || "",
    //   amount: Number(amount || 0),
    //   address: `${property?.address}`,
    //   landlord: client?.name || "",
    //   landlordNumber: client?.mobile || "",
    //   logo: logo,
    //   "landlord-pan": "",
    //   occupancy,
    //   flatName,
    //   propType: property.type,
    // });
    // await transactionDB.updateReceipt({
    //   id: transId,
    //   receipt,
    // });
    receipts.push({
      name: transTitle,
      receipt,
      paidDate: moment().format("DD MMM, YYYY"),
      amount: Number(amtPaid || 0),
      mode: CONSTANTS.TRANSACTION_MODES.OFFLINE,
      transGId,
    });

    let dueType = await getDueName(allDues[0].dueType);

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
      amount,
      dueType,
      null,
      null,
      null
    );

    if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      await moveOutDB.subtractTenantDue({
        clientId,
        tenantId,
        tenantDues: amount,
        propId: allDues[0].propId,
        roomId: allDues[0].roomId,
      });
    }

    const tenantDues = await duesDB.getByTenantId({
      tenantId,
    });
    if (!tenantDues) {
      await tenantDB.updateLastReminded({
        id: 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 (notiSettings && Number(notiSettings.rentReminder) === 1 && Number(notiSettings.markPaidNotification) === 1) {
      sendWhatsappPaymentConfirmationClient(
        client.mobile,
        client.name,
        tenant.name,
        amount,
        property.name,
        `${flatName} (${room.roomNum})`,
        dueName,
        moment(collectionDate).format("DD MMM YY HH:mm:ss"),
        modeVal,
        recordedBy,
        Number(clientId),
      );
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Receipt [${receipt}], Whatsapp messages sent successfully`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Is Receipt Generated [${receipt}],  Paid Amount [${amount}],  ${
        amount <= 0 ? "Due marked as paid successfully" : "Due paid partially."
      }`
    );

    return res.status(200).json({
      msg: "Due marked as paid 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,
    });
  }
};

//Not Being Used -- 2026-01-14
dues.MarkPaidX = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "MarkPaidX";

  try {
    let {
      id,
      mode,
      collectionDate,
      amount,
      ledgerReferenceId,
      paymentType,
      transactionId,
      tenantId,
      isEvicted,
      paymentAccountNo = null,
      paymentAccountName = null,
    } = req.body;

    // if (collectionDate.includes("T")) {
    //   collectionDate = collectionDate.split("T")[0];
    //   collectionDate = `${collectionDate} ${moment().format("HH:mm:ss")}`;
    // }

    const oldCollectionDate = collectionDate;

    collectionDate = moment(collectionDate).utc();
    collectionDate = collectionDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");

    const userType = req.userType;
    // let excessPayment = 0;
    let clientId = req.id;
    let dueDescription = "";
    let receiptDescription: any = "";
    let recordedBy = "";
    // let isEvicted = false;
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], UTC Collection Date [${oldCollectionDate}], Amount [${amount}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;
      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], UTC Collection Date [${oldCollectionDate}], Amount [${amount}], Staff Id [${req.id}], Payment Type [${paymentType}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Payment Account Number [${paymentAccountNo}], Payment Account Name [${paymentAccountName}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], UTC Collection Date [${oldCollectionDate}], Amount [${amount}], Payment Type [${paymentType}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Payment Account Number [${paymentAccountNo}], Payment Account Name [${paymentAccountName}], Client Requested....`
      );
    }

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

    if (req.userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

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

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

    let allDues = await duesDB.getByIdForTransaction({ id });
    if (isEvicted) {
      allDues = await moveOutDuesDB.getByIdForTransaction({ id });
    }

    if (!allDues) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], User Type [${userType}], User Id [${req.id}], Is Evicted [${isEvicted}], No Dues Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const tenant = await tenantDB.getById({ id: tenantId });
    const property = await propertyDB.getById({ id: allDues[0].propId });
    const room = await roomDB.getById({ id: allDues[0].roomId });

    let duesStatsArr: any = [];
    let dueStats: any = {};
    let excessDueStats = {};
    let excessDueflag = false;

    if (paymentType == 2) {
      // excessPayment = amount;
      amount = amount + allDues[0].balance;
    }
    let amtPaid = amount;

    const transGId: string = await generateTransId();
    let transTitle: string = "";
    if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
      transTitle = allDues[0].title;
      if (transTitle === null) {
        transTitle = await getDueDescription(allDues[0].type);
      }
    } else if (
      allDues[0].title &&
      allDues[0].title.toLowerCase().includes("advance rent")
    ) {
      transTitle = "Advance Rent";
    } else {
      transTitle = await getDueDescription(allDues[0].type);
    }
    const transId = await transactionDB.add({
      gId: transGId,
      clientId: client.id,
      tenantId: tenantId,
      roomId: allDues[0].roomId,
      propId: allDues[0].propId,
      amount: amount,
      name:
        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"
            )}`,
      type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
      transactionFor: allDues[0].type,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      dueDate: allDues[0].dueDate, //lengthOfDues - 1
      receipt: "",
      mode: Number(mode),
      // collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
      collectionDate: collectionDate,
      propName: property.name,
      roomNum: room.roomNum,
      ledgerReferenceId: ledgerReferenceId,
      recordedBy: recordedBy,
      discount: amount >= allDues[0].balance ? allDues[0].discount : 0,
      paymentAccountNo: paymentAccountNo || null,
      paymentAccountName: paymentAccountName || null,
      title: allDues[0].title || null,
      bankRefNum: transactionId ? transactionId.substring(0, 150) : null,
    });

    await setTransactionTallyStatus(
      Number(clientId),
      transId,
      CONSTANTS.TALLY_STATUS.PENDING,
    );

    await setTransactionTallyBillRef(
      Number(clientId),
      transId,
      allDues[0].tallyBillRef,
    );

    if (isEvicted) {
      ({ dueStats, duesStatsArr, receiptDescription } =
        await MarkPaidForMovedOut(
          tenantId,
          amtPaid,
          allDues,
          occupancy,
          ledgerReferenceId,
          transId
        ));
    } else {
      ({ dueStats, duesStatsArr, receiptDescription } = await MarkPaid(
        tenantId,
        amtPaid,
        allDues,
        occupancy,
        ledgerReferenceId,
        transId,
        0, //markedFromSecurity placeholder
      ));
    }

    // Too settle the excess payment from the pre exsisting dues of that tenant
    if (paymentType == 2) {
      let allExsistingDues = await duesDB.getByTenantIdAndPropId({
        tenantId,
        propId: occupancy.propId,
      });

      if (isEvicted) {
        allExsistingDues = await moveOutDuesDB.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;
            }
            if (isEvicted) {
              await adjustExcessPaymentsForMovedOutX({
                tenantId: tenantId,
                clientId: client.id,
                amountPaid: Math.abs(Number(prevBalance)),
                ledgerReferenceId: due.ledgerReferenceId,
                markedFromSecurity: 1,
                recordedBy: recordedBy,
              });
            } else {
              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 = duesStatsArr.find((item: any) => item.title === "Discount");
      if (dueStatsArrDiscountEntry) {
        dueStatsArrDiscountEntry.amount -= discountAmountForExcess;
      }
    }

    let settings = await settingsDB.getByClientIdAndPropId({
      clientId,
      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);

    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;
    }
    log.info(`[${C}], [${F}], Logo [${logo}], Flat Name [${flatName}]`);

    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;
    }
    const receipt = await createReceiptMultipleDues({
      // title: receiptDescription,
      title: `${transId}_${transGId}`,
      roomNum: room.roomNum,
      propName: property.name,
      dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
      mode: getModeName(Number(mode)),
      transGId: transGId,
      paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
      month: moment(allDues[0].dueDate).format("MMM, YYYY"),
      tenantName: tenant?.name || "",
      amount: Number(amount || 0),
      address: `${property?.address}`,
      landlord: businessName || "",
      landlordNumber: property?.ownerMobile || "",
      logo: logo,
      dueStats: duesStatsArr,
      "landlord-pan": "",
      occupancy,
      flatName,
      propType: property.type,
      isGstEnabled,
      transactionInvoiceNo,
      gstNo,
    });

    if(0 != isGstEnabled && 0 != invoiceNo) {
      await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
    } else {
      await transactionDB.updateReceipt({
        id: transId,
        receipt,
      });
    }

    let dueType = await getDueName(allDues[0].type);
    if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
      dueType = allDues[0].title;
    }

    const modeName = mode === CONSTANTS.TRANSACTION_MODES.OFFLINE ? "cash" : mode === CONSTANTS.TRANSACTION_MODES.UPI ? "upi" : mode === CONSTANTS.TRANSACTION_MODES.CARD ? "card" : "netbanking";
    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
      amount,
      dueType,
      allDues[0].rentStartDate,
      allDues[0].rentEndDate,
      modeName,
    );

    if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      await moveOutDB.subtractTenantDue({
        clientId,
        tenantId,
        tenantDues: amount,
        propId: allDues[0].propId,
        roomId: allDues[0].roomId,
      });
    }

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

      if (isEvicted) {
        await occupancyDB.updateLastReminded({
          clientId,
          tenantId,
          lastRemindedOn: null,
        });
      } else {
        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 (notiSettings && Number(notiSettings.rentReminder) === 1 && Number(notiSettings.markPaidNotification) === 1) {
      sendWhatsappPaymentConfirmationClient(
        client.mobile,
        client.name,
        tenant.name,
        amount,
        property.name,
        `${flatName} (${room.roomNum})`,
        dueName,
        moment(collectionDate).format("DD MMM YY HH:mm:ss"),
        modeVal,
        recordedBy,
        Number(clientId),
      );
      log.info(
        `[${C}], [${F}], Tenant Id [${tenantId}], Receipt [${receipt}], Whatsapp messages sent successfully`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Is Receipt Generated [${receipt}],  Paid Amount [${amount}],  ${
        amount <= 0 ? "Due marked as paid successfully" : "Due paid partially."
      }`
    );

    return res.status(200).json({
      msg: "Due marked as paid 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,
    });
  }
};

dues.MarkPaidWithReceipt = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "MarkPaidWithReceipt";

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    let {
      id,
      mode,
      collectionDate,
      amount,
      ledgerReferenceId,
      paymentType,
      transactionId,
      tenantId,
      isEvicted,
      paymentAccountNo = null,
      paymentAccountName = null,
      remarks = null,
      useSecurityDeposit=0,
      depositAdjusted=0,
    } = req.body;

    // if (collectionDate.includes("T")) {
    //   collectionDate = collectionDate.split("T")[0];
    //   collectionDate = `${collectionDate} ${moment().format("HH:mm:ss")}`;
    // }

    amount = Number(amount);
    useSecurityDeposit = Number(useSecurityDeposit);
    depositAdjusted = Number(depositAdjusted);
    paymentType = Number(paymentType);
    mode = Number(mode);
    tenantId = Number(tenantId);
    id = Number(id);

    let amountLeft = 0;

    if (typeof isEvicted === "string") {
      isEvicted = isEvicted.toLowerCase() === "true";
    }

    const oldCollectionDate = collectionDate;

    collectionDate = moment(collectionDate).utc();
    collectionDate = collectionDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");

    const userType = req.userType;
    // let excessPayment = 0;
    let clientId = req.id;
    let dueDescription = "";
    let receiptDescription: any = "";
    let recordedBy = "";
    // let isEvicted = false;
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], UTC Collection Date [${oldCollectionDate}], Amount [${amount}], Staff Id [${req.id}], No Staff Found`
        );

        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;
      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], UTC Collection Date [${oldCollectionDate}], Amount [${amount}], Staff Id [${req.id}], Payment Type [${paymentType}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Payment Account Number [${paymentAccountNo}], Payment Account Name [${paymentAccountName}], Client Remarks [${remarks}], Using Security [${useSecurityDeposit}], Security Used [${depositAdjusted}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"}, Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${id}], Mode [${mode}], Collection Date [${collectionDate}], UTC Collection Date [${oldCollectionDate}], Amount [${amount}], Payment Type [${paymentType}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Payment Account Number [${paymentAccountNo}], Payment Account Name [${paymentAccountName}], Client Remarks [${remarks}], Using Security [${useSecurityDeposit}], Security Used [${depositAdjusted}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"}, Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Amount [${amount}], No Client Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (req.userType === CONSTANTS.USER_TYPE.CLIENT) {
      const parentClient = await clientDB.getById({id: req.parentClientId});
      recordedBy = parentClient ? parentClient?.name : client.name;
    }

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

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

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    let allDues = await duesDB.getByIdForTransaction({ id });
    if (isEvicted) {
      allDues = await moveOutDuesDB.getByIdForTransaction({ id });
    }

    if (!allDues) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], LedgerReferenceId [${ledgerReferenceId}], User Type [${userType}], User Id [${req.id}], Is Evicted [${isEvicted}], No Dues Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (Number(paymentType) !== 2) {
      amountLeft = allDues[0].balance - (Number(amount) || 0) - (Number(depositAdjusted) || 0);
      amountLeft = amountLeft < 0 ? 0 : amountLeft;
    }
    
    if (Number(useSecurityDeposit) === 1 && allDues[0].type === CONSTANTS.DUES_TYPES.SECURITY) {
      log.info(
        `[${C}], [${F}], Client Id [${allDues[0].clientId}], Using Security [${useSecurityDeposit}], Due Type [${allDues[0].type}], Cannot Mark Security Due From Security`
      );

      return res.status(400).json({
        msg: "Cannot use security to pay this due",
        isSuccess: false,
      });
    }

    const tenant = await tenantDB.getById({ id: tenantId });
    const property = await propertyDB.getById({ id: allDues[0].propId });
    const room = await roomDB.getById({ id: allDues[0].roomId });

    let duesStatsArr: any = [];
    let dueStats: any = {};
    let excessDueStats = {};
    let excessDueflag = false;

    if (paymentType == 2) {
      // excessPayment = amount;
      amount = amount + allDues[0].balance;
    }
    let amtPaid = amount;

    const transGId: string = await generateTransId();
    let transTitle: string = "";
    if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
      transTitle = allDues[0].title;
      if (transTitle === null) {
        transTitle = await getDueDescription(allDues[0].type);
      }
    } else if (
      allDues[0].title &&
      allDues[0].title.toLowerCase().includes("advance rent")
    ) {
      transTitle = "Advance Rent";
    } else {
      transTitle = await getDueDescription(allDues[0].type);
    }

    let transId = null;

    if (amount > 0) {
      transId = await transactionDB.add({
        gId: transGId,
        clientId: client.id,
        tenantId: tenantId,
        roomId: allDues[0].roomId,
        propId: allDues[0].propId,
        amount: amount,
        name:
          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"
              )}`,
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: allDues[0].type,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: allDues[0].dueDate, //lengthOfDues - 1
        receipt: "",
        mode: Number(mode),
        // collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
        collectionDate: collectionDate,
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: ledgerReferenceId,
        recordedBy: recordedBy,
        discount: amount >= allDues[0].balance ? allDues[0].discount : 0,
        paymentAccountNo: paymentAccountNo || null,
        paymentAccountName: paymentAccountName || null,
        title: allDues[0].title || null,
        bankRefNum:  transactionId ? transactionId.substring(0, 150) : null,
        remarks: remarks || null,
        isFinanciallyApplicable: 1,
      });

      await setTransactionTallyStatus(
        Number(clientId),
        transId,
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await setTransactionTallyBillRef(
        Number(clientId),
        transId,
        allDues[0].tallyBillRef,
      );
    }

    if (file) {
      const folderName = `transaction_${transId}`;
      const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `uploadedDoc.${fileExtension}`;

      if (!fs.existsSync(folderPath)) {
        await fsPromises.mkdir(folderPath, { recursive: true });
      }

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      const url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

      await transactionDB.updateUploadedDoc({
        id: transId,
        docs: url,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Transaction Id [${transId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
      );
    }

    if (isEvicted) {
      ({ dueStats, duesStatsArr, receiptDescription } =
        await MarkPaidForMovedOut(
          tenantId,
          amtPaid,
          allDues,
          occupancy,
          ledgerReferenceId,
          transId
        ));
    } else {
      ({ dueStats, duesStatsArr, receiptDescription } = await MarkPaid(
        tenantId,
        amtPaid,
        allDues,
        occupancy,
        ledgerReferenceId,
        transId,
        0, //placeholder for useSecurityDeposit
      ));
    }

    // Too settle the excess payment from the pre exsisting dues of that tenant
    if (paymentType == 2) {
      let allExsistingDues = await duesDB.getByTenantIdAndPropId({
        tenantId,
        propId: occupancy.propId,
      });

      if (isEvicted) {
        allExsistingDues = await moveOutDuesDB.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;
            }
            if (isEvicted) {
              await adjustExcessPaymentsForMovedOutX({
                tenantId: tenantId,
                clientId: client.id,
                amountPaid: Math.abs(Number(prevBalance)),
                ledgerReferenceId: due.ledgerReferenceId,
                markedFromSecurity: 1,
                recordedBy: recordedBy,
              });
            } else {
              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 = duesStatsArr.find((item: any) => item.title === "Discount");
      if (dueStatsArrDiscountEntry) {
        dueStatsArrDiscountEntry.amount -= discountAmountForExcess;
      }
    }

    let settings = await settingsDB.getByClientIdAndPropId({
      clientId,
      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);

    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;
    }

    let receipt = null;
    if (amount > 0) {
      // receipt = await createReceiptMultipleDues({
      //   // title: receiptDescription,
      //   title: `${transId}_${transGId}`,
      //   roomNum: room.roomNum,
      //   propName: property.name,
      //   dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
      //   mode: getModeName(Number(mode)),
      //   transGId: transGId,
      //   paidDate: moment(collectionDate).format("DD MMM, YYYY"),
      //   month: moment(allDues[0].dueDate).format("MMM, YYYY"),
      //   tenantName: tenant?.name || "",
      //   amount: Number(amount || 0),
      //   address: `${property?.address}`,
      //   landlord: businessName || "",
      //   landlordNumber: property?.ownerMobile || "",
      //   logo: logo,
      //   dueStats: duesStatsArr,
      //   "landlord-pan": "",
      //   occupancy,
      //   flatName,
      //   propType: property.type,
      //   isGstEnabled,
      //   transactionInvoiceNo,
      //   gstNo,
      // });
      receipt = await generateRentReciept({
        // title: receiptDescription,
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
        mode: getModeName(Number(mode)),
        transGId: transGId,
        paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
        month: moment(allDues[0].dueDate).format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: Number(amount || 0),
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        dueStats: duesStatsArr,
        "landlord-pan": "",
        occupancy,
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy,
      });
  
      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }
    }

    //Mark From Security Handling
    if (useSecurityDeposit === 1) {
      let allDues = await duesDB.getByIdForTransaction({ id });
      const transGId: string = await generateTransId();
      let transTitle: string = "";
      if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
        transTitle = allDues[0].title;
        if (transTitle === null) {
          transTitle = await getDueDescription(allDues[0].type);
        }
      } else if (
        allDues[0].title &&
        allDues[0].title.toLowerCase().includes("advance rent")
      ) {
        transTitle = "Advance Rent";
      } else {
        transTitle = await getDueDescription(allDues[0].type);
      }
      const transId = await transactionDB.add({
        gId: transGId,
        clientId: client.id,
        tenantId: tenantId,
        roomId: allDues[0].roomId,
        propId: allDues[0].propId,
        amount: depositAdjusted,
        name:
          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"
              )}`,
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: allDues[0].type,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: allDues[0].dueDate, //lengthOfDues - 1
        receipt: "",
        mode: CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY,
        // collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
        collectionDate: collectionDate,
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: ledgerReferenceId,
        recordedBy: recordedBy,
        discount: depositAdjusted >= allDues[0].balance ? allDues[0].discount : 0,
        paymentAccountNo: paymentAccountNo || null,
        paymentAccountName: paymentAccountName || null,
        title: allDues[0].title || null,
        bankRefNum:  transactionId ? transactionId.substring(0, 150) : null,
        remarks: remarks || null,
        isFinanciallyApplicable: 0,
      });

      await setTransactionTallyStatus(
        Number(clientId),
        transId,
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await setTransactionTallyBillRef(
        Number(clientId),
        transId,
        allDues[0].tallyBillRef,
      );


      ({ dueStats, duesStatsArr, receiptDescription } = await MarkPaid(
        tenantId,
        depositAdjusted,
        allDues,
        occupancy,
        ledgerReferenceId,
        transId,
        useSecurityDeposit,
      ));

      // const receipt = await createReceiptMultipleDues({
      //   // title: receiptDescription,
      //   title: `${transId}_${transGId}`,
      //   roomNum: room.roomNum,
      //   propName: property.name,
      //   dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
      //   mode: getModeName(CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY),
      //   transGId: transGId,
      //   paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
      //   month: moment(allDues[0].dueDate).format("MMM, YYYY"),
      //   tenantName: tenant?.name || "",
      //   amount: depositAdjusted,
      //   address: `${property?.address}`,
      //   landlord: businessName || "",
      //   landlordNumber: property?.ownerMobile || "",
      //   logo: logo,
      //   dueStats: duesStatsArr,
      //   "landlord-pan": "",
      //   occupancy,
      //   flatName,
      //   propType: property.type,
      //   isGstEnabled,
      //   transactionInvoiceNo,
      //   gstNo,
      // });

      receipt = await generateRentReciept({
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(allDues[0].dueDate).format("MMM, YYYY"),
        mode: getModeName(CONSTANTS.TRANSACTION_MODES.ADJUSTED_FROM_SECURITY),
        transGId: transGId,
        paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
        month: moment(allDues[0].dueDate).format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: depositAdjusted,
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        dueStats: duesStatsArr,
        "landlord-pan": "",
        occupancy,
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy
      });
  
      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }
      
      let dueType = await getDueName(allDues[0].type);
      if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
        dueType = allDues[0].title;
      }
  
      const modeName = "adjusted from security";
      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        tenantId,
        CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
        depositAdjusted,
        dueType,
        allDues[0].rentStartDate,
        allDues[0].rentEndDate,
        modeName,
      );
    }

    let dueType = await getDueName(allDues[0].type);
    if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
      dueType = allDues[0].title;
    }

    const modeName = mode === CONSTANTS.TRANSACTION_MODES.OFFLINE ? "cash" : mode === CONSTANTS.TRANSACTION_MODES.UPI ? "upi" : mode === CONSTANTS.TRANSACTION_MODES.CARD ? "card" : "netbanking";

    if (amount > 0) {
      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        tenantId,
        CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
        amount,
        dueType,
        allDues[0].rentStartDate,
        allDues[0].rentEndDate,
        modeName,
      );
    }

    if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      await moveOutDB.subtractTenantDue({
        clientId,
        tenantId,
        tenantDues: amount,
        propId: allDues[0].propId,
        roomId: allDues[0].roomId,
      });
    }

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

      if (isEvicted) {
        await occupancyDB.updateLastReminded({
          clientId,
          tenantId,
          lastRemindedOn: null,
        });
      } else {
        await occupancyDB.updateLastReminded({
          clientId,
          tenantId,
          lastRemindedOn: null,
        });
      }
    }

    const combinedAmount = Number(amount) + Number(depositAdjusted);

    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) {
      // const regIds = await clientDB.getRegIds({
      //   id: clientId,
      // });

      // if (regIds && regIds.length > 0) {
      //   for (let regId of regIds) {
      //     await sendNotification({
      //       title: `Due amount received`,
      //       message: `Tenant Name: ${tenant.name}\nAmount: ₹${amount}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment(collectionDate).format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: ${recordedBy}`,
      //       regId: regId.regId,
      //       userId: client.id,
      //       userType: CONSTANTS.USER_TYPE.CLIENT,
      //       notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.OFFLINE_PAYMENT_CONFIRM,
      //       clientId: client.id,
      //     });
      //   }
      // } else {
        // }
        await sendNotification({
          title: `Due amount received`,
          message: `Tenant Name: ${tenant.name}\nAmount: ₹${combinedAmount}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment(collectionDate).format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: ${recordedBy}`,
          regId: client.regId,
          userId: client.id,
          userType: CONSTANTS.USER_TYPE.CLIENT,
          notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.OFFLINE_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: ₹${combinedAmount}\nProperty: ${property.name}\nRoom No: ${flatName} (${room.roomNum})\nDue Type: ${dueName}\nPayment Date & Time: ${moment(collectionDate).format("DD MMM YY HH:mm:ss")}\nMode: ${modeVal}\nRecorded By: ${recordedBy}`,
          regId: partner.regId,
          userId: partner.id,
          userType: CONSTANTS.USER_TYPE.CLIENT,
          notiCategory: CONSTANTS.NOTIFICATION_CATEGORY.OFFLINE_PAYMENT_CONFIRM,
          clientId: client.id,
        });
      }
    }
    let whatsappAgreegrator = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.WHATSAPP_AGGREGATOR
    });
    if (notiSettings && Number(notiSettings.rentReminder) === 1 && Number(notiSettings.markPaidNotification) === 1) {
      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 to client through chat mitra`
        );
        let bodyValues = [
                { type: "text", text: `${tenant?.name}` },
                { type: "text", text: `${combinedAmount}` },
                { type: "text", text: `${property.name}` },
                { type: "text", text: `${flatName} (${room.roomNum})` },
                { type: "text", text: `${dueName}`},
                { type: "text", text: `${moment(collectionDate).format("DD MMM YY HH:mm:ss")}` },
                { type: "text", text: `${modeVal}` },
                { type: "text", text: `${recordedBy}` },
              ];
        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: `${combinedAmount}`,
            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: `${recordedBy}`
          };
          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 to client through intrakt`
          );
          sendWhatsappPaymentConfirmationClient(
            client.mobile,
            client.name,
            tenant.name,
            combinedAmount,
            property.name,
            `${flatName} (${room.roomNum})`,
            dueName,
            moment(collectionDate).format("DD MMM YY HH:mm:ss"),
            modeVal,
            recordedBy,
            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;
          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 to partner through chat mitra`
            );
            let bodyValues = [
                    { type: "text", text: `${tenant?.name}` },
                    { type: "text", text: `${combinedAmount}` },
                    { type: "text", text: `${property.name}` },
                    { type: "text", text: `${flatName} (${room.roomNum})` },
                    { type: "text", text: `${dueName}`},
                    { type: "text", text: `${moment(collectionDate).format("DD MMM YY HH:mm:ss")}` },
                    { type: "text", text: `${modeVal}` },
                    { type: "text", text: `${recordedBy}` },
                  ];
            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 client`
              );
              const bodyValues: Record<string, string> = {
                body_1: `${tenant?.name}`,
                body_2: `${combinedAmount}`,
                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: `${recordedBy}`
              };
              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 to partner through intrakt`
              );
              sendWhatsappPaymentConfirmationClient(
                partnerMobile,
                partnerName,
                tenant.name,
                combinedAmount,
                property.name,
                `${flatName} (${room.roomNum})`,
                dueName,
                moment(collectionDate).format("DD MMM YY HH:mm:ss"),
                modeVal,
                recordedBy,
                Number(clientId),
              );
            }
          }

        }
    }

    if (notiSettings && Number(notiSettings.rentReminder) === 1 && Number(notiSettings.markPaidTenantNotification) === 1) {

        let whatsAppProvider = await clientConfigDB.getClientConfig({
          clientId,
          provider : CONSTANTS.CLIENT_CONFIG_PROVIDER.CHAT_MITRA,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY,
        });
        const receiptUrl = `${receipt}?v=${moment().format("YYYYMMDDHHmmss")}`;
        if(whatsAppProvider && whatsAppProvider?.value !='') {
          log.info(
          `[${C}], [${F}], Client Id [${clientId}], TenantId [${tenantId}], Sending to tenant through chat mitra`
          )
          let bodyValues = [
            { type: "text", text: `${combinedAmount}` },
            { type: "text", text: `${property.name}` },
            { type: "text", text: `${flatName} (${room.roomNum})` },
            { type: "text", text: `${notiSettings?.footer}` },
          ];
          let headerValues = [
            {
              type: "document",
              document: {
                link: receiptUrl,
                filename: `Receipt_${transGId || 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`
              );
              let footerText = notiSettings.footer || "The Kipinn Team";
              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 to tenant through intrakt`
              );
              sendWhatsappPaymentConfirmationTenant(
                tenant.mobile,
                tenant.name,
                combinedAmount,
                property.name,
                `${flatName} (${room.roomNum})`,
                receiptUrl,
                notiSettings?.footer,
                Number(clientId)
              );
            }
        }
        
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Due Id [${id}], Mode [${mode}], Is Receipt Generated [${receipt}],  Paid Amount [${amount}], Is File Uploaded ${file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"}, ${
        (amount + depositAdjusted) >= allDues[0].amount ? "Due marked as paid successfully" : "Due paid partially."
      }`
    );

    return res.status(200).json({
      msg: "Due marked as paid successfully",
      isSuccess: true,
      dueAmountLeft: amountLeft || 0,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (file) {
      await removeTmpImages();
    }

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

dues.DuesList = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "DuesList";

  try {
    let { pageNum, tenantId, propId, m, s, y, filter, sort, startDate = moment().startOf("month").format("YYYY-MM-DD"), endDate = moment().endOf("month").format("YYYY-MM-DD"), propFilters, dueType, locationFilters } = req.query;
    const userType = req.userType;
    //let clientId = req.id;
    const limit = 10;
    //let isPartner = false;
    let duesList: any = [];
    let monthlyDuesAmount = 0;
    let totalDuesAmount = 0;
    let tenantDuesCount = 0;
    let totalDueCount = 0;

    if (filter && typeof filter === 'string' && filter !== "undefined") {
      dueType = filter;
    }

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

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Sort by [${sort}], Start Date [${startDate}], End Date [${endDate}], Prop Filter [${propFilters}], Due Type [${dueType}], Location Filters [${locationFilters}]`
    );

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

    if (!month || month > 12 || !year) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Sort by [${sort}], Not a Valid Month or Year`
      );
      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)
    );

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      //const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Sort by [${sort}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Date [${date}], ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      if (propId) {

        if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
          duesList = await duesDB.getByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
            pageNum,
            limit,
          });

          const totalDues = await duesDB.getTotalByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          monthlyDuesAmount = totalDues || 0;
          totalDuesAmount = totalDues || 0;

          const tenantCount = await duesDB.getTotalTenantsByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          tenantDuesCount = tenantCount || 0;

          totalDueCount = await duesDB.getTotalDuesByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
        } else {
          duesList = await duesDB.getByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            propId,
            pageNum,
            limit,
            startDate,
            endDate,
            type: dueType || null,
            sortBy: sort || null,
          });

          totalDueCount = await duesDB.getTotalDuesCountByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            propId,
            startDate,
            endDate,
            type: dueType || null,
          });

          const totalDues = await duesDB.getTotalByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propId,
          });
          monthlyDuesAmount = totalDues || 0;

          const totalOutstandingDues = await duesDB.getTotalDuesByPropId({
            propId,
          });
          totalDuesAmount = totalOutstandingDues?.totalDues || 0;

          const tenantCount = await duesDB.getTenantCountByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propId,
          });
          tenantDuesCount = tenantCount || 0;
        }
      } else {

        if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
          duesList = await duesDB.getByClientIdAndTenantNameMobile({
            clientId,
            searchVal: s,
            pageNum,
            limit,
          });

          totalDueCount = await duesDB.getTotalDuesByClientIdAndTenantNameMobile({
            clientId,
            searchVal: s,
          });

          const totalDues = await duesDB.getTotalByClientIdAndTenantNameMobile({
            clientId,
            searchVal: s,
          });
          monthlyDuesAmount = totalDues || 0;
          totalDuesAmount = totalDues || 0;
  
          const tenantCount = await duesDB.getTotalTenantsByClientIdAndTenantNameMobile({
            clientId,
            searchVal: s,
          });
          tenantDuesCount = tenantCount || 0;
        } else {
          duesList = await duesDB.getByClientIdAndFiltersAndDateRange({
            clientId,
            pageNum,
            limit,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
            sortBy: sort || null,
            locationIds: locationFilters || null,
          });

          totalDueCount = await duesDB.getTotalDuesCountByClientIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
            locationIds: locationFilters || null,
          });
          
          const totalDues = await duesDB.getTotalByClientIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
            locationIds: locationFilters || null,
          });
          monthlyDuesAmount = totalDues || 0;

          const totalOutstandingDues = await duesDB.getTotalDuesByClientId({
            clientId,
          });
          totalDuesAmount = totalOutstandingDues?.totalDues || 0;
  
          const tenantCount = await duesDB.getTenantCountByClientIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
            locationIds: locationFilters || null,
          });
          tenantDuesCount = tenantCount || 0;
        }
      }
    } else {
      const staffId = req.id;

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Filter [${filter}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      if (propId) {

        if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
          duesList = await duesDB.getByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
            pageNum,
            limit,
          });

          totalDueCount = await duesDB.getTotalDuesByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });

          const totalDues = await duesDB.getTotalByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          monthlyDuesAmount = totalDues || 0;
          totalDuesAmount = totalDues || 0;

          const tenantCount = await duesDB.getTotalTenantsByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          tenantDuesCount = tenantCount || 0;
        } else {
          duesList = await duesDB.getByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            propId,
            pageNum,
            limit,
            startDate,
            endDate,
            type: dueType || null,
            sortBy: sort || null,
          });

          totalDueCount = await duesDB.getTotalDuesCountByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            propId,
            startDate,
            endDate,
            type: dueType || null,
          });

          const totalDues = await duesDB.getTotalByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propId,
          });
          monthlyDuesAmount = totalDues || 0;

          const totalOutstandingDues = await duesDB.getTotalDuesByPropId({
            propId,
          });
          totalDuesAmount = totalOutstandingDues?.totalDues || 0;

          const tenantCount = await duesDB.getTenantCountByClientIdAndPropIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propId,
          });
          tenantDuesCount = tenantCount || 0;
        }
      } else {
        const staffLinkedProps = await propertyDB.getPropsByStaffId({
          staffId,
        });
        if (staffLinkedProps) {
          const propertiesIds = staffLinkedProps
            .map((prop: propertiesTypes) => prop.id)
            .join(",");

          if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
            duesList = await duesDB.getByClientIdAndTenantNameMobileForStaff({
              clientId,
              searchVal: s,
              pageNum,
              limit,
              propertiesIds,
            });

            totalDueCount = await duesDB.getTotalDuesCountByClientIdAndTenantNameMobileForStaff({
              clientId,
              searchVal: s,
              propertiesIds,
            });

            const totalDues = await duesDB.getTotalByClientIdAndTenantNameMobileForStaff({
              clientId,
              searchVal: s,
              propertiesIds,
            });
            monthlyDuesAmount = totalDues || 0;
            totalDuesAmount = totalDues || 0;
    
            const tenantCount = await duesDB.getTotalTenantsByClientIdAndTenantNameMobileForStaff({
              clientId,
              searchVal: s,
              propertiesIds,
            });
            tenantDuesCount = tenantCount || 0;
          } else {
            duesList = await duesDB.getByClientIdAndFiltersAndDateRangeForStaff({
              clientId,
              propertiesIds,
              pageNum,
              limit,
              startDate,
              endDate,
              type: dueType || null,
              propIds: propFilters || null,
              sortBy: sort || null,
              locationIds: locationFilters || null,
            });

            totalDueCount = await duesDB.getTotalDuesCountByClientIdAndFiltersAndDateRangeForStaff({
              clientId,
              propertiesIds,
              startDate,
              endDate,
              type: dueType || null,
              propIds: propFilters || null,
              locationIds: locationFilters || null,
            });
  
            const totalDues = await duesDB.getTotalByClientIdAndFiltersAndDateRangeForStaff({
              clientId,
              startDate,
              endDate,
              type: dueType || null,
              propIds: propFilters || null,
              propertiesIds,
              locationIds: locationFilters || null,
            });
            monthlyDuesAmount = totalDues || 0;

            const totalOutstandingDues = await duesDB.getTotalDuesForStaff({
              clientId,
              propertiesIds,
            });
            totalDuesAmount = totalOutstandingDues?.totalDues || 0;
  
            const tenantCount = await duesDB.getTenantCountByClientIdAndFiltersAndDateRangeForStaff({
              clientId,
              startDate,
              endDate,
              type: dueType || null,
              propIds: propFilters || null,
              propertiesIds,
              locationIds: locationFilters || null,
            });
            tenantDuesCount = tenantCount || 0;
          }
        }
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Staff Id [${staffId}], Filter [${filter}], Staff Requested....`
      );
    }

    if (duesList && duesList.length > 0) {
      for (let due of duesList) {
        if (due.propertyType === CONSTANTS.PROPERTY_TYPE.FLAT) {
          const { name } = await flatDB.getById({ id: due.flatId });
          due.flatName = name;
        } else {
          due.flatName = due.floor;
        }
        // const tenant = await tenantDB.getById({
        //   id: due.tenantId,
        // });
        let occupancy = await occupancyDB.getDetailByTenantIdAndClientId({
          tenantId: due.tenantId,
          clientId,
        });
        due.kycStatus = occupancy ? occupancy.kycStatus : CONSTANTS.KYC_STATUS.PENDING;
        due.lastRemindedOn = occupancy?.lastRemindedOn;
        due.isRemindedOnLast24Hours = false;
        if(occupancy?.lastRemindedOn) {
          const now = moment();
          const reminderTime = moment(occupancy?.lastRemindedOn);
          const diffInHours = now.diff(reminderTime, "hours");
          due.isRemindedOnLast24Hours = diffInHours <= 24;
        }
        //due.kycStatus = tenant?.kycStatus || 0;

        // const tenantDoc = await documentDB.getByTenantIdAndType({
        //   tenantId: tenant.id,
        //   type: CONSTANTS.DOCUMENT_TYPES.SELFI,
        //   moveOut: 0,
        // });
        const tenantDoc = await documentDB.getIDByType({
          tenantId: due.tenantId,
          clientId,
          type: CONSTANTS.DOCUMENT_TYPES.SELFI,
          moveOut: 0,
        });
        due.profilePicture = tenantDoc?.value || null;

        const securityRemaining = await ledgerDB.getUnUsedSecurityByTenantIdAndClientId({
          clientId,
          tenantId: occupancy.tenantId,
          createdAt: occupancy.createdAt,
        });

        due.securityRemaining = securityRemaining || 0;
      }
    }

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

    let canSendBulkReminder = client?.canSendRentReminderToAll || 0;
    if(duesList == false){ 
      canSendBulkReminder = 0;
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Can Send Bulk Reminder [${canSendBulkReminder}], Tenant Dues list sent successfully`
    );

    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      dues: duesList || [],
      monthlyDuesAmount,
      totalDuesAmount: totalDuesAmount || 0,
      tenantDuesCount,
      canSendBulkReminder,
      propList: propList || [],
      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,
    });
  }
};

//Not being used for now, maybe in future dont remove -- 2025-12-26
dues.DuesListX = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "DuesListX";

  try {
    let { pageNum, tenantId, propId, m, s, y, filter, sort, startDate = moment().startOf("month").format("YYYY-MM-DD"), endDate = moment().endOf("month").format("YYYY-MM-DD"), propFilters, dueType, locationFilters } = req.query;
    const userType = req.userType;
    //let clientId = req.id;
    const limit = 20;
    //let isPartner = false;
    let duesList: any = [];
    let monthlyDuesAmount = 0;
    let totalDuesAmount = 0;
    let tenantDuesCount = 0;

    if (filter && typeof filter === 'string' && filter !== "undefined") {
      dueType = filter;
    }

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

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

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Sort by [${sort}], Start Date [${startDate}], End Date [${endDate}], Prop Filter [${propFilters}], Location Filters [${locationFilters}], Due Type [${dueType}]`
    );

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

    if (!month || month > 12 || !year) {
      log.info(
        `[${C}], [${F}], Client Id [${req.id}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Sort by [${sort}], Location Filters [${locationFilters}], Prop Filter [${propFilters}], Due Type [${dueType}], Not a Valid Month or Year`
      );
      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)
    );

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      //const client = await clientDB.getById({ id: clientId });

      if (!client) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Sort by [${sort}], Location Filters [${locationFilters}], Prop Filter [${propFilters}], Due Type [${dueType}], No Client Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Date [${date}], Sort by [${sort}], Start Date [${startDate}], End Date [${endDate}], Prop Filter [${propFilters}], Due Type [${dueType}] ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      if (propId) {
        propFilters = [propId.toString()];
        if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
          duesList = await duesDB.getByPropIdAndTenantNameMobileForGroupedDues({
            clientId,
            propId,
            searchVal: s,
            pageNum,
            limit,
          });

          const totalDues = await duesDB.getTotalByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          monthlyDuesAmount = totalDues || 0;
          totalDuesAmount = totalDues || 0;

          const tenantCount = await duesDB.getTotalTenantsByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          tenantDuesCount = tenantCount || 0;
        } else {
          duesList = await duesDB.getByClientIdAndFilters({
            clientId,
            pageNum,
            limit,
            type: dueType || null,
            propIds: propFilters || null,
            sortBy: sort || null,
            startDate,
            endDate,
          });
          const totalDues = await duesDB.getTotalByClientIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
          });
          monthlyDuesAmount = totalDues || 0;

          const totalLifetimeDues = await duesDB.getTotalByClientIdAndFilters({
            clientId,
            type: dueType || null,
            propIds: propFilters || null,
          });
          totalDuesAmount = totalLifetimeDues || 0;

          const tenantCount = await duesDB.getTenantCountByClientIdAndFiltersAndDateRange({
            clientId,
            type: dueType || null,
            propIds: propFilters || null,
            startDate,
            endDate,
          });
          tenantDuesCount = tenantCount || 0;
        }
      } else {

        if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
          duesList = await duesDB.getByClientIdAndTenantNameMobileForGroupedDues({
            clientId,
            searchVal: s,
            pageNum,
            limit,
          });

          const totalDues = await duesDB.getTotalByClientIdAndTenantNameMobile({
            clientId,
            searchVal: s,
          });
          monthlyDuesAmount = totalDues || 0;
          totalDuesAmount = totalDues || 0;

          const tenantCount = await duesDB.getTotalTenantsByClientIdAndTenantNameMobile({
            clientId,
            searchVal: s,
          });
          tenantDuesCount = tenantCount || 0;
        } else {
          duesList = await duesDB.getByClientIdAndFilters({
            clientId,
            pageNum,
            limit,
            type: dueType || null,
            propIds: propFilters || null,
            sortBy: sort || null,
            startDate,
            endDate,
            locationIds: locationFilters || null,
          });
          const totalDues = await duesDB.getTotalByClientIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
            locationIds: locationFilters || null,
          });
          monthlyDuesAmount = totalDues || 0;

          const totalLifetimeDues = await duesDB.getTotalByClientIdAndFilters({
            clientId,
            type: dueType || null,
            propIds: propFilters || null,
            locationIds: locationFilters || null,
          });
          totalDuesAmount = totalLifetimeDues || 0;

          const tenantCount = await duesDB.getTenantCountByClientIdAndFiltersAndDateRange({
            clientId,
            type: dueType || null,
            propIds: propFilters || null,
            startDate,
            endDate,
            locationIds: locationFilters || null,
          });
          tenantDuesCount = tenantCount || 0;
        }
      }
    } else {
      const staffId = req.id;

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Filter [${filter}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      if (propId) {
        propFilters = [propId.toString()];
        if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
          duesList = await duesDB.getByPropIdAndTenantNameMobileForGroupedDues({
            clientId,
            propId,
            searchVal: s,
            pageNum,
            limit,
          });

          const totalDues = await duesDB.getTotalByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          monthlyDuesAmount = totalDues || 0;
          totalDuesAmount = totalDues || 0;

          const tenantCount = await duesDB.getTotalTenantsByPropIdAndTenantNameMobile({
            clientId,
            propId,
            searchVal: s,
          });
          tenantDuesCount = tenantCount || 0;
        } else {
          duesList = await duesDB.getByClientIdAndFilters({
            clientId,
            pageNum,
            limit,
            type: dueType || null,
            propIds: propFilters || null,
            sortBy: sort || null,
            startDate,
            endDate,
          });
          const totalDues = await duesDB.getTotalByClientIdAndFiltersAndDateRange({
            clientId,
            startDate,
            endDate,
            type: dueType || null,
            propIds: propFilters || null,
          });
          monthlyDuesAmount = totalDues || 0;

          const totalLifetimeDues = await duesDB.getTotalByClientIdAndFilters({
            clientId,
            type: dueType || null,
            propIds: propFilters || null,
          });
          totalDuesAmount = totalLifetimeDues || 0;

          const tenantCount = await duesDB.getTenantCountByClientIdAndFiltersAndDateRange({
            clientId,
            type: dueType || null,
            propIds: propFilters || null,
            startDate,
            endDate,
          });
          tenantDuesCount = tenantCount || 0;
        }
      } else {
        const staffLinkedProps = await propertyDB.getPropsByStaffId({
          staffId,
        });
        if (staffLinkedProps) {
          const propertiesIds = staffLinkedProps
            .map((prop: propertiesTypes) => prop.id)
            .join(",");

          if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
            duesList = await duesDB.getByClientIdAndTenantNameMobileForStaffGroupBy({
              clientId,
              searchVal: s,
              pageNum,
              limit,
              propertiesIds,
            });

            const totalDues = await duesDB.getTotalByClientIdAndTenantNameMobileForStaff({
              clientId,
              searchVal: s,
              propertiesIds,
            });
            monthlyDuesAmount = totalDues || 0;
            totalDuesAmount = totalDues || 0;
    
            const tenantCount = await duesDB.getTotalTenantsByClientIdAndTenantNameMobileForStaff({
              clientId,
              searchVal: s,
              propertiesIds,
            });
            tenantDuesCount = tenantCount || 0;
          } else {
            duesList = await duesDB.getByClientIdAndFiltersForStaff({
              clientId,
              propertiesIds,
              pageNum,
              limit,
              type: dueType || null,
              propIds: propFilters || null,
              sortBy: sort || null,
              startDate,
              endDate,
              locationIds: locationFilters || null,
            });
  
            const totalDues = await duesDB.getTotalByClientIdAndFiltersForStaff({
              clientId,
              type: dueType || null,
              propIds: propFilters || null,
              propertiesIds,
              startDate,
              endDate,
              locationIds: locationFilters || null,
            });
            monthlyDuesAmount = totalDues || 0;

            const totalLifetimeDues = await duesDB.getLifetimeTotalByClientIdAndFiltersForStaff({
              clientId,
              type: dueType || null,
              propIds: propFilters || null,
              propertiesIds,
              locationIds: locationFilters || null,
            });
            totalDuesAmount = totalLifetimeDues || 0;
  
            const tenantCount = await duesDB.getTenantCountByClientIdAndFiltersAndDateRangeForStaff({
              clientId,
              type: dueType || null,
              propIds: propFilters || null,
              propertiesIds,
              startDate,
              endDate,
              locationIds: locationFilters || null,
            });
            tenantDuesCount = tenantCount || 0;
          }
        }
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Month [${m}], Year [${y}], Search Val [${s}], Page Num [${pageNum}], Staff Id [${staffId}], Filter [${filter}], Staff Requested....`
      );
    }

    if (duesList && duesList.length > 0) {
      const tenantIdArray = duesList.map((item: any) => item.tenantId);
      let limitTenantDues: any = [];
      if (s && typeof s === 'string' && s !== "undefined" && s.trim() !== "") {
        limitTenantDues = await duesDB.getDuesForClientMultipleTenants({
          clientId, 
          tenantIds: tenantIdArray, 
          type: dueType || null,
          startDate: null,
          endDate: null,
        });
      } else {
        limitTenantDues = await duesDB.getDuesForClientMultipleTenants({
          clientId, 
          tenantIds: tenantIdArray, 
          type: dueType || null,
          startDate,
          endDate
        });
      }
      

      for (let due of duesList) {
        if (due.propertyType === CONSTANTS.PROPERTY_TYPE.FLAT) {
          const { name } = await flatDB.getById({ id: due.flatId });
          due.flatName = name;
        } else {
          due.flatName = due.floor;
        }
        let occupancy = await occupancyDB.getDetailByTenantIdAndClientId({
          tenantId: due.tenantId,
          clientId,
        });
        due.kycStatus = occupancy ? occupancy.kycStatus : CONSTANTS.KYC_STATUS.PENDING;
        due.status = occupancy ? occupancy.status : CONSTANTS.OCCUPANCY_STATUS.OCCUPIED;
        due.lastRemindedOn = occupancy?.lastRemindedOn;
        due.isRemindedOnLast24Hours = false;
        if(occupancy?.lastRemindedOn) {
          const now = moment();
          const reminderTime = moment(occupancy?.lastRemindedOn);
          const diffInHours = now.diff(reminderTime, "hours");
          due.isRemindedOnLast24Hours = diffInHours <= 24;
        }
        //log.info(`clientId [{${clientId}], Tenant Id [${due.tenantId}], Last Reminded On [${occupancy?.lastRemindedOn}], Is Reminded In 24 Hours [${due.isRemindedOnLast24Hours}]`);
        const tenantDoc = await documentDB.getIDByType({
          tenantId: due.tenantId,
          clientId,
          type: CONSTANTS.DOCUMENT_TYPES.SELFI,
          moveOut: 0,
        });
        due.profilePicture = tenantDoc?.value || null;
        const singleTenantDues = limitTenantDues.filter((item: any) => item.tenantId === due.tenantId);
        due.dues= singleTenantDues || [];
        due.totalPending = Number(due.balance);
        due.propName =  due.propertyName;
        due.remarks = singleTenantDues.length > 0 ? singleTenantDues[0].remarks : null;

        // let dueImages = await documentDB.getByLedgerReferenceId({
        //   ledgerReferenceId: due.ledgerReferenceId,
        // });

        // due.dueImages = dueImages 
        // ? dueImages.map((img:any) => ({ id: img.id, value: img.value }))
        // : [];
      }
    }

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

    let canSendBulkReminder = client?.canSendRentReminderToAll || 0;
    if(duesList == false){ 
      canSendBulkReminder = 0;
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Page Num [${pageNum}], Month [${m}], Year [${y}], Search Val [${s}], Filter [${filter}], Can Send Bulk Reminder [${canSendBulkReminder}],  Tenant Dues list sent successfully`
    );

    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      dues: duesList || [],
      monthlyDuesAmount,
      totalDuesAmount: totalDuesAmount || 0,
      tenantDuesCount,
      canSendBulkReminder,
      propList: propList || [],
      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,
    });
  }
};

dues.DuesListForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "DuesListForWeb";

  try {
    let { startDate, endDate, tenantId, propId, s, t, filter, sortBy, typeFilter, locationFilter=null, } = req.query;
    const userType = req.userType;
    //let clientId = req.id;
    const limit = 10e9;
    const pageNum = 1;
    //let isPartner = false;
    let duesList: any = [];
    let finalDuesAmt = 0;
    let finalDueTenantCount = 0;
    let finalDuesList: any = [];
    let finalOverDuesAmt = 0;
    let finalCurrentMonthDues = 0;
    let finalRentDues = 0;
    let finalSecurityShortFall = 0;
    let finalUtilityDuesAmt = 0;
    let finalOtherDuesAmt = 0;
    let finalFineAmt = 0;

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

    log.info(
      `[${C}], [${F}], Client Id [${req.id}], Prop Id [${propId}], Tenant Id [${tenantId}], Start Date [${startDate}], End Date [${endDate}], Search Val [${s}], Search Type [${t}], Filter [${filter}], Sort By [${sortBy}], Type Filter [${typeFilter}], Location Filter [${locationFilter}]`
    );

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

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Search Val [${s}], Filter [${filter}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      const client = await clientDB.getById({ id: clientId });

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

      log.info(
        `[${C}], [${F}], ${
          isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Prop Id [${propId}], Tenant Id [${tenantId}], Search Val [${s}], ${
          isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      const {
        totalDuesAmt,
        totalDueTenantCount,
        duesList,
        overDuesAmt,
        totalDuesForCurrentMonthAmt,
        totalRentDuesAmt,
        securityShortFall,
        utilityDuesAmt,
        otherDuesAmt,
        fineDuesAmt,
      } = await duesForWeb(
        Number(clientId),
        Number(propId),
        Number(tenantId),
        0, //staffId
        String(startDate),
        String(endDate),
        Number(pageNum),
        limit,
        date,
        String(filter),
        String(s),
        Number(t),
        Number(locationFilter),
      );

      finalDuesAmt = totalDuesAmt;
      finalDueTenantCount = totalDueTenantCount;
      finalDuesList = duesList || [];
      finalOverDuesAmt = overDuesAmt;
      finalCurrentMonthDues = totalDuesForCurrentMonthAmt;
      finalRentDues = totalRentDuesAmt;
      finalSecurityShortFall = securityShortFall;
      finalUtilityDuesAmt = utilityDuesAmt;
      finalOtherDuesAmt = otherDuesAmt;
      finalFineAmt = fineDuesAmt;
    } else {
      const staffId = req.id;

      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Tenant Id [${tenantId}], Search Val [${s}], Staff Id [${staffId}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      const {
        totalDuesAmt,
        totalDueTenantCount,
        duesList,
        overDuesAmt,
        totalDuesForCurrentMonthAmt,
        totalRentDuesAmt,
        securityShortFall,
        utilityDuesAmt,
        otherDuesAmt,
        fineDuesAmt,
      } = await duesForWeb(
        Number(clientId),
        Number(propId),
        Number(tenantId),
        Number(staff.id),
        String(startDate),
        String(endDate),
        Number(pageNum),
        limit,
        date,
        String(filter),
        String(s),
        Number(t),
        Number(locationFilter),
      );

      finalDuesAmt = totalDuesAmt;
      finalDueTenantCount = totalDueTenantCount;
      finalDuesList = duesList || [];
      finalOverDuesAmt = overDuesAmt;
      finalCurrentMonthDues = totalDuesForCurrentMonthAmt;
      finalRentDues = totalRentDuesAmt;
      finalSecurityShortFall = securityShortFall;
      finalUtilityDuesAmt = utilityDuesAmt;
      finalOtherDuesAmt = otherDuesAmt;
      finalFineAmt = fineDuesAmt;

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

    const bankAccounts = await bankDB.getByClientIdLimitedFields({ clientId });

    let canSendBulkReminder = client?.canSendRentReminderToAll || 0;
    if(finalDuesList.length < 1){ 
      canSendBulkReminder = 0;
    }

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

    if (finalDuesList && finalDuesList.length > 0) {
      
      const ledgerReferenceIds = [
        ...new Set(
          finalDuesList.map((d: any) => d.ledgerReferenceId).filter(Boolean),
        ),
      ];

      // Fetching all due images
      const allDueImages = await documentDB.getByMultipleLedgerReferenceIds({
        ledgerReferenceIds,
      });

      // Mapping images
      const dueImagesMap = new Map<string, any[]>();

      if (allDueImages && allDueImages.length > 0) {
        for (const img of allDueImages) {
          if (!dueImagesMap.has(img.ledgerReferenceId)) {
            dueImagesMap.set(img.ledgerReferenceId, []);
          }
          dueImagesMap.get(img.ledgerReferenceId)!.push({
            id: img.id,
            value: img.value,
          });
        }
      }

      for (let due of finalDuesList) {

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

        if (!occupancy) {
          due.securityRemaining = 0;
          continue;
        }
        let securityRemaining = await ledgerDB.getUnUsedSecurityByTenantIdAndClientId({
          clientId,
          tenantId: occupancy.tenantId,
          createdAt: occupancy.createdAt,
        });


    // const tenantIds = [...new Set(finalDuesList.map((d: any) => d.tenantId))];
    // // const occupancies = await occupancyDB.getByMultipleTenantIdAndClientId({ tenantIds, clientId });
    // // const occupancyMap = new Map(occupancies.map((occ: any) => [occ.tenantId, occ]));
    
    // const securitiesRemaining = await ledgerDB.getUnUsedSecurityByTenantIdsAndClientId({
    //   clientId,
    //   tenantIds,
    // });

    // let securityRemainingMap = new Map();

    // if (securitiesRemaining && securitiesRemaining.length > 0) {
    //   for (const item of securitiesRemaining) {
    //     securityRemainingMap.set(item.tenantId, item);
    //   }
    // }

    // if (finalDuesList && finalDuesList.length > 0) {
    //   for (let due of finalDuesList) {

    //     // let occupancy = await occupancyDB.getByTenantIdAndClientId({
    //     //   tenantId: due.tenantId,
    //     //   clientId,
    //     // });
    //     // let occupancy: any = occupancyMap.get(due.tenantId);

    //     // if (!occupancy) {
    //     //   due.securityRemaining = 0;
    //     //   continue;
    //     // }
    //     // let securityRemaining = await ledgerDB.getUnUsedSecurityByTenantIdAndClientId({
    //     //   clientId,
    //     //   tenantId: occupancy.tenantId,
    //     //   createdAt: occupancy.createdAt,
    //     // });
    //     let securityRemaining = securityRemainingMap.get(due.tenantId);

    //     if (!securitiesRemaining) {
    //       due.securityRemaining = 0;
    //       continue;
    //     }

        due.securityRemaining = securityRemaining || 0;

        // let dueImages = await documentDB.getByLedgerReferenceId({
        //   ledgerReferenceId: due.ledgerReferenceId,
        // });

        // due.dueImages = dueImages 
        // ? dueImages.map((img:any) => ({ id: img.id, value: img.value }))
        // : [];

        due.dueImages = dueImagesMap.get(due.ledgerReferenceId) || [];
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Id [${tenantId}], Search Val [${s}], Filter [${filter}], Sort By [${sortBy}], Type Filter [${typeFilter}], Tenant Dues list sent successfully`
    );

    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      summary: {
        totalDues: Number(finalDuesAmt) || 0,
        overDuesAmt: Number(finalOverDuesAmt) || 0,
        currentMonthDues: Number(finalCurrentMonthDues) || 0,
        rentDues: Number(finalRentDues) || 0,
        dueTenantCount: Number(finalDueTenantCount) || 0,
        securityShortFall: Number(finalSecurityShortFall) || 0,
        utilityDues: Number(finalUtilityDuesAmt) || 0,
        otherDueAmt: Number(finalOtherDuesAmt) || 0,
        fineDuesAmt: Number(finalFineAmt) || 0,
      },
      dues: finalDuesList || [],
      canHideDues: canToggleDue ? Number(canToggleDue.value) : 0,
      bankAccounts,
      canSendBulkReminder,
      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,
    });
  }
};

dues.MarkAllPaidForTenant = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "MarkAllPaidForTenant";

  try {
    let { tenantId, mode, collectionDate, transactionId, isEvicted, dueIds = "NA", remarks=null, paymentAccountNo = null, paymentAccountName = null} = req.body;
    const oldCollectionDate = collectionDate;

    collectionDate = moment(collectionDate).utc();
    collectionDate = collectionDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Due Ids [${dueIds}], Mode [${mode}], Collection Date [${collectionDate}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Client Remarks [${remarks}],  Payment Account No [${paymentAccountNo}], Payment Account Name [${paymentAccountName}]`
    );

    const userType = req.userType;
    let recordedBy = "";

    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"
        }....`
      );
      if(isPartner) {
        const staff = await staffDB.getById({ id: req.id });
        if (staff) {
          recordedBy = staff.name;
        }
      }
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Requested....`
      );
    }

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

    if (req.userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    let allDues = [];

    if (dueIds === "NA") {
      // allDues = isEvicted
      //   ? await moveOutDuesDB.getByTenantId({ tenantId })
      //   : await duesDB.getByTenantId({ tenantId });
      //Pallav - Multi tenant scenario
      allDues = isEvicted
        ? await moveOutDuesDB.getByTenantIdAndClientId({ tenantId, clientId })
        : await duesDB.getByTenantIdAndClientId({ tenantId, clientId });
    } else {
      allDues = isEvicted
        ? await moveOutDuesDB.getAllDues({ ids: dueIds })
        : await duesDB.getAllDues({ ids: dueIds });
    }

    if (!allDues || allDues.length === 0) {
      log.info(
        `[${C}], [${F}], Dues Ids [${dueIds}], No Dues Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const property = await propertyDB.getById({ id: allDues[0].propId });
    const room = await roomDB.getById({ id: allDues[0].roomId });
    
    const modeName = mode === CONSTANTS.TRANSACTION_MODES.OFFLINE ? "cash" : mode === CONSTANTS.TRANSACTION_MODES.UPI ? "upi" : mode === CONSTANTS.TRANSACTION_MODES.CARD ? "card" : "netbanking";
    
    for(let due of allDues) {
      const tenant = await tenantDB.getById({ id: due.tenantId });

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

      if ((isEvicted || occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) && due.type === CONSTANTS.DUES_TYPES.SECURITY) {
        continue
      }
      // let dueDescription = "";
      let receiptDescription: any = "";
      let duesStatsArr: any = [];
      let dueStats: any = {};
      const transGId: string = await generateTransId();
      let transTitle: string = "";
      if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
        transTitle = due.title;
        if (transTitle === null) {
          transTitle = await getDueDescription(due.type);
        }
      } else if (
        due.title &&
        due.title.toLowerCase().includes("advance rent")
      ) {
        transTitle = "Advance Rent";
      } else {
        transTitle = await getDueDescription(due.type);
      }
      const transId = await transactionDB.add({
        gId: transGId,
        clientId: client.id,
        tenantId: due.tenantId,
        roomId: due.roomId,
        propId: due.propId,
        amount: due.balance,
        name:
          due.rentStartDate !== null
            ? `${transTitle} for ${moment(due.rentStartDate).format(
                "DD MMM YY"
              )} to ${moment(due.rentEndDate).format("DD MMM YY")}`
            : `${transTitle} for ${moment(due.dueDate).format(
                "MMM YYYY"
              )}`,
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: due.type,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: due.dueDate, //lengthOfDues - 1
        receipt: "",
        mode: Number(mode),
        // collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
        collectionDate: collectionDate,
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: due.ledgerReferenceId,
        recordedBy: recordedBy,
        discount: due.discount,
        title: due.title || null,
        bankRefNum: transactionId ? transactionId.substring(0, 150) : null,
        remarks: remarks || null,
        isFinanciallyApplicable: 1,
        paymentAccountNo: paymentAccountNo || null,
        paymentAccountName: paymentAccountName || null,
      });

      if (isEvicted) {
        ({ dueStats, duesStatsArr, receiptDescription } =
          await MarkPaidForMovedOut(
            due.tenantId,
            due.balance,
            [due],
            occupancy,
            due.ledgerReferenceId,
            transId
          ));
      } else {
        ({ dueStats, duesStatsArr, receiptDescription } = await MarkPaid(
          due.tenantId,
          due.balance,
          [due],
          occupancy,
          due.ledgerReferenceId,
          transId,
          0, //markedFromSecurity placeholder
        ));
      }

      let settings = await settingsDB.getByClientIdAndPropId({
        clientId,
        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);

      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;
      }
      log.info(`[${C}], [${F}], Logo [${logo}], Flat Name [${flatName}]`);

      
      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;
      }

      // const receipt = await createReceiptMultipleDues({
      //   // title: receiptDescription,
      //   title: `${transId}_${transGId}`,
      //   roomNum: room.roomNum,
      //   propName: property.name,
      //   dueDate: moment(due.dueDate).format("MMM, YYYY"),
      //   mode: getModeName(Number(mode)),
      //   transGId: transGId,
      //   paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
      //   month: moment(due.dueDate).format("MMM, YYYY"),
      //   tenantName: tenant?.name || "",
      //   amount: Number(due.balance || 0),
      //   address: `${property?.address}`,
      //   landlord: businessName || "",
      //   landlordNumber: property?.ownerMobile || "",
      //   logo: logo,
      //   dueStats: duesStatsArr,
      //   "landlord-pan": "",
      //   occupancy,
      //   flatName,
      //   propType: property.type,
      //   isGstEnabled,
      //   transactionInvoiceNo,
      //   gstNo,
      // });
      const receipt = await generateRentReciept({
        // title: receiptDescription,
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(due.dueDate).format("MMM, YYYY"),
        mode: getModeName(Number(mode)),
        transGId: transGId,
        paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
        month: moment(due.dueDate).format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: Number(due.balance || 0),
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        dueStats: duesStatsArr,
        "landlord-pan": "",
        occupancy,
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy
      });

      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }

      if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.subtractTenantDue({
          clientId,
          tenantId: due.tenantId,
          tenantDues: due.balance,
          propId: due.propId,
          roomId: due.roomId,
        });
      }
      let dueType = await getDueName(due.type);
      if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
        dueType = allDues[0].title;
      }
      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
        due.balance,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        modeName,
      );

    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Mode [${mode}], Is Evicted [${isEvicted}], All Dues Marked as Paid Successfully`
    );

    return res.status(200).json({
      msg: "All dues marked as paid 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,
    });
  }
};

dues.MarkAllPaidForTenantX = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "MarkAllPaidForTenantX";
  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };
  try {
    let { tenantId, mode, collectionDate, transactionId, isEvicted, dueIds = "NA", remarks=null, paymentAccountNo = null, paymentAccountName = null} = req.body;
    isEvicted = isEvicted === "true";
    const oldCollectionDate = collectionDate;

    collectionDate = moment(collectionDate).utc();
    collectionDate = collectionDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Due Ids [${dueIds}], Mode [${mode}], Collection Date [${collectionDate}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Client Remarks [${remarks}], Payment Account No [${paymentAccountNo}], Payment Account Name [${paymentAccountName}]`
    );

    const userType = req.userType;
    let recordedBy = "";

    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"
        }....`
      );
      if(isPartner) {
        const staff = await staffDB.getById({ id: req.id });
        if (staff) {
          recordedBy = staff.name;
        }
      }
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        if (file) {
          await fsPromises.unlink(file.path);
        }
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], No Client Found`
      );

      if (file) {
        await fsPromises.unlink(file.path);
      }

      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    if (req.userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    let allDues = [];

    if (dueIds === "NA") {
      // allDues = isEvicted
      //   ? await moveOutDuesDB.getByTenantId({ tenantId })
      //   : await duesDB.getByTenantId({ tenantId });
      //Pallav - Multi tenant scenario
      log.info(
        `[${C}], [${F}], Dues Ids [${dueIds}], isEvicted [${isEvicted}], Getting All Dues for Tenant Id and Client Id`
      );
      allDues = isEvicted
        ? await moveOutDuesDB.getByTenantIdAndClientId({ tenantId, clientId })
        : await duesDB.getByTenantIdAndClientId({ tenantId, clientId });
    } else {
      allDues = isEvicted
        ? await moveOutDuesDB.getAllDues({ ids: dueIds })
        : await duesDB.getAllDues({ ids: dueIds });
    }

    if (!allDues || allDues.length === 0) {
      log.info(
        `[${C}], [${F}], Dues Ids [${dueIds}], No Dues Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const property = await propertyDB.getById({ id: allDues[0].propId });
    const room = await roomDB.getById({ id: allDues[0].roomId });
    
    const modeName = mode === CONSTANTS.TRANSACTION_MODES.OFFLINE ? "cash" : mode === CONSTANTS.TRANSACTION_MODES.UPI ? "upi" : mode === CONSTANTS.TRANSACTION_MODES.CARD ? "card" : "netbanking";
    
    for(let due of allDues) {
      const tenant = await tenantDB.getById({ id: due.tenantId });

      const occupancy = isEvicted
        ? await moveOutDB.getByTenantIdAndClientId({
            clientId: clientId,
            tenantId: due.tenantId,
          })
        : await occupancyDB.getByTenantIdAndClientId({
            clientId: clientId,
            tenantId: due.tenantId,
          });
      if (!occupancy) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
        );
        if (file) {
          await fsPromises.unlink(file.path);
        }
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if ((isEvicted || occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) && due.type === CONSTANTS.DUES_TYPES.SECURITY) {
        continue
      }
      // let dueDescription = "";
      let receiptDescription: any = "";
      let duesStatsArr: any = [];
      let dueStats: any = {};
      const transGId: string = await generateTransId();
      let transTitle: string = "";
      if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
        transTitle = due.title;
        if (transTitle === null) {
          transTitle = await getDueDescription(due.type);
        }
      } else if (
        due.title &&
        due.title.toLowerCase().includes("advance rent")
      ) {
        transTitle = "Advance Rent";
      } else {
        transTitle = await getDueDescription(due.type);
      }
      const transId = await transactionDB.add({
        gId: transGId,
        clientId: client.id,
        tenantId: due.tenantId,
        roomId: due.roomId,
        propId: due.propId,
        amount: due.balance,
        name:
          due.rentStartDate !== null
            ? `${transTitle} for ${moment(due.rentStartDate).format(
                "DD MMM YY"
              )} to ${moment(due.rentEndDate).format("DD MMM YY")}`
            : `${transTitle} for ${moment(due.dueDate).format(
                "MMM YYYY"
              )}`,
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: due.type,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: due.dueDate, //lengthOfDues - 1
        receipt: "",
        mode: Number(mode),
        // collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
        collectionDate: collectionDate,
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: due.ledgerReferenceId,
        recordedBy: recordedBy,
        discount: due.discount,
        title: due.title || null,
        bankRefNum: transactionId ? transactionId.substring(0, 150) : null,
        remarks: remarks || null,
        isFinanciallyApplicable: 1,
        paymentAccountNo: paymentAccountNo || null,
        paymentAccountName: paymentAccountName || null,
      });

      await setTransactionTallyStatus(
        Number(clientId),
        transId,
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await setTransactionTallyBillRef(
        Number(clientId),
        transId,
        due.tallyBillRef,
      );


      let url = "";
      let isTransDocUploaded = false;
      if (file && isTransDocUploaded === false) {
        const folderName = `transaction_${transId}`;
        const folderPath = `uploads/documents/client_${client.id}/${folderName}`;
        const fileExtension = file.mimetype.split("/")[1];
        const filename = `uploadedDoc.${fileExtension}`;

        if (!fs.existsSync(folderPath)) {
          await fsPromises.mkdir(folderPath, { recursive: true });
        }

        const oldPath = `uploads/tmp/${file.filename}`;
        const newPath = `${folderPath}/${filename}`;
        url = `${process.env.UPLOAD_PATH}/documents/client_${client.id}/${folderName}/${filename}`;

        await fsPromises.copyFile(oldPath, newPath);
        isTransDocUploaded = true;
        // await transactionDB.updateUploadedDoc({
        //   id: transId,
        //   docs: url,
        // });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Transaction Id [${transId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Document Saved in DB.`
        );
      }

      if(isTransDocUploaded){
        await transactionDB.updateUploadedDoc({
          id: transId,
          docs: url,
        });
      }

      if (isEvicted) {
        ({ dueStats, duesStatsArr, receiptDescription } =
          await MarkPaidForMovedOut(
            due.tenantId,
            due.balance,
            [due],
            occupancy,
            due.ledgerReferenceId,
            transId
          ));
      } else {
        ({ dueStats, duesStatsArr, receiptDescription } = await MarkPaid(
          due.tenantId,
          due.balance,
          [due],
          occupancy,
          due.ledgerReferenceId,
          transId,
          0, //markedFromSecurity placeholder
        ));
      }

      let settings = await settingsDB.getByClientIdAndPropId({
        clientId,
        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);

      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;
      }
      log.info(`[${C}], [${F}], Logo [${logo}], Flat Name [${flatName}]`);

      
      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;
      }

      // const receipt = await createReceiptMultipleDues({
      //   // title: receiptDescription,
      //   title: `${transId}_${transGId}`,
      //   roomNum: room.roomNum,
      //   propName: property.name,
      //   dueDate: moment(due.dueDate).format("MMM, YYYY"),
      //   mode: getModeName(Number(mode)),
      //   transGId: transGId,
      //   paidDate: moment(collectionDate).format("DD MMM, YYYY"),
      //   month: moment(due.dueDate).format("MMM, YYYY"),
      //   tenantName: tenant?.name || "",
      //   amount: Number(due.balance || 0),
      //   address: `${property?.address}`,
      //   landlord: businessName || "",
      //   landlordNumber: property?.ownerMobile || "",
      //   logo: logo,
      //   dueStats: duesStatsArr,
      //   "landlord-pan": "",
      //   occupancy,
      //   flatName,
      //   propType: property.type,
      //   isGstEnabled,
      //   transactionInvoiceNo,
      //   gstNo,
      // });
      const receipt = await generateRentReciept({
        // title: receiptDescription,
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(due.dueDate).format("MMM, YYYY"),
        mode: getModeName(Number(mode)),
        transGId: transGId,
        paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
        month: moment(due.dueDate).format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: Number(due.balance || 0),
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        dueStats: duesStatsArr,
        "landlord-pan": "",
        occupancy,
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy
      });

      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }

      if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.subtractTenantDue({
          clientId,
          tenantId: due.tenantId,
          tenantDues: due.balance,
          propId: due.propId,
          roomId: due.roomId,
        });
      }
      let dueType = await getDueName(due.type);
      if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
        dueType = allDues[0].title;
      }
      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
        due.balance,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        modeName,
      );

    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Mode [${mode}], Is Evicted [${isEvicted}], All Dues Marked as Paid Successfully`
    );

    return res.status(200).json({
      msg: "All dues marked as paid successfully",
      isSuccess: true,
    });

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


dues.MarkPaidForMultipleTenant = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "MarkPaidForMultipleTenant";

  try {
    let { mode, collectionDate, transactionId, isEvicted, tenantIds = "NA", remarks=null, } = req.body;
    const oldCollectionDate = collectionDate;

    collectionDate = moment(collectionDate).utc();
    collectionDate = collectionDate.tz('Asia/Kolkata').format("YYYY-MM-DD HH:mm:ss");

    log.info(
      `[${C}], [${F}], Tenant Ids [${tenantIds}], Mode [${mode}], Collection Date [${collectionDate}], Transaction Id [${transactionId}], Is Evicted [${isEvicted}], Client Remarks [${remarks}]`
    );

    const userType = req.userType;
    let recordedBy = "";

    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"
        }....`
      );
      if(isPartner) {
        const staff = await staffDB.getById({ id: req.id });
        if (staff) {
          recordedBy = staff.name;
        }
      }
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Requested....`
      );
    }

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

    if (req.userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedBy = client.name;
    }

    let allDues = [];

    if (tenantIds === "NA") {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Ids [${tenantIds}], Tenant Ids Missing in Request`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      
    } else {
      allDues = isEvicted
        ? await moveOutDuesDB.getTenantAllDues({ tenantIds, clientId })
        : await duesDB.getTenantAllDues({ tenantIds, clientId });
    }

    if (!allDues || allDues.length === 0) {
      log.info(
        `[${C}], [${F}], Tenant Ids [${tenantIds}], No Dues Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const property = await propertyDB.getById({ id: allDues[0].propId });
    const room = await roomDB.getById({ id: allDues[0].roomId });
    
    const modeName = mode === CONSTANTS.TRANSACTION_MODES.OFFLINE ? "cash" : mode === CONSTANTS.TRANSACTION_MODES.UPI ? "upi" : mode === CONSTANTS.TRANSACTION_MODES.CARD ? "card" : "netbanking";
    
    for(let due of allDues) {
      const tenant = await tenantDB.getById({ id: due.tenantId });

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

      if ((isEvicted || occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) && due.type === CONSTANTS.DUES_TYPES.SECURITY) {
        continue
      }
      // let dueDescription = "";
      let receiptDescription: any = "";
      let duesStatsArr: any = [];
      let dueStats: any = {};
      const transGId: string = await generateTransId();
      let transTitle: string = "";
      if (due.type === CONSTANTS.DUES_TYPES.OTHER) {
        transTitle = due.title;
        if (transTitle === null) {
          transTitle = await getDueDescription(due.type);
        }
      } else if (
        due.title &&
        due.title.toLowerCase().includes("advance rent")
      ) {
        transTitle = "Advance Rent";
      } else {
        transTitle = await getDueDescription(due.type);
      }
      const transId = await transactionDB.add({
        gId: transGId,
        clientId: client.id,
        tenantId: due.tenantId,
        roomId: due.roomId,
        propId: due.propId,
        amount: due.balance,
        name:
          due.rentStartDate !== null
            ? `${transTitle} for ${moment(due.rentStartDate).format(
                "DD MMM YY"
              )} to ${moment(due.rentEndDate).format("DD MMM YY")}`
            : `${transTitle} for ${moment(due.dueDate).format(
                "MMM YYYY"
              )}`,
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: due.type,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: due.dueDate, //lengthOfDues - 1
        receipt: "",
        mode: Number(mode),
        // collectionDate: moment(collectionDate).format("YYYY-MM-DD HH:mm:ss"),
        collectionDate: collectionDate,
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: due.ledgerReferenceId,
        recordedBy: recordedBy,
        discount: due.discount,
        title: due.title || null,
        bankRefNum: transactionId ? transactionId.substring(0, 150) : null,
        remarks: remarks || null,
        isFinanciallyApplicable: 1,
      });

      await setTransactionTallyStatus(
        Number(clientId),
        transId,
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await setTransactionTallyBillRef(
        Number(clientId),
        transId,
        due.tallyBillRef,
      );

      if (isEvicted) {
        ({ dueStats, duesStatsArr, receiptDescription } =
          await MarkPaidForMovedOut(
            due.tenantId,
            due.balance,
            [due],
            occupancy,
            due.ledgerReferenceId,
            transId
          ));
      } else {
        ({ dueStats, duesStatsArr, receiptDescription } = await MarkPaid(
          due.tenantId,
          due.balance,
          [due],
          occupancy,
          due.ledgerReferenceId,
          transId,
          0, //markedFromSecurity placeholder
        ));
      }

      let settings = await settingsDB.getByClientIdAndPropId({
        clientId,
        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);

      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;
      }
      log.info(`[${C}], [${F}], Logo [${logo}], Flat Name [${flatName}]`);

      
      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;
      }

      // const receipt = await createReceiptMultipleDues({
      //   // title: receiptDescription,
      //   title: `${transId}_${transGId}`,
      //   roomNum: room.roomNum,
      //   propName: property.name,
      //   dueDate: moment(due.dueDate).format("MMM, YYYY"),
      //   mode: getModeName(Number(mode)),
      //   transGId: transGId,
      //   paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
      //   month: moment(due.dueDate).format("MMM, YYYY"),
      //   tenantName: tenant?.name || "",
      //   amount: Number(due.balance || 0),
      //   address: `${property?.address}`,
      //   landlord: businessName || "",
      //   landlordNumber: property?.ownerMobile || "",
      //   logo: logo,
      //   dueStats: duesStatsArr,
      //   "landlord-pan": "",
      //   occupancy,
      //   flatName,
      //   propType: property.type,
      //   isGstEnabled,
      //   transactionInvoiceNo,
      //   gstNo,
      // });

      const receipt = await generateRentReciept({
        // title: receiptDescription,
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(due.dueDate).format("MMM, YYYY"),
        mode: getModeName(Number(mode)),
        transGId: transGId,
        paidDate: moment(collectionDate).format("DD MMM, YYYY HH:mm:ss"),
        month: moment(due.dueDate).format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: Number(due.balance || 0),
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        dueStats: duesStatsArr,
        "landlord-pan": "",
        occupancy,
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy,
      });

      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }

      if (occupancy.status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.subtractTenantDue({
          clientId,
          tenantId: due.tenantId,
          tenantDues: due.balance,
          propId: due.propId,
          roomId: due.roomId,
        });
      }
      let dueType = await getDueName(due.type);
      if (allDues[0].type === CONSTANTS.DUES_TYPES.OTHER) {
        dueType = allDues[0].title;
      }
      await logActivity(
        req.userType!,
        Number(req.id!),
        Number(req.parentClientId!),
        Number(req.platform),
        due.tenantId,
        CONSTANTS.ACTIVITY_TYPES.MARK_PAID,
        due.balance,
        dueType,
        due.rentStartDate,
        due.rentEndDate,
        modeName,
      );

    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Mode [${mode}], Is Evicted [${isEvicted}], Tenant Ids [${tenantIds}], All tenants Dues Marked as Paid Successfully`
    );

    return res.status(200).json({
      msg: "All dues marked as paid 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,
    });
  }
};

dues.SettleExcessPayment = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "SettleExcessPayment";

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

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

    const userType = req.userType;
    let recordedBy = "";
    let amtReturn = Math.abs(amount);

    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"
        }....`
      );
      if(isPartner) {
        const staff = await staffDB.getById({ id: req.id });
        if (staff) {
          recordedBy = staff.name;
        }
      }
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && 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}], Non-Admin Not Authorized to settle excess payment`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      recordedBy = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Requested....`
      );
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      const client = await clientDB.getById({id: clientId});
      recordedBy = client?.name || "";
    } else if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      recordedBy = staff?.name || "";
    }

    const modeVal = getModeName(Number(mode));

    // const allDues = await moveOutDuesDB.getByTenantId({
    //   tenantId,
    // });
    const allDues = await moveOutDuesDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    const securityAdjustEntry = await ledgerDB.getLastEntry({
      tenantId,
      clientId,
    });
    
    if (allDues && allDues.length > 0) {

      if (securityAdjustEntry !== false) {
        await ledgerDB.removeById({
          id: securityAdjustEntry.id,
        });
      }

      for (let due of allDues) {
        // if (amtReturn === 0) break;

        await moveOutDuesDB.removeDue({
          id: due.id,
        });

        await ledgerDB.add({
          tenantId,
          roomId: due.roomId,
          propId: due.propId,
          clientId: due.clientId,
          amount: -Math.abs(due.balance),
          balance: 0,
          referenceId: due.ledgerReferenceId,
          transactionId: null,
          type: due.type,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEndDate,
          dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
          description: due.discount > 0 
            ? `Settled while paying back security deposit and any excess amount, Discount of ₹${due.discount} given for this due`
            : `Settled while paying back security deposit and any excess amount`,
          discount: due.discount,
          title: due?.title || null,
          subType: CONSTANTS.LEDGER_SUBTYPES.MARKED_FROM_SECURITY,
        });

        // amtReturn -= due.balance;
        // if (due.balance <= amtReturn) {
        // } else {

        //   await duesDB.updateBalance({
        //     id: due.id,
        //     balance: due.balance - amtReturn,
        //   })

        //   await ledgerDB.add({
        //     tenantId,
        //     roomId: due.roomId,
        //     propId: due.propId,
        //     clientId: due.clientId,
        //     amount: -amtReturn,
        //     balance: due.balance - amtReturn,
        //     referenceId: due.ledgerReferenceId,
        //     transactionId: null,
        //     type: due.type,
        //     rentStartDate: due.rentStartDate,
        //     rentEndDate: due.rentEndDate,
        //     dueDate: due.dueDate || moment().format("YYYY-MM-DD HH:mm:ss"),
        //     description: `Settled while paying back security deposit and any excess amount, Discount of ₹${due.discount} given for this due`,
        //     discount: due.discount,
        //     title: due?.title || null,
        //   });
        //   amtReturn = 0;
        // }
      }

      await ledgerDB.add({
        tenantId: securityAdjustEntry.tenantId,
        roomId: securityAdjustEntry.roomId,
        propId: securityAdjustEntry.propId,
        clientId: 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,
      });
    }

    const occupancy = await moveOutDB.getByTenantIdAndClientIdOrderByDesc({
      clientId,
      tenantId,
    });

    const property = await propertyDB.getById({
      id: occupancy.propId,
    });

    const room = await roomDB.getById({ id: occupancy.roomId });
    const client = await clientDB.getById({ id: occupancy.clientId });
    const tenant = await tenantDB.getById({ id: tenantId });

    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;
    }
    
    let transId = null;
    if (amtReturn > 0) {
      const transGId: string = await generateTransId();
      transId = await transactionDB.add({
        gId: transGId,
        clientId: occupancy.clientId,
        tenantId: occupancy.tenantId,
        roomId: occupancy.roomId,
        propId: occupancy.propId,
        amount: -amount,
        name: "Excess payment or security returned",
        type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
        transactionFor: CONSTANTS.TRANSACTION_FOR.SETTLEMENT,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        dueDate: moment(occupancy.moveOutDate).format("YYYY-MM-DD"),
        receipt: "",
        mode: mode,
        //collectionDate: moment(paidDate).format("YYYY-MM-DD"),
        collectionDate: moment(paidDate).format("YYYY-MM-DD") + " " + moment().format("HH:mm:ss"),
        propName: property.name,
        roomNum: room.roomNum,
        ledgerReferenceId: securityAdjustEntry?.referenceId || null,
        recordedBy: recordedBy,
        title: null,
        isFinanciallyApplicable: 1,
      });
  
      let settings = await settingsDB.getByClientIdAndPropId({
        clientId,
        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);
  
      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;
      }

      // const receipt = await createReceiptMultipleDues({
      //   // title: transTitle,
      //   title: `${transId}_${transGId}`,
      //   roomNum: room.roomNum,
      //   propName: property.name,
      //   dueDate: moment(occupancy?.moveOutDate).format("MMM, YYYY") || null,
      //   mode: getModeName(mode),
      //   transGId,
      //   paidDate: moment(paidDate).format("DD MMM, YYYY HH:mm:ss"),
      //   month:
      //     moment(occupancy?.moveOutDate).format("MMM, YYYY") ||
      //     moment().format("MMM, YYYY"),
      //   tenantName: tenant?.name || "",
      //   amount: Number(amtReturn) || 0,
      //   address: `${property?.address}`,
      //   landlord: businessName || "",
      //   landlordNumber: property?.ownerMobile || "",
      //   logo: logo,
      //   "landlord-pan": "",
      //   occupancy,
      //   dueStats: [{title: "Excess Payment/Security returned", amount: amount}],
      //   flatName,
      //   propType: property.type,
      //   isGstEnabled,
      //   transactionInvoiceNo,
      //   gstNo,
      // });
      const receipt = await generateRentReciept({
        // title: transTitle,
        title: `${transId}_${transGId}`,
        roomNum: room.roomNum,
        propName: property.name,
        dueDate: moment(occupancy?.moveOutDate).format("MMM, YYYY") || null,
        mode: getModeName(mode),
        transGId,
        paidDate: moment(paidDate).format("DD MMM, YYYY") + " " + moment().format("HH:mm:ss"),
        month:
          moment(occupancy?.moveOutDate).format("MMM, YYYY") ||
          moment().format("MMM, YYYY"),
        tenantName: tenant?.name || "",
        amount: Number(amtReturn) || 0,
        address: `${property?.address}`,
        landlord: businessName || "",
        landlordNumber: property?.ownerMobile || "",
        logo: logo,
        "landlord-pan": "",
        occupancy,
        dueStats: [{title: "Excess Payment/Security returned", amount: amount}],
        flatName,
        propType: property.type,
        isGstEnabled,
        transactionInvoiceNo,
        gstNo,
        recordedBy
      });
      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt,
        });
      }
    }

    await ledgerDB.settleExcessPaymentAfterEviction({
      tenantId,
      clientId,
      transactionId: transId,
      description: `Paid back ₹${amount} to tenant via ${modeVal} on ${moment(paidDate).format("DD MMM YYYY")}`,
    });

    const moveOutOccupancies = await moveOutDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    for (let occupancy of moveOutOccupancies) {
      await moveOutDB.updateRefundStatus({
        id: occupancy.id,
        refundStatus: CONSTANTS.REFUND_STATUS.PROCESSED,
      });
    }

    const token = jwt.sign(
      {
        id: client?.id,
        type: CONSTANTS.USER_TYPE.CLIENT,
        platform: client?.device,
      },
      process.env.TOKEN_SECRET!,
      { expiresIn: process.env.TOKEN_EXPIRY }
    );

    const axiosHeaders = {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
    };

    const data = await axios.get(
      `${process.env.API_BASE}/tenant/get/final/breakdown?tenantId=${tenantId}&sendFnF=0`,
      axiosHeaders,
    );
    
    if (data?.data.link) {
      const link = data?.data.link;
      if(0 != isGstEnabled && 0 != invoiceNo) {
        await transactionDB.updateReceiptWithInvoice ({ id : transId, receipt: link, invoiceNo: transactionInvoiceNo, gstNo });
      } else {
        await transactionDB.updateReceipt({
          id: transId,
          receipt: link,
        });
      }
    }

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.SETTLE_EXCESS,
      amount,
      paidDate,
      modeVal,
      null,
      null,
    );

    if (requestId) {
      const request = await requestDB.getById({ id: requestId });

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

      await requestDB.updateStatus({
        id: requestId,
        status: CONSTANTS.REQUEST_STATUS.APPROVED,
      });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Refund Request Approved`
      );
    }

    const pendingRefundRequest = await requestDB.getByTenantIdAndClientId({
      tenantId, 
      clientId, 
      status: CONSTANTS.REQUEST_STATUS.PENDING, 
      type: CONSTANTS.REQUEST_TYPE.REFUND,
    });

    if (pendingRefundRequest) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Refund Request Id [${pendingRefundRequest?.id}], Pending Refund Request Found, Updating Status To Approved`
      )
      await requestDB.updateStatus({
        id: pendingRefundRequest?.id,
        status: CONSTANTS.REQUEST_STATUS.APPROVED,
      });
    }

    await hiddenDuesDB.removeByTenantIdAndClientIdAndOccupancyId({
      clientId,
      tenantId,
      occupancyId: occupancy?.id,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Mode [${mode}], Amount [${amount}], Paid Date [${paidDate}], Excess Payment Settled Successfully`
    );

    return res.status(200).json({
      msg: "Refund settled 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,
    });
  }
};

dues.ForfeitSecurityRefund = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "ForfeitSecurityRefund";

  try {
    const { tenantId } = req.query;

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

    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) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Non-Admin Not Authorized to forfeit refund payment`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Requested....`
      );
    }

    const moveOutOccupancies = await moveOutDB.getAllByTenantIdAndClientId({
      tenantId,
      clientId,
    });
    if (!moveOutOccupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Move Out Record Found`
      );

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const refundEntry = await ledgerDB.getLastEntryX({
      tenantId,
      clientId,
      createdAt: moveOutOccupancies[0].moveInDate,
    });

    if (!refundEntry) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Latest Move In Date [${moveOutOccupancies[0].moveInDate}], No Refund Record Found`
      );

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    await ledgerDB.updateBalance({
      tenantId,
      clientId,
      balance: 0,
      type: refundEntry.type,
      referenceId: refundEntry.referenceId,
    });

    await ledgerDB.updateDescription({
      id: refundEntry.id,
      description: `Refund of amount ₹${Math.abs(Number(refundEntry.balance))} has been forfeited after eviction.`,
    });

    for (let occupancy of moveOutOccupancies) {
      await moveOutDB.updateRefundStatus({
        id: occupancy.id,
        refundStatus: CONSTANTS.REFUND_STATUS.FORFEITED,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Forfeited Amount [${Math.abs(Number(refundEntry.balance))}], Refund Forfeited Successfully`
    );

    return res.status(200).json({
      msg: "Refund has been forfeited",
      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,
    });
  }
};

dues.SetRemark = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "SetRemark";

  try {
    const { remarks, id} = req.body;
    const { e } = req.query;

    let isEvicted = e === "1";

    log.info(`[${C}], [${F}], Remarks [${remarks}], Tenant Id [${id}], Is Evicted [${isEvicted}]`);

    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) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Non-Admin Not Authorized to settle excess payment`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Admin/Warden Requested....`
      );
    }

    if(!isEvicted) {
	    await duesDB.updateRemarks({
		    tenantId: id,
		    clientId,
		    remarks,
	    });
    } else {
	    await moveOutDuesDB.updateRemarks({
        tenantId: id,
        clientId,
        remarks,
      });
    }

    await tenantNotesDB.addNote({
      tenantId: id,
      clientId: clientId,
      addedBy: userType === CONSTANTS.USER_TYPE.CLIENT ? 0 : Number(req.id),
      note: remarks,
      type: CONSTANTS.NOTES_TYPES.TENANT_DUES,
    });
    
    log.info(`[${C}], [${F}], Client Id [${clientId}], Remarks [${remarks}], Tenant Id [${id}], Remarks Updated Successfully`);

    return res.status(200).json({
      msg: "Remarks 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,
    });
  }
};

dues.GetQRCode = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "GetQRCode";

  try {
    const {dueId, e=0} = req.query;
    let isEvicted = Number(e) === 1;

    log.info(`[${C}], [${F}], Due Id [${dueId}]`);
    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) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Non-Admin Not Authorized`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Admin/Warden Requested....`
      );
    }
    let due = await duesDB.getByIdAndClientId({ id: Number(dueId), clientId });
    if(isEvicted) {
      due = await moveOutDuesDB.getByIdAndClientId({ id: Number(dueId), clientId });
    }
    
    if (!due) {
      log.info(
        `[${C}], [${F}], No Due Found for Due Id [${dueId}]`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    let tenantId = due?.tenantId;
    let tenant = await tenantDB.getById({ id: tenantId });
    const client = await clientDB.getById({ id: clientId });

    let occupancy = await occupancyDB.getByTenantIdAndClientId({
          tenantId: Number(tenantId),
          clientId: clientId,
        });
    if(isEvicted) {
      occupancy = await moveOutDB.getByTenantIdAndClientId({
            tenantId: Number(tenantId),
            clientId: clientId,
          });
    }
    const property = await propertyDB.getById({
      id: occupancy.propId,
    });
    let paymentQRCode = null;
    let paymentLink = null;
    let replyMessage = "";

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

    let footerText = propSettings.footer || "The Kipinn Team";
    if (
      process.env.WEB_LINK_ENABLED === "true" &&
      property.isOnlinePaymentEnabled === 1 &&
      occupancy.isOnlinePaymentEnabled === 1
    ) {
        paymentLink = `${process.env.PAYU_WEB_CHECKOUT_URL}/${occupancy.gId}/${dueId}`;
        if (client?.paymentLinkBaseUrl && client?.paymentLinkBaseUrl.trim() !== "") {
          paymentLink = `${client?.paymentLinkBaseUrl.trim()}/${occupancy.gId}/${dueId}`;
        }
        paymentLink = paymentLink;
        paymentQRCode = await generateQRCode(paymentLink);
        log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], QrCode Generated Successfully`
      );
      replyMessage = CONSTANTS.MSG.SHARE_DUE_PAYMENT_WITH_LINK
      .replace("{#var1#}", tenant?.name || "")
      .replace("{#var2#}", due.balance.toString())
      .replace("{#var3#}", paymentLink)
      .replace("{#var4#}", footerText || "");
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Due Id [${dueId}], Online Payment Not Enabled`
      );
      replyMessage = CONSTANTS.MSG.SHARE_DUE_PAYMENT_WITHOUT_LINK
      .replace("{#var1#}", tenant?.name || "")
      .replace("{#var2#}", due.balance.toString())
      .replace("{#var3#}", footerText || "");
    }
    
    
    
    return res.status(200).json({
      msg: "QR code generated successfully",
      isSuccess: true,
      link: paymentLink,
      qrCode: paymentQRCode,
      tenantName: tenant?.name || "",
      tenantMobile: tenant?.mobile || "",
      //tenantMobile: "9599054423",
      reminderText: replyMessage,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }

};

dues.ToggleDueForTenant = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "ToggleDueForTenant";

  try {
    const { dueId, toggleValue, e } = req.body;

    let isEvicted = e === "1";

    log.info(
      `[${C}], [${F}], Due Id [${dueId}], Toggle Value [${toggleValue}], Is Evicted [${e}]`
    );

    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) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Non-Admin Not Authorized`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Admin/Warden Requested....`
      );
    }

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

    if (!canToggleDue || (canToggleDue && Number(canToggleDue.value) === 0)) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Client Config Value [${canToggleDue?.value}], Flag Not Enabled In Client Config`
      );

      return res.status(400).json({
        msg: "Feature not enabled for you. Please contact kipinn.",
        isSuccess: false,
      });
    }

    if (Number(toggleValue) === 1) {
      let due = await duesDB.getById({ id: dueId });
      if (isEvicted) {
        due = await moveOutDuesDB.getById({ id: dueId });
        // isEvicted = true;
      }

      if (!due) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], No Due Found In Either Dues or MoveOutDues`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      if (due.amount !== due.balance) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], Due Amount [${due.amount}], Due Balance [${due.balance}], Due Is Partially Paid, Cannot Hide`
        );
        return res.status(400).json({
          msg: "Cannot hide paritally paid dues",
          isSuccess: false,
        });
      }

      const hiddenDueId = await hiddenDuesDB.add({
        tenantId: due.tenantId,
        amount: due.amount,
        occupancyId: isEvicted ? due.moveOutId : due.occupancyId,
        roomId: due.roomId,
        propId: due.propId,
        clientId: due.clientId,
        rentStartDate: due.rentStartDate,
        rentEndDate: due.rentEndDate,
        dueDate: due.dueDate,
        type: due.type,
        balance: due.balance,
        ledgerReferenceId: due.ledgerReferenceId,
        description: due.description,
        title: due.title,
        discount: due.discount,
        remarks: due.remarks,
      });

      if (isEvicted) {
        await moveOutDuesDB.removeDue({ id: dueId });
      } else {
        await duesDB.removeDue({ id: dueId });
      }

      //In Future if want partially paid due to be hidden below needs to change
      await ledgerDB.remove({
        referenceId: due.ledgerReferenceId,
      });
    } else {
      let due = await hiddenDuesDB.getById({ id: dueId });
      if (!due) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], No Hidden Due Found`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

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

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

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

        isEvicted = true;
      }

      const referenceId = await generateLedgerReferenceId({ clientId });

      if (isEvicted) {

        const securityAdjustEntry = await ledgerDB.getLastEntry({
          tenantId: due.tenantId,
          clientId,
        });

        await moveOutDuesDB.add({
          tenantId: due.tenantId,
          amount: due.amount,
          moveOutId: due.occupancyId,
          roomId: due.roomId,
          propId: due.propId,
          clientId,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEnDate,
          type: due.type,
          dueDate: due.dueDate,
          balance: due.balance,
          ledgerReferenceId: referenceId,
          description: due.description,
          title: due.title,
          discount: due.discount,
        });

        await moveOutDuesDB.updateRemarks({
          tenantId: due.tenantId,
          clientId,
          remarks: due.remarks
        });

        await ledgerDB.add({
          tenantId: due.tenantId,
          roomId: due.roomId,
          propId: due.propId,
          clientId,
          amount: due.amount,
          balance: due.balance,
          referenceId: referenceId,
          transactionId: null,
          type: due.type,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEndDate,
          dueDate: due.dueDate,
          description: due.description,
          title: due.title,
        });

        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: 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,
          });
        }
      } else {
        await duesDB.addWithStartEndDateX({
          tenantId: due.tenantId,
          amount: due.amount,
          occupancyId: due.occupancyId,
          roomId: due.roomId,
          propId: due.propId,
          clientId: clientId,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEndDate,
          type: due.type,
          dueDate: due.dueDate,
          balance: due.balance,
          ledgerReferenceId: referenceId,
          description: due.description,
          title: due.title,
          discount: due.discount,
        });

        await duesDB.updateRemarks({
          tenantId: due.tenantId,
          clientId,
          remarks: due.remarks
        });

        await ledgerDB.add({
          tenantId: due.tenantId,
          roomId: due.roomId,
          propId: due.propId,
          clientId,
          amount: due.amount,
          balance: due.balance,
          referenceId: referenceId,
          transactionId: null,
          type: due.type,
          rentStartDate: due.rentStartDate,
          rentEndDate: due.rentEndDate,
          dueDate: due.dueDate,
          description: due.description,
          title: due.title,
        });
      }

      await hiddenDuesDB.removeById({ id: dueId });
    }

    // await duesDB.toggleHide({
    //   id: dueId,
    //   hideFromTenant: toggleValue,
    // });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Due Id [${dueId}], Toggle Value [${toggleValue}], Due Toggled Successfully`
    );

    return res.status(200).json({
      msg: `${toggleValue === 1 ? "The due is now hidden from the tenant." : "The due is now visible to the tenant."}`,
      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,
    });
  }
};

dues.ListHiddenDues = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "ListHiddenDues";

  try {
    const userType = req.userType;

    let dues: any = [];
    let total = 0;

    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"
        }....`
      );

      dues = await hiddenDuesDB.getByClientId({ clientId });
      total = await hiddenDuesDB.getTotalByClientId({ clientId });
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.WARDEN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Non-Admin Not Authorized`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Admin/Warden Requested....`
      );

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId: staff.id,
      });
      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        dues = await hiddenDuesDB.getByClientIdForStaff({ clientId, propertiesIds });
        total = await hiddenDuesDB.getTotalByClientIdForStaff({ clientId, propertiesIds });
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Hidden Dues list sent successfully`
    );

    return res.status(200).json({
      msg: "Hidden Dues list sent successfully",
      list: dues || [],
      total: total || 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,
    });
  }
};

dues.ListMoveOutTenantDues = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "ListMoveOutTenantDues";

  try {
    const { tenantId } = req.params;

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

    if(userType === CONSTANTS.USER_TYPE.TENANT) {
      clientId = req.clientId || 0;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Tenant Requested....`
      );
      if(!req.clientId){
          log.info(`[${C}], [${F}], Tenant Id [${tenantId}], Client Id [${clientId}], No Client Id Found in tenant Token`);
          return res.status(401).json({
            msg: "Session Expired",
            isSuccess: false,
          });
      }
    } else {
      if(userType === CONSTANTS.USER_TYPE.CLIENT) {
        clientId = req.id || 0;
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Client Requested....`
        );
      } else if(userType === CONSTANTS.USER_TYPE.STAFF) {
        const staff = await staffDB.getById({ id: req.id });
        if (!staff) {
          log.info(
            `[${C}], [${F}], Tenant Id [${tenantId}], Staff Id [${req.id}], No Staff Found`
          );
          return res
            .status(400)
            .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
        }
        clientId = staff.clientId;
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Staff Requested....`
        );
      }
    }

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

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

    const occupancy = await moveOutDB.getByTenantIdAndClientIdDesc({
      tenantId: tenantId,
      clientId: clientId,
    });
    if (!occupancy) {
      log.info(`[${C}], [${F}], Tenant Id [${tenantId}], No Move Out Record Found`);
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const property = await propertyDB.getById({
      id: occupancy.propId,
    });

    const dues = await moveOutDuesDB.getByTenantIdAndClientId({ tenantId, clientId });
    const { totalDues } = await moveOutDuesDB.getTotalDuesByTenantId({
      tenantId: tenant.id,
      propId: occupancy.propId,
    });

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Tenant Dues list sent successfully`
    );
    const mode = {
      upi: client.upiEnabled,
      card: client.creditCardEnabled,
      netbanking: client.netBankingEnabled,
    };
    let isGstEnabled = property?.isGstEnabled || 0;
    let gstCharges = 0.00;
    if(1 == isGstEnabled) {
      gstCharges = Number((totalDues * (property?.gstCharges / 100)).toFixed(2));
    }
    const combinedDues = true;
    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], totalDues [${totalDues}], gstCharges [${gstCharges}], Tenant Dues list sent successfully`
    );
    return res.status(200).json({
      msg: "Tenant Dues list sent successfully",
      data: {
        mode,
        isGSTInvoice: isGstEnabled,
        gstAmount : gstCharges,
        totalDues: totalDues || 0,
        list: dues || [],
        combinedDues,
      },
      property,
      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,
    });
  }
};

dues.FinalTenantDuesStatement = async (req: CustomRequest, res: Response) => {
  const C = "Dues Controller";
  const F = "FinalTenantDuesStatement";
  
  try {
    const { tenantId } = req.query;

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

    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) {
        log.info(
          `[${C}], [${F}], No Staff Found for Id [${req.id}]`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      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}], Non-Admin Not Authorized`
        );
        return res
          .status(400)
          .json({ msg: "Unauthorized access", isSuccess: false });
      }

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Admin/Warden Requested....`
      );
    }

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

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

    //log.info(`Occupancy [${JSON.stringify(occupancy)}]`);

    let totalPendingDueAmount = 0;
    let totalAdjustedDueAmount = 0;

    const pendingDues = await moveOutDuesDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    //log.info(`Pending Dues [${JSON.stringify(pendingDues)}]`);

    if (pendingDues) {
      for (let due of pendingDues) {
        totalPendingDueAmount += Number(due.balance);
      }
    }

    const securityAdjustedDues = await ledgerDB.getSecurityAdjustedRecordsAfterEviction({
      clientId,
      tenantId,
      moveOutDate: moment(occupancy.moveOutDate).format("YYYY-MM-DD"),
    });

    //log.info(`Security Adjusted Dues [${JSON.stringify(securityAdjustedDues)}]`);

    if (securityAdjustedDues) {
      for (let due of securityAdjustedDues) {
        totalAdjustedDueAmount += Math.abs(Number(due.amount));
      }
    }

    // const securityUsedDues = await ledgerDB.getUsedSecurityByTenantIdAndClientIdForFnf({
    //   clientId,
    //   tenantId,
    //   createdAt: moment(occupancy.moveInDate).format("YYYY-MM-DD"),
    //   moveOutDate: moment(occupancy.moveOutDate).format("YYYY-MM-DD"),
    // });

    const securityUsedDues = await ledgerDB.getMarkedFromSecurityDuesForFnF({
      clientId,
      tenantId,
      moveInDate: moment(occupancy.moveInDate).format("YYYY-MM-DD"),
    });
    
    //log.info(`Security Used [${JSON.stringify(securityUsedDues)}]`);

    if (securityUsedDues) {
      for (let due of securityUsedDues) {
        totalAdjustedDueAmount += Math.abs(Number(due.amount));
      }
    }

    let securityPaid = await ledgerDB.getSecurityTransactionAmountX({
      tenantId,
      type: CONSTANTS.DUES_TYPES.SECURITY,
      roomId: occupancy.roomId,
      propId: occupancy.propId,
    });

    securityPaid = Math.abs(Number(securityPaid?.amount)) || 0;

    const totalRefund = Number(securityPaid) - Number(totalPendingDueAmount) - Number(totalAdjustedDueAmount);

    const settlementSummaryDoc = await documentDB.getIDByTypeAndStatus({
      tenantId,
      clientId,
      type: CONSTANTS.DOCUMENT_TYPES.SETTLEMENT_SUMMARY,
      moveOut: 1,
      status: CONSTANTS.DOCUMENT_STATUS.UNVERIFIED,
    });

    //log.info(`Settle Summary Doc [${JSON.stringify(settlementSummaryDoc)}]`);
    
    let finalSecurityDuesList = [];
    if (securityAdjustedDues && securityAdjustedDues.length > 0) finalSecurityDuesList.push(...securityAdjustedDues);
    if (securityUsedDues && securityUsedDues.length > 0) finalSecurityDuesList.push(...securityUsedDues);

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Security Paid [${securityPaid}], Total Pending Due Amount [${totalPendingDueAmount}], Total Adjusted Due Amount [${totalAdjustedDueAmount}], Total Refund Calculated [${totalRefund}], Settlement Summary Sent Successfully`
    );

    return res.status(200).json({
      msg: "Tenant final summary fetched successfully",
      isSuccess: true,
      data: {
        pendingDues: pendingDues || [],
        securityAdjustedDues: finalSecurityDuesList || [],
        // securityAdjustedDues: securityAdjustedDues || [],
        // securityUsedDues: securityUsedDues || [],
        securityPaid: securityPaid || 0,
        totalPendingDueAmount: totalPendingDueAmount || 0,
        totalAdjustedDueAmount: totalAdjustedDueAmount || 0,
        totalRefund: totalRefund,
        settlementSummaryDoc: settlementSummaryDoc?.value || null,
      }
    });
  } 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 dues;
