import { Response } from "express";
import moment, { min, months } from "moment";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import bankDB from "../models/bank.model";
import bedDB from "../models/beds.model";
import clientDB from "../models/client.model";
import duesDB from "../models/dues.model";
import propertyDB from "../models/property.model";
import roomDB from "../models/room.model";
import roomOptionDB from "../models/roomOption.model";
import staffDB from "../models/staff.model";
import transactionDB from "../models/transaction.model";
import bedTypes from "../schemas/bed.schema";
import CustomRequest from "../types/requestType";
import generateRoomNum from "../utils/generateRoomNum";
import extraChargeDB from "../models/extraCharges.model";
import electricity from "./electricity.controller";
import createIncomeStats from "../utils/client/createIncomeStats";
import flatDB from "../models/flat.model";
import occupancyDB from "../models/occupancy.model";
import setDefaultNotificationSettings from "../utils/setDefaultNotificationSettings";
import {
  propertiesPendingRentForCurMonth,
  propertyDailyPendingRent,
  propertyDailyPendingRentForMonth,
  propertyMonthlyPendingRent,
} from "../utils/client/rentPendingStats";
import { isUserFinanceAdmin, isUserPartner, isUserSuperAdmin } from "../utils/isUserPartner";
import tenantDB from "../models/tenant.model";
import ledgerDB from "../models/ledger.model";
import generateLedgerReferenceId from "../utils/generateLedgerReferenceId";
import adjustInitialSettlement, {
  propertySwitchExcessSettlement,
} from "../utils/adjustInitialSettlement";
import { calculateMonthlyRent, calculateRentAsPerRentalType, calculateRentAsPerRentalTypeForReserved } from "../utils/calculateRentPerDay";
import getDueDescription from "../utils/getDueDescription";
import adjustExcessPayments from "../utils/adjustExcessPayment";
import convertTypes from "../utils/convertTypes";
import notice from "./notice.controller";
import expenseDB from "../models/expense.model";
import { logActivity, logLeaseActivity, logPropertyActivity } from "../utils/logActivity";
import occupancyReportDB from "../models/occupancyReport.model";
import leadDB from "../models/lead.model";
import complaintDB from "../models/complaint.model";
import noticeDB from "../models/notice.model";
import feedbackDB from "../models/feedback.model";
import moveOutDB from "../models/moveOut.model";
import requestDB from "../models/request.model";
import settingsDB from "../models/settings.model";
import {
  clientExpenseGraph,
  clientExpenseGraphWeb,
  clientOccupancyGraph,
  clientOccupancyGraphFY,
  clientOccupancyGraphFYForWeb,
  propertyExpenseGraph,
  propertyOccupancyGraph,
  propertyOccupancyGraphFY,
  staffOccupancyGraph,
  staffOccupancyGraphFY,
  staffOccupancyGraphFYWeb,
} from "../utils/client/getGraphData";
import landlordDB from "../models/landlord.model";
import propertyLeaseDB from "../models/propertyLease.model";
import vendorDB from "../models/vendor.model";
import recurringExpenseDB from "../models/recurringExpense.model";
import landlordDocumentDB from "../models/landlordDocuments.model";
import getDocumentTitle from "../utils/getDocumentTitle";
import landlordAccountDB from "../models/landlordAccount.model";
import { isPrivilegedStaff } from "../utils/isPrivilegedStaff";
import clientConfigDB from "../models/clientConfig.model";
import landlordBankAccountsDB from "../models/landlordBankAccounts.model";
import ClientPendingTasks, { StaffPendingTasks } from "../utils/client/getPendingTasks";
import locationDB from "../models/location.model";
import fsPromises from "fs/promises";
import fs from "fs";
import propertyDocumentDB from "../models/propertyDocuments.model";
import { setExpenseJournalTallyStatus, setPropertyIncomeSecurityLedgerTally } from "../utils/setTallyStatus";
import { salesHeadWebDashboardData, salesHeadWebDashboardDataForClient } from "../utils/client/getDashboardData";
import foodDB from "../models/food.model";
import path from "path";
import kitchenInventoryDB from "../models/kitchenInventory.model";
import { copyRentAgreement } from "../utils/copyRentAgreement";
import walletDB from "../models/wallet.model";

const property: any = {};

property.AddBasicDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "Add Property - 1.BasicDetails";

  try {
    let {
      propertyName,
      ownerName,
      ownerMobile,
      city: address = "",
      address: streetAddress,
      pincode = null,
      type,
      floorCount,
      flatCount,
      isGroundIncluded,
      locationId = null,
    } = req.body;

    const userType = req.userType;

    let clientId = req.id;
    let staff = null;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Property Name [${propertyName}], Owner Name [${ownerName}], Owner Mobile [${ownerMobile}], Address [${address}], Street Address [${streetAddress}], Pincode [${pincode}], Type [${type}], Floor Count [${floorCount}], Flat Count [${flatCount}], isGroundIncluded [${isGroundIncluded}], Location Id [${locationId}], 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}], Property Name [${propertyName}], Owner Name [${ownerName}], Owner Mobile [${ownerMobile}], Address [${address}], Street Address [${streetAddress}], Pincode [${pincode}], Type [${type}], Floor Count [${floorCount}], Flat Count [${flatCount}], isGroundIncluded [${isGroundIncluded}], Location Id [${locationId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Name [${propertyName}], Owner Name [${ownerName}], Owner Mobile [${ownerMobile}], Address [${address}], Street Address [${streetAddress}], Pincode [${pincode}], Type [${type}], Floor Count [${floorCount}], Flat Count [${flatCount}], isGroundIncluded [${isGroundIncluded}], Location Id [${locationId}], Client Requested....`
      );
    }

    if (type === CONSTANTS.PROPERTY_TYPE.PG && floorCount <= 0) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Name [${propertyName}], Owner Name [${ownerName}], Owner Mobile [${ownerMobile}], Address [${address}], Street Address [${streetAddress}], Pincode [${pincode}], Type [${type}], Floor Count [${floorCount}], Flat Count [${flatCount}], isGroundIncluded [${isGroundIncluded}], Location Id [${locationId}], Floor Count Should Be Greater Than 0`
      );
      return res
        .status(400)
        .json({ msg: "Please provide number of floors", isSuccess: false });
    } else if (type === CONSTANTS.PROPERTY_TYPE.FLAT && flatCount <= 0) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Name [${propertyName}], Owner Name [${ownerName}], Owner Mobile [${ownerMobile}], Address [${address}], Street Address [${streetAddress}], Pincode [${pincode}], Type [${type}], Floor Count [${floorCount}], Flat Count [${flatCount}], isGroundIncluded [${isGroundIncluded}], Location Id [${locationId}], Floor Count Should Be Greater Than 0`
      );
      return res
        .status(400)
        .json({ msg: "Please provide number of floors", isSuccess: false });
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Name [${propertyName}], Owner Name [${ownerName}], Owner Mobile [${ownerMobile}], Address [${address}], Street Address [${streetAddress}], Pincode [${pincode}], Type [${type}], Floor Count [${floorCount}], Flat Count [${flatCount}], isGroundIncluded [${isGroundIncluded}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const isExistWithSameDetails = await propertyDB.getByNameAndAdress({
      name: propertyName,
      address,
      streetAddress,
      clientId,
    });

    if (isExistWithSameDetails) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Name [${propertyName}], Property already exists with same name and address`
      );
      return res.status(400).json({
        msg: "Property already exists with same name and address",
        isSuccess: false,
      });
    }

    let gId =
      CONSTANTS.PROP_GID_SUFFIX +
      (Math.floor(Math.random() * 1999) + 9999).toString();

    let isExists = await propertyDB.getByGId({ gId });

    while (isExists) {
      gId =
        CONSTANTS.PROP_GID_SUFFIX +
        (Math.floor(Math.random() * 1999) + 9999).toString();

      isExists = await propertyDB.getByGId({ gId });
    }

    log.info(`[${C}], [${F}], GID [${gId}], Client Id [${clientId}]`);

    const propId = await propertyDB.addBasicDetails({
      gId,
      clientId: client?.id,
      name: propertyName,
      ownerName,
      ownerMobile,
      address,
      streetAddress,
      type,
      floorCount,
      isGroundIncluded,
      pincode,
      parentId: client?.parentId,
    });

    if (locationId) {
      await propertyDB.updateLocation({
        id: propId,
        locationId: locationId,
      });
    } else {
      let newLocationId = null;
      const isExists = await locationDB.getByClientIdAndName({
        clientId,
        name: address,
      });

      if (!isExists) {
        newLocationId = await locationDB.addLocation({
          clientId,
          name: address,
          description: "",
        });
      } else {
        newLocationId = isExists?.id;
      }

      await propertyDB.updateLocation({
        id: propId,
        locationId: newLocationId,
      });

      await propertyDB.concatAddress({
        id: propId,
      });
    }

    await propertyDB.updatePayuMidAnDKeyDetails({
      id: propId,
      payuMid: client.payuMid || null,
      payuKey: client.payuKey || null,
    });

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      await staffDB.link({ clientId, propId, staffId: staff.id });
    }

    const superAdminStaffs = await staffDB.getSuperAdminByClientId({
      clientId,
    });

    if (superAdminStaffs && superAdminStaffs.length > 0) {
      for (let superAdminStaff of superAdminStaffs) {
        if (userType === CONSTANTS.USER_TYPE.STAFF && Number(superAdminStaff.id) === Number(req.id)) continue;
        await staffDB.link({ staffId: superAdminStaff.id, propId: propId, clientId });
      }
    }

    if (type === CONSTANTS.PROPERTY_TYPE.FLAT && Number(flatCount) > 0) {
      const flatArr = Array.from(Array(flatCount));

      let flatNum = 1;
      for (let _ of flatArr) {
        await flatDB.create({
          clientId,
          propId,
          name: flatNum,
        });
        flatNum++;
      }

      log.info(
        `[${C}], [${F}], GID [${gId}], ${flatCount} Flats added successfully`
      );
    }

    log.info(
      `[${C}], [${F}], GID [${gId}], Basic details has been added successfully`
    );

    await setDefaultNotificationSettings(Number(clientId), propId);

    return res.status(200).json({
      msg: "Basic details has been added successfully",
      propId,
      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,
    });
  }
};

property.AddTenantAgreementDetails = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Property Controller";
  const F = "Add Property - 2.TenantAgreementDetails";

  try {
    const {
      tenantPreference,
      securityDeposit,
      agreementPeriod,
      noticePeriod,
      lockInPeriod,
      propId,
    } = 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}], Prop Id [${propId}], Tenant Preference [${tenantPreference}], Security Deposit [${securityDeposit}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], LockIn Period [${lockInPeriod}], 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}], Prop Id [${propId}], Tenant Preference [${tenantPreference}], Security Deposit [${securityDeposit}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], LockIn Period [${lockInPeriod}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Preference [${tenantPreference}], Security Deposit [${securityDeposit}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], LockIn Period [${lockInPeriod}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Preference [${tenantPreference}], Security Deposit [${securityDeposit}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], LockIn Period [${lockInPeriod}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await propertyDB.addTenantAgreementDetails({
      tenantPreference,
      security: securityDeposit,
      agreementPeriod,
      noticePeriod,
      lockInPeriod,
      id: propId,
    });

    await flatDB.updateGenderByPropId({
      propId: propId,
      gender: tenantPreference,
    });

    log.info(
      `[${C}], [${F}], Tenant & agreement details has been added successfully`
    );

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

property.AddRentDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "Add Property - 3.RentDetails";

  try {
    const { rentalCycle, gracePeriod, fine, fineType, propId } = req.body;

    const userType = req.userType;

    let clientId = req.id;
    let staff = null;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], Rental Cycle [${rentalCycle}], Grace Period [${gracePeriod}], Fine [${fine}], FineType [${fineType}], 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}], Prop Id [${propId}], Rental Cycle [${rentalCycle}], Grace Period [${gracePeriod}], Fine [${fine}], FineType [${fineType}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Rental Cycle [${rentalCycle}], Grace Period [${gracePeriod}], Fine [${fine}], FineType [${fineType}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Rental Cycle [${rentalCycle}], Grace Period [${gracePeriod}], Fine [${fine}], FineType [${fineType}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    await propertyDB.addRentDetails({
      rentalCycle,
      gracePeriod,
      fine,
      fineType,
      id: propId,
      status: CONSTANTS.PROPERTY_STATUS.COMPLETED,
    });

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

    if (isCopyRoomOptionEnabled && Number(isCopyRoomOptionEnabled?.value) === 1) {
      const properties = await propertyDB.getAllActiveByClientId({
        clientId,
      });

      if (properties && properties.length > 0) {
        const roomOptions = await roomOptionDB.getByPropId({
          propId: properties[0].id,
        });

        if (roomOptions && roomOptions.length > 0) {
          for (let roomOption of roomOptions) {
            const roomOptionId = await roomOptionDB.create({
              propId,
              rent: roomOption.rent,
              security: roomOption.security,
              type: roomOption.type,
              name: roomOption.name,
              amenities: roomOption.amenities,
              totalBedCount: roomOption.totalBedCount,
              furnishingType: roomOption.furnishingType,
            });
          }
        }
      }
    }

    // const superAdminStaffs = await staffDB.getSuperAdminByClientId({
    //   clientId,
    // });

    // if (superAdminStaffs && superAdminStaffs.length > 0) {
    //   for (let superAdminStaff of superAdminStaffs) {
    //     await staffDB.link({ staffId: superAdminStaff.id, propId: propId, clientId });
    //   }
    // }

    // const BASE_UPLOADS_DIR = '/var/www/html/PROJECTS/Kipinn/cms/backend/uploads';
    // const SIGNATURES_BASE = path.join(BASE_UPLOADS_DIR, 'owner_signatures');
    // const AGREEMENTS_BASE = path.join(BASE_UPLOADS_DIR, 'rent_agreements');

    // let sourceSignaturePropId = null;
    // let sourceAgreementPropId = null;

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

    // if (activeProperties && activeProperties.length > 0) {
    //   for (let activeProperty of activeProperties) {
    //     const sigPath = path.join(SIGNATURES_BASE, String(activeProperty.id));
    //     const agreementPath = path.join(AGREEMENTS_BASE, String(activeProperty.id));
    //     if (fs.existsSync(sigPath) && fs.existsSync(agreementPath)) {
    //       sourceSignaturePropId = activeProperty.id;
    //       sourceAgreementPropId = activeProperty.id;
    //       break;
    //     }
    //   }
    // }

    // log.info(
    //   `Signature Source Prop Id [${sourceSignaturePropId}], Signature Dest Prop Id [${propId}], Agreement Source Prop Id [${sourceAgreementPropId}], Agreement Dest Prop Id [${propId}]`
    // );

    // // const copyFolderRecursive = (srcDir: any, targetDir: any) => {
    // //   if (!fs.existsSync(srcDir)) return;
    // //   fs.mkdirSync(targetDir, { recursive: true });

    // //   fs.cpSync(srcDir, targetDir, { recursive: true });
    // // };

    // if (sourceSignaturePropId) {
    //   const signatureSrc = path.join(SIGNATURES_BASE, String(sourceSignaturePropId));
    //   const signatureDest = path.join(SIGNATURES_BASE, String(propId));
    //   // copyFolderRecursive(signatureSrc, signatureDest);

    //   fs.mkdirSync(signatureDest, { recursive: true });
    //   fs.cpSync(signatureSrc, signatureDest, { recursive: true });
    // }

    // if (sourceAgreementPropId) {
    //   const agreementSrc = path.join(AGREEMENTS_BASE, String(sourceAgreementPropId));
    //   const agreementDest = path.join(AGREEMENTS_BASE, String(propId));
    //   // copyFolderRecursive(agreementSrc, agreementDest);

    //   fs.mkdirSync(agreementDest, { recursive: true });
    //   fs.cpSync(agreementSrc, agreementDest, { recursive: true });
    // }

    await copyRentAgreement({
      clientId: Number(clientId),
      propId: Number(propId),
    });

    log.info(`[${C}], [${F}], Rent details has been added successfully`);

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

property.List = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "List";

  try {
    const { userType } = req;
    let { pageNum, filter, searchVal, locationId } = req.query;
    // let clientId = req.id;
    let properties = [];

    const platform = req.platform;

    log.info(`[${C}], [${F}], Page Number [${pageNum}], Filter [${filter}], Search Value [${searchVal}], Location Id [${locationId}], Platform [${platform}]`);

    // let isPartner = false;

    // if (userType === CONSTANTS.USER_TYPE.STAFF) {
    //   const staff = await staffDB.getById({ id: req.id });
    //   if (!staff) {
    //     log.info(`[${C}], [${F}], Staff Id [${req.id}] No Staff Found`);
    //     return res
    //       .status(400)
    //       .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    //   }
    //   isPartner = staff.role === CONSTANTS.STAFF_ROLES.PARTNER ? true : false;
    //   clientId = staff.clientId;
    // }

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    let summary = null;
    let flatSummary = null;
    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      //const clientId = req.id;

      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Num [${pageNum}], Filter [${filter}], Search Val [${searchVal}], Platform [${platform}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }...`
      );

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

      let limit = 10;

      if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
        limit = 10000;
      }

      if (searchVal) {
        properties = await propertyDB.getSearchResultsByClientId({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          searchVal,
        });
      } else if (filter === "A") {
        properties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
          locationId: locationId,
        });
      } else if (filter === "P") {
        properties = await propertyDB.getByClientIdIncompleteProps({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
          // status: CONSTANTS.PROPERTY_STATUS.PENDING,
        });
      } else if (filter === "I") {
        properties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.INACTIVE,
          locationId: locationId,
        });
      } else if (filter === "LV") { //Landlord Verified
        properties = await propertyDB.getByClientIdWithVerifiedLandlord({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "LBV") { //Landlord Basic Verified
        properties = await propertyDB.getByCLientIdWithBasicLandlord({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "LNV") { //Landlord Not Verified
        properties = await propertyDB.getByClientIdWithNotVerifiedLandlord({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "FO") {//Fully Occupied
        properties = await propertyDB.getFullyOccupiedByClientId({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "NFO") {//Not Fully Occupied
        properties = await propertyDB.getNotFullyOccupiedByClientId({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "OPY") {//Online Payment Enabled
        properties = await propertyDB.getPaymentEnabledOrNotForClient({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          isOnlinePaymentEnabled: 1,
        });
      } else if (filter === "OPN") {//Online Payment Not Enabled
        properties = await propertyDB.getPaymentEnabledOrNotForClient({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          isOnlinePaymentEnabled: 0,
        });
      } else if (filter === "LMP") {//Loss Making Properties
        const allProperties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit: 10e9,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
          locationId: locationId,
        });

        if (allProperties && allProperties.length > 0) {
          for (let property of allProperties) {
            //below income is after subtracting refund amount
            const income = await transactionDB.getTotalByPropId({
              propId: property.id,
              month: moment().format("YYYY-MM-DD"),
            });
            const duesToBeAdded = await occupancyDB.getRentToBeAddedForProperty({
              clientId,
              propId: property.id,
            });
            const expenses = await expenseDB.getTotalByPropIdAndDateRange({
              propId: property.id,
              startDate: moment().startOf("month").format("YYYY-MM-DD"),
              endDate: moment().endOf("month").format("YYYY-MM-DD"),
            });
            const expenseToBeAdded = await recurringExpenseDB.expenseToBeAddedByPropId({
              clientId,
              propId: property.id,
            });
            const unPaidExpenses = await expenseDB.getTotalUnMarkedByClientIdAndPropId({
              clientId,
              propId: property.id,
            });

            const landlordRentPerMonth = await propertyLeaseDB.getTotalMonthlyRentByClientIdForWeb({
              clientId,
              propIds: [property.id],
            });

            const monthlyRentFromTenants = await occupancyDB.getTotalMonthlyRentByClientIdAndPropId({
              clientId,
              propId: property.id,
            });

            // const profitLoss = (Number(income) + Number(duesToBeAdded)) - (Number(expenses) + Number(expenseToBeAdded) + Number(unPaidExpenses));
            const profitLoss = Number(monthlyRentFromTenants) - Number(landlordRentPerMonth);

            log.info(`[${C}], [${F}], Client Id [${clientId}], Landlord Rent Per Month [${landlordRentPerMonth}], Monthly Tenant Rent [${monthlyRentFromTenants}] Prop Name [${property.name}], Prop Id [${property.id}], Income [${income}], Dues to be added [${duesToBeAdded}], Expenses [${expenses}], Expense To be added [${expenseToBeAdded}], UnPaid Expense [${unPaidExpenses}], Profit Loss [${profitLoss}]`);

            if (profitLoss < 0) {
              properties.push(property);
            }
          }
        }
      } else if (filter === "PMP") {//Profit Making Properties
        const allProperties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit: 10e9,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
          locationId: locationId,
        });

        if (allProperties && allProperties.length > 0) {
          for (let property of allProperties) {
            //below income is after subtracting refund amount
            const income = await transactionDB.getTotalByPropId({
              propId: property.id,
              month: moment().format("YYYY-MM-DD"),
            });
            const duesToBeAdded = await occupancyDB.getRentToBeAddedForProperty({
              clientId,
              propId: property.id,
            });
            const expenses = await expenseDB.getTotalByPropIdAndDateRange({
              propId: property.id,
              startDate: moment().startOf("month").format("YYYY-MM-DD"),
              endDate: moment().endOf("month").format("YYYY-MM-DD"),
            });
            const expenseToBeAdded = await recurringExpenseDB.expenseToBeAddedByPropId({
              clientId,
              propId: property.id,
            });
            const unPaidExpenses = await expenseDB.getTotalUnMarkedByClientIdAndPropId({
              clientId,
              propId: property.id,
            });

            const landlordRentPerMonth = await propertyLeaseDB.getTotalMonthlyRentByClientIdForWeb({
              clientId,
              propIds: [property.id],
            });

            const monthlyRentFromTenants = await occupancyDB.getTotalMonthlyRentByClientIdAndPropId({
              clientId,
              propId: property.id,
            });

            const profitLoss = (Number(monthlyRentFromTenants) - Number(landlordRentPerMonth));
            // const profitLoss = (Number(income) + Number(duesToBeAdded)) - (Number(expenses) + Number(expenseToBeAdded) + Number(unPaidExpenses));

            log.info(`[${C}], [${F}], Client Id [${clientId}], Landlord Rent Per Month [${landlordRentPerMonth}], Monthly Tenant Rent [${monthlyRentFromTenants}] Prop Name [${property.name}], Prop Id [${property.id}], Income [${income}], Dues to be added [${duesToBeAdded}], Expenses [${expenses}], Expense To be added [${expenseToBeAdded}], UnPaid Expense [${unPaidExpenses}], Profit Loss [${profitLoss}]`);

            if (profitLoss >= 0) {
              properties.push(property);
            }
          }
        }
      } else {
        properties = await propertyDB.getByClientId({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      }
      summary = await propertyDB.getSummaryByClientIdX({
        clientId,
        locationId: locationId,
      });

      flatSummary = await propertyDB.getFlatSummaryByClientId({
        clientId,
        locationId: locationId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property list sent successfully`
      );
    } else {
      const staffId = req.id;

      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Page Num [${pageNum}], Filter [${filter}], Search Val [${searchVal}], Staff Requesting...`
      );

      const staff = await staffDB.getById({ id: staffId });

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

      let limit = 10;

      if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
        limit = 10000;
      }

      if (searchVal) {
        properties = await staffDB.getPropertiesSearchResult({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          searchVal,
        });
      } else if (filter === "A") {
        properties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
          locationId: locationId,
        });
      } else if (filter === "P") {
        properties = await propertyDB.getByClientIdIncompletePropsForStaff({
          clientId: staff.clientId,
          staffId,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "I") {
        properties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.INACTIVE,
          locationId: locationId,
        });
      } else if (filter === "LV") { //Landlord Verified
        properties = await propertyDB.getByClientIdWithVerifiedLandlordForStaff({
          clientId: clientId,
          staffId,
          pageNum: Number(pageNum),
          limit,
          locaitonId: locationId,
        });
      } else if (filter === "LBV") { //Landlord Basic Verified
        properties = await propertyDB.getByCLientIdWithBasicLandlordForStaff({
          clientId: clientId,
          staffId,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "LNV") { //Landlord Not Verified
        properties = await propertyDB.getByClientIdWithNotVerifiedLandlordForStaff({
          clientId: clientId,
          staffId,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "FO") {//Fully Occupied
        properties = await propertyDB.getFullyOccupiedByClientIdForStaff({
          clientId: clientId,
          staffId: staffId,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "NFO") {//Not Fully Occupied
        properties = await propertyDB.getNotFullyOccupiedByClientIdForStaff({
          clientId: clientId,
          staffId: staffId,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      } else if (filter === "OPY") {//Online Payment Enabled
        properties = await propertyDB.getPaymentEnabledOrNotForStaff({
          clientId: staff.clientId,
          staffId,
          pageNum: Number(pageNum),
          limit,
          isOnlinePaymentEnabled: 1,
        });
      } else if (filter === "OPN") {//Online Payment Not Enabled
        properties = await propertyDB.getPaymentEnabledOrNotForStaff({
          clientId: staff.clientId,
          staffId,
          pageNum: Number(pageNum),
          limit,
          isOnlinePaymentEnabled: 0,
        });
      } else if (filter === "LMP") {//Loss Making Properties
        const allProperties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
          locationId: locationId,
        });

        if (allProperties && allProperties.length > 0) {
          for (let property of allProperties) {
            //below income is after subtracting refund amount
            const income = await transactionDB.getTotalByPropId({
              propId: property.id,
              month: moment().format("YYYY-MM-DD"),
            });
            const duesToBeAdded = await occupancyDB.getRentToBeAddedForProperty({
              clientId,
              propId: property.id,
            });
            const expenses = await expenseDB.getTotalByPropIdAndDateRange({
              propId: property.id,
              startDate: moment().startOf("month").format("YYYY-MM-DD"),
              endDate: moment().endOf("month").format("YYYY-MM-DD"),
            });
            const expenseToBeAdded = await recurringExpenseDB.expenseToBeAddedByPropId({
              clientId,
              propId: property.id,
            });
            const unPaidExpenses = await expenseDB.getTotalUnMarkedByClientIdAndPropId({
              clientId,
              propId: property.id,
            });


            const profitLoss = (Number(income) + Number(duesToBeAdded)) - (Number(expenses) + Number(expenseToBeAdded) + Number(unPaidExpenses));

            log.info(`Prop Name [${property.name}], Prop Id [${property.id}], Income [${income}], Dues to be added [${duesToBeAdded}], Expenses [${expenses}], Expense To be added [${expenseToBeAdded}], UnPaid Expense [${unPaidExpenses}], Profit Loss [${profitLoss}]`);

            if (profitLoss < 0) {
              properties.push(property);
            }
          }
        }
      } else if (filter === "PMP") {//Profit Making Properties
        const allProperties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
          locationId: locationId,
        });

        if (allProperties && allProperties.length > 0) {
          for (let property of allProperties) {
            //below income is after subtracting refund amount
            const income = await transactionDB.getTotalByPropId({
              propId: property.id,
              month: moment().format("YYYY-MM-DD"),
            });
            const duesToBeAdded = await occupancyDB.getRentToBeAddedForProperty({
              clientId,
              propId: property.id,
            });
            const expenses = await expenseDB.getTotalByPropIdAndDateRange({
              propId: property.id,
              startDate: moment().startOf("month").format("YYYY-MM-DD"),
              endDate: moment().endOf("month").format("YYYY-MM-DD"),
            });
            const expenseToBeAdded = await recurringExpenseDB.expenseToBeAddedByPropId({
              clientId,
              propId: property.id,
            });
            const unPaidExpenses = await expenseDB.getTotalUnMarkedByClientIdAndPropId({
              clientId,
              propId: property.id,
            });

            const profitLoss = (Number(income) + Number(duesToBeAdded)) - (Number(expenses) + Number(expenseToBeAdded) + Number(unPaidExpenses));

            log.info(`Prop Name [${property.name}], Prop Id [${property.id}], Income [${income}], Dues to be added [${duesToBeAdded}], Expenses [${expenses}], Expense To be added [${expenseToBeAdded}], UnPaid Expense [${unPaidExpenses}], Profit Loss [${profitLoss}]`);

            if (profitLoss >= 0) {
              properties.push(property);
            }
          }
        }
      } else {
        properties = await staffDB.getProperties({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          locationId: locationId,
        });
      }
      summary = await propertyDB.getSummaryByStaffIdX({
        clientId,
        staffId,
        locationId: locationId,
      });
      flatSummary = await propertyDB.getFlatSummaryByStaffId({
        clientId,
        staffId,
        locationId: locationId,
      });

      log.info(
        `[${C}], [${F}], Staff Id [${staff?.id}], Property list sent successfully`
      );
    }

    let totalBedsForClient = 0;
    let occupiedBedsForClient = 0;
    let vacantBedsForClient = 0;
    let movingOutTenantsForClient = await occupancyDB.getMovingOutTenantsCountByClientId({
      clientId,
    });

    let movingOutTenantCountByProperty = await occupancyDB.getMovingOutCountByProp({
      clientId,
    });
    let movingOutTenantMap = new Map();
    if (movingOutTenantCountByProperty && movingOutTenantCountByProperty?.length > 0) {
      movingOutTenantCountByProperty.forEach((item: any) => {
          movingOutTenantMap.set(item.propertyId, item.count);
      });
    }
    
    let occupancyBookingTenantCountByProperty = await occupancyDB.getCurMonthBookingCountByProp({
      clientId,
    });
    let occupancyBookingTenantMap = new Map();
    if (occupancyBookingTenantCountByProperty && occupancyBookingTenantCountByProperty?.length > 0) {
      occupancyBookingTenantCountByProperty.forEach((item: any) => {
          occupancyBookingTenantMap.set(item.propertyId, item.count);
      });
    }

    let moveOutBookingTenantCountByProperty = await occupancyDB.getCurMonthBookingCountByProp({
      clientId,
    });
    let moveOutBookingTenantMap = new Map();
    if (moveOutBookingTenantCountByProperty && moveOutBookingTenantCountByProperty?.length > 0) {
      moveOutBookingTenantCountByProperty.forEach((item: any) => {
          moveOutBookingTenantMap.set(item.propertyId, item.count);
      });
    }
    
    let paidByTenantByProperty = await transactionDB.getPaidByCountByProp({
      clientId,
    });
    let collectionTenantMap = new Map();
    if (paidByTenantByProperty && paidByTenantByProperty?.length > 0) {
      paidByTenantByProperty.forEach((item: any) => {
          collectionTenantMap.set(item.propertyId, item.count);
      });
    }


    if (properties) {
      for (let property of properties) {

        property.movingOutTenants = movingOutTenantMap.get(property.id) || 0;

        let activeBookings = occupancyBookingTenantMap.get(property.id) || 0;
        let movedOutBookings = moveOutBookingTenantMap.get(property.id) || 0;
        property.bookingTenants = Number(activeBookings) + Number(movedOutBookings);
        
        property.paidByTenants = collectionTenantMap.get(property.id) || 0;

        const flats = await flatDB.getByPropId({ propId: property.id });

        if (flats && flats.length > 0) {
          for (let flat of flats) {
            const flatLease = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
              clientId,
              propId: property.id,
              flatId: flat.id,
            });
            flat.isLeaseAdded = flatLease ? true : false;
          }
        }

        const leaseDetails = await propertyLeaseDB.isPropertyLeaseCompleted({
          clientId,
          propId: property.id,
        });

        property.leaseAdded = leaseDetails ? false : true;

        const rooms = await roomDB.getCountsByPropId({ propId: property.id });
        const beds = await bedDB.getCountsByPropId({ propId: property.id });
        const transactionStats =
          await transactionDB.getTotalIncomeStatsByPropCurrentMonth({
            propId: property.id,
            status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
          });

        const propertyDues = await duesDB.getTotalDuesByPropIdDateRange({
          propId: property.id,
          startDate: moment().startOf("month").format("YYYY-MM-DD"),
          endDate: moment().endOf("month").format("YYYY-MM-DD"),
        });
        property.totalDues = propertyDues.totalDues;
        property.roomCount = rooms?.totalRooms || 0;
        property.bedCount = beds?.total || 0;
        property.flats = flats || [];
        property.totalCollection =
          transactionStats[transactionStats.length - 1]?.totalIncome || 0;
        property.vacantBedCount = beds?.vacant || 0;
        property.occupiedBedCount = beds?.occupied || 0;

        totalBedsForClient += beds?.total || 0;
        occupiedBedsForClient += beds?.occupied || 0;
        vacantBedsForClient += beds?.vacant || 0;
        // let tenantAndroidLink = await clientConfigDB.getClientConfig({
        //   clientId,
        //   provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.APP,
        //   type: CONSTANTS.CLIENT_CONFIG_TYPE.ANDROID,
        // });
        // let tenantIosLink = await clientConfigDB.getClientConfig({
        //   clientId,
        //   provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.APP,
        //   type: CONSTANTS.CLIENT_CONFIG_TYPE.IOS,
        // });
        let tenantAndroidLink: any;
        let tenantIosLink: any;
        let setting = await settingsDB.getByClientIdAndPropId({ clientId, propId: property?.id });
        if (setting) {
          if (setting?.android) {
            tenantAndroidLink = setting?.android;
          } else {
            tenantAndroidLink = null;
          }
          if (setting?.ios) {
            tenantIosLink = setting?.ios;
          } else {
            tenantIosLink = null;
          }
        }


        if (tenantAndroidLink || tenantIosLink) {
          property.tenantAndriodLink = tenantAndroidLink;
          property.tenantIosLink = tenantIosLink;
        } else {
          property.tenantAndriodLink = CONSTANTS.DEFAULT_APP_LINK.ANDROID;
          property.tenantIosLink = CONSTANTS.DEFAULT_APP_LINK.IOS;
        }

        const location = await locationDB.getById({
          id: property?.locationId || null,
        });
        if (location) {
          property.locationName = location?.name || "";
        }

        const tenantCount = await occupancyDB.getTenantCountByClientIdAndPropId({
          clientId,
          propId: property.id,
        });

        property.tenantCount = tenantCount;
      }
    }

    const locations = await locationDB.getByClientId({
      clientId,
    });

    if (flatSummary) {
      summary.totalFlatCount = flatSummary.totalFlatCount;
      summary.activeFlatCount = flatSummary.activeFlatCount;
      summary.inactiveFlatCount = flatSummary.inactiveFlatCount;
    }
    /*
      //For Web Inventory
      const stats = {
        totalBeds: totalBedsForClient,
        occupiedBeds: occupiedBedsForClient,
        vacantBeds: vacantBedsForClient,
        movingOut: movingOutTenantsForClient,
      };*/

    return res.status(200).json({
      msg: "Property list sent successfully",
      data: properties || [],
      locations: locations || [],
      //stats,
      summary,
      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,
    });
  }
};

property.MakeActive = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "MakeActive";

  try {
    let { propId } = 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}], Prop Id [${propId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role === CONSTANTS.STAFF_ROLES.PARTNER) {
        clientId = staff.clientId;

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Partner Id [${req.id}], Partner Requested.....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Staff Id [${req.id}], Staff not allowed to change property status`
        );
        return res.status(400).json({
          msg: "Staff not allowed to activate property",
          isSuccess: false,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Client Requested....`
      );
    }

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

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

    const rooms = await roomDB.getByPropId({ propId });
    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Rooms Found`
      );
      return res.status(400).json({
        msg: "Please add a bed to activate this property",
        isSuccess: false,
      });
    }

    let isBedAvailable = false;

    for (const room of rooms) {
      const beds = await bedDB.getByRoomId({ roomId: room.id });
      if (beds) isBedAvailable = true;
    }

    if (!isBedAvailable) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Beds Found`
      );
      return res.status(400).json({
        msg: "Please add a bed to activate this property",
        isSuccess: false,
      });
    }

    // if (Number(property?.isRentAgreementEnabled) === 0) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Rent Agreement Not Enabled`
    //   );
    //   return res.status(400).json({
    //     msg: "Please add rent agreement and signature before activating property.",
    //     isSuccess: false,
    //   });
    // }

    await propertyDB.updateStatus({
      id: propId,
      status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
    });

    if (property.tallyStatus === null) {
      await setPropertyIncomeSecurityLedgerTally(
        Number(client.id),
        Number(property.id),
        CONSTANTS.TALLY_STATUS.PENDING,
      );
    }

    await logPropertyActivity(
      req.userType!,
      Number(req.id),
      Number(req.parentClientId!),
      req.platform!,
      CONSTANTS.ACTIVITY_TYPES.MAKE_PROPERTY_ACTIVE,
      propId,
      0,
      0,
    );

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Property activated successfully`
    );

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

property.MakeDeactive = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "MakeDeactive";

  try {
    let { propId } = 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}], Prop Id [${propId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role === CONSTANTS.STAFF_ROLES.PARTNER || staff.role === CONSTANTS.STAFF_ROLES.ADMIN || staff.role === CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        clientId = staff.clientId;

        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Staff Id [${staff.id}], Staff Role [${staff.role}], Staff Requested.....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Staff Id [${req.id}], Staff not allowed to change property status`
        );
        return res.status(400).json({
          msg: "Staff not allowed to deactivate property",
          isSuccess: false,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Client Requested....`
      );
    }

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

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

    if (property.status !== CONSTANTS.PROPERTY_STATUS.ACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Property is not active`
      );
      return res.status(400).json({
        msg: "Please make property active first",
        isSuccess: false,
      });
    }

    const rooms = await roomDB.getByPropId({ propId });
    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Rooms Found`
      );
      return res.status(400).json({
        msg: "Please add a bed to activate this property",
        isSuccess: false,
      });
    }

    let isAllVacant = true;

    for (const room of rooms) {
      const beds = await bedDB.getByRoomId({ roomId: room.id });
      if (beds) {
        const bed = beds.find(
          (bed: bedTypes) => bed.status !== CONSTANTS.BED_STATUS.VACANT
        );
        if (bed) isAllVacant = false;
      }
    }
    if (!isAllVacant) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], All beds are not vacant`
      );
      return res.status(400).json({
        msg: "Every bed should be vacant in order to deactivate this property",
        isSuccess: false,
      });
    }

    await propertyDB.updateStatus({
      id: propId,
      status: CONSTANTS.PROPERTY_STATUS.INACTIVE,
    });

    await logPropertyActivity(
      req.userType!,
      Number(req.id),
      Number(req.parentClientId!),
      req.platform!,
      CONSTANTS.ACTIVITY_TYPES.MAKE_PROPERTY_INACTIVE,
      propId,
      0,
      0,
    );

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Property deactivated successfully`
    );

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

property.Details = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "Details";

  try {
    const { propId } = req.params;

    const userType = req.userType;

    let clientId = req.id;
    let staff = null;
    let month = moment().format("M");
    let year = moment().format("YYYY");
    let expectedRent = 0;
    let expectedSecurity = 0;
    let expectedExtraCharges = 0;
    let movingInCurrentMonthCount = 0;
    let movingInNextMonthCount = 0;
    let movingInNextToNextMonthCount = 0;
    let movingOutCurrentMonthCount = 0;
    let movingOutNextMonthCount = 0;
    let currentMonthAmount = 0;
    let nextMonthAmount = 0;
    let totalLandlordRentToBePaid = 0;
    let rentPending = 0;
    let curMonthRentPaid = 0;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], 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}], Prop Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Client Requested....`
      );
    }

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

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

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

    if (Number(property.type) === CONSTANTS.PROPERTY_TYPE.FLAT) {
      const flats = await flatDB.getByPropId({ propId });
      property.flats = flats || [];
    } else {
      property.flats = [];
    }

    // let stats = [
    //   {
    //     totalIncome: 0,
    //     totalDues: 0,
    //     month: moment().format("M"),
    //     year: moment().format("YYYY"),
    //   },
    // ];

    const isElecPriceAdded = await extraChargeDB.getElectricityPrice({
      propId,
      clientId,
      type: CONSTANTS.EXTRA_CHARGE_TYPES.ELECTRICITY,
    });

    const { totalRooms } = await roomDB.getCountsByPropId({
      propId,
    });

    // const { received } = await transactionDB.getMonthlyRentReceived({
    //   propId,
    //   type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
    //   status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
    // });

    const { occupied, total, vacant } = await bedDB.getCountsByPropId({
      propId,
    });

    const extraCharges = await extraChargeDB.getByPropId({ propId });

    let staffs = [];
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      staffs = await staffDB.getAllExcludingRequester({
        clientId,
        id: staff.id,
        status: 1,
      });
    } else {
      staffs = await staffDB.getByPropId({ propId });
    }

    let bankDetails = false;
    if (property.bankId) {
      bankDetails = await bankDB.getById({ id: property.bankId });
      if (!bankDetails) {
        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], No Bank Details Found`
        );
      }
    }

    const summary = await occupancyDB.getOccupancyStatsByPropId(
      clientId,
      propId
    );

    // const occupancyData = await occupancyReportDB.getOccupancyReportForProp({
    //   propId,
    // });

    let curMonthOccupancy = Number(((occupied / total) * 100).toFixed(2)) || 0;
    let prevMonthOccupancy = null;

    // if (occupancyData && occupancyData.length > 0) {
    //   const curMonth = occupancyData?.find((d:any) => d.month === Number(moment().month() + 1));
    //   const prevMonth = occupancyData?.find((d:any) => d.month === Number(moment().month()));

    //   curMonthOccupancy = Number(((occupied / total) * 100).toFixed(2)) || 0;
    //   prevMonthOccupancy = Number(((prevMonth?.occupied / prevMonth?.total) * 100).toFixed(2)) || 0;
    // }

    const prevMonthOccupancyData = await occupancyReportDB.getOccupancyReportForPropByYearMonth({
      propId,
      year: moment().subtract(1, "month").format("YYYY"),
      month: moment().subtract(1, "month").format("MM"),
    });

    if (prevMonthOccupancyData) {
      prevMonthOccupancy = ((prevMonthOccupancyData?.occupied / prevMonthOccupancyData?.total) * 100).toFixed(2) || 0;
    }

    summary.curMonthOccupancy = curMonthOccupancy || 0;
    summary.prevMonthOccupancy = prevMonthOccupancy || 0;

    const { received } = await transactionDB.getRentReceivedByYearMonth({
      propId,
      type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      year,
      month,
    });
    const { duesForMonth } = await duesDB.getTotalDuesByPropIdYearMonth({
      propId,
      year,
      month,
    });

    expectedRent = await occupancyDB.getExpectedRentForProperty({
      clientId,
      propId,
    });

    expectedSecurity = await occupancyDB.getExpectedSecurityForProperty({
      clientId,
      propId,
    });

    expectedExtraCharges = await occupancyDB.getExpectedExtraChargesForProperty({
      clientId,
      propId,
    })

    expectedSecurity = expectedSecurity || 0;
    // const totalExpected = Number(expectedRent) + Number(expectedSecurity);
    let totalExpected = Number(expectedRent);
    if (Number(clientId) === 168) {
      totalExpected = Number(expectedRent) + Number(expectedSecurity) + Number(expectedExtraCharges);
    }

    const movingInCurrentMonth =
      await occupancyDB.getMovingInCurrentMonthCountProp({
        propId,
      });

    const movingOutCurrentMonth =
      await occupancyDB.getMovingOutCurrentMonthCountProp({
        propId,
      });

    const movingInNextMonth = await occupancyDB.getMovingInNextMonthCountProp({
      propId,
    });

    const movingInNextToNextMonth =
      await occupancyDB.getMovingInNextToNextMonthCountProp({
        propId,
      });

    const movingOutNextMonth = await occupancyDB.getMovingOutNextMonthCountProp(
      {
        propId,
      }
    );

    movingInCurrentMonthCount = movingInCurrentMonth.count;
    movingInNextMonthCount = movingInNextMonth.count;
    movingInNextToNextMonthCount = movingInNextToNextMonth.count;
    movingOutCurrentMonthCount = movingOutCurrentMonth.count;
    movingOutNextMonthCount = movingOutNextMonth.count;
    currentMonthAmount =
      Number(movingInNextMonth.totalRent) -
      Number(movingOutCurrentMonth.totalRent);
    nextMonthAmount =
      Number(movingInNextToNextMonth.totalRent) -
      Number(movingOutNextMonth.totalRent);

    let vacancyLoss = 0;
    const vacantBeds = await roomDB.getVacancyLossForProp({
      propId: propId,
    });
    // if (vacantBeds) {
    //   for (let bed of vacantBeds) {
    //     const lastTenant = await moveOutDB.getByBedId({
    //       bedId: bed.bedId,
    //     });
    //     let moveOut = bed.createdAt;
    //     if (lastTenant) {
    //       moveOut = lastTenant.moveOutDate;
    //     }
    //     if (moment(moveOut).startOf("day").isBefore(moment().startOf("month"))) {
    //       moveOut = moment().startOf("month");
    //     }
    //     const daysVacant = Math.abs(
    //       moment(moveOut).startOf("day").diff(moment().startOf("day"), "days")
    //     );
    //     const loss = Math.ceil((bed.rent)/Number(moment().daysInMonth())) * daysVacant;
    //     vacancyLoss += loss;
    //   }
    // }
    if (vacantBeds && vacantBeds.length > 0) {
      const bedIds = vacantBeds.map((b: any) => b.bedId);
      const lastMoveOuts = await moveOutDB.getLastMoveOutsByBedIds({
        bedIds,
        clientId,
      });

      const moveOutMap = new Map<number, string>();
      lastMoveOuts.forEach((entry: any) => {
        moveOutMap.set(entry.bedId, entry.moveOutDate);
      });

      const today = moment().startOf("day");
      const monthStart = moment().startOf("month");
      const daysInMonth = moment().daysInMonth();

      for (let bed of vacantBeds) {
        let moveOut = bed.createdAt;

        if (moveOutMap.has(bed.bedId)) {
          moveOut = moveOutMap.get(bed.bedId)!;
        }

        let moveOutMoment = moment(moveOut).startOf("day");

        if (moveOutMoment.isBefore(monthStart)) {
          moveOutMoment = monthStart;
        }

        const daysVacant = today.diff(moveOutMoment, "days");
        const perDayRent = Math.ceil(bed.rent / daysInMonth);
        vacancyLoss += perDayRent * daysVacant;
      }
    }

    let incomeData = await transactionDB.getTotalIncomeStatsByMonthProp({
      clientId: clientId,
      propId: propId,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      year: moment().year(),
      month: moment().month() + 1,
    });

    let incomeDataWithoutRefunds = await transactionDB.getTotalIncomeStatsByMonthPropWithoutRefunds({
      clientId: clientId,
      propId: propId,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      year: moment().year(),
      month: moment().month() + 1,
    });

    let income = incomeDataWithoutRefunds[0]?.totalIncome || 0;
    let incomewithRefunds = incomeData[0]?.totalIncome || 0;

    let expense = await expenseDB.getTotalByClientIdAndPropId({
      clientId,
      month: moment().format("YYYY-MM-DD"),
      propId: propId,
    });

    if (expense === null) {
      expense = 0;
    }

    let netProfitLoss = Number(incomewithRefunds) - Number(expense);

    let leaseDetails = await propertyLeaseDB.getByClientIdAndPropIdAll({
      clientId,
      propId,
    });

    if (leaseDetails && leaseDetails.length === 1) {
      leaseDetails = leaseDetails[0];
    }

    curMonthRentPaid = await expenseDB.getTotalByClientIdAndPropIdAndDateRangeAndType({
      clientId,
      propId,
      startDate: moment().startOf('month').format("YYYY-MM-DD"),
      endDate: moment().endOf('month').format("YYYY-MM-DD"),
      type: 1, //Building Rent
    });
    rentPending = await expenseDB.getTotalUnMarkedByPropIdAndTypeAndDateRange({
      propId,
      startDate: moment().startOf('month').format("YYYY-MM-DD"),
      endDate: moment().endOf('month').format("YYYY-MM-DD"),
      type: 1, //Building Rent
    });
    totalLandlordRentToBePaid = await propertyLeaseDB.getTotalMonthlyRentByPropId({
      propId,
    });

    const docs = await propertyDocumentDB.getByClientIdAndPropId({ clientId, propId });
    const propImages = await propertyDB.getListingImagesById({ propId });

    if (propImages && propImages.length > 0) {
      for (let image of propImages) {
        image.imageUrl = `${process.env.UPLOAD_PATH}/${image.imageUrl}`;
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Property Details Sent Successfully`
    );

    return res.status(200).json({
      msg: "Property details sent successfully",
      data: {
        property,
        docs: docs || [],
        roomCount: totalRooms,
        staffs,
        //stats,
        extraCharges,
        electricityPrice: isElecPriceAdded?.pricePerUnit || 0,
        //dueTenantCount: Number(dueTenantCount) || 0,
        floorCount: property.floorCount,
        summary: summary || 0,
        //rent: { received: received || 0, dues: totalDues || 0 },
        beds: {
          occupied: Number(occupied) || 0,
          total: Number(total) || 0,
          vacant: Number(vacant) || 0,
        },
        bankDetails,
      },
      incomeData: {
        totalIncome: received || 0,
        totalDues: duesForMonth || 0,
        year: year,
        month: month,
        expectedRent: totalExpected || 0,
        movingInCurrentMonthCount,
        movingInNextMonthCount,
        movingOutCurrentMonthCount,
        movingOutNextMonthCount,
        currentMonthAmount,
        nextMonthAmount,
      },
      profitLossData: {
        income: income,
        expense: expense,
        netProfitLoss: netProfitLoss,
      },
      dueData: {
        expectedRent: totalExpected || 0,
        vacancyLoss: vacancyLoss || 0,
        totalDues: duesForMonth || 0,
      },
      landlordData: {
        curMonthRentPaid: curMonthRentPaid || 0,
        rentPending: rentPending || 0,
        totalRentToBePaid: totalLandlordRentToBePaid || 0,
      },
      leaseDetails: leaseDetails ? leaseDetails : false,
      propImages: propImages || [],
      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,
    });
  }
};

property.GetInfo = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetInfo";

  try {
    const { propId, roomId } = req.query;

    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}], Client Id [${clientId}], Prop Id [${propId}], Room Id [${roomId}], 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}], Prop Id [${propId}], Room Id [${roomId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Room Id [${roomId}], Client Requested....`
      );
    }

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

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

    const property = await propertyDB.getAllInfoById({ id: propId });

    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Room Id [${roomId}], No Property Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    property.canFullyOccupied = client.canFullyOccupied;

    const room = await roomDB.getById({ id: roomId });
    if (room) {
      const containExtraBed = await roomDB.containExtraBed({
        id: room.id,
      });
      room.containExtraBed = containExtraBed;

      const roomOption = await roomOptionDB.getById({ id: room.roomOptionId });
      if (Number(roomOption.security) && Number(roomOption.security) > 0) {
        property.security = roomOption.security;
      }
    }

    const canTakeBookingAmt = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.BOOKING_AMT_ENABLED,
    });
    if (canTakeBookingAmt && Number(canTakeBookingAmt?.value) === 1) {
      property.canTakeBookingAmt = 1;
    } else {
      property.canTakeBookingAmt = 0;
    }

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Room Id [${roomId}], Property & Room Details Sent Successfully`
    );

    return res.status(200).json({
      msg: "Property details sent successfully",
      data: { property, room },
      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,
    });
  }
};

property.DuplicateFloor = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "DuplicateFloor";

  try {
    const { floor, floors, propId } = 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}], Property Id [${propId}], Floor [${floor}], Floors Length [${floors.length}], 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}], Property Id [${propId}], Floor [${floor}], Floors Length [${floors.length}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Floor [${floor}], Floors Length [${floors.length}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Floor [${floor}], Floors Length [${floors.length}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

    if (!property) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Floor [${floor}], Floors Length [${floors.length}], No Property Found`
      );

      return res.status(400).json({
        msg: "Invalid Property Id",
        isSuccess: false,
      });
    }

    const rooms = await roomDB.getByPropIdAndFloor({ propId, floor });

    if (!rooms) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Floor [${floor}], Floors Length [${floors.length}], No Rooms Found`
      );
      return res.status(400).json({
        msg: "No rooms available to duplicate",
        isSuccess: false,
      });
    }

    let lastRoomNum: string = "";

    for (const floorNum of floors) {
      const isRoomExists = await roomDB.getByPropIdAndFloor({
        propId,
        floor: floorNum,
      });
      if (isRoomExists) {
        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Floor Num [${floorNum}], Unable to duplicate floor because room already exists`
        );
        continue;
      }

      for (const room of rooms) {
        const { roomOptionId } = room;

        const roomOption = await roomOptionDB.getById({ id: roomOptionId });
        const roomNum = generateRoomNum(lastRoomNum, floorNum);
        const roomId = await roomDB.create({
          propId,
          roomOptionId,
          floor: floorNum,
          roomNum,
        });

        const bedArr = Array.from(Array(roomOption.totalBedCount));

        for (const _ of bedArr) {
          const bedId = await bedDB.create({ roomId, isExtraBed: 0 });
          log.info(
            `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Room Option Id [${roomOptionId}], Floor Num [${floorNum}], Room Num [${roomNum}], Bed Id [${bedId}] Bed Added Successfully`
          );
        }

        lastRoomNum = roomNum.toString();

        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Room Option Id [${roomOptionId}], Floor Num [${floorNum}], Room Num [${roomNum}], Room Added Successfully`
        );
      }

      lastRoomNum = "";

      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property Id [${propId}], Floor Num [${floorNum}], Floor duplicated successfully`
      );
    }

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

property.LinkedProperty = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "LinkedProperty";

  try {
    const { userType } = req;
    let { pageNum, filter } = req.query;
    let properties = [];
    const limit = 10;
    // let clientId = req.id;

    // let isPartner = false;

    // if (userType === CONSTANTS.USER_TYPE.STAFF) {
    //   const staff = await staffDB.getById({ id: req.id });
    //   if (!staff) {
    //     log.info(`[${C}], [${F}], Staff Id [${req.id}] No Staff Found`);
    //     return res
    //       .status(400)
    //       .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    //   }
    //   isPartner = staff.role === CONSTANTS.STAFF_ROLES.PARTNER ? true : false;
    //   clientId = staff.clientId;
    // }

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

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

      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 (filter == "BANK") {
        properties = await propertyDB.getByClientAndStatusAndBank({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        });
        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Filtered Linked Property  sent successfully`
        );
      } else {
        properties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        });
        log.info(
          `[${C}], [${F}], Client Id [${client?.id}], Linked Property  sent successfully`
        );
      }
    } else {
      const staffId = req.id;

      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Page Num [${pageNum}], Filter [${filter}] , Staff Requesting...`
      );

      const staff = await staffDB.getById({ id: staffId });

      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (filter == "BANK") {
        properties = await staffDB.getPropertiesByStatusAndBank({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        });
        log.info(
          `[${C}], [${F}], Staff  Id [${staff?.id}], Filtered Linked Property  sent successfully`
        );
      } else {
        properties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        });

        log.info(
          `[${C}], [${F}], Staff Id [${staff?.id}], Linked property  sent successfully`
        );
      }
    }
    return res.status(200).json({
      msg: "Linked property  sent successfully",
      data: properties || [],
      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,
    });
  }
};

property.GetList = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetList";
  try {
    const userType = req.userType;
    let properties = [];

    let personalAndPartnerProperties = [];

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      // const clientId = req.id;

      log.info(
        `[${C}], [${F}], ${isPartner ? "Partner ID" : "Client Id"} [${req.id
        }], ${isPartner ? "Partner" : "Client"} Requesting....`
      );
      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 });
      }
      // properties = await propertyDB.getListWithClientId({
      //   clientId: client?.id,
      // });
      properties = await propertyDB.getAllActiveByClientId({
        clientId: client?.id,
      });

      personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
        clientId: req.parentClientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property List Sent Successfully`
      );
    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Requesting....`);
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      properties = await staffDB.getListWithStaffIdAndClientId({
        id: staffId,
        clientId: staff.clientId,
      });
      log.info(
        `[${C}], [${F}], Staff Id [${staff?.id}], Property List Sent Successfully`
      );
    }
    return res.status(200).json({
      msg: "Property list sent successfully",
      data: properties,
      personalAndPartnerProperties: personalAndPartnerProperties || [],
      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,
    });
  }
};

property.GetListWithVacany = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetListWithVacany";
  try {
    const userType = req.userType;
    let properties = [];

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      // const clientId = req.id;

      log.info(
        `[${C}], [${F}], ${isPartner ? "Partner ID" : "Client Id"} [${req.id
        }], ${isPartner ? "Partner" : "Client"} Requesting....`
      );
      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 });
      }
      properties = await propertyDB.getAllActiveWithVacanyByClientId({
        clientId: client?.id,
      });
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property List Sent Successfully`
      );
    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Requesting....`);
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      properties = await propertyDB.getAllActiveWithVacanyByClientIdForStaff({
        clientId: staff.clientId,
        staffId: staffId,
      });
      log.info(
        `[${C}], [${F}], Staff Id [${staff?.id}], Property List Sent Successfully`
      );
    }
    return res.status(200).json({
      msg: "Property list sent successfully",
      data: properties,
      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,
    });
  }
};

property.IncomeDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "IncomeDetails";

  try {
    let { propId } = req.params;
    let { y, m } = req.query;
    const userType = req.userType;
    let clientId = req.id;
    let staff = null;
    let year = y;
    let month = m;
    let expectedRent = 0;
    let expectedExtraCharges = 0;
    let expectedSecurity = 0;

    let date;

    if (!month || !year || "undefined" == month) {
      date = moment()
        .month(Number(month) - 1)
        .year(Number(year))
        .format("YYYY-MM-DD");
      month = moment().format("M");
      year = moment().format("YYYY");
    }

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Prop Id [${propId}], 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}], Prop Id [${propId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Month [${month}], Year [${year}], Client Requested....`
      );
    }

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

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

    const { dueTenantCount } = await duesDB.getTenantCountByPropIdYearMonth({
      propId,
      month: month,
      year: year,
    });
    const { received } = await transactionDB.getRentReceivedByYearMonth({
      propId,
      type: CONSTANTS.TRANSACTION_TYPES.TENANT_PAID,
      status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
      year,
      month,
    });
    const { duesForMonth } = await duesDB.getTotalDuesByPropIdYearMonth({
      propId,
      year,
      month,
    });

    expectedRent = await occupancyDB.getExpectedRentForProperty({
      clientId,
      propId,
    });

    expectedExtraCharges = await occupancyDB.getExpectedExtraChargesForProperty({
      clientId,
      propId,
    });

    expectedSecurity = await occupancyDB.getExpectedSecurityForProperty({
      clientId,
      propId,
    });

    expectedSecurity = expectedSecurity || 0;

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Property DashInfo Sent Successfully`
    );

    // const totalExpected = Number(expectedRent) + Number(expectedSecurity);
    let totalExpected = Number(expectedRent);
    if (Number(clientId) === 168) {
      totalExpected = Number(expectedRent) + Number(expectedSecurity) + Number(expectedExtraCharges);
    }

    return res.status(200).json({
      msg: "Property income info sent successfully",
      data: {
        totalIncome: received || 0,
        totalDues: duesForMonth || 0,
        //dueTenantCount: Number(dueTenantCount) || 0, // Not Being Used on front End
        year: year,
        month: month,
        expectedRent: totalExpected || 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,
    });
  }
};

property.PendingRent = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "PendingRent";
  try {
    const userType = req.userType;
    let { propId } = req.params;
    let monthlyData: any = null;
    let dailyData: any = null;
    let stats = [
      {
        totalIncome: 0,
        totalDues: 0,
        dueTenantCount: 0,
        month: moment().format("M"),
        year: moment().format("YYYY"),
      },
    ];
    let dueTenantCount = 0;
    //let data: any = null;
    let clientId;
    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staffId = req.id;
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Property Id [${propId}], Staff Requesting...`
      );
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (
        staff.role === CONSTANTS.STAFF_ROLES.ADMIN ||
        staff.role === CONSTANTS.STAFF_ROLES.PARTNER
      ) {
        clientId = staff.clientId;
        log.info(
          `[${C}], [${F}], Client Id [${clientId}] , Staff Requesting.....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], Normal Staff Not Allowed`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    } else {
      clientId = req.id;
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Client Requesting...`
      );
      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 });
      }
    }

    // const transactionStats = await transactionDB.getTotalIncomeStatsByProp({
    //   propId,
    //   status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
    // });

    // const duesStats = await duesDB.getTotalDuesStatsByPropId({
    //   propId,
    // });

    // stats = await createIncomeStats(stats, transactionStats, duesStats);
    // for (let stat of stats) {
    //   const tenantCount = await duesDB.getTenantCountByPropIdYearMonth({ propId, month: stat.month, year: stat.year });
    //   dueTenantCount = tenantCount.dueTenantCount;
    //   stat.dueTenantCount = dueTenantCount;
    // }

    monthlyData = await propertyMonthlyPendingRent({ propId: Number(propId) });
    dailyData = await propertyDailyPendingRent({ propId: Number(propId) });
    log.info(
      `[${C}], [${F}], Client Id [${clientId}] , Pending Rent data sent successfully....`
    );

    return res.status(200).json({
      msg: `Data shared successfully`,
      monthlyData: monthlyData,
      dailyData: dailyData,
      // perDayStats,
      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,
    });
  }
};

property.PendingRentX = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "PendingRentX";
  try {
    const userType = req.userType;
    let { propId } = req.params;
    const { m, y } = req.query;

    let data: any = null;

    let month = m;
    let year = y;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Month [${month}], Year [${year}]`
    );

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

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner || isFinanceAdmin) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Prop Id [${propId}], Month [${month}], Year [${year}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staffId = req.id;
      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Property Id [${propId}], Staff Requesting...`
      );
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (staff.role === CONSTANTS.STAFF_ROLES.ADMIN) {
        // clientId = staff.clientId;
        log.info(
          `[${C}], [${F}], Client Id [${clientId}] , Staff Requesting.....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Staff Id [${staffId}], Normal Staff Not Allowed`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
    }

    if (month && year) {
      data = await propertyDailyPendingRentForMonth({
        propId: Number(propId),
        year,
        month,
      });
    } else {
      data = await propertyMonthlyPendingRent({ propId: Number(propId) });
    }

    const securityInHand = await occupancyDB.getTotalSecurityInHandForProp({
      clientId: clientId,
      propId: propId,
    })

    log.info(
      `[${C}], [${F}], Client Id [${clientId}] , Pending Rent data sent successfully....`
    );

    return res.status(200).json({
      msg: `Data shared successfully`,
      data,
      securityInHand: Math.abs(securityInHand),
      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,
    });
  }
};

property.GetListForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetListForWeb";
  try {
    const userType = req.userType;
    let properties = [];
    //let clientId;
    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id)
    );
    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? "Partner ID" : "Client Id"} [${req.id
        }], ${isPartner ? "Partner" : "Client"} Requesting....`
      );

      //clientId = req.id;
      //log.info(
      //`[${C}], [${F}], Client Id [${clientId}], Client Requesting....`
      //);
      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 });
      }
      properties = await propertyDB.getListWithClientId({
        clientId: client?.id,
      });
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property List Sent Successfully`
      );
    } else if (userType === CONSTANTS.USER_TYPE.LANDLORD) {
      const landlordId = req.id;
      const landlord = await landlordDB.getById({
        id: landlordId,
      });
      if (!landlord) {
        log.info(`[${C}], [${F}], Landlord Id [${landlordId}], No Landlord Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      properties = await propertyDB.getByLandlordIdAndFilters({
        landlordId: landlord.id,
      });
    } else {
      const staffId = req.id;
      log.info(`[${C}], [${F}], Staff Id [${staffId}], Staff Requesting....`);
      const staff = await staffDB.getById({ id: staffId });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${staffId}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      clientId = staff.clientId;
      properties = await staffDB.getActiveListWithStaffIdAndClientId({
        id: staffId,
        clientId: staff.clientId,
      });
      log.info(
        `[${C}], [${F}], Staff Id [${staff?.id}], Property List Sent Successfully`
      );
    }
    if (properties) {
      for (let property of properties) {
        const flats = await flatDB.getByPropId({ propId: property.id });
        const tenantCount = await occupancyDB.getCountByClientIdAndPropId({
          clientId,
          propId: property.id,
        });
        const rooms = await roomDB.getCountsByPropId({ propId: property.id });
        const beds = await bedDB.getCountsByPropId({ propId: property.id });
        // const transactionStats = await transactionDB.getTotalIncomeStatsByPropCurrentMonth({
        //   propId: property.id,
        //   status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        // });
        property.roomCount = rooms?.totalRooms || 0;
        property.bedCount = beds?.total || 0;
        property.flats = flats || [];
        property.tenantCount = tenantCount || 0;
        // property.totalCollection = transactionStats[transactionStats.length-1]?.totalIncome || 0;
        property.vacantBedCount = beds?.vacant || 0;
        property.occupiedBedCount = beds?.occupied || 0;

        const location = await locationDB.getById({
          id: property?.locationId || null,
        });
        if (location) {
          property.locationName = location?.name || "";
        }
      }
    }
    return res.status(200).json({
      msg: "Property list sent successfully",
      data: properties,
      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,
    });
  }
};

property.SwitchProperty = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "SwitchProperty";
  try {
    const userType = req.userType;
    // assuming in case sec is not paid adjustSecurity option is not available
    let {
      tenantId,
      newPropId,
      newRoomId,
      newRent,
      // newSecurity,
      // adjustSecurity,
      rentalCycle,
      shiftingDate,
      discardDues,
    } = req.body;

    log.info(
      `[${C}], [${F}], Prop Id [${newPropId}], Room ID [${newRoomId}], Rent [${newRent}], Rental Cycle [${rentalCycle}], Shifting Date [${shiftingDate}], Tenant Id [${tenantId}], Discard Dues [${discardDues}]`
    );

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

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], 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}], Staff Id [${req.id}], Staff Requesting...`);
    } else {
      log.info(`[${C}], [${F}], Client Id [${req.id}], Client Requesting...`);
    }
    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], ${isPartner ? "Partner Id" : "Client Id"} [${req.id
        }], No ${isPartner ? "Partner" : "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}], Room Id [${newRoomId}], Prop Id [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], No Tenant Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const newProperty = await propertyDB.getById({ id: newPropId });
    if (!newProperty) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}], PropID [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], No New Property Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const newRoom = await roomDB.getById({ id: newRoomId });
    if (!newRoom) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}],  Tenant Id [${tenantId}], PropID [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], New Room Not Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (newProperty.status === CONSTANTS.PROPERTY_STATUS.INACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}],  Tenant Id [${tenantId}], PropID [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], New Property is Inactive`
      );
      return res.status(400).json({
        msg: "This property is not active",
        isSuccess: false,
      });
    }

    if (newRoom.status === CONSTANTS.ROOM_STATUS.OCCUPIED) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}],  Tenant Id [${tenantId}], PropID [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], New Room is not vacant`
      );
      return res.status(400).json({
        msg: "This room is not vacant",
        isSuccess: false,
      });
    }

    if (newRoom.status === CONSTANTS.ROOM_STATUS.INACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}], Tenant Id [${tenantId}], PropID [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], New Room is not active`
      );
      return res.status(400).json({
        msg: "This room is not active",
        isSuccess: false,
      });
    }

    const occupancy = await occupancyDB.getByTenantIdForSwitchRoom({
      tenantId,
      clientId,
    });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}], Tenant Id [${tenantId}], Prop Id [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], Old Bed Id [${occupancy[0].bedId}], Occupancy Not Found`
      );
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const firstOccupancy = occupancy[occupancy.length - 1];

    const bed = await bedDB.getVacantBed({
      roomId: newRoomId,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    if (!bed) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}], Tenant Id [${tenantId}], Prop Id [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], No Vacant Bed Found`
      );
      return res.status(400).json({
        msg: "This room is not vacant",
        isSuccess: false,
      });
    }

    const bedCount = await bedDB.getCountsByRoomId({
      id: occupancy[0].roomId,
    });
    const totalBeds = bedCount.totalBeds;

    const oldProperty = await propertyDB.getById({ id: occupancy[0].propId });

    if (!rentalCycle) {
      rentalCycle = occupancy[0].rentalCycle;
    }

    await duesDB.updateOccupancyByTenant({
      tenantId,
      clientId,
      occupancyId: occupancy[0].id,
      propId: newPropId,
      roomId: newRoomId,
    });

    await transactionDB.updatePropIdAndRoomIdForPropSwitch({
      tenantId: tenantId,
      clientId: clientId,
      propId: newPropId,
      roomId: newRoomId,
    });

    for (let i = 1; i < occupancy.length; i++) {
      await occupancyDB.deleteFullOccupiedExtraEntry({
        tenantId,
        clientId,
      });
    }

    if (newProperty.type === CONSTANTS.PROPERTY_TYPE.FLAT) {
      await occupancyDB.switchPropertyFlat({
        id: occupancy[0].id,
        roomId: newRoomId,
        flatId: newRoom.flatId,
        bedId: bed.id,
        floor: newRoom.floor,
        rent: newRent,
        propId: newProperty.id,
      });
      if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.switchPropertyFlat({
          clientId: occupancy[0].clientId,
          tenantId: occupancy[0].tenantId,
          roomId: newRoomId,
          flatId: newRoom.flatId,
          bedId: bed.id,
          floor: newRoom.floor,
          rent: newRent,
          propId: newProperty.id,
        });
      }
    } else {
      await occupancyDB.switchPropertyPg({
        id: occupancy[0].id,
        roomId: newRoomId,
        bedId: bed.id,
        floor: newRoom.floor,
        rent: newRent,
        propId: newProperty.id,
      });
      if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
        await moveOutDB.switchPropertyFlat({
          clientId: occupancy[0].clientId,
          tenantId: occupancy[0].tenantId,
          roomId: newRoomId,
          flatId: null,
          bedId: bed.id,
          floor: newRoom.floor,
          rent: newRent,
          propId: newProperty.id,
        });
      }
    }

    if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.RESERVED) {
      //update new room & bed status
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
      });
    } else if (occupancy[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.MOVING_OUT,
      });
    } else {
      await bedDB.updateStatus({
        id: bed.id,
        status: CONSTANTS.BED_STATUS.OCCUPIED,
      });
    }

    const vacantBeds = await bedDB.getVacantBeds({
      roomId: newRoomId,
      status: CONSTANTS.BED_STATUS.VACANT,
    });

    if (!vacantBeds) {
      await roomDB.updateStatus({
        id: newRoom.id,
        status: CONSTANTS.ROOM_STATUS.OCCUPIED,
      });
    } else {
      await roomDB.updateStatus({
        id: newRoom.id,
        status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
      });
    }

    for (let occ of occupancy) {
      const oldBed = await bedDB.getById({ id: occ.bedId });

      const tenants = await occupancyDB.getAllByBedId({
        bedId: oldBed.id,
      });

      if (tenants && tenants.length > 1) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Reserved`
        );
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.RESERVED,
        });
      } else if (tenants && tenants.length === 1) {
        if (tenants[0].status === CONSTANTS.OCCUPANCY_STATUS.RESERVED) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Reserved`
          );
          //update old room & bed status
          await bedDB.updateStatus({
            id: occ.bedId,
            status: CONSTANTS.BED_STATUS.VACANT_RESERVED,
          });
        } else if (tenants[0].status === CONSTANTS.OCCUPANCY_STATUS.MOVING_OUT) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Moving Out`
          );
          //update old room & bed status
          await bedDB.updateStatus({
            id: occ.bedId,
            status: CONSTANTS.BED_STATUS.MOVING_OUT,
          });
        }
      } else if (oldBed?.status === CONSTANTS.BED_STATUS.RESERVED) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Bed Status [${oldBed?.status}], Setting Status Moving Out`
        );
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.MOVING_OUT,
        });
      } else {
        //update old room & bed status
        await bedDB.updateStatus({
          id: occ.bedId,
          status: CONSTANTS.BED_STATUS.VACANT,
        });
      }
    }

    for (let occ of occupancy) {
      const vacantBedsForOldRoom = await bedDB.getVacantBeds({
        roomId: occ.roomId,
        status: CONSTANTS.BED_STATUS.VACANT,
      });

      const room = await roomDB.getById({ id: occ.roomId });
      const bedCount = await bedDB.getCountsByRoomId({
        id: occ.roomId,
      });
      const totalBeds = bedCount.totalBeds;

      if (vacantBedsForOldRoom.length === totalBeds) {
        await roomDB.updateStatus({
          id: occ.roomId,
          status: CONSTANTS.ROOM_STATUS.VACANT,
        });
      } else {
        await roomDB.updateStatus({
          id: occ.roomId,
          status: CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED,
        });
      }
    }

    // if (adjustSecurity) {
    //   let securityDiff = Math.abs(
    //     Number(newSecurity) - Number(occupancy[0].security)
    //   );

    //   if (Number(newSecurity) > Number(occupancy[0].security)) {
    //     //add due for remaining diff security
    //     let prevBalance = await ledgerDB.getPreviousBalance({
    //       tenantId,
    //       clientId,
    //     });
    //     const referenceId = await generateLedgerReferenceId({ clientId });
    //     await duesDB.add({
    //       tenantId,
    //       amount: securityDiff,
    //       occupancyId: occupancy.id,
    //       roomId: newRoomId,
    //       propId: newPropId,
    //       clientId,
    //       type: CONSTANTS.DUES_TYPES.SECURITY,
    //       dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
    //       balance: securityDiff,
    //       ledgerReferenceId: referenceId,
    //     });
    //     await ledgerDB.add({
    //       tenantId,
    //       roomId: newRoomId,
    //       propId: newPropId,
    //       clientId,
    //       amount: newSecurity,
    //       balance: newSecurity,
    //       referenceId: referenceId,
    //       transactionId: null,
    //       type: CONSTANTS.DUES_TYPES.SECURITY,
    //       rentStartDate: null,
    //       rentEndDate: null,
    //       dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
    //       description: "Initial security deposit",
    //     });

    //     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}], Old Security [${occupancy[0].security}], New Security [${newSecurity}], Added Due for Remaining Difference Security`
    //     );
    //   } else if (Number(newSecurity) < Number(occupancy[0].security)) {
    //     //add excess payment for diff security
    //     let prevBalance = await ledgerDB.getPreviousBalance({
    //       tenantId,
    //       clientId,
    //     });
    //     if (prevBalance && prevBalance < 0) {
    //       // add excess in prev excess
    //       await ledgerDB.updateLastEntryBalance({
    //         tenantId: tenantId,
    //         clientId: clientId,
    //         amount: -securityDiff,
    //       });
    //     } else {
    //       const ledgerReferenceId = await generateLedgerReferenceId({ clientId });
    //       await adjustInitialSettlement({
    //         tenantId: tenantId,
    //         clientId: clientId,
    //         ledgerReferenceId: ledgerReferenceId,
    //         amountPaid: Number(securityDiff),
    //         propId: newPropId,
    //         type: CONSTANTS.INITIAL_SETTLEMENT_TYPE.EXCESS_AMOUNT,
    //       });
    //     }
    //   } else {
    //     //no change in security
    //     log.info(
    //       `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Old Security [${occupancy[0].security}], New Security [${newSecurity}], No Change in Security`
    //     );
    //   }
    // }
    const rentDiff = Math.abs(Number(newRent) - Number(occupancy[0].rent));

    const lastRentDue = await ledgerDB.getLastRent({
      tenantId: tenantId,
      clientId: clientId,
      shiftingDate: moment(shiftingDate).toDate(),
    });

    let oldRentDueAmount = 0;

    const prevRentPaid = await duesDB.getLastRentDue({
      tenantId: tenantId,
      clientId: clientId,
      shiftingDate: moment(shiftingDate).toDate(),
    });

    if (lastRentDue !== false && discardDues === false) {
      const oldRentStartDate = lastRentDue.rentStartDate;

      let formattedShiftingDate = moment(shiftingDate);
      // let daysStayedInOld = Number(
      //   formattedShiftingDate.diff(oldRentStartDate, "days")
      // );

      let daysStayedInOld = 0;

      const daysInMonth = Number(moment(shiftingDate, "YYYY-MM").daysInMonth());

      if (Number(rentalCycle) > Number(formattedShiftingDate.date())) {
        daysStayedInOld =
          Number(rentalCycle) - Number(formattedShiftingDate.date());
        oldRentDueAmount =
          Math.ceil((Number(occupancy[0].rent) / daysInMonth) * daysStayedInOld);
      } else if (
        moment(occupancy[0].moveInDate)
          .startOf("day")
          .isSame(moment(formattedShiftingDate).startOf("day"))
      ) {
        oldRentDueAmount = 0;
      } else {
        daysStayedInOld =
          Number(formattedShiftingDate.date()) - Number(rentalCycle);
        oldRentDueAmount =
          Math.ceil(Number(occupancy[0].rent) / daysInMonth) * daysStayedInOld;
      }

      // oldRentDueAmount = Math.ceil(Number(occupancy[0].rent)/daysInMonth * daysStayedInOld);

      if (prevRentPaid === false) {
        //Means rent is paid

        // const excessRentPaid = occupancy[0].rent - oldRentDueAmount;
        const excessRentPaid = lastRentDue.amount - oldRentDueAmount;

        let prevBalance = await ledgerDB.getPreviousBalance({
          tenantId,
          clientId,
        });
        if (prevBalance && prevBalance < 0) {
          // add excess in prev excess
          await ledgerDB.updateLastEntryBalance({
            tenantId: tenantId,
            clientId: clientId,
            amount: -excessRentPaid,
          });
        } else {
          await propertySwitchExcessSettlement({
            tenantId: tenantId,
            clientId: clientId,
            ledgerReferenceId: null, // to be discussed
            amountPaid: Number(excessRentPaid),
            propId: newPropId,
            type: CONSTANTS.INITIAL_SETTLEMENT_TYPE.EXCESS_AMOUNT,
          });
        }
      } else {
        const partialPaid = prevRentPaid.amount - prevRentPaid.balance;

        if (partialPaid === 0) {
          // Added old rent amount in new rent itself

          // await duesDB.updateLastRentDue({
          //   tenantId: tenantId,
          //   clientId: clientId,
          //   ledgerReferenceId: lastRentDue.referenceId,
          //   amount: oldRentDueAmount,
          //   rentStartDate: lastRentDue.rentStartDate,
          //   rentEndDate: moment().format("YYYY-MM-DD HH:mm:ss"),
          //   balance: oldRentDueAmount,
          // });

          // await ledgerDB.updateLastRentDue({
          //   tenantId: tenantId,
          //   clientId: clientId,
          //   referenceId: lastRentDue.referenceId,
          //   amount: oldRentDueAmount,
          //   balance: oldRentDueAmount,
          //   rentStartDate: lastRentDue.rentStartDate,
          //   rentEndDate: moment().format("YYYY-MM-DD HH:mm:ss"),
          //   roomId: newRoomId,
          //   propId: newPropId,
          // });
          await duesDB.removeDue({ id: prevRentPaid.id });
          await ledgerDB.remove({
            referenceId: lastRentDue.referenceId,
          });
        } else {
          if (oldRentDueAmount > partialPaid) {
            await duesDB.removeDue({ id: prevRentPaid.id });
            await ledgerDB.remove({
              referenceId: lastRentDue.referenceId,
            });
            oldRentDueAmount -= partialPaid;
          } else if (oldRentDueAmount < partialPaid) {
            await duesDB.removeDue({
              id: prevRentPaid.id,
            });

            await ledgerDB.updateLastRentDue({
              tenantId: tenantId,
              clientId: clientId,
              referenceId: lastRentDue.referenceId,
              amount: oldRentDueAmount,
              balance: 0,
              rentStartDate: lastRentDue.rentStartDate,
              rentEndDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              roomId: newRoomId,
              propId: newPropId,
            });

            let prevBalance = await ledgerDB.getPreviousBalance({
              tenantId,
              clientId,
            });
            if (prevBalance && prevBalance < 0) {
              // add excess in prev excess
              await ledgerDB.updateLastEntryBalance({
                tenantId: tenantId,
                clientId: clientId,
                amount: oldRentDueAmount - partialPaid,
              });
            } else {
              await propertySwitchExcessSettlement({
                tenantId: tenantId,
                clientId: clientId,
                ledgerReferenceId: null, // to be discussed
                amountPaid: Number(partialPaid - oldRentDueAmount),
                propId: newPropId,
                type: CONSTANTS.INITIAL_SETTLEMENT_TYPE.EXCESS_AMOUNT,
              });
            }
          } else {
            await duesDB.removeDue({
              id: lastRentDue.id,
            });

            await ledgerDB.updateLastRentDue({
              tenantId: tenantId,
              clientId: clientId,
              referenceId: lastRentDue.referenceId,
              amount: oldRentDueAmount - partialPaid,
              balance: 0,
              rentStartDate: lastRentDue.rentStartDate,
              rentEndDate: moment().format("YYYY-MM-DD HH:mm:ss"),
              roomId: newRoomId,
              propId: newPropId,
            });
          }
        }
      }
    }

    if (true === discardDues) {
      let transactions = await transactionDB.getByTenantIdAndClientId({
        clientId,
        tenantId,
      });

      transactions = Array.isArray(transactions) ? transactions : [];

      const transactionSet = new Set(transactions.map((t: any) => t.ledgerReferenceId));

      const dues = await duesDB.getByTenantIdAndClientId({
        tenantId,
        clientId,
      });

      let dueIdsToDelete = [];

      if (dues && dues.length > 0) {
        dueIdsToDelete = dues
          .filter((due: any) => !transactionSet.has(due.ledgerReferenceId))
          .map((due: any) => due.id);
      }

      if (dueIdsToDelete.length > 0) {
        await duesDB.removeDuesByIds({ ids: dueIdsToDelete });
      }

      const ledgerRecords = await ledgerDB.getByTenantId({
        tenantId,
        clientId,
        pageNum: 1,
        limit: 10e9,
      });

      let ledgerRefIdsToDelete = [];

      if (ledgerRecords && ledgerRecords.length > 0) {
        ledgerRefIdsToDelete = ledgerRecords
          .filter((ledger: any) => !transactionSet.has(ledger.referenceId))
          .map((ledger: any) => ledger.referenceId);
      }

      if (ledgerRefIdsToDelete.length > 0) {
        await ledgerDB.removeByReferenceIds({ referenceIds: ledgerRefIdsToDelete });
      }

      // await duesDB.removeAllDues({
      //   tenantId,
      //   clientId,
      // });

      // await ledgerDB.removeAllEntries({
      //   tenantId,
      //   clientId,
      // });
    }
    if (moment(occupancy[0].moveInDate).isSameOrBefore(moment())) {
      let newRents = calculateMonthlyRent(shiftingDate, rentalCycle, newRent);
      let dueDescription = getDueDescription(CONSTANTS.DUES_TYPES.RENT);
      if (Array.isArray(newRents) && newRents.length > 0) {
        let firstRentFlag = 0;
        for (let rent of newRents) {
          let rentAmount = rent.rent;
          let rentStartDate = rent.startDate;
          if (firstRentFlag === 0) {
            if (prevRentPaid === false) {
              rentAmount = rent.rent;
            } else {
              rentAmount += oldRentDueAmount;
            }
            if (lastRentDue !== false) {
              rentStartDate = lastRentDue.rentStartDate;
            }
            firstRentFlag = 1;
          }
          let referenceId = await generateLedgerReferenceId({ clientId });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Start Date [${rent.startDate}], End Date [${rent.endDate}], Rent [${rent.rent}]`
          );
          let prevBalance = await ledgerDB.getPreviousBalance({
            tenantId,
            clientId,
          });

          await duesDB.addWithStartEndDateX({
            tenantId,
            amount: rentAmount,
            occupancyId: occupancy[0].id,
            roomId: newRoomId,
            propId: newPropId,
            clientId,
            // rentStartDate: rent.startDate,
            rentStartDate: rentStartDate,
            rentEndDate: rent.endDate,
            dueDate: moment(rent.startDate).format("YYYY-MM-DD HH:mm:ss"),
            type: CONSTANTS.DUES_TYPES.RENT,
            balance: rentAmount,
            ledgerReferenceId: referenceId,
            title: "Rent",
            description: "Added during property switch",
          });
          await ledgerDB.add({
            tenantId: tenantId,
            roomId: newRoomId,
            propId: newPropId,
            clientId,
            amount: rentAmount,
            balance: rentAmount,
            referenceId: referenceId,
            transactionId: null,
            type: CONSTANTS.DUES_TYPES.RENT,
            rentStartDate: rentStartDate,
            rentEndDate: rent.endDate,
            dueDate: moment(rent.startDate).format("YYYY-MM-DD HH:mm:ss"),
            //changed below description moment(---) at 17/12/2024 20:03:00
            description: `${dueDescription} for ${moment(rent.startDate).format(
              "MMM YYYY"
            )}`,
            title: "Rent",
          });
          if (prevBalance && prevBalance < 0) {
            await adjustExcessPayments({
              tenantId: tenantId,
              clientId: clientId,
              amountPaid: Math.abs(Number(prevBalance)),
              ledgerReferenceId: referenceId,
            });
          }
        }
      }

      const extraCharges = await extraChargeDB.getAllByClientIdAndPropId({
        clientId,
        propId: newPropId,
      });

      if (extraCharges) {
        for (let extraCharge of extraCharges) {
          let type = 0;
          if (extraCharge.type === CONSTANTS.EXTRA_CHARGE_TYPES.MOVE_OUT_CHARGES) {
            continue;
          }
          type = convertTypes({
            from: "extraChargeType",
            to: "dueType",
            type: extraCharge.type,
          });
          let rents = calculateMonthlyRent(
            shiftingDate,
            rentalCycle,
            extraCharge.amount
          );
          if (extraCharge.repetitionType === 1) {
            rents = [
              {
                month: moment().format("MMMM"),
                year: moment().year(),
                rent: extraCharge.amount,
                startDate: shiftingDate,
                endDate: "null",
              },
            ];
          }
          for (let rent of rents) {
            if (type === 0) continue;
            let prevBalance = await ledgerDB.getPreviousBalance({
              tenantId,
              clientId,
            });
            let referenceId = await generateLedgerReferenceId({ clientId });
            dueDescription = getDueDescription(extraCharge.type);
            await duesDB.addX({
              tenantId,
              amount: rent.rent,
              occupancyId: occupancy[0].id,
              roomId: newRoomId,
              propId: newPropId,
              clientId,
              dueDate: moment(rent.startDate).format("YYYY-MM-DD HH:mm:ss"),
              type,
              balance: rent.rent,
              ledgerReferenceId: referenceId,
              title: dueDescription,
              description: `Added during property switch`,
            });
            await ledgerDB.add({
              tenantId: tenantId,
              roomId: newRoomId,
              propId: newPropId,
              clientId,
              amount: rent.rent,
              balance: rent.rent,
              referenceId: referenceId,
              transactionId: null,
              type: type,
              rentStartDate: null,
              rentEndDate: null,
              dueDate: moment(rent.startDate).format("YYYY-MM-DD HH:mm:ss"),
              description: dueDescription,
              title: dueDescription
            });
            if (prevBalance && prevBalance < 0) {
              await adjustExcessPayments({
                tenantId: tenantId,
                clientId: clientId,
                amountPaid: Math.abs(Number(prevBalance)),
                ledgerReferenceId: referenceId,
              });
            }
          }
        }
      }
    }

    //Updating Transaction and ledger security propId and roomId
    await ledgerDB.updateRoomAndPropForSwitch({
      clientId: clientId,
      tenantId: tenantId,
      type: CONSTANTS.DUES_TYPES.SECURITY,
      subType: CONSTANTS.LEDGER_SUBTYPES.MARKED_FROM_SECURITY,
      propId: firstOccupancy?.propId,
      roomId: firstOccupancy?.roomId,
      newPropId: newPropId,
      newRoomId: newRoom.id,
    });
    await transactionDB.updateRoomAndPropForSwitch({
      clientId: clientId,
      tenantId: tenantId,
      type: CONSTANTS.TRANSACTION_FOR.SECURITY,
      propId: firstOccupancy?.propId,
      roomId: firstOccupancy?.roomId,
      newPropId: newPropId,
      newRoomId: newRoom.id,
    });

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

    if ((isResetRentAgreement && Number(isResetRentAgreement.value) === 1)) {
      await occupancyDB.setRentAgreementStatus({
        clientId,
        tenantId,
        isRentAgreementSigned: 0,
      });

      await occupancyDB.updateKycStatusWithClientId({
        kycStatus: CONSTANTS.KYC_STATUS.PERSONAL_INFO,
        tenantId,
        clientId,
      });
    }

    await logActivity(
      req.userType!,
      Number(req.id!),
      Number(req.parentClientId!),
      Number(req.platform),
      tenantId,
      CONSTANTS.ACTIVITY_TYPES.PROPERTY_CHANGE,
      newProperty.name,
      oldProperty.name,
      shiftingDate,
      moment().format("YYYY-MM-DD"),
      null
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Room Id [${newRoomId}], Tenant Id [${tenantId}], Prop Id [${newPropId}],  Tenant Id [${tenantId}], Rent [${newRent}], Rental Cycle [${rentalCycle}], Property Switched Successfully`
    );

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

property.GetRentingRulesForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetRentingRulesForWeb";
  try {
    const { propId } = req.params;
    const userType = req.userType;

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

    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}]`
        }, Prop Id [${propId}], ${isPartner ? `Partner` : `Client`
        } Requesting....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });

      if (!staff) {
        log.info(
          `[${C}], [${F}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          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}], User Id [${req.id}], User Type [${userType}] Unauthorized Access`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

      clientId = staff.clientId;

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

    const data = await propertyDB.getById({ id: propId });

    const daysInMonth = moment().daysInMonth();

    log.info(
      `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
      }, Prop Id [${propId}], Data Fetched Successfully`
    );

    return res.status(200).json({
      msg: "Renting rules data fetched successfully",
      isSuccess: true,
      data: {
        rentalCycle: data.rentalCycle || 1,
        daysInMonth,
        dailyCharges: data.fine,
        rent: data.rent || "",
        security: data.security || "",
        lockInPeriod: data.lockInPeriod || "",
        noticePeriod: data.noticePeriod || "",
        agreementDuration: data.agreementPeriod || "",
      },
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

property.EditRentingRules = async (req: CustomRequest, res: Response) => {
  //Now using for app also
  const C = "Property Controller";
  const F = "EditRentingRules";
  try {
    let {
      propId,
      rent = null,
      name = null,
      tenantPreference = null,
      rentalCycle = null,
      lockInPeriod,
      noticePeriod,
      agreementPeriod,
      security,
      replicate = 0,
      locationId = null,
    } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Property Name [${name}], Rent [${rent}], Security [${security}], Tenant Preference [${tenantPreference}], Rental Cycle [${rentalCycle}], Notice Period [${noticePeriod}], Lock-In Period [${lockInPeriod}], Agreement Period [${agreementPeriod}], Security [${security}], Location Id [${locationId}], Replicate To All [${replicate}]`
    );

    if (propId && typeof propId === "string" && propId.trim() !== "") {
      propId = propId.split(",").map((id) => Number(id.trim()));
    }

    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` : `Client`} Requesting....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], User Type [${userType}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Not Authorized`
        );

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

      log.info(
        `[${C}], [${F}], User Type [${userType}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requesting...`
      )
    }

    if (Number(replicate) === 1) {
      await propertyDB.updateRentingRulesForAll({
        clientId: clientId,
        rentalCycle: rentalCycle,
        noticePeriod: noticePeriod,
        lockInPeriod: lockInPeriod,
        agreementPeriod: agreementPeriod,
        security: security,
        locationId: locationId || null,
      });
    } else if (propId && propId.length > 0) {
      for (let id of propId) {
        const property = await propertyDB.getById({ id });
        if (!property) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Property Id [${id}], No Property Found, Skippin This Id`
          );
          continue;
        }

        await propertyDB.updateRentingRules({
          id: id,
          name: name ? name : property.name,
          tenantPreference: tenantPreference ? tenantPreference : property.tenantPreference,
          rentalCycle: rentalCycle ? rentalCycle : property.rentalCycle,
          noticePeriod: noticePeriod,
          lockInPeriod: lockInPeriod,
          agreementPeriod: agreementPeriod,
          security: security,
          locationId: locationId || null,
        });
      }
    } else {
      const property = await propertyDB.getById({ id: propId });
      if (!property) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], No Property Found`
        );
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      await propertyDB.updateRentingRules({
        id: propId,
        name: name ? name : property.name,
        tenantPreference: tenantPreference ? tenantPreference : property.tenantPreference,
        rentalCycle: rentalCycle ? rentalCycle : property.rentalCycle,
        noticePeriod: noticePeriod,
        lockInPeriod: lockInPeriod,
        agreementPeriod: agreementPeriod,
        security: security,
        locationId: locationId || null,
      });
    }

    log.info(
      `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
      }, Prop Id [${propId}], Rental Cycle [${rentalCycle}], Notice Period [${noticePeriod}], Lock-In Period [${lockInPeriod}], Agreement Period [${agreementPeriod}], Security [${security}], Tenant Preference [${tenantPreference}], Renting Rules Data Updated Successfully`
    );

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

property.GetManagementDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetManagementDetails";
  try {
    const { propId } = req.params;
    const userType = req.userType;

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

    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}]`
        }, Prop Id [${propId}], ${isPartner ? `Partner` : `Client`
        } Requesting...`
      );
    } else {
      log.info(
        `[${C}], [${F}], User Type [${userType}], User Id [${req.id}], Staff Not Authorized`
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const data = await propertyDB.getById({ id: propId });

    return res.status(200).json({
      msg: "Management details fetched successfully",
      isSuccess: true,
      data: {
        name: data.ownerName || "",
        email: data.ownerEmail || "",
        mobile: data.ownerMobile || "",
        // type: data.type,
        businessName: data.businessName || "",
        gstNo: data.gstNo || "",
      },
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

property.EditManagementDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "EditManagementDetails";
  try {
    const { propId, name, email, mobile, businessName, gstNo } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Name [${name}], Email [${email}], Mobile [${mobile}], Business Name [${businessName}], GST No [${gstNo}]`
    );

    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` : `Client`} Requesting...`
      );
    } else {
      log.info(
        `[${C}], [${F}], User Type [${userType}], User Id [${req.id}], Staff Not Authorized`
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    await propertyDB.updateManagementDetails({
      id: propId,
      ownerName: name,
      ownerMobile: mobile,
      ownerEmail: email,
      businessName: businessName,
      gstNo: gstNo,
    });

    log.info(
      `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
      }, Prop Id [${propId}], Management details updated successfully.`
    );

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


property.WebDashboard = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "WebDashboard";
  try {
    const userType = req.userType;
    const {
      propId,
    } = req.params;

    const {
      locationId,
      propertyId,
      startDate = moment().startOf("month").format("YYYY-MM-DD"),
      endDate = moment().endOf("month").format("YYYY-MM-DD"),
      dashboardType,
    } = req.query;

    let financialYearStartDate = moment().year(moment().year()).month(3).date(1).format("YYYY-MM-DD");
    let financialYearEndDate = moment().year(moment().year() + 1).month(2).date(31).format("YYYY-MM-DD");
    if(moment().month() < 3){
      financialYearStartDate = moment().year(moment().year() - 1).month(3).date(1).format("YYYY-MM-DD");
      financialYearEndDate = moment().year(moment().year()).month(2).date(31).format("YYYY-MM-DD");
    }

    const recentTransLimit = 5;

    let personalAndPartnerProperties: any = [];
    let staffAccounts: any = [];

    log.info(
      `[${C}], [${F}], User Type [${userType}], User Id [${req.id}], Prop Id [${propertyId}], Location Id [${locationId}], Start Date [${startDate}], End Date [${endDate}], Dashboard Type [${dashboardType}]`
    );

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

    let { isSuperAdmin } = await isUserSuperAdmin(
      Number(userType),
      Number(req.id)
    );

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

    let propFilters = [];

    if (propertyId && typeof propertyId === "string") {
      propFilters.push(...propertyId
        .split(",")
        .map(v => Number(v.trim()))
        .filter(id => Number.isInteger(id) && id > 0)
      );
    } else if (locationId && Number(locationId) > 0) {
      const locationProperties = await propertyDB.getByLocationId({
        clientId,
        locationId: locationId,
      });

      if (locationProperties && locationProperties.length > 0) {
        propFilters.push(...locationProperties.map((p: any) => p.id));
      } else {
        propFilters.push(-1); // No property attached with this location
      }
    }

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

      if (isPartner || isSuperAdmin) {
        const staff = await staffDB.getById({ id: req.id });

        if (!staff) {
          log.info(
            `[${C}], [${F}], Staff Id [${req.id}], No Staff Found For Partner Or Finance Admin`
          );
          return res.status(400).json({
            msg: CONSTANTS.MSG.INVALID_REQUEST,
            isSuccess: false,
          });
        }

        staffAccounts = await staffDB.getStaffAccounts({
          mobile: staff.mobile,
        });
      }

      //Updating last login web
      await clientDB.updateLastLogin({ id: clientId });

      if (Number(dashboardType) === CONSTANTS.WEB_DASHBOARD_TYPES.SALES) {
        const {summary, staffSummary, leadSourceSummary} = await salesHeadWebDashboardDataForClient(
          client,
          Number(propertyId) && Number(propertyId) > 0 ? Number(propertyId) : null,
          Number(locationId) && Number(locationId) > 0 ? Number(locationId) : null,
        );

        const staffList = await staffDB.getActiveByClientId({
          clientId,
        });
        
        const locations = await locationDB.getByClientIdWithLimitedFields({
          clientId,
        });

        const vendorTypes = await vendorDB.getVendorTypes();

        log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Sales Head Dashboard Data Sent For Client`);

        return res.status(200).json({
          msg: "DashBoard Data Sent Successfully",
          data: true,
          summary,
          isSuperAdmin : isSuperAdmin ? 1 : 0,
          isEkycBulkEnabled : 0,
          staffSummary,
          leadSourceSummary,
          staffList: staffList || [],
          locations: locations || [],
          staffAccounts: staffAccounts || [],
          vendorTypes: vendorTypes || [],
          isSuccess: true,
        });
      } else if (Number(dashboardType) === CONSTANTS.WEB_DASHBOARD_TYPES.KITCHEN) {

        const openComplaints = await complaintDB.getByClientIdAndTitle({
          clientId,
          title: "Food and Meal",
          propId: propId || null,
        });

        let tenantCount = await occupancyDB.getFoodEnabledTenantsCountByClientId({clientId});
        let propertiesIds = null;
        if(Number(propertyId) && Number(propertyId)> 0 ) {
          propertiesIds = [propertyId];
        }
        let summary = await foodDB.getStatsForStaff({clientId, propIds: propertiesIds, mealDate:startDate});
        if(false ===  summary) {
          summary = {
            "totalMealOptIn": 0,
            "breakfastOptIn": 0,
            "breakfastServed": 0,
            "lunchOptIn": 0,
            "lunchServed": 0,
            "snacksOptIn": 0,
            "snacksServed": 0,
            "dinnerOptIn": 0,
            "dinnerServed": 0,
            "mealOptOut": 0
          };
        }
        summary.openComplaints = openComplaints;
        summary.breakfastNotResponded = Number(tenantCount) - Number(summary?.breakfastOptIn) - Number(summary?.mealOptOut);
        summary.lunchNotResponded = Number(tenantCount) - Number(summary?.lunchOptIn) - Number(summary?.mealOptOut);
        summary.snacksNotResponded = Number(tenantCount) - Number(summary?.snacksOptIn) - Number(summary?.mealOptOut);
        summary.dinnerNotResponded = Number(tenantCount) - Number(summary?.dinnerOptIn) - Number(summary?.mealOptOut);
        const locations = await locationDB.getByClientIdWithLimitedFields({
          clientId,
        });

        const vendorTypes = await vendorDB.getVendorTypes();

        const vendors = await vendorDB.getByClientId({
          clientId,
        });
        
        let year = Number(moment().format("YYYY"));
        
        if (moment().month() < 3) {
          year = year - 1;      
        }
        
        const FYStartDate = moment(`${year}-04-01`).format("YYYY-MM-DD");
        const FYEndDate = moment(`${year + 1}-03-31`).format("YYYY-MM-DD");

        const purchaseGraphData = await kitchenInventoryDB.getPurchaseTotalByClientIdForWebGraph({
          clientId,
          startDate: FYStartDate,
          endDate: FYEndDate,
        });

        const transferGraphData = await kitchenInventoryDB.getTransferTotalByClientIdForWebGraph({
          clientId,
          startDate: FYStartDate,
          endDate: FYEndDate,
        });

        const purchaseMap = new Map(
          (purchaseGraphData || []).map((row: any) => [`${row.year}-${row.month}`, Number(row.totalPurchase)])
        );
        
        const transferMap = new Map(
          (transferGraphData || []).map((row: any) => [`${row.year}-${row.month}`, Number(row.totalTransfer)])
        );
        
        // 4. Generate continuous 12-month Financial Year Array (April to March)
        const combinedGraphData = [];
        let currentMonth = moment(`${year}-04-01`);
        
        for (let i = 0; i < 12; i++) {
          const mYear = currentMonth.year();
          const mMonth = currentMonth.month() + 1; // Moment month is 0-indexed, MySQL MONTH() is 1-indexed
          const key = `${mYear}-${mMonth}`;
        
          combinedGraphData.push({
            year: mYear,
            month: mMonth,
            purchaseTotal: purchaseMap.get(key) || 0,
            transferTotal: transferMap.get(key) || 0,
          });
        
          currentMonth.add(1, "month");
        }

        const kitchenInventoryItemSummary = await kitchenInventoryDB.getInventoryItemSummaryForWeb({
          clientId,
        });

        let itemSummary = {
          lowStock: 0,
          outOfStock: 0,
          healthyStock: 0
        };
        if(kitchenInventoryItemSummary) {
          itemSummary.lowStock = Number(kitchenInventoryItemSummary.lowStock) || 0;
          itemSummary.outOfStock = Number(kitchenInventoryItemSummary.outOfStock) || 0;
          itemSummary.healthyStock = Number(kitchenInventoryItemSummary.healthyStock) || 0;
        }

        let inventoryStocks = await kitchenInventoryDB.getInventoryStockForWebDashboard({
          clientId,
          pageNum: 1,
          limit: 5,
        });
        
        log.info(`[${C}], [${F}], Client Id [${clientId}], Meal Date [${startDate}], Kitchen Manager Dashboard Data Sent For Client`);

        return res.status(200).json({
          msg: "DashBoard Data Sent Successfully",
          data: true,
          summary,
          isSuperAdmin : isSuperAdmin ? 1 : 0,
          isEkycBulkEnabled : 0,
          locations: locations || [],
          staffAccounts: staffAccounts || [],
          vendorTypes: vendorTypes || [],
          vendors: vendors || [],
          purchaseTransferGraphData: combinedGraphData,
          itemSummary,
          inventoryStocks: inventoryStocks || [],
          isSuccess: true,
        });
      }

      securityInHand = await occupancyDB.getTotalSecurityInHandForWeb({
        clientId: clientId,
        propIds: propFilters,
      });
      const properties = await propertyDB.getPropIdsByClientId({
        clientId: clientId,
        status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
      });

      //client Summary
      const totalTenants = await occupancyDB.getTotalCountByClientIdForWeb({
        clientId,
        propIds: propFilters,
      });
      const totalStaffs = await staffDB.getAllByClientIdForWeb({
        clientId,
        status: CONSTANTS.STAFF_STATUS.ACTIVE,
        propIds: propFilters,
      });

      const tenantSummary = await occupancyDB.getOccupancyStatsForWebClient({ clientId, propIds: propFilters });

      const clientTodayCollection = await transactionDB.getTodaysCollectionForWeb({
        clientId,
        propIds: propFilters,
      });

      const clientCurMonthCollection = await transactionDB.getTotalIncomeByClientIdForWeb({
        clientId: clientId,
        startDate: startDate,
        endDate: endDate,
        propIds: propFilters,
      });

      const clientCurMonthDues = await duesDB.getDuesByClientIdForWeb({
        clientId,
        propIds: propFilters,
        startDate: startDate,
        endDate: endDate,
      });

      const clientExpectedRent = await occupancyDB.getExpectedRentForClientForWeb({
        clientId: clientId,
        propIds: propFilters,
      });

      let clientExpectedSecurity = await occupancyDB.getExpectedSecurityForClientForWeb({
        clientId,
        propIds: propFilters,
      });
      if (clientExpectedSecurity === null) {
        clientExpectedSecurity = 0;
      }
      let clientExpectedExtraCharges = await occupancyDB.getExpectedExtraChargesForClientForWeb({
        clientId,
        propIds: propFilters,
      });
      let clientTotalExpected = Number(clientExpectedRent);

      if (clientId === 168) {
        clientTotalExpected = Number(clientExpectedRent) + Number(clientExpectedSecurity) + Number(clientExpectedExtraCharges);
      }

      let previousMonthsExpected = 0;

      if (moment(startDate.toString()).isBefore(moment().startOf("month"), "date")) {

        const previousMonthsRentCollection = await transactionDB.getTotalByClientIdAndFilters({
          clientId,
          transactionFor: [`${CONSTANTS.TRANSACTION_FOR.RENT}`],
          propIds: propFilters,
          startDate: startDate,
          endDate: moment(endDate.toString()).isBefore(moment().startOf("month"), "date")
            ? endDate
            : moment().subtract(1, "months").endOf("month").format("YYYY-MM-DD"),
        });
        const previousMonthsRentDues = await duesDB.getTotalByClientIdAndFiltersAndDateRange({
          clientId,
          type: [`${CONSTANTS.DUES_TYPES.RENT}`],
          propIds: propFilters,
          startDate: startDate,
          endDate: moment(endDate.toString()).isBefore(moment().startOf("month"), "date")
            ? endDate
            : moment().subtract(1, "months").endOf("month").format("YYYY-MM-DD"),
        });
        previousMonthsExpected += Number(previousMonthsRentCollection) + Number(previousMonthsRentDues);

        //log.info(`Previous Month Rent Collection [${previousMonthsRentCollection}], Previous Month Rent Dues [${previousMonthsRentDues}], Previous Month Expected [${previousMonthsExpected}]`);
      }

      const clientRecentTrans = await transactionDB.getRecentTransactions({
        clientId,
        limit: recentTransLimit,
      });

      const beds = await bedDB.getCountsByClientIdForWeb({ clientId, propIds: propFilters });
      let bedCount = beds?.total || 0;
      let occupiedBeds = beds?.occupied || 0;
      let occupiedPercentage = bedCount > 0 ? ((occupiedBeds / bedCount) * 100).toFixed(2) : 0;

      //graph
      const expenseMonthlyStats = await clientExpenseGraphWeb(client, 3, propFilters); //3 for FY Monthly
      const expenseYearlyStats = await clientExpenseGraphWeb(client, 2, propFilters);
      const occupancyGraphStats = await clientOccupancyGraphFYForWeb(client, propFilters); //Occupancy data according to FY

      const vendorTypes = await vendorDB.getVendorTypes();

      //Expense Stats
      let curMonthExpense = 0;
      let curMonthAssetExpense = 0;
      let curFYAssetExpense = 0;
      if (Number(clientId) === 113) {
        curMonthExpense = await expenseDB.getTotalByClientIdAndNatureForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.OPERATING,
        });
        
        curMonthAssetExpense = await expenseDB.getTotalByClientIdAndNatureForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });

        curFYAssetExpense = await expenseDB.getTotalByClientIdAndNatureForWeb({
          clientId,
          propIds: propFilters,
          startDate: financialYearStartDate,
          endDate: financialYearEndDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });
      } else {
        curMonthExpense = await expenseDB.getTotalByClientIdAndNatureAndPaidDateForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.OPERATING,
        });
        
        curMonthAssetExpense = await expenseDB.getTotalByClientIdAndNatureAndPaidDateForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });

        curFYAssetExpense = await expenseDB.getTotalByClientIdAndNatureAndPaidDateForWeb({
          clientId,
          propIds: propFilters,
          startDate: financialYearStartDate,
          endDate: financialYearEndDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });
      }

      const totalAssetExpense = await expenseDB.getLifetimeTotalByClientIdAndNatureForWeb({
        clientId,
        propIds: propFilters,
        expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
      });

      const curMonthRentPaid = await expenseDB.getTotalByClientIdAndTypeForWeb({
        clientId,
        propIds: propFilters,
        startDate: startDate,
        endDate: endDate,
        type: 1, //Building Rent
      });
      const lifetimeSecurityPaid = await expenseDB.getTotalByClientIdAndTypeAndLandlordForWeb({
        clientId,
        propIds: propFilters,
        type: 43,
      });
      const rentPending = await expenseDB.getTotalUnMarkedByClientIdAndTypeAndDateRangeForWeb({
        clientId,
        propIds: propFilters,
        startDate: moment().startOf('month').format("YYYY-MM-DD"),
        endDate: moment().endOf('month').format("YYYY-MM-DD"),
        type: 1, //Building Rent
      });

      const curMonthRefunded = await transactionDB.getRefundedAmountByClientIdForWeb({
        clientId,
        propIds: propFilters,
        startDate: startDate,
        endDate: endDate,
      });

      const totalLandlordRentToBePaid = await propertyLeaseDB.getTotalMonthlyRentByClientIdForWeb({
        clientId,
        propIds: propFilters,
      });


      let vacancyLossFullMonth = 0;
      const vacantBeds = await roomDB.getVacancyLossForClientForWeb({
        clientId,
        propIds: propFilters,
      });
      if (vacantBeds && vacantBeds.length > 0) {
        for (let bed of vacantBeds) {
          vacancyLossFullMonth += bed.rent;
        }
      }

      if (Number(userType) === CONSTANTS.USER_TYPE.CLIENT) {
        personalAndPartnerProperties = await propertyDB.getPersonalAndPartnerProperties({
          clientId: req.switchingClient,
        });
      } else {
        personalAndPartnerProperties = [];
      }

      let pendingTasks = await ClientPendingTasks(clientId, Number(req.platform));

      let complaintCount = await complaintDB.graphDataForWeb({
        clientId,
        propIds: propFilters,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      let complaintCountLastMonth = await complaintDB.graphDataForWeb({
        clientId,
        propIds: propFilters,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      let curMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearForWeb({
        clientId,
        propIds: propFilters,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      let nextMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearForWeb({
        clientId,
        propIds: propFilters,
        month: moment().add(1, "month").format("MM"),
        year: moment().add(1, "month").format("YYYY"),
      });
      let lastMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearForWeb({
        clientId,
        propIds: propFilters,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      let curMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearForWeb({
        clientId,
        propIds: propFilters,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      let lastMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearForWeb({
        clientId,
        propIds: propFilters,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });

      const lastMonthOccupancyData = await occupancyReportDB.getOccupancyReportByYearMonthForWeb({
        clientId,
        propIds: propFilters,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      let lastMonthOccupancyPercent = ((lastMonthOccupancyData.occupied / lastMonthOccupancyData.total) * 100) || 0;

      const locations = await locationDB.getByClientIdWithLimitedFields({
        clientId,
      });

      let estimatedIncome = 0;
      if (moment(endDate.toString()).isBefore(moment().startOf("month"), "date")) {
        estimatedIncome = (Number(previousMonthsExpected)) - (Number(curMonthExpense) + Number(rentPending));
        //log.info(`Estimated Income [${estimatedIncome}], Cur Month Expense [${curMonthExpense}], Rent Pending [${rentPending}], Previous Month Expected [${previousMonthsExpected}]`);
      } else {
        estimatedIncome = (Number(clientTotalExpected) + Number(previousMonthsExpected)) - (Number(curMonthExpense) + Number(rentPending));
        //log.info(`Estimated Income [${estimatedIncome}], Cur Month Expense [${curMonthExpense}], Rent Pending [${rentPending}], Cur Month Expected [${clientTotalExpected}], Previous Month Expected [${previousMonthsExpected}]`);
      }

      propFilters = propFilters.filter(v => v !== -1); //To remove -1 so that it does not affect count

      const bankAccounts = await bankDB.getByClientIdLimitedFields({ clientId });
      const clientStampPaper = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_STAMP_PAPER_REQUIRED,
      });
      let isStampPaperRequired = 0;
      if (clientStampPaper && Number(clientStampPaper.value)) {
        isStampPaperRequired = Number(clientStampPaper.value);
      }

      const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
      });
      let isPayoutEnabled = 0;
      if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
        isPayoutEnabled = Number(isPayoutEnabledConfig.value);
      }

      if (isSuperAdmin || isPartner) {
        const wallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId: req.id,
        });

        if (!wallet) {
          isPayoutEnabled = 0;
        }
      }

      const curMonthAdvancePaid = await ledgerDB.getCurMonthAdvanceRentPaid({
        clientId,
      });

      const staffList = await staffDB.getActiveByClientId({
        clientId,
      });

      //CAN PUSH EKYC BULK
      let isEkycBulkEnabled = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EKYC_BULK_REMINDER,
      });
      if (!isEkycBulkEnabled) {
        isEkycBulkEnabled = 0;
      } else {
        isEkycBulkEnabled = Number(isEkycBulkEnabled.value) || 0;
      }

      const vendors = await vendorDB.getByClientId({
        clientId,
      });

      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, DashBoard Data Sent Successfully`
      );

      return res.status(200).json({
        msg: "DashBoard Data Sent Successfully",
        isSuccess: true,
        data: true,
        isSuperAdmin : isSuperAdmin ? 1 : 0,
        staffList: staffList || [],
        defaultTenantFilter: client?.defaultTenantFilter || '',
        isStampPaperRequired,
        isPayoutEnabled,
        isEkycBulkEnabled: isEkycBulkEnabled,
        bankAccounts: bankAccounts || [],
        vendorTypes: vendorTypes || [],
        vendors: vendors || [],
        personalAndPartnerProperties: personalAndPartnerProperties || [],
        staffAccounts: staffAccounts || [],
        pendingTasks,
        locations: locations || [],
        complaintSummary: {
          total: complaintCount.totalComplaints,
          resolved: complaintCount.resolved,
          pending: complaintCount.pending,
          lastMonthTotal: complaintCountLastMonth.totalComplaints,
          lastMonthResolved: complaintCountLastMonth.resolved,
          lastMonthPending: complaintCountLastMonth.pending,
        },
        businessImpact: {
          vacantInventoryValue: vacancyLossFullMonth || 0,
          curMonth: {
            moveIn: curMonthMoveIn,
            moveOut: curMonthMoveOut,
            occupancyPercent: occupiedPercentage,
          },
          lastMonth: {
            moveIn: lastMonthMoveIn,
            moveOut: lastMonthMoveOut,
            occupancyPercent: lastMonthOccupancyPercent,
          },
          nextMonth: {
            net: Number(curMonthMoveIn.totalRent) + Number(nextMonthMoveIn.totalRent) - Number(curMonthMoveOut.totalRent),
          }
        },
        clientSummary: {
          totalProperties: propertyId || locationId ? propFilters?.length || 0 : properties?.length || 0,
          totalTenants: totalTenants.totalOccupanciesCount || 0,
          totalStaffs: totalStaffs?.length || 0,
          vacantBeds: beds?.vacant || 0,
          occupiedBeds: beds?.occupied || 0,
          securityInHand: securityInHand || 0,
          movingOutTenants: tenantSummary?.moveOut,
          movingInTenants: tenantSummary?.moveIn,
          reservedTenants: tenantSummary?.reserved,
          newTenants: tenantSummary?.newTenants,
          todayBookingTenants: tenantSummary?.todayBooking,
        },
        curPropCollectionSummary: {
          curMonthAdvancePaid: Number(curMonthAdvancePaid) ? Number(curMonthAdvancePaid) : 0,
          curMonthCollection: clientCurMonthCollection[0]?.totalIncome || 0,
          expectedRent: clientTotalExpected || 0,
          todayCollection: {
            todayCollection: clientTodayCollection || 0,
          },
          dues: {
            curMonthDues: clientCurMonthDues?.duesForMonth || 0,
          },
          curMonthRefunded: curMonthRefunded ? Math.abs(curMonthRefunded) : 0,
          estimatedIncome: estimatedIncome || 0,
        },
        recentTransactions: {
          curPropTrans: clientRecentTrans || [],
        },
        expenseGraph: {
          curProp: {
            monthlyData: expenseMonthlyStats,
            yearlyData: expenseYearlyStats,
          },
        },
        occupancyGraph: {
          curProps: occupancyGraphStats,
        },
        expenseStats: {
          curMonthExpense: curMonthExpense || 0,
          curMonthAssetExpense: curMonthAssetExpense || 0,
          curFYAssetExpense: curFYAssetExpense || 0,
          totalAssetExpense: totalAssetExpense || 0,
          curMonthRent: curMonthRentPaid || 0,
          lifetimeSecurityPaid: lifetimeSecurityPaid || 0,
          rentPending: rentPending || 0,
          totalLandlordRentToBePaid: totalLandlordRentToBePaid || 0,
        },
      });
    } else {

      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(
          `[${C}], [${F}], User Type [${userType}], User Id [${req.id}], No Staff Found`
        );

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

      staffAccounts = await staffDB.getStaffAccounts({
        mobile: staff.mobile,
      });

      let isStaffAllowed = await isPrivilegedStaff(staff.role, 3);

      if (!isStaffAllowed) {
        log.info(
          `[${C}], [${F}], User Type [${userType}], Staff Id [${req.id}], Staff Role [${staff?.role}], Staff Not Authorized`
        );

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

      // updating last login web
      await staffDB.updateLastLogin({ id: staff.id });

      log.info(
        `[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requested....`
      );
      const properties = await propertyDB.getPropIdsByClientIdForStaff({
        clientId: clientId,
        status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        staffId: staff.id,
      });

      let propertiesIds:any = "";

      if (properties) {
        propertiesIds = properties.map((p: any) => p.id).join(',');
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff.id}], Staff Role [${staff.role}], No Property Linked With Staff`
        );

        return res.status(200).json({
          msg: "No property linked",
          isSuccess: false,
        });
      }

      if (staff?.role === CONSTANTS.STAFF_ROLES.SALES_HEAD) {
        const {summary, staffSummary, leadSourceSummary} = await salesHeadWebDashboardData(
          staff,
          Number(propertyId) && Number(propertyId) > 0 ? Number(propertyId) : null,
          Number(locationId) && Number(locationId) > 0 ? Number(locationId) : null,
        );

        const staffList = await staffDB.getActiveByClientId({
          clientId,
        });
        
        const locations = await locationDB.getByClientIdWithLimitedFields({
          clientId,
          propertiesIds,
        });

        const vendorTypes = await vendorDB.getVendorTypes();

        const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
        });
        let isPayoutEnabled = 0;
        if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
          isPayoutEnabled = Number(isPayoutEnabledConfig.value);
        }

        const wallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId: req.id,
        });

        if (!wallet) {
          isPayoutEnabled = 0;
        }

        log.info(`[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Sales Head Dashboard Data`);

        return res.status(200).json({
          msg: "DashBoard Data Sent Successfully",
          data: true,
          summary,
          isSuperAdmin : 0,
          isPayoutEnabled: isPayoutEnabled || 0,
          isEkycBulkEnabled : 0,
          staffSummary,
          leadSourceSummary,
          staffList: staffList || [],
          locations: locations || [],
          staffAccounts: staffAccounts || [],
          vendorTypes: vendorTypes || [],
          isSuccess: true,
        });
      }

      else if (staff?.role === CONSTANTS.STAFF_ROLES.KITCHEN_MANAGER) {
        
        const openComplaints = await complaintDB.getByClientIdAndTitle({
          clientId,
          title: "Food and Meal",
          propId: propId || null,
        });
        if(Number(propertyId) && Number(propertyId)> 0 ) {
          propertiesIds = [propertyId];
        }
        let tenantCount = await occupancyDB.getFoodEnabledTenantsCountByClientIdForStaff({clientId, propertiesIds});

        let summary = await foodDB.getStatsForStaff({clientId, propIds: propertiesIds, mealDate:startDate});
        if(false ===  summary) {
          summary = {
            "totalMealOptIn": 0,
            "breakfastOptIn": 0,
            "breakfastServed": 0,
            "lunchOptIn": 0,
            "lunchServed": 0,
            "snacksOptIn": 0,
            "snacksServed": 0,
            "dinnerOptIn": 0,
            "dinnerServed": 0,
            "mealOptOut": 0
          };
        }
        summary.openComplaints = openComplaints;
        summary.breakfastNotResponded = Number(tenantCount) - Number(summary?.breakfastOptIn) - Number(summary?.mealOptOut);
        summary.lunchNotResponded = Number(tenantCount) - Number(summary?.lunchOptIn) - Number(summary?.mealOptOut);
        summary.snacksNotResponded = Number(tenantCount) - Number(summary?.snacksOptIn) - Number(summary?.mealOptOut);
        summary.dinnerNotResponded = Number(tenantCount) - Number(summary?.dinnerOptIn) - Number(summary?.mealOptOut);
        const locations = await locationDB.getByClientIdWithLimitedFields({
          clientId,
          propertiesIds,
        });

        const vendorTypes = await vendorDB.getVendorTypes();

        const vendors = await vendorDB.getByClientId({
          clientId,
        });

        let year = Number(moment().format("YYYY"));
        
        if (moment().month() < 3) {
          year = year - 1;      
        }
        
        const FYStartDate = moment(`${year}-04-01`).format("YYYY-MM-DD");
        const FYEndDate = moment(`${year + 1}-03-31`).format("YYYY-MM-DD");

        const purchaseGraphData = await kitchenInventoryDB.getPurchaseTotalByClientIdForWebGraph({
          clientId,
          startDate: FYStartDate,
          endDate: FYEndDate,
        });

        const transferGraphData = await kitchenInventoryDB.getTransferTotalByClientIdForWebGraph({
          clientId,
          startDate: FYStartDate,
          endDate: FYEndDate,
        });

        const purchaseMap = new Map(
          (purchaseGraphData || []).map((row: any) => [`${row.year}-${row.month}`, Number(row.totalPurchase)])
        );
        
        const transferMap = new Map(
          (transferGraphData || []).map((row: any) => [`${row.year}-${row.month}`, Number(row.totalTransfer)])
        );
        
        // 4. Generate continuous 12-month Financial Year Array (April to March)
        const combinedGraphData = [];
        let currentMonth = moment(`${year}-04-01`);
        
        for (let i = 0; i < 12; i++) {
          const mYear = currentMonth.year();
          const mMonth = currentMonth.month() + 1; // Moment month is 0-indexed, MySQL MONTH() is 1-indexed
          const key = `${mYear}-${mMonth}`;
        
          combinedGraphData.push({
            year: mYear,
            month: mMonth,
            purchaseTotal: purchaseMap.get(key) || 0,
            transferTotal: transferMap.get(key) || 0,
          });
        
          currentMonth.add(1, "month");
        }

        const kitchenInventoryItemSummary = await kitchenInventoryDB.getInventoryItemSummaryForWeb({
          clientId,
        });

        let itemSummary = {
          lowStock: 0,
          outOfStock: 0,
          healthyStock: 0
        };
        if(kitchenInventoryItemSummary) {
          itemSummary.lowStock = Number(kitchenInventoryItemSummary.lowStock) || 0;
          itemSummary.outOfStock = Number(kitchenInventoryItemSummary.outOfStock) || 0;
          itemSummary.healthyStock = Number(kitchenInventoryItemSummary.healthyStock) || 0;
        }

        let inventoryStocks = await kitchenInventoryDB.getInventoryStockForWebDashboard({
          clientId,
          pageNum: 1,
          limit: 5,
        });

        const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
        });
        let isPayoutEnabled = 0;
        if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
          isPayoutEnabled = Number(isPayoutEnabledConfig.value);
        }

        const wallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId: req.id,
        });

        if (!wallet) {
          isPayoutEnabled = 0;
        }

        log.info(`[${C}], [${F}], Client Id [${staff.clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Meal Date [${startDate}], Kitchen Manager Dashboard Data`);

        return res.status(200).json({
          msg: "DashBoard Data Sent Successfully",
          data: true,
          summary,
          isSuperAdmin : 0,
          isPayoutEnabled : isPayoutEnabled || 0,
          isEkycBulkEnabled : 0,
          locations: locations || [],
          staffAccounts: staffAccounts || [],
          vendorTypes: vendorTypes || [],
          vendors: vendors || [],
          purchaseTransferGraphData: combinedGraphData,
          itemSummary,
          inventoryStocks: inventoryStocks || [],
          isSuccess: true,
        });
      }

      // let propertyCheck = propFilters.filter(v => v !== -1);
      // if (propertyCheck && propertyCheck.length > 0) {

      // }

      //client Summary
      securityInHand = await occupancyDB.getTotalSecurityInHandForWebStaff({
        clientId: clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
      });


      const totalTenants = await occupancyDB.getTotalCountForWebStaff({
        clientId,
        propertiesIds: propertiesIds,
        propIds: propFilters,
      });


      const totalStaffs = await staffDB.getAllByClientIdForWeb({
        clientId,
        status: CONSTANTS.STAFF_STATUS.ACTIVE,
        propIds: propFilters,
      });
      const tenantSummary = await occupancyDB.getOccupancyStatsForWebStaff({ clientId, propertiesIds, propIds: propFilters });
      const clientTodayCollection = await transactionDB.getTodaysCollectionForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
      });
      const clientCurMonthCollection = await transactionDB.getTotalIncomeByClientIdForWebStaff({
        clientId: clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
        status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
        startDate: startDate,
        endDate: endDate,
      });
      const clientCurMonthDues = await duesDB.getDuesByClientIdForWebForStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
        startDate: startDate,
        endDate: endDate,
      });
      const clientExpectedRent = await occupancyDB.getExpectedRentForWebStaff({
        clientId: clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
      });
      let clientExpectedSecurity = await occupancyDB.getExpectedSecurityForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
      });
      if (clientExpectedSecurity === null) {
        clientExpectedSecurity = 0;
      }
      let clientExpectedExtraCharges = await occupancyDB.getExpectedExtraChargesForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
      });
      let clientTotalExpected = Number(clientExpectedRent);

      if (clientId === 168) {
        clientTotalExpected = Number(clientExpectedRent) + Number(clientExpectedSecurity) + Number(clientExpectedExtraCharges);
      }

      let previousMonthsExpected = 0;

      if (moment(startDate.toString()).isBefore(moment().startOf("month"), "date")) {
        const previousMonthsRentCollection = await transactionDB.getTotalByClientIdAndFiltersForStaff({
          clientId,
          transactionFor: [`${CONSTANTS.TRANSACTION_FOR.RENT}`],
          propIds: propFilters,
          startDate: startDate,
          endDate: moment(endDate.toString()).isBefore(moment().startOf("month"), "date")
            ? endDate
            : moment().subtract(1, "months").endOf("month").format("YYYY-MM-DD"),
          propertiesIds: propertiesIds,
        });

        const previousMonthsRentDues = await duesDB.getTotalByClientIdAndFiltersAndDateRangeForStaff({
          clientId,
          type: [`${CONSTANTS.DUES_TYPES.RENT}`],
          propIds: propFilters,
          startDate: startDate,
          endDate: moment(endDate.toString()).isBefore(moment().startOf("month"), "date")
            ? endDate
            : moment().subtract(1, "months").endOf("month").format("YYYY-MM-DD"),
          propertiesIds: propertiesIds,
        });

        previousMonthsExpected += Number(previousMonthsRentCollection) + Number(previousMonthsRentDues);
      }

      const clientRecentTrans = await transactionDB.getRecentTransactionsForStaff({
        clientId,
        propertiesIds: propertiesIds,
        limit: recentTransLimit,
      });

      const beds = await bedDB.getStatsForWebStaff({ clientId, propertiesIds: propertiesIds, propIds: propFilters, });
      let bedCount = beds?.total || 0;
      let occupiedBeds = beds?.occupied || 0;
      let occupiedPercentage = bedCount > 0 ? ((occupiedBeds / bedCount) * 100).toFixed(2) : 0;
      //graph
      const expenseMonthlyStats = await clientExpenseGraphWeb(client, 3, propFilters);
      const expenseYearlyStats = await clientExpenseGraphWeb(client, 2, propFilters);
      const occupancyGraphStats = await staffOccupancyGraphFYWeb(staff, propFilters);
      const vendorTypes = await vendorDB.getVendorTypes();

      //Expense Stats
      //Using the client function because the expenses is shown as client wise
      let curMonthExpense = 0;
      let curMonthAssetExpense = 0;
      let curFYAssetExpense = 0;
      if (Number(clientId) === 113) {
        curMonthExpense = await expenseDB.getTotalByClientIdAndNatureForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.OPERATING,
        });
        
        curMonthAssetExpense = await expenseDB.getTotalByClientIdAndNatureForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });

        curFYAssetExpense = await expenseDB.getTotalByClientIdAndNatureForWeb({
          clientId,
          propIds: propFilters,
          startDate: financialYearStartDate,
          endDate: financialYearEndDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });
      } else {
        curMonthExpense = await expenseDB.getTotalByClientIdAndNatureAndPaidDateForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.OPERATING,
        });
        
        curMonthAssetExpense = await expenseDB.getTotalByClientIdAndNatureAndPaidDateForWeb({
          clientId,
          propIds: propFilters,
          startDate: startDate,
          endDate: endDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });

        curFYAssetExpense = await expenseDB.getTotalByClientIdAndNatureAndPaidDateForWeb({
          clientId,
          propIds: propFilters,
          startDate: financialYearStartDate,
          endDate: financialYearEndDate,
          expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
        });
      }

      const totalAssetExpense = await expenseDB.getLifetimeTotalByClientIdAndNatureForWeb({
        clientId,
        propIds: propFilters,
        expenseNature:CONSTANTS.EXPENSE_NATURE.ASSET_INVESTMENT,
      });

      const curMonthRentPaid = await expenseDB.getTotalByClientIdAndTypeForWeb({
        clientId,
        propIds: propFilters,
        startDate: startDate,
        endDate: endDate,
        type: 1, //Building Rent
      });
      const lifetimeSecurityPaid = await expenseDB.getTotalByClientIdAndTypeAndLandlordForWebStaff({
        clientId,
        propIds: propFilters,
        type: 43,
        propertiesIds: propertiesIds,
      });
      const rentPending = await expenseDB.getTotalUnMarkedByClientIdAndTypeAndDateRangeForWeb({
        clientId,
        propIds: propFilters,
        startDate: startDate,
        endDate: endDate,
        type: 1, //Building Rent
      });
      const curMonthRefunded = await transactionDB.getRefundedAmountByClientIdForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
        startDate: startDate,
        endDate: endDate,
      });
      const totalLandlordRentToBePaid = await propertyLeaseDB.getTotalMonthlyRentByClientIdForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
      });
      let vacancyLossFullMonth = 0;
      const vacantBeds = await roomDB.getVacancyLossForWebStaff({
        clientId,
        propertiesIds: propertiesIds,
        propIds: propFilters,
      });
      if (vacantBeds && vacantBeds.length > 0) {
        for (let bed of vacantBeds) {
          vacancyLossFullMonth += bed.rent;
        }
      }
      let pendingTasks = await StaffPendingTasks(clientId, Number(req.id), Number(req.platform));
      let complaintCount = await complaintDB.graphDataForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      let complaintCountLastMonth = await complaintDB.graphDataForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds: propertiesIds,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      let curMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      let nextMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
        month: moment().add(1, "month").format("MM"),
        year: moment().add(1, "month").format("YYYY"),
      });
      let lastMonthMoveIn = await occupancyDB.getMovingInImpactCountByMonthYearForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      let curMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
        month: moment().format("MM"),
        year: moment().format("YYYY"),
      });
      let lastMonthMoveOut = await occupancyDB.getMovingOutImpactCountByMonthYearForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      const lastMonthOccupancyData = await occupancyReportDB.getOccupancyReportByYearMonthForWebStaff({
        clientId,
        propIds: propFilters,
        propertiesIds,
        month: moment().subtract(1, "month").format("MM"),
        year: moment().subtract(1, "month").format("YYYY"),
      });
      let lastMonthOccupancyPercent = ((lastMonthOccupancyData.occupied / lastMonthOccupancyData.total) * 100) || 0;
      propFilters = propFilters.filter(v => v !== -1); //To remove -1 so that it does not affect count
      const locations = await locationDB.getByClientIdWithLimitedFields({
        clientId,
        propertiesIds,
      });

      const bankAccounts = await bankDB.getByClientIdLimitedFields({ clientId });
      const clientStampPaper = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_STAMP_PAPER_REQUIRED,
      });
      let isStampPaperRequired = 0;
      if (clientStampPaper && Number(clientStampPaper.value)) {
        isStampPaperRequired = Number(clientStampPaper.value);
      }

      const isPayoutEnabledConfig = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.IS_PAYOUT_ENABLED,
      });
      let isPayoutEnabled = 0;
      if (isPayoutEnabledConfig && Number(isPayoutEnabledConfig.value)) {
        isPayoutEnabled = Number(isPayoutEnabledConfig.value);
      }

      const wallet = await walletDB.getByClientIdAndStaffId({
        clientId,
        staffId: req.id,
      });

      if (!wallet) {
        isPayoutEnabled = 0;
      }

      const curMonthAdvancePaid = await ledgerDB.getCurMonthAdvanceRentPaidForStaff({
        clientId,
        propertiesIds,
      });

      const staffList = await staffDB.getActiveByClientId({
        clientId,
      });

      //CAN PUSH EKYC BULK
      let isEkycBulkEnabled = await clientConfigDB.getClientConfig({
        clientId,
        provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
        type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.EKYC_BULK_REMINDER,
      });
      if (!isEkycBulkEnabled) {
        isEkycBulkEnabled = 0;
      } else {
        isEkycBulkEnabled = Number(isEkycBulkEnabled.value) || 0;
      }
      
      const vendors = await vendorDB.getByClientId({
        clientId,
      });

      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, DashBoard Data Sent Successfully`
      );

      return res.status(200).json({
        msg: "DashBoard Data Sent Successfully",
        isSuccess: true,
        data: true,
        isSuperAdmin : staff?.isSuperAdmin || 0,
        staffList: staffList || [],
        defaultTenantFilter: client?.defaultTenantFilter || '',
        bankAccounts: bankAccounts || [],
        vendorTypes: vendorTypes || [],
        vendors: vendors || [],
        locations: locations || [],
        isStampPaperRequired,
        isEkycBulkEnabled: isEkycBulkEnabled,
        isPayoutEnabled: isPayoutEnabled || 0,
        personalAndPartnerProperties: personalAndPartnerProperties || [],
        staffAccounts: staffAccounts || [],
        pendingTasks,
        complaintSummary: {
          total: complaintCount.totalComplaints,
          resolved: complaintCount.resolved,
          pending: complaintCount.pending,
          lastMonthTotal: complaintCountLastMonth.totalComplaints,
          lastMonthResolved: complaintCountLastMonth.resolved,
          lastMonthPending: complaintCountLastMonth.pending,
        },
        businessImpact: {
          vacantInventoryValue: vacancyLossFullMonth || 0,
          curMonth: {
            moveIn: curMonthMoveIn,
            moveOut: curMonthMoveOut,
            occupancyPercent: occupiedPercentage,
          },
          lastMonth: {
            moveIn: lastMonthMoveIn,
            moveOut: lastMonthMoveOut,
            occupancyPercent: lastMonthOccupancyPercent,
          },
          nextMonth: {
            net: Number(curMonthMoveIn.totalRent) + Number(nextMonthMoveIn.totalRent) - Number(curMonthMoveOut.totalRent),
          }
        },
        clientSummary: {
          totalProperties: propertyId || locationId ? propFilters?.length || 0 : properties?.length || 0,
          totalTenants: totalTenants.totalOccupanciesCount || 0,
          totalStaffs: totalStaffs?.length || 0,
          vacantBeds: beds?.vacant || 0,
          occupiedBeds: beds?.occupied || 0,
          securityInHand: securityInHand || 0,
          movingOutTenants: tenantSummary?.moveOut,
          movingInTenants: tenantSummary?.moveIn,
          reservedTenants: tenantSummary?.reserved,
          newTenants: tenantSummary?.newTenants,
          todayBookingTenants: tenantSummary?.todayBooking,
        },
        curPropCollectionSummary: {
          curMonthAdvancePaid: Number(curMonthAdvancePaid) ? Number(curMonthAdvancePaid) : 0,
          curMonthCollection: clientCurMonthCollection[0]?.totalIncome || 0,
          expectedRent: clientTotalExpected || 0,
          todayCollection: {
            todayCollection: clientTodayCollection || 0,
          },
          dues: {
            curMonthDues: clientCurMonthDues?.totalDues || 0,
          },
          curMonthRefunded: curMonthRefunded ? Math.abs(curMonthRefunded) : 0,
        },
        recentTransactions: {
          curPropTrans: clientRecentTrans || [],
        },
        expenseGraph: {
          curProp: {
            monthlyData: expenseMonthlyStats,
            yearlyData: expenseYearlyStats,
          },
        },
        occupancyGraph: {
          curProps: occupancyGraphStats,
        },
        expenseStats: {
          curMonthExpense: curMonthExpense || 0,
          curMonthAssetExpense: curMonthAssetExpense || 0,
          curFYAssetExpense: curFYAssetExpense || 0,
          totalAssetExpense: totalAssetExpense || 0,
          curMonthRent: curMonthRentPaid || 0,
          lifetimeSecurityPaid: lifetimeSecurityPaid || 0,
          rentPending: rentPending || 0,
          totalLandlordRentToBePaid: totalLandlordRentToBePaid || 0,
        },
      });
    }
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

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

  try {
    const { userType } = req;
    let { filter, searchVal } = req.query;
    // let clientId = req.id;
    let properties = [];
    let pageNum = 1;
    const limit = 200;

    let summary = null;

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

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      //const clientId = req.id;

      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Page Num [${pageNum}], Filter [${filter}], Search Val [${searchVal}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );

      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 (searchVal) {
        properties = await propertyDB.getSearchResultsByClientId({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          searchVal,
        });
      } else if (filter === "A") {
        properties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        });
      } else if (filter === "P") {
        properties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.PENDING,
        });
        // let property = await propertyDB.getByClientIdAndStatus({
        //   clientId: client?.id,
        //   pageNum: Number(pageNum),
        //   limit,
        //   status: CONSTANTS.PROPERTY_STATUS.COMPLETED,
        // });
        //properties = [...properties, property];
      } else if (filter === "I") {
        properties = await propertyDB.getByClientIdAndStatus({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.INACTIVE,
        });
      } else {
        properties = await propertyDB.getByClientIdForWeb({
          clientId: client?.id,
          pageNum: Number(pageNum),
          limit,
        });
      }

      summary = await propertyDB.getSummaryByClientId({
        clientId: client?.id,
      });

      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Property list sent successfully`
      );
    } else {
      const staffId = req.id;

      log.info(
        `[${C}], [${F}], Staff Id [${staffId}], Page Num [${pageNum}], Filter [${filter}], Search Val [${searchVal}], Staff Requesting....`
      );

      const staff = await staffDB.getById({ id: staffId });

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

      if (searchVal) {
        properties = await staffDB.getPropertiesSearchResult({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          searchVal,
        });
      } else if (filter === "A") {
        properties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.ACTIVE,
        });
      } else if (filter === "P") {
        properties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.PENDING,
        });
      } else if (filter === "I") {
        properties = await staffDB.getPropertiesByStatus({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
          status: CONSTANTS.PROPERTY_STATUS.INACTIVE,
        });
      } else {
        properties = await staffDB.getProperties({
          staffId,
          clientId: staff.clientId,
          pageNum: Number(pageNum),
          limit,
        });
      }

      summary = propertyDB.getSummaryByStaffId({
        staffId,
      });

      log.info(
        `[${C}], [${F}], Staff Id [${staff?.id}], Property list sent successfully`
      );
    }
    if (properties) {
      for (let property of properties) {
        const flats = await flatDB.getByPropId({ propId: property.id });

        const rooms = await roomDB.getCountsByPropId({ propId: property.id });
        const beds = await bedDB.getCountsByPropId({ propId: property.id });
        const transactionStats =
          await transactionDB.getTotalIncomeStatsByPropCurrentMonth({
            propId: property.id,
            status: CONSTANTS.TRANSACTION_STATUS.SUCCESS,
          });
        property.roomCount = rooms?.totalRooms || 0;
        property.bedCount = beds?.total || 0;
        property.flats = flats || [];
        property.totalCollection =
          transactionStats[transactionStats.length - 1]?.totalIncome || 0;
        property.vacantBedCount = beds?.vacant || 0;
        property.occupiedBedCount = beds?.occupied || 0;
      }
    }

    return res.status(200).json({
      msg: "Property list sent successfully",
      data: properties || [],
      summary: summary || [],
      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,
    });
  }
};

property.EditName = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "EditName";

  try {
    const { propId, name } = req.body;
    const userType = req.userType;

    log.info(`[${C}], [${F}], Prop Id [${propId}], Name [${name}]`);

    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}]`
        }, Prop Id [${propId}], Name [${name}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Name [${name}], Admin Requesting....`
      );
    }

    await propertyDB.editName({
      id: propId,
      name: name,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Name [${name}], Property Name Updated Successfully`
    );

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

property.Delete = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "Delete";

  try {
    const { propId } = req.body;
    const userType = req.userType;

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

    const property = await propertyDB.getById({
      id: propId,
    });
    if (!property) {
      log.info(`[${C}], [${F}], Prop Id [${propId}], Property Not Found`);
      return res.status(400).json({
        msg: "Property not found",
        isSuccess: false,
      });
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Not Allowed To Delete Properties`
      );
      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const isTenants = await occupancyDB.getByPropIdForWeb({
      propId,
    });

    if (isTenants) {
      log.info(
        `[${C}], [${F}], Prop Id [${propId}], Property Cannot Be Deleted Due To Exisiting Tenants`
      );
      return res.status(400).json({
        msg: "Property cannot be deleted due to existing tenants",
        isSuccess: false,
      });
    }

    const staffs = await staffDB.getByPropId({
      propId,
    });

    if (staffs) {
      for (let staff of staffs) {
        await staffDB.unlink({
          clientId,
          propId,
          staffId: staff.id,
        });
      }
    }

    const lease = await propertyLeaseDB.getByClientIdAndPropId({
      clientId: clientId,
      propId: propId,
    });

    if (lease) {
      await propertyLeaseDB.delete({
        id: lease.id,
      });
    }

    await recurringExpenseDB.deleteByPropIdAndType({
      clientId,
      propId,
      type: 1 //building rent
    });

    await roomDB.deactivateByPropId({
      propId,
    });

    await propertyDB.updateStatus({
      id: propId,
      status: CONSTANTS.PROPERTY_STATUS.DELETED,
    });

    await logPropertyActivity(
      req.userType!,
      Number(req.id),
      Number(req.parentClientId!),
      req.platform!,
      CONSTANTS.ACTIVITY_TYPES.DELETE_PROPERTY,
      propId,
      0,
      0,
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Status Updated To Deleted Successfully`
    );
    return res.status(200).json({
      msg: "Property deleted 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,
    });
  }
};

property.UpdateLongitudeLatitude = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Property Controller";
  const F = "UpdateLongitudeLatitude";

  try {
    const { longitude, latitude, propId } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Longitude [${longitude}], Latitude [${latitude}]`
    );

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Admin Requesting....`
      );
    }

    await propertyDB.updateLongitudeLatitude({
      id: propId,
      longitude: longitude,
      latitude: latitude,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Longitude [${longitude}], Latitude [${latitude}] Updated Successfully`
    );

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

property.EditTenantOnboardingSetting = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Property Controller";
  const F = "EditTenantOnboardingSetting";

  try {
    let { isEnabled, propId, type } = req.body;

    const propIds: string[] = propId
      ? propId.toString().split(',').map((v: any) => v.trim()).filter(Boolean)
      : [];

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Is Enabled [${isEnabled}], Type [${type}]`
    );

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Prop Id [${propId}], Is Enabled [${isEnabled}], Type [${type}], Admin Requesting....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Is Enabled [${isEnabled}], Type [${type}], Non Admin Staff Not Allowed....`
        );

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
    }

    let paramToUpdate = "";

    if (type === "aadhaarOtpVerification") {
      paramToUpdate = "isIdVerificationEnabled";
      if (1 === isEnabled) {
        isEnabled = 2;
      }
    } else if (type === "policeVerification") {
      paramToUpdate = "isPoliceVerificationEnabled";
    } else if (type === "onlinePayment") {
      paramToUpdate = "isOnlinePaymentEnabled";
    } else if (type === "rentAgreement") {
      paramToUpdate = "isRentAgreementEnabled";
    } else if (type === "rentalBond") {
      paramToUpdate = "isBondAvailable";
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Type [${type}], Is Enabled [${isEnabled}], Invalid Type [${type}]`
      );
      return res.status(400).json({
        msg: "Invalid Type",
        isSuccess: false,
      });
    }
    if (type != "rentAgreement" && type != "onlinePayment") {
      if (propId && propIds.length > 0) {
        await propertyDB.updateTenantOnboardingSetting({
          propIds,
          isEnabled: isEnabled,
          setting: paramToUpdate,
        });
      } else {
        await propertyDB.updateTenantOnboardingSettingForClient({
          clientId: clientId,
          isEnabled: isEnabled,
          setting: paramToUpdate,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Is Enabled [${isEnabled}], Type [${type}] Updated Successfully`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Is Enabled [${isEnabled}], Type [${type}], Update Ignored`
      );
    }
    return res.status(200).json({
      msg: "Property tenant onboarding setting 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,
    });
  }
};

property.ListTenantOnboardingSetting = async (
  req: CustomRequest,
  res: Response
) => {
  const C = "Property Controller";
  const F = "ListTenantOnboardingSetting";

  try {
    const { propId } = req.params;

    const propIds: string[] = propId
      ? propId.toString().split(',').map((v: any) => v.trim()).filter(Boolean)
      : [];

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Prop Id [${propId}], Admin Requesting....`
        );
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Non Admin Staff Not Allowed....`
        );

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
    }

    let finalTenantOnboardingSetting = {
      isIdVerificationEnabled: 2,
      isPoliceVerificationEnabled: 1,
      isOnlinePaymentEnabled: 1,
      isRentAgreementEnabled: 1,
      path: null,
      isBondAvailable: 1,
    };

    let tenantOnboardingSetting;

    if (propId && propIds.length > 0) {
      tenantOnboardingSetting = await propertyDB.getTenantOnboardingSetting({
        propIds,
      });
    } else {
      tenantOnboardingSetting = await propertyDB.getTenantOnboardingSettingForClient({
        clientId,
      });
    }

    if (tenantOnboardingSetting && tenantOnboardingSetting.length > 0) {
      for (let setting of tenantOnboardingSetting) {
        if (setting.isIdVerificationEnabled === 0) finalTenantOnboardingSetting.isIdVerificationEnabled = 0;
        if (setting.isPoliceVerificationEnabled === 0) finalTenantOnboardingSetting.isPoliceVerificationEnabled = 0;
        if (setting.isOnlinePaymentEnabled === 0) finalTenantOnboardingSetting.isOnlinePaymentEnabled = 0;
        if (setting.isRentAgreementEnabled === 0) finalTenantOnboardingSetting.isRentAgreementEnabled = 0;
        if (setting.isBondAvailable === 0) finalTenantOnboardingSetting.isBondAvailable = 0;
      }
      tenantOnboardingSetting = finalTenantOnboardingSetting;
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Tenant Onboarding Setting Fetched Successfully`
    );
    return res.status(200).json({
      msg: "Tenant onboarding setting fetched successfully",
      isSuccess: true,
      data: {
        aadhaarOtpVerification: tenantOnboardingSetting.isIdVerificationEnabled,
        policeVerification: tenantOnboardingSetting.isPoliceVerificationEnabled,
        onlinePayment: tenantOnboardingSetting.isOnlinePaymentEnabled,
        rentAgreement: tenantOnboardingSetting.isRentAgreementEnabled,
        rentAgreementUrl: tenantOnboardingSetting.path,
        rentalBond: tenantOnboardingSetting.isBondAvailable,
      },
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

property.GetIncomeExpenseSummaryForWeb = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetIncomeExpenseSummaryForWeb";

  try {
    const userType = req.userType;

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

    let propertiesIds: any = [];
    const limit = 1000;
    const pageNum = 1;

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

      const props = await propertyDB.getAllByClientId({
        clientId: clientId,
      });

      if (props && props.length > 0) {
        props.forEach((prop: any) => {
          propertiesIds.push(prop.id);
        });
      }

    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${staff?.id}], Non Admin Staff Not Allowed....`
        );

        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      } else {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
        );
        const props = await propertyDB.getPropsByStaffId({
          staffId: staff.id,
        });

        if (props && props.length > 0) {
          props.forEach((prop: any) => {
            propertiesIds.push(prop.id);
          });
        }

      }
    }

    const data = await propertiesPendingRentForCurMonth({
      propertiesIds,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Property Ids [${JSON.stringify(propertiesIds)}], Data Sent Successfully`);

    return res.status(200).json({
      msg: "Income-Expense data fetched successfully",
      data,
      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,
    });
  }
};

property.AddLandlordDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "AddLandlordDetails";

  try {
    let { propId, name, mobile, rent, security, startDate, endDate, notice, rentDate, incrementType = 0, incrementValue = 0, incrementMonth = null, lockInPeriod = null, rentCollectionType = 1 } = req.body;

    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Rent Date [${rentDate}], Notice [${notice}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}]`
    );
    if ("" == incrementType) {
      incrementType = 0;
    }

    if (!rentDate) rentDate = startDate;

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Admin Requesting....`
      );
    }

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

    let landlord = await landlordDB.getByMobile({ mobile: mobile });

    if (!landlord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Name [${name}], Landlord Mobile [${mobile}], No Landlord Found, Creating New Landlord`
      );

      await landlordDB.create({
        name: name,
        mobile: mobile,
      });

      landlord = await landlordDB.getByMobile({ mobile: mobile });
    }

    let landlordVendorRecord = await vendorDB.getByMobileAndClientId({ mobile: mobile, clientId });

    if (!landlordVendorRecord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Name [${name}], Landlord Mobile [${mobile}], No Vendor Record Found, Creating New Vendor`
      );

      await vendorDB.create({
        mobile: mobile,
        name: name,
        clientId,
        type: CONSTANTS.VENDOR_TYPES.LANDLORD,
      });

      landlordVendorRecord = await vendorDB.getByMobile({ mobile: mobile });
    }

    let propertyLease = await propertyLeaseDB.getByClientIdAndPropId({
      clientId,
      propId,
    });

    if (propertyLease) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
      );

      return res.status(400).json({
        msg: "Property lease already exists",
        isSuccess: false,
      });
    }

    await propertyLeaseDB.create({
      clientId,
      propId,
      landlordId: landlord.id,
      startDate,
      endDate,
      notice,
      rent,
      security,
      rentDate,
      incrementType,
      incrementValue,
      incrementMonth,
      lockInPeriod,
      rentCollectionType,
    });

    if (security > 0) {
      let expenseId = await expenseDB.create({
        type: 43, //Building Security
        amount: security,
        clientId,
        paidDate: null,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlordVendorRecord.id,
        paidToUserType: CONSTANTS.USER_TYPE.VENDOR,
        description: `Building security to landlord`,
        paymentMethod: 0,
        repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
        noOfMonths: 0,
        dueDate: rentDate,
        isPaid: 0,
      });

      await expenseDB.addProperty({
        expenseId: expenseId,
        propId: Number(propId),
        clientId: clientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${expenseId}], Unpaid Security Added`
      );
    }

    let recurringExpenseId = await recurringExpenseDB.create({
      type: 1, //Building Rent Expense
      amount: rent,
      clientId,
      propId: Number(propId),
      paidDate: moment().format("YYYY-MM-DD"),
      paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
      paidBy: clientId,
      paidTo: landlordVendorRecord.id,
      paidToUserType: CONSTANTS.USER_TYPE.VENDOR,
      description: `Monthly building rent paid to landlord`,
      paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
      noOfMonths: 1,
      dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
      expenseCycle: 1,
      referenceId: null,
    });

    await recurringExpenseDB.updateReferenceId({
      id: recurringExpenseId,
      referenceId: recurringExpenseId,
    });

    //Pallav Added
    if (rent > 0) {
      let rentExpenseId = await expenseDB.create({
        type: 1, //Building Rent
        amount: rent,
        clientId,
        paidDate: null,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlordVendorRecord.id,
        paidToUserType: CONSTANTS.USER_TYPE.VENDOR,
        description: `Monthly building rent to landlord`,
        paymentMethod: 0,
        repetitionType: 2,
        noOfMonths: 0,
        dueDate: rentDate,
        isPaid: 0,
      });

      await expenseDB.addProperty({
        expenseId: rentExpenseId,
        propId: Number(propId),
        clientId: clientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${rentExpenseId}], Unpaid Rent Added`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Notice [${notice}], Rent Date [${rentDate}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Landlord Details Added Successfully`
    );

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

property.AddLandlordDetailsX = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "AddLandlordDetailsX";

  try {
    let { propId, flatId = null, name, mobile, rent, security, startDate, endDate, notice, rentDate, incrementType = 0, incrementValue = 0, incrementMonth = null, lockInPeriod = null, rentCollectionType = 1, propType, ownership } = req.body;

    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Flat Id [${flatId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Rent Date [${rentDate}], Notice [${notice}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Property Type [${propType}], Landlord Ownership [${ownership}]`
    );
    if ("" == incrementType) {
      incrementType = 0;
    }

    if (!rentDate) rentDate = startDate;

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Admin Requesting....`
      );
    }

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

    let landlord = await landlordDB.getByMobile({ mobile: mobile });

    if (!landlord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Name [${name}], Landlord Mobile [${mobile}], No Landlord Found, Creating New Landlord`
      );

      await landlordDB.create({
        name: name,
        mobile: mobile,
      });

      landlord = await landlordDB.getByMobile({ mobile: mobile });
    }

    let landlordVendorRecord = await vendorDB.getByMobileAndClientId({ mobile: mobile, clientId });

    if (!landlordVendorRecord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Name [${name}], Landlord Mobile [${mobile}], No Vendor Record Found, Creating New Vendor`
      );

      await vendorDB.create({
        mobile: mobile,
        name: name,
        clientId,
        type: CONSTANTS.VENDOR_TYPES.LANDLORD,
      });

      landlordVendorRecord = await vendorDB.getByMobile({ mobile: mobile });
    }

    // let propertyLease = await propertyLeaseDB.getByClientIdAndPropId({
    //   clientId,
    //   propId,
    // });

    // if (propertyLease) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
    //   );

    //   return res.status(400).json({
    //     msg: "Property lease already exists",
    //     isSuccess: false,
    //   });
    // }

    if (flatId) {
      let propertyLeaseWithoutFlat = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
        clientId,
        propId,
        flatId: null,
      });

      if (propertyLeaseWithoutFlat) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Property lease already exists",
          isSuccess: false,
        });
      }

      let propertyLeaseFlat = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
        clientId,
        propId,
        flatId,
      });

      if (propertyLeaseFlat) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Id [${flatId}], Flat Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Flat lease already exists",
          isSuccess: false,
        });
      }
    } else {
      let propertyLease = await propertyLeaseDB.getByClientIdAndPropId({
        clientId,
        propId,
      });
      if (propertyLease) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Property lease already exists",
          isSuccess: false,
        });
      }
    }

    await propertyLeaseDB.createX({
      clientId,
      propId,
      flatId: flatId || null,
      landlordId: landlord.id,
      startDate,
      endDate,
      notice,
      rent,
      security,
      rentDate,
      incrementType,
      incrementValue,
      incrementMonth,
      lockInPeriod,
      rentCollectionType,
      propType,
      ownership,
    });

    if (security > 0) {
      let expenseId = await expenseDB.create({
        type: 43, //Building Security
        amount: security,
        clientId,
        paidDate: null,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlordVendorRecord.id,
        paidToUserType: CONSTANTS.USER_TYPE.VENDOR,
        description: `Building security to landlord`,
        paymentMethod: 0,
        repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
        noOfMonths: 0,
        dueDate: rentDate,
        isPaid: 0,
      });

      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(expenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await expenseDB.addProperty({
        expenseId: expenseId,
        propId: Number(propId),
        flatId: Number(flatId) ? Number(flatId) : null,
        clientId: clientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${expenseId}], Unpaid Security Added`
      );
    }

    let recurringExpenseId = await recurringExpenseDB.create({
      type: 1, //Building Rent Expense
      amount: rent,
      clientId,
      propId: Number(propId),
      flatId: Number(flatId) ? Number(flatId) : null,
      paidDate: moment().format("YYYY-MM-DD"),
      paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
      paidBy: clientId,
      paidTo: landlordVendorRecord.id,
      paidToUserType: CONSTANTS.USER_TYPE.VENDOR,
      description: `Monthly building rent paid to landlord`,
      paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
      noOfMonths: 1,
      dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
      expenseCycle: 1,
      referenceId: null,
    });

    await recurringExpenseDB.updateReferenceId({
      id: recurringExpenseId,
      referenceId: recurringExpenseId,
    });

    //Pallav Added
    if (rent > 0) {
      let rentExpenseId = await expenseDB.create({
        type: 1, //Building Rent
        amount: rent,
        clientId,
        paidDate: null,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlordVendorRecord.id,
        paidToUserType: CONSTANTS.USER_TYPE.VENDOR,
        description: `Monthly building rent to landlord`,
        paymentMethod: 0,
        repetitionType: 2,
        noOfMonths: 0,
        dueDate: rentDate,
        isPaid: 0,
      });

      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(rentExpenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await expenseDB.addProperty({
        expenseId: rentExpenseId,
        propId: Number(propId),
        flatId: Number(flatId) ? Number(flatId) : null,
        clientId: clientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${rentExpenseId}], Unpaid Rent Added`
      );
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Notice [${notice}], Rent Date [${rentDate}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Landlord Details Added Successfully`
    );

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

property.AddLeaseDetails_Old = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "AddLeaseDetails";

  try {
    let { propId, flatId = null, landlordId, rent, security, startDate, endDate, notice, rentDate, incrementType = 0, incrementValue = 0, incrementMonth = null, lockInPeriod = null, rentCollectionType = 1, propType, ownership } = req.body;

    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Flat Id [${flatId}], Landlord Id [${landlordId}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Rent Date [${rentDate}], Notice [${notice}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Property Type [${propType}], Landlord Ownership [${ownership}]`
    );
    if ("" == incrementType) {
      incrementType = 0;
    }

    if (!rentDate) rentDate = startDate;

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Admin Requesting....`
      );
    }

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

    let landlord = await landlordDB.getById({ id: landlordId });

    if (!landlord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], No Landlord Found, Creating New Landlord`
      );

      return res.status(400).json({
        msg: "Invalid Landlord Id",
        isSuccess: false,
      });
    }

    if (flatId) {
      let propertyLeaseWithoutFlat = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
        clientId,
        propId,
        flatId: null,
      });

      if (propertyLeaseWithoutFlat) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Property lease already exists",
          isSuccess: false,
        });
      }

      let propertyLeaseFlat = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
        clientId,
        propId,
        flatId,
      });

      if (propertyLeaseFlat) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Id [${flatId}], Flat Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Flat lease already exists",
          isSuccess: false,
        });
      }
    } else {
      let propertyLease = await propertyLeaseDB.getByClientIdAndPropId({
        clientId,
        propId,
      });
      if (propertyLease) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Property lease already exists",
          isSuccess: false,
        });
      }
    }

    await propertyLeaseDB.createX({
      clientId,
      propId,
      flatId: flatId || null,
      landlordId: landlord.id,
      startDate,
      endDate,
      notice,
      rent,
      security,
      rentDate,
      incrementType,
      incrementValue,
      incrementMonth,
      lockInPeriod,
      rentCollectionType,
      propType,
      ownership,
    });

    if (security > 0) {
      let expenseId = await expenseDB.create({
        type: 43, //Building Security
        amount: security,
        clientId,
        paidDate: null,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlord.id,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        description: `Building security to landlord`,
        paymentMethod: 0,
        repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
        noOfMonths: 0,
        dueDate: rentDate,
        isPaid: 0,
      });

      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(expenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await expenseDB.addProperty({
        expenseId: expenseId,
        propId: Number(propId),
        clientId: clientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${expenseId}], Unpaid Security Added`
      );
    }

    // let recurringExpenseId = await recurringExpenseDB.create({
    //   type: 1, //Building Rent Expense
    //   amount: rent,
    //   clientId,
    //   propId: Number(propId),
    //   paidDate: moment().format("YYYY-MM-DD"),
    //   paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
    //   paidBy: clientId,
    //   paidTo: landlord.id,
    //   paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
    //   description: `Monthly building rent paid to landlord`,
    //   paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
    //   noOfMonths: 1,
    //   dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
    //   expenseCycle: 1,
    //   referenceId: null,
    // });

    let recurringExpenseId = null;
    let recurrenceDate = moment().format("YYYY-MM-DD");

    if (Number(rentCollectionType) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
      recurrenceDate = moment().add(1, 'months').format("YYYY-MM-DD");
    } else {
      recurrenceDate = moment().add(1, 'months').format("YYYY-MM-DD");
    }

    if (Number(rentCollectionType) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
      recurringExpenseId = await recurringExpenseDB.createNew({
        type: 1,
        amount: rent,
        clientId,
        propId: Number(propId),
        paidDate: moment().format("YYYY-MM-DD"),
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlord.id,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        description: `Monthly building rent paid to landlord`,
        paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        noOfMonths: 1,
        dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
        recurrenceDate: recurrenceDate,
        expenceCycleType: Number(rentCollectionType),
        expenseCycle: 1,
        referenceId: null,
      });
    } else {
      recurringExpenseId = await recurringExpenseDB.createNew({
        type: 1, //Rent
        amount: rent,
        clientId,
        propId: Number(propId),
        paidDate: moment().format("YYYY-MM-DD"),
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlord.id,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        description: `Monthly building rent paid to landlord`,
        paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        noOfMonths: 1,
        dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
        recurrenceDate: recurrenceDate,
        expenceCycleType: Number(rentCollectionType),
        expenseCycle: 1,
        referenceId: null,
      });
    }

    await recurringExpenseDB.updateReferenceId({
      id: recurringExpenseId,
      referenceId: recurringExpenseId,
    });

    //Pallav Added
    if (rent > 0) {
      let rentExpenseId = await expenseDB.create({
        type: 1, //Building Rent
        amount: rent,
        clientId,
        paidDate: null,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlord.id,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        description: `Monthly building rent to landlord`,
        paymentMethod: 0,
        repetitionType: 2,
        noOfMonths: 0,
        dueDate: rentDate,
        isPaid: 0,
      });

      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(rentExpenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await expenseDB.updateRecurringExpenseId({
        id: rentExpenseId,
        recurringExpenseId: recurringExpenseId
      });

      await expenseDB.addProperty({
        expenseId: rentExpenseId,
        propId: Number(propId),
        clientId: clientId,
      });
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${rentExpenseId}], Unpaid Rent Added`
      );
    }

    await logLeaseActivity(
      Number(req.userType),
      Number(req.id),
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.ADD_PROPERTY_LEASE,
      landlordId,
      propId,
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Landlord Id [${landlordId}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Notice [${notice}], Rent Date [${rentDate}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Landlord Details Added Successfully`
    );

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

property.AddLeaseDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "AddLeaseDetails";

  try {
    let { propId, flatIds = null, landlordId, rent, security, startDate, endDate, notice, rentDate, incrementType = 0, incrementValue = 0, incrementMonth = null, lockInPeriod = null, rentCollectionType = 1, propType, ownership } = req.body;

    const userType = req.userType;

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

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Flat Ids [${flatIds}], Landlord Id [${landlordId}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Rent Date [${rentDate}], Notice [${notice}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Property Type [${propType}], Landlord Ownership [${ownership}]`
    );
    if ("" == incrementType) {
      incrementType = 0;
    }

    if (!rentDate) rentDate = startDate;

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Admin Requesting....`
      );
    }

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

    let landlord = await landlordDB.getById({ id: landlordId });

    if (!landlord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], No Landlord Found, Creating New Landlord`
      );

      return res.status(400).json({
        msg: "Invalid Landlord Id",
        isSuccess: false,
      });
    }

    if (flatIds && flatIds.length > 0) {
      for (let flatId of flatIds) {
        let propertyLeaseWithoutFlat = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
          clientId,
          propId,
          flatId: null,
        });

        if (propertyLeaseWithoutFlat) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
          );

          return res.status(400).json({
            msg: "Property lease already exists",
            isSuccess: false,
          });
        }

        let propertyLeaseFlat = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
          clientId,
          propId,
          flatId,
        });

        if (propertyLeaseFlat) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Id [${flatId}], Flat Lease Already Exists`
          );

          return res.status(400).json({
            msg: "Flat lease already exists",
            isSuccess: false,
          });
        }

        await propertyLeaseDB.createX({
          clientId,
          propId,
          flatId: flatId || null,
          landlordId: landlord.id,
          startDate,
          endDate,
          notice,
          rent,
          security,
          rentDate,
          incrementType,
          incrementValue,
          incrementMonth,
          lockInPeriod,
          rentCollectionType,
          propType,
          ownership,
        });

        if (security > 0) {
          let expenseId = await expenseDB.create({
            type: 43, //Building Security
            amount: security,
            clientId,
            paidDate: null,
            paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
            paidBy: clientId,
            paidTo: landlord.id,
            paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
            description: `Building security to landlord`,
            paymentMethod: 0,
            repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
            noOfMonths: 0,
            dueDate: rentDate,
            isPaid: 0,
          });

          await setExpenseJournalTallyStatus(
            Number(clientId),
            Number(expenseId),
            CONSTANTS.TALLY_STATUS.PENDING,
          );

          await expenseDB.addProperty({
            expenseId: expenseId,
            propId: Number(propId),
            flatId: Number(flatId) ? Number(flatId) : null,
            clientId: clientId,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${expenseId}], Unpaid Security Added`
          );
        }

        let recurringExpenseId = null;
        let recurrenceDate = moment().format("YYYY-MM-DD");

        if (Number(rentCollectionType) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
          recurrenceDate = moment().add(1, 'months').format("YYYY-MM-DD");
        } else {
          recurrenceDate = moment().add(1, 'months').format("YYYY-MM-DD");
        }

        if (Number(rentCollectionType) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
          recurringExpenseId = await recurringExpenseDB.createNew({
            type: 1,
            amount: rent,
            clientId,
            propId: Number(propId),
            flatId: Number(flatId) ? Number(flatId) : null,
            paidDate: moment().format("YYYY-MM-DD"),
            paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
            paidBy: clientId,
            paidTo: landlord.id,
            paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
            description: `Monthly building rent paid to landlord`,
            paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
            noOfMonths: 1,
            dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
            recurrenceDate: recurrenceDate,
            expenceCycleType: Number(rentCollectionType),
            expenseCycle: 1,
            referenceId: null,
          });
        } else {
          recurringExpenseId = await recurringExpenseDB.createNew({
            type: 1, //Rent
            amount: rent,
            clientId,
            propId: Number(propId),
            flatId: Number(flatId) ? Number(flatId) : null,
            paidDate: moment().format("YYYY-MM-DD"),
            paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
            paidBy: clientId,
            paidTo: landlord.id,
            paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
            description: `Monthly building rent paid to landlord`,
            paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
            noOfMonths: 1,
            dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
            recurrenceDate: recurrenceDate,
            expenceCycleType: Number(rentCollectionType),
            expenseCycle: 1,
            referenceId: null,
          });
        }

        await recurringExpenseDB.updateReferenceId({
          id: recurringExpenseId,
          referenceId: recurringExpenseId,
        });

        //Pallav Added
        if (rent > 0) {
          let rentExpenseId = await expenseDB.create({
            type: 1, //Building Rent
            amount: rent,
            clientId,
            paidDate: null,
            paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
            paidBy: clientId,
            paidTo: landlord.id,
            paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
            description: `Monthly building rent to landlord`,
            paymentMethod: 0,
            repetitionType: 2,
            noOfMonths: 0,
            dueDate: rentDate,
            isPaid: 0,
          });

          await setExpenseJournalTallyStatus(
            Number(clientId),
            Number(rentExpenseId),
            CONSTANTS.TALLY_STATUS.PENDING,
          );

          await expenseDB.updateRecurringExpenseId({
            id: rentExpenseId,
            recurringExpenseId: recurringExpenseId
          });

          await expenseDB.addProperty({
            expenseId: rentExpenseId,
            propId: Number(propId),
            flatId: Number(flatId) ? Number(flatId) : null,
            clientId: clientId,
          });
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${rentExpenseId}], Unpaid Rent Added`
          );
        }
      }
    } else {
      let propertyLease = await propertyLeaseDB.getByClientIdAndPropId({
        clientId,
        propId,
      });
      if (propertyLease) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Lease Already Exists`
        );

        return res.status(400).json({
          msg: "Property lease already exists",
          isSuccess: false,
        });
      }

      await propertyLeaseDB.createX({
        clientId,
        propId,
        flatId: null,
        landlordId: landlord.id,
        startDate,
        endDate,
        notice,
        rent,
        security,
        rentDate,
        incrementType,
        incrementValue,
        incrementMonth,
        lockInPeriod,
        rentCollectionType,
        propType,
        ownership,
      });

      if (security > 0) {
        let expenseId = await expenseDB.create({
          type: 43, //Building Security
          amount: security,
          clientId,
          paidDate: null,
          paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
          paidBy: clientId,
          paidTo: landlord.id,
          paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
          description: `Building security to landlord`,
          paymentMethod: 0,
          repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
          noOfMonths: 0,
          dueDate: rentDate,
          isPaid: 0,
        });

        await setExpenseJournalTallyStatus(
          Number(clientId),
          Number(expenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );

        await expenseDB.addProperty({
          expenseId: expenseId,
          propId: Number(propId),
          clientId: clientId,
        });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${expenseId}], Unpaid Security Added`
        );
      }

      let recurringExpenseId = null;
      let recurrenceDate = moment().format("YYYY-MM-DD");

      if (Number(rentCollectionType) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
        recurrenceDate = moment().add(1, 'months').format("YYYY-MM-DD");
      } else {
        recurrenceDate = moment().add(1, 'months').format("YYYY-MM-DD");
      }

      if (Number(rentCollectionType) === CONSTANTS.RECURRING_EXPENCE_CYCLE_TYPE.MONTH_END) {
        recurringExpenseId = await recurringExpenseDB.createNew({
          type: 1,
          amount: rent,
          clientId,
          propId: Number(propId),
          paidDate: moment().format("YYYY-MM-DD"),
          paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
          paidBy: clientId,
          paidTo: landlord.id,
          paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
          description: `Monthly building rent paid to landlord`,
          paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
          noOfMonths: 1,
          dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
          recurrenceDate: recurrenceDate,
          expenceCycleType: Number(rentCollectionType),
          expenseCycle: 1,
          referenceId: null,
        });
      } else {
        recurringExpenseId = await recurringExpenseDB.createNew({
          type: 1, //Rent
          amount: rent,
          clientId,
          propId: Number(propId),
          paidDate: moment().format("YYYY-MM-DD"),
          paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
          paidBy: clientId,
          paidTo: landlord.id,
          paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
          description: `Monthly building rent paid to landlord`,
          paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
          noOfMonths: 1,
          dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
          recurrenceDate: recurrenceDate,
          expenceCycleType: Number(rentCollectionType),
          expenseCycle: 1,
          referenceId: null,
        });
      }

      await recurringExpenseDB.updateReferenceId({
        id: recurringExpenseId,
        referenceId: recurringExpenseId,
      });

      //Pallav Added
      if (rent > 0) {
        let rentExpenseId = await expenseDB.create({
          type: 1, //Building Rent
          amount: rent,
          clientId,
          paidDate: null,
          paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
          paidBy: clientId,
          paidTo: landlord.id,
          paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
          description: `Monthly building rent to landlord`,
          paymentMethod: 0,
          repetitionType: 2,
          noOfMonths: 0,
          dueDate: rentDate,
          isPaid: 0,
        });

        await setExpenseJournalTallyStatus(
          Number(clientId),
          Number(rentExpenseId),
          CONSTANTS.TALLY_STATUS.PENDING,
        );

        await expenseDB.updateRecurringExpenseId({
          id: rentExpenseId,
          recurringExpenseId: recurringExpenseId
        });

        await expenseDB.addProperty({
          expenseId: rentExpenseId,
          propId: Number(propId),
          clientId: clientId,
        });
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Expense Id [${rentExpenseId}], Unpaid Rent Added`
        );
      }
    }


    await logLeaseActivity(
      Number(req.userType),
      Number(req.id),
      Number(req.parentClientId),
      Number(req.platform),
      CONSTANTS.ACTIVITY_TYPES.ADD_PROPERTY_LEASE,
      landlordId,
      propId,
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Landlord Id [${landlordId}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Notice [${notice}], Rent Date [${rentDate}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Landlord Details Added Successfully`
    );

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

property.GetLeaseDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "GetLeaseDetails";

  try {
    let { propId, flatId } = req.query;
    const userType = req.userType;

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

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Flat Id [${flatId}]`
    );

    if (!propId) {
      log.info(`[${C}], [${F}], Prop Id [${propId}], Invalid Request`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Prop Id [${propId}], Staff Requesting....`
      );
    }

    let leaseDetails = await propertyLeaseDB.getByClientIdAndPropId({
      clientId,
      propId,
    });

    if (flatId) {
      leaseDetails = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
        clientId,
        propId,
        flatId,
      })
    }

    let docs: any[] | null = null;
    let landlordAccount: any[] | null = null;
    let landlordBanks: any[] | null = null;
    if (leaseDetails) {
      docs = flatId ? await landlordDocumentDB.getByClientIdAndLandlordIdAndPropIdAndFlatId({
        landlordId: leaseDetails.landlordId,
        clientId,
        propId,
        flatId
      }) : await landlordDocumentDB.getByClientIdAndLandlordIdAndPropId({
        landlordId: leaseDetails.landlordId,
        clientId,
        propId
      });

      if (docs && docs.length > 0) {
        for (let doc of docs) {
          const title = await getDocumentTitle(doc.type);
          doc.title = title;
        }
      }
      landlordAccount = await landlordAccountDB.getAccountDetailForPropId({
        clientId,
        landlordId: leaseDetails.landlordId,
        propId
      });

      landlordBanks = await landlordBankAccountsDB.getByClientIdAndLandlordId({
        clientId,
        landlordId: leaseDetails.landlordId,
      });
    }

    let noLeaseFlats = [];

    let isPropertyLease = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
      clientId,
      propId,
      flatId: null,
    });

    if (!isPropertyLease && Number(property.type) !== CONSTANTS.PROPERTY_TYPE.PG && !isPropertyLease) {
      noLeaseFlats = await propertyDB.getLeasePendingFlats({ clientId, propId });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Lease Details Fetched Successfully`
    );

    return res.status(200).json({
      msg: "Lease details fetched successfully",
      isSuccess: true,
      data: {
        leaseDetails: leaseDetails || false,
        docs: docs || [],
        landlordAccount,
        landlordBanks: landlordBanks || [],
        noLeaseFlats: noLeaseFlats || [],
      },
    });

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

property.EditLandlordDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "EditLandlordDetails";

  try {
    const { landlordId, propId, flatId, name, mobile, rent, security, startDate, endDate, notice, rentDate, incrementType = 0, incrementValue = 0, incrementMonth = null, lockInPeriod = null, rentCollectionType = 1, propType = 1, ownership = 1 } = req.body;

    const userType = req.userType;

    log.info(
      `[${C}], [${F}], LandlordId [${landlordId}], Prop Id [${propId}], Flat Id [${flatId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Rent Date [${rentDate}], Notice [${notice}], Rent Increment Type [${incrementType}], Rent Increment Value [${incrementValue}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Property Type [${propType}], Ownership [${ownership}]`
    );

    // let oldMobile = mobile;

    if (!landlordId) {
      log.info(`[${C}], [${F}], LandlordId Not Given`);

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

    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}], No Client Found`
      );

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


    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}]` : `Client Id [${clientId}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Prop Id [${propId}], Admin Requesting....`
      );
    }

    // let landlordNumDetail = await landlordDB.getByMobile({mobile: mobile});
    // //log.info(`Checking ${JSON.stringify(landlordNumDetail)}`);
    // if(landlordNumDetail && Number(landlordNumDetail?.id) != Number(landlordId)){
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Landlord Mobile [${mobile}], Number already registered with another landlord`
    //   );
    //   return res.status(400).json({
    //       msg: "Number already registered with other landlord",
    //       isSuccess: false,
    //     });
    // }
    let landlord = await landlordDB.getById({ id: landlordId });

    if (!landlord) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Landlord Id [${landlordId}], Landlord Name [${name}], Landlord Mobile [${mobile}], No Landlord Found,`
      );

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

    // let landlordVendorRecord = await vendorDB.getByMobileAndClientId({mobile: oldMobile, clientId});

    // if (!landlordVendorRecord) {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Landlord Name [${name}], Landlord Mobile [${oldMobile}], No Vendor Record Found, Creating New Vendor`
    //   );

    //   await vendorDB.create({
    //     mobile: mobile,
    //     name: name,
    //     clientId,
    //     type: CONSTANTS.VENDOR_TYPES.LANDLORD,
    //   });

    //   landlordVendorRecord = await vendorDB.getByMobileAndClientId({mobile: mobile, clientId});
    // } else {
    //   log.info(
    //     `[${C}], [${F}], Client Id [${clientId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Vendor Record Found, Updating Vendor Details`
    //   );

    //   await vendorDB.edit({
    //     id: landlordVendorRecord.id,
    //     mobile: mobile,
    //     name: name,
    //     clientId,
    //     type: CONSTANTS.VENDOR_TYPES.LANDLORD,
    //   });

    //   landlordVendorRecord = await vendorDB.getByMobileAndClientId({mobile: mobile, clientId});
    // }

    let propertyLease = await propertyLeaseDB.getByClientIdAndPropId({
      clientId,
      propId,
    });

    if (flatId) {
      propertyLease = await propertyLeaseDB.getByClientIdAndPropIdAndFlatId({
        clientId,
        propId,
        flatId,
      });
    }

    if (!propertyLease) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Property Does Not Lease Exists`
      );

      return res.status(400).json({
        msg: "Property lease does not exists",
        isSuccess: false,
      });
    }

    await propertyLeaseDB.edit({
      id: propertyLease.id,
      clientId,
      propId,
      flatId: flatId || null,
      landlordId,
      startDate,
      endDate,
      notice,
      rent,
      security,
      rentDate,
      incrementType,
      incrementValue,
      incrementMonth,
      lockInPeriod,
      rentCollectionType,
      propType,
      ownership,
    });

    let securityExpense = await expenseDB.getSecurityExpensesForLandlordByPropId({
      clientId,
      propId,
      limit: 1,
      pageNum: 1,
    });

    if (!securityExpense && security > 0 && Number(client.canPayLandlord) === 0) {
      let expenseId = await expenseDB.create({
        type: 43, //Building Security
        amount: security,
        clientId,
        paidDate: moment().format("YYYY-MM-DD HH:mm:ss"),
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlordId,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        description: `Building security paid to landlord`,
        paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
        noOfMonths: 0,
        dueDate: null,
        isPaid: 1,
      });

      await setExpenseJournalTallyStatus(
        Number(clientId),
        Number(expenseId),
        CONSTANTS.TALLY_STATUS.PENDING,
      );

      await expenseDB.addProperty({
        expenseId: expenseId,
        propId: Number(propId),
        flatId: Number(flatId) ? Number(flatId) : null,
        clientId: clientId,
      });
    } else if (securityExpense) {
      await expenseDB.update({
        type: securityExpense[0].type,
        amount: security,
        balance: security,
        paidDate: securityExpense[0].paidDate,
        paidByUserType: securityExpense[0].paidByUserType,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        paidBy: securityExpense[0].paidBy,
        paidTo: landlordId,
        description: securityExpense[0].description,
        paymentMethod: securityExpense[0].paymentMethod,
        id: securityExpense[0].id,
      });
    }

    const recurringRentExpense = await recurringExpenseDB.getByPropIdAndType({
      propId,
      type: 1 //Building Rent Expense
    });

    if (!recurringRentExpense) {
      let recurringExpenseId = await recurringExpenseDB.create({
        type: 1, //Building Rent Expense
        amount: rent,
        clientId,
        propId: Number(propId),
        flatId: Number(flatId) ? Number(flatId) : null,
        paidDate: moment().format("YYYY-MM-DD"),
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidBy: clientId,
        paidTo: landlordId,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        description: `Monthly building rent paid to landlord`,
        paymentMethod: CONSTANTS.TRANSACTION_MODES.OFFLINE,
        noOfMonths: 1,
        dueDate: moment().add(1, 'months').date(1).format("YYYY-MM-DD"),
        expenseCycle: 1,
        referenceId: null,
      });

      await recurringExpenseDB.updateReferenceId({
        id: recurringExpenseId,
        referenceId: recurringExpenseId,
      });
    } else {
      await recurringExpenseDB.update({
        amount: rent,
        paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
        paidToUserType: CONSTANTS.USER_TYPE.LANDLORD,
        paidBy: clientId,
        paidTo: landlordId,
        description: `Monthly building rent paid to landlord`,
        id: recurringRentExpense.id,
        noOfMonths: 1,
        dueDate: recurringRentExpense.dueDate,
        expenseCycle: 1,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Landlord Name [${name}], Landlord Mobile [${mobile}], Rent [${rent}], Security [${security}], Start Date [${startDate}], End Date [${endDate}], Notice [${notice}], Increment Month [${incrementMonth}], Lock In Period [${lockInPeriod}], Rent Collection Type [${rentCollectionType}], Property Type [${propType}], Ownership [${ownership}], Landlord Details Updated Successfully`
    );

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

property.UpdatePropertyFine = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "UpdatePropertyFine";

  try {
    const { propId, fine, fineType, updateTenants = 0, gracePeriod } = req.body;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Fine [${fine}], Fine Type [${fineType}], Enable for Tenants [${updateTenants}], Grace Period [${gracePeriod}]`
    );

    const userType = req.userType;

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

    if (!propId) {
      log.info(`[${C}], [${F}], Prop Id [${propId}], Invalid Request`);
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Prop Id [${propId}], Staff Requesting....`
      );
    }

    await propertyDB.updateFineDetails({
      id: propId,
      fine: fine,
      fineType: fineType,
      gracePeriod: gracePeriod,
    });

    if (Number(updateTenants) === 1) {
      await occupancyDB.updateTenantFineDetails({
        clientId,
        propId,
        fine: fine,
        fineType: fineType,
        gracePeriod: gracePeriod,
      });
    }

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Fine [${fine}], Fine Type [${fineType}], Update Tenant Fine [${updateTenants}], Grace Period [${gracePeriod}], Fine Details Updated Successfully`
    );

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

property.ToggleOnlinePayment = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "ToggleOnlinePayment";

  try {
    const { propId, isOnlinePaymentEnabled } = req.body;

    log.info(
      `[${C}], [${F}], Property Id [${propId}], Online Payment Enabled [${isOnlinePaymentEnabled}]`
    );

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Prop Id [${propId}], Staff Requesting....`
      );
    }

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

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

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

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

    if (Number(isOnlinePaymentEnabled) === 1) {
      let merchantKeyForClient = client?.payuKey;
      let merchantMidForClient = client?.payuMid;
      let merchantKeyForProp = property?.payuKey;
      let merchantMidForProp = property?.payuMid;
      let subMerchantIdForClient = null;
      let subMerchantIdForProp = null;
      let vendorId = null;
      if (Number(client?.paymentGateway) === Number(0)) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Payment Gateway [${client?.paymentGateway}], Payment Gateway Not Enabled`
        );

        return res.status(200).json({
          msg: "Please add payment gateway and bank credentials before enabling online payment",
          isSuccess: false,
        });
      } else if (Number(client?.paymentGateway) === CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ) {
        merchantKeyForClient = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.EASEBUZZ,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY
        });
        merchantMidForClient = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.EASEBUZZ,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.SALT
        });

        merchantKeyForProp = await clientConfigDB.getClientConfigByPropId({
          clientId,
          propId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.EASEBUZZ,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.KEY
        });
        merchantMidForProp = await clientConfigDB.getClientConfigByPropId({
          clientId,
          propId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.EASEBUZZ,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.SALT
        });

        subMerchantIdForClient = await clientConfigDB.getClientConfig({
          clientId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.EASEBUZZ,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.SUB_ACCOUNT_MID
        });
        subMerchantIdForProp = await clientConfigDB.getClientConfigByPropId({
          clientId,
          propId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.EASEBUZZ,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.SUB_ACCOUNT_MID
        });

      } else if (Number(client?.paymentGateway) === CONSTANTS.PAYMENT_GATEWAY.CASHFREE) {
        const propertyVendorId = await clientConfigDB.getClientConfigByPropId({
          clientId,
          propId,
          provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
          type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.VENDOR_ID,
        });

        if (propertyVendorId) {
          vendorId = propertyVendorId.value;
        } else {
          const clientVendorId = await clientConfigDB.getClientConfig({
            clientId,
            provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.CASHFREE,
            type: CONSTANTS.CLIENT_CONFIG_TYPE.CASHFREE.VENDOR_ID,
          });
          if (clientVendorId) {
            vendorId = clientVendorId.value;
          }
        }
      }
      // log.info(
      //     `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Payment Gateway [${client?.paymentGateway}], EaseBuzz Merchant Key For Prop [${merchantKeyForProp}], EaseBuzz Merchant Mid For Prop [${merchantMidForProp}], EaseBuzz Merchant Key For Client [${merchantKeyForClient}], EaseBuzz Merchant Mid For Client [${merchantMidForClient}], Sub Merchant Id For Prop [${subMerchantIdForProp}], Sub Merchant Id For Client [${subMerchantIdForClient}]`
      //   );

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

      if ((selfGateway && Number(selfGateway?.value) === CONSTANTS.CLIENT_CONFIG_VALUE.GATEWAY_OWNERSHIP.OWNED) || Number(client?.paymentGateway) === CONSTANTS.PAYMENT_GATEWAY.PAYU) {
        if ((!merchantKeyForProp && !merchantMidForProp) && (!merchantKeyForClient && !merchantMidForClient)) {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Payment Gateway Self Owned [${selfGateway?.value}], Payment Gateway [${client?.paymentGateway}], Payment Gateway Credentials Missing`
          );

          return res.status(200).json({
            msg: "Please add payment gateway and bank credentials before enabling online payment",
            isSuccess: false,
          });
        }
      }
      if (Number(client?.paymentGateway) === CONSTANTS.PAYMENT_GATEWAY.EASEBUZZ && !subMerchantIdForClient && !subMerchantIdForProp) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Payment Gateway Self Owned [${selfGateway?.value}], Payment Gateway [${client?.paymentGateway}], Payment Gateway Credentials Missing`
        );

        return res.status(200).json({
          msg: "Please add payment gateway and bank credentials before enabling online payment",
          isSuccess: false,
        });
      }

      if (Number(client?.paymentGateway) === CONSTANTS.PAYMENT_GATEWAY.CASHFREE && !vendorId) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Payment Gateway [${client?.paymentGateway}], Vendor Id [${vendorId}], Vendor Id Missing`
        );

        return res.status(200).json({
          msg: "Please add payment gateway and bank credentials before enabling online payment",
          isSuccess: false,
        });
      }
    }

    await propertyDB.updateIsOnlinePaymentEnabled({
      id: propId,
      isOnlinePaymentEnabled,
    });

    await occupancyDB.updateIsOnlinePaymentEnabledByPropId({
      propId,
      isOnlinePaymentEnabled,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Payment Gateway [${client?.paymentGateway}], Is Online Payment Enabled [${isOnlinePaymentEnabled}], Online Payment ${Number(isOnlinePaymentEnabled) === 0 ? "Disabled" : "Enabled"} Successfully`
    );

    return res.status(200).json({
      msg: `Online payment ${Number(isOnlinePaymentEnabled) === 0 ? "disabled" : "enabled"} successfully.`,
      isSuccess: true,
      isOnlinePaymentEnabled,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

property.ToggleFood = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "ToggleFood";

  try {
    const { propId, isFoodEnabled } = req.body;

    log.info(
      `[${C}], [${F}], Property Id [${propId}], Food Enabled [${isFoodEnabled}]`
    );

    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}]`
        }, Prop Id [${propId}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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 &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN
      ) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Not Allowed`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Prop Id [${propId}], Staff Requesting....`
      );
    }

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

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

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

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

    await propertyDB.updateIsFoodEnabled({
      id: propId,
      isFoodEnabled,
    });

    await occupancyDB.updateIsFoodOptedByPropId({
      propId,
      isFoodOpted: isFoodEnabled,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Food ${Number(isFoodEnabled) === 0 ? "Disabled" : "Enabled"} Successfully`
    );

    return res.status(200).json({
      msg: `Food ${Number(isFoodEnabled) === 0 ? "disabled" : "enabled"} successfully.`,
      isSuccess: true,
      isFoodEnabled,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

property.AddLocation = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "AddLocation";

  try {
    let { name, description } = req.body;
    name = name.trim();
    description = description.trim();
    log.info(`[${C}], [${F}], Name [${name}], Description [${description}]`);

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Staff Requesting....`
      );
    }

    const isExists = await locationDB.getByClientIdAndName({
      clientId,
      name,
    });

    if (isExists) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Location Name [${name}], Location Already Exsits With Same Name`);

      return res.status(400).json({
        msg: "Location already exsits with same name",
        isSuccess: false,
      });
    }

    await locationDB.addLocation({
      clientId,
      name,
      description,
    });

    const locations = await locationDB.getByClientId({ clientId });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Description [${description}], Location Added Successfully`
    );

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

property.EditLocation = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "EditLocation";

  try {
    let { locationId, name, description } = req.body;
    name = name.trim();
    description = description.trim();
    log.info(`[${C}], [${F}], Name [${name}], Location Id [${locationId}], Description [${description}]`);

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Staff Requesting....`
      );
    }

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

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

    const isExists = await locationDB.getByClientIdAndNameForEdit({
      clientId,
      name,
      id: locationId
    });

    if (isExists) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Name [${name}], Location Id [${locationId}], Location Already Exsits With Same Name`);

      return res.status(400).json({
        msg: "Location already exsits with same name",
        isSuccess: false,
      });
    }

    await locationDB.editLocation({
      id: locationId,
      name,
      description,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Name [${name}], Location Id [${locationId}], Description [${description}], Location Editted Successfully`
    );

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

property.DeleteLocation = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "DeleteLocation";

  try {
    let { locationId } = req.body;

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

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Staff Requesting....`
      );
    }

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

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

    const linkedProperties = await propertyDB.getAllByLocationId({
      clientId,
      locationId,
    });

    if (linkedProperties) {
      log.info(`[${C}], [${F}], Location Id [${locationId}], Properites Linked With The Location, Cannot Deleted`);

      return res.status(400).json({
        msg: "Unlink location from properties before deleting",
        isSuccess: false,
      });
    }

    await locationDB.deleteLocation({
      id: locationId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Location Id [${locationId}], Location Deleted Successfully`
    );

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

property.UpdateLocation = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "EditLocation";

  try {
    const { locationId, propId } = req.body;

    log.info(`[${C}], [${F}], Location Id [${locationId}], Property Id [${propId}]`);

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Staff Requesting....`
      );
    }

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

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

    await propertyDB.updateLocation({
      id: propId,
      locationId: locationId,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Location Id [${locationId}], Property Id [${propId}], Property Location Updated Successfully`
    );

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

property.ListLocations = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "ListLocations";

  try {
    const { pageNum = 1 } = req.query;
    const userType = req.userType;
    const platform = req.platform;
    const limit = 10;

    log.info(`[${C}], [${F}], PageNum [${pageNum}], Request Platform [${platform}]`);

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

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Role [${staff.role}] Staff Requesting....`
      );
    }

    let locations = [];

    if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
      locations = await locationDB.getByClientId({
        clientId,
      });
    } else {
      locations = await locationDB.getByClientIdAndPage({
        clientId,
        pageNum: Number(pageNum) || 1,
        limit: limit,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Location List Sent Successfully`
    );

    return res.status(200).json({
      msg: "Location list fetched successfully.",
      locations: locations || [],
      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,
    });
  }
};

property.UploadDocs = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "UploadDocs";

  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 {
    const { propId, title } = req.body;

    log.info(
      `[${C}], [${F}], Prop Id [${propId}], Document Title [${title}], Is File Uploaded [${file ? `Yes, File [${JSON.stringify(file)}]` : "No"}]`
    );

    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}], Staff Id [${req.id}], No Staff Found`);
        await removeTmpImages();
        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}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        await removeTmpImages();
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    if (!file) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], No files uploaded`
      );
      return res
        .status(400)
        .json({ msg: "No files uploaded", isSuccess: false });
    }

    const folderName = `property_${propId}`;
    const folderPath = `uploads/documents/client_${clientId}/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

    const oldFrontPath = `uploads/tmp/${file.filename}`;

    const ext = file.mimetype.split("/")[1];
    const fileName = `${title}_${moment().format("YYMMDDHHmmss")}.${ext}`;

    const newPath = `${folderPath}/${fileName}`;
    await fsPromises.copyFile(oldFrontPath, newPath);
    const url = `${urlBase}/${fileName}`;

    await propertyDocumentDB.addDoc({
      clientId: clientId,
      propId: propId || null,
      title: title || null,
      value: url,
    });

    const docs = await propertyDocumentDB.getByClientIdAndPropId({ clientId, propId });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Document uploaded sucessfully`
    );

    return res.status(200).json({
      msg: "Document uploaded sucessfully",
      isSuccess: true,
      docs: docs || [],
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    await removeTmpImages();

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

property.EditDetails = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "EditDetails";
  try {
    const { id, name, locationId, tenantPreference, security, type, source, rentalCycle, agreementPeriod, noticePeriod, lockInPeriod, address, pincode } = req.body;

    log.info(
      `[${C}], [${F}], Property Id [${id}], Name [${name}], Location Id [${locationId}], Tenant Gender Preference [${tenantPreference}], Security [${security}], Type [${type}], Property Source [${source}], Rental Cycle [${rentalCycle}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], Lock-In Period [${lockInPeriod}], Address [${address}], Pincode [${pincode}]`
    );

    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}], Staff Id [${req.id}], No Staff Found`);
        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.SALES_HEAD && staff.role !== CONSTANTS.STAFF_ROLES.SUPER_ADMIN) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

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

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

    await propertyDB.updateDetails({
      id,
      name: name || property.name,
      locationId: locationId || property.locationId,
      tenantPreference: tenantPreference || property.tenantPreference,
      security: security || property.security,
      type: type || property.type,
      source: source || property.source,
      rentalCycle: rentalCycle || property.rentalCycle,
      agreementPeriod: agreementPeriod || property.agreementPeriod,
      noticePeriod: noticePeriod || property.noticePeriod,
      lockInPeriod: lockInPeriod || property.lockInPeriod,
      streetAddress: address || property.streetAddress,
      pincode: pincode || property.pincode,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Id [${id}], Name [${name}], Location Id [${locationId}], Tenant Gender Preference [${tenantPreference}], Security [${security}], Type [${type}], Property Source [${source}], Rental Cycle [${rentalCycle}], Agreement Period [${agreementPeriod}], Notice Period [${noticePeriod}], Lock-In Period [${lockInPeriod}], Address [${address}], Pincode [${pincode}], Property Details Updated Successfully`
    );

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

property.ShareListing = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "ShareListing";

  try {
    const { propGId } = req.query;

    log.info(
      `[${C}], [${F}], Property GId [${propGId}]`
    );

    const property = await propertyDB.getByGId({
      gId: propGId,
    });

    const roomOptions = await roomOptionDB.getByPropIdForShare({
      propId: property.id,
    });

    const propertyImages = await propertyDB.getListingImagesById({
      propId: property.id,
    });

    if (propertyImages && propertyImages.length > 0) {
      for (let image of propertyImages) {
        image.imageUrl = `${process.env.EXTERNAL_IMAGE_VIEW_PATH}/${image.imageUrl}`;
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${property?.clientId}], Property Id [${propGId}], Property Details Fetched Successfully`
    );

    return res.status(200).json({
      msg: "Property details fetched successfully",
      isSuccess: true,
      property: property,
      roomOptions: roomOptions,
      propertyImages: propertyImages,
    });

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

property.UploadListingImages = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "UploadListingImages";

  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 {
    const { propId, type } = req.body;


    log.info(
      `[${C}], [${F}], Property Id [${propId}], Type [${type}], Is File Uploaded ${files ? `[Yes], ${files.length} Files` : "[No]"}`
    );

    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}], Staff Id [${req.id}], No Staff Found`);

        if (files) {
          await removeTmpImages();
        }

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

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

    if (!files || files.length === 0) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], No File Found`);
      return res
        .status(400)
        .json({ msg: "No file uploaded", isSuccess: false });
    }

    for (const file of files) {
      const lastPropImage = await propertyDB.getLastPropertyImage();
      let lastId = 1;
      if (lastPropImage) lastId = lastPropImage.id;

      const folderName = `${propId}`;
      const folderPath = `uploads/documents/${clientId}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `listingImage_${type}_${lastId + 1}.${fileExtension}`;

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

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      let url = `documents/${clientId}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

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

      await propertyDB.addListingImage({
        clientId: clientId,
        propId: propId,
        type: type,
        imageUrl: url,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Files Uploaded Successfully`
    );

    await removeTmpImages();

    return res.status(200).json({
      msg: "Property image uploaded successfully",
      isSuccess: true,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

    if (files) {
      await removeTmpImages();
    }

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

property.ToggleWebsiteVisibility = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "ToggleWebsiteVisibility";

  try {
    const { propId, isVisible } = req.body;

    log.info(
      `[${C}], [${F}], Property Id [${propId}], Is Visible [${isVisible}], Toggling Website Visibilty`
    );

    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}], Staff Id [${req.id}], No Staff Found`);

        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.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.FINANCE_ADMIN
      ) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Not Authorized`);
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false
        });
      }

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

    await propertyDB.toggleWebsiteVisibility({
      id: propId,
      isVisibleOnWebsite: isVisible,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Is Visible [${isVisible}], Property Visibilty Updated Successfully`
    );

    return res.status(200).json({
      msg: isVisible ? "Property published on website successfully" : "Property unpublished from website 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,
    });
  }
};

property.UpdateWifiPassword = async (req: CustomRequest, res: Response) => {
  const C = "Property Controller";
  const F = "UpdateWifiPassword";

  try {
    const { propId, wifiPassword, flatId } = req.body;

    log.info(
      `[${C}], [${F}], Property Id [${propId}], Wifi Password [${wifiPassword}], Flat Id [${flatId}],`
    );

    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}], Staff Id [${req.id}], No Staff Found`);

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

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

    if(flatId) {
      await flatDB.updateWifiPassword({
        id: flatId,
        wifiPassword: wifiPassword,
      });
    } else {
      await propertyDB.updateWifiPassword({
        id: propId,
        wifiPassword: wifiPassword,
      });

      await flatDB.updateWifiPasswordByPropId({
        propId: propId,
        wifiPassword: wifiPassword,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Property Id [${propId}], Flat Id [${flatId}], Wifi Password [${wifiPassword}], Wifi Password Updated Successfully`
    );

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

export default property;
