Duffer Derek

Current Path : /var/www/api-mk-planner.bitkit.dk/httpdocs/Backend/src/services/api/
Upload File :
Current File : /var/www/api-mk-planner.bitkit.dk/httpdocs/Backend/src/services/api/inquiry.js

import Podio from "@phasesdk/api-client-for-podio";
import axios from "axios";
import FormData from "form-data";
import fs from "fs";
import { dirname } from "path";
import { fileURLToPath } from "url";
import { appAuthentication } from "../podio/podio.js";
import { uploadFile } from "../fileUpload.js";
import inquiry from "../../database/models/inquiry.js";
import filesModel from "../../database/models/files.js";
import Log from "../../configs/logger.js";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const filePath = `${__dirname}/../../storage/public/files`;

/**
 * Convert ISO date string to Podio date format (YYYY-MM-DD)
 * @param {string} isoDateString - ISO 8601 date string (e.g., "2025-02-15T00:00:00Z")
 * @returns {string} - Formatted date string (e.g., "2025-02-15")
 */
const formatDateForPodio = (isoDateString) => {
  if (!isoDateString) return null;
  try {
    const date = new Date(isoDateString);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  } catch (error) {
    Log.error("Error formatting date for Podio:", error);
    return null;
  }
};

/**
 * Upload file to Podio using Inquiry app credentials
 * @param {string} filePath - Local file path
 * @param {string} fileName - File name
 * @returns {Promise<number>} Podio file_id
 */
const podioFileUpload = async (filePath, fileName) => {
  try {
    // Use INQUIRY_APP_ID and INQUIRY_APP_TOKEN for office partner file uploads
    // Files are uploaded to the inquiry app, not a separate file app
    if (!process.env.INQUIRY_APP_ID || !process.env.INQUIRY_APP_TOKEN) {
      Log.error("INQUIRY_APP_ID or INQUIRY_APP_TOKEN is not set");
      return 0;
    }
    
    Log.info(`Uploading file to Podio using INQUIRY_APP_ID: ${process.env.INQUIRY_APP_ID}`);
    const auth = await appAuthentication(
      process.env.INQUIRY_APP_ID,
      process.env.INQUIRY_APP_TOKEN
    );
    
    if (!auth || !auth.access_token) {
      Log.error("Failed to authenticate with Podio for file upload");
      return 0;
    }
    
    let data = new FormData();
    data.append("source", fs.createReadStream(filePath));
    data.append("filename", fileName);
    
    // Use default Podio API URL if not set in environment
    const podioApiUrl = process.env.PODIO_API_URL || "https://api.podio.com/";
    const fileUploadUrl = podioApiUrl.endsWith("/") 
      ? `${podioApiUrl}file/` 
      : `${podioApiUrl}/file/`;
    
    Log.info(`Uploading file to Podio URL: ${fileUploadUrl}`);
    
    const config = {
      method: "post",
      url: fileUploadUrl,
      headers: {
        Authorization: `Bearer ${auth.access_token}`,
        "Content-Type": "multipart/form-data",
        ...data.getHeaders(),
      },
      data: data,
    };
    try {
      let res = await axios(config);
      if (res.data && res.data.file_id) {
        Log.info(`File uploaded successfully to Podio, file_id: ${res.data.file_id}`);
        return res.data.file_id;
      } else {
        Log.error("Podio file upload response missing file_id:", res.data);
        return 0;
      }
    } catch (err) {
      Log.error("Error uploading file to Podio:", err);
      if (err.response) {
        Log.error("Podio API error response:", err.response.data);
      }
      return 0;
    }
  } catch (err) {
    Log.error("Error in podioFileUpload:", err);
    if (err.response) {
      Log.error("Podio API error response:", err.response.data);
    }
    return 0;
  }
};

/**
 * Upload inquiry files to server and Podio, then save to database
 * @param {Array} files - Array of file objects from multer
 * @param {number} item_id - Inquiry item_id
 * @returns {Promise<Array>} Array of Podio file_ids
 */
const uploadInquiryFiles = async (files, item_id) => {
  const podioFileIds = [];
  
  if (!files || files.length === 0) {
    Log.warn("uploadInquiryFiles: No files provided");
    return podioFileIds;
  }

  Log.info(`uploadInquiryFiles: Processing ${files.length} file(s) for item_id: ${item_id}`);

  for (const file of files) {
    try {
      Log.info(`Processing file: ${file.originalname}, size: ${file.size}, mimetype: ${file.mimetype}`);
      
      const lastDotIndex = file.originalname.lastIndexOf(".");
      const extension = lastDotIndex > 0 ? file.originalname.substring(lastDotIndex) : "";
      const randomString = Math.random().toString(36).substring(2, 10);
      const filenameWithoutExtension = lastDotIndex > 0 
        ? file.originalname.substring(0, lastDotIndex) 
        : file.originalname;
      const fileName = `${filenameWithoutExtension}_${randomString}${extension}`;
      const path = `${filePath}/${item_id}/${fileName}`;
      
      Log.info(`Saving file to server: ${path}`);
      // Save file to server
      await uploadFile(path, file);
      Log.info(`File saved to server successfully: ${fileName}`);
      
      // Upload to Podio
      Log.info(`Uploading file to Podio: ${fileName}`);
      const podioFileId = await podioFileUpload(path, fileName);
      if (podioFileId && podioFileId > 0) {
        Log.info(`File uploaded to Podio successfully: ${fileName}, file_id: ${podioFileId}`);
        
        // Save to database
        const insertFile = {
          item_id: item_id,
          keyword: "office_partner",
          filename: fileName, // Save only filename, not path
          file_id: podioFileId,
          field: "images-from-partners",
        };
        
        await filesModel.create(insertFile);
        Log.info("File saved to database successfully:", insertFile);
        
        podioFileIds.push(podioFileId);
      } else {
        Log.error(`Failed to upload file to Podio: ${fileName}, podioFileId: ${podioFileId}`);
      }
    } catch (error) {
      Log.error(`Error uploading inquiry file ${file.originalname}:`, error);
      Log.error("Error stack:", error.stack);
    }
  }
  
  Log.info(`uploadInquiryFiles: Completed. Uploaded ${podioFileIds.length} out of ${files.length} file(s)`);
  return podioFileIds;
};

/**
 * Update an inquiry in Podio and database
 * @param {Object} data - Inquiry data
 * @param {Object} req - Express request object (for file uploads)
 * @param {number} data.item_id - Inquiry item_id from Podio (required)
 * @param {string} data.first_name - First name
 * @param {string} data.last_name - Last name
 * @param {string} data.full_address - Full address
 * @param {Object|null} data.housing_type - Housing type object {id: number, value: string}
 * @param {string} data.phone_number - Phone number
 * @param {string} data.email - Email
 * @param {string} data.task_postal_code - Task postal code
 * @param {string} data.task_description - Task description
 * @param {string} data.task_start_preferred_date - Task start preferred date
 * @param {string} data.agreed_followup - Agreed followup date (ISO string)
 * @param {Object|null} data.followup_status - Followup status object {id: number, value: string}
 * @param {string} data.subcontractor_conversation_note - Subcontractor conversation note
 * @param {string} data.conversation_comment - Conversation comment
 * @param {Object|null} data.inspection_date - Inspection date object {start: string, end: string} (ISO strings)
 * @param {Object|null} data.status - Status object {id: number, value: string}
 * @param {Object|null} data.painterkanon_employee - Painterkanon employee object {id: number, value: string}
 * @param {Object|null} data.region - Region object {id: number, value: string}
 * @param {string} data.estimated_material_usage - Estimated material usage
 * @param {string} data.task_description_offer_pdf - Task description for offer PDF
 * @param {number} data.task_price_estimate - Task price estimate
 * @param {number} data.hourly_rate - Hourly rate
 * @param {number} data.estimated_hours - Estimated hours
 * @param {string} data.estimate_sent_date - Estimate sent date (ISO string)
 * @param {string} data.offer_sent_date - Offer sent date (ISO string)
 * @param {number} data.price_for_the_task - Price for the task
 * @param {string} data.estimated_days - Estimated days
 * @param {string} data.additional_services_text - Additional services text
 * @param {string} data.not_included_in_offer - Not included in offer
 * @param {string} data.free_of_charge - Free of charge
 * @param {string} data.task_description_offer2 - Task description for offer 2
 * @param {number} data.offer_price_2 - Offer price 2
 * @param {string} data.offer_sent_date_2 - Offer sent date 2 (ISO string)
 * @param {string} data.additional_info - Additional info
 * @param {Object|null} data.rooms_to_paint - Rooms to paint object {id: number, value: string}
 * @param {Object|null} data.partner - Partner object {id: number, value: string} or item_id
 * @param {string} data.deletion - JSON string of file IDs to delete
 * @returns {Promise<Object>} Updated inquiry
 */
export const updateInquiry = async (data, req) => {
  try {
    const {
      item_id,
      first_name,
      last_name,
      full_address,
      housing_type,
      phone_number,
      email,
      task_postal_code,
      task_description,
      task_start_preferred_date,
      agreed_followup,
      followup_status,
      subcontractor_conversation_note,
      conversation_comment,
      inspection_date,
      status,
      painterkanon_employee,
      region,
      estimated_material_usage,
      task_description_offer_pdf,
      task_price_estimate,
      hourly_rate,
      estimated_hours,
      estimate_sent_date,
      offer_sent_date,
      price_for_the_task,
      estimated_days,
      additional_services_text,
      not_included_in_offer,
      free_of_charge,
      task_description_offer2,
      offer_price_2,
      offer_sent_date_2,
      additional_info,
      rooms_to_paint,
      partner,
      add_product,
    } = data;

    // Validate required fields
    if (!item_id) {
      throw new Error("item_id is required");
    }

    // Check if inquiry exists
    const existingInquiry = await inquiry.findOne({
      where: { item_id: item_id },
    });

    if (!existingInquiry) {
      throw new Error(`Inquiry not found with item_id: ${item_id}`);
    }

    // Prepare Podio fields
    const podioFields = {};

    // Text fields
    if (first_name !== undefined) podioFields["titel"] = first_name;
    if (last_name !== undefined) podioFields["dit-efternavn"] = last_name;
    if (full_address !== undefined) podioFields["fulde-adresse"] = full_address;
    if (phone_number !== undefined) podioFields["dit-telefon-nr"] = phone_number;
    if (email !== undefined) podioFields["din-email"] = email;
    if (task_postal_code !== undefined) podioFields["by-hvor-opgaven-skal-udfores"] = task_postal_code;
    if (task_description !== undefined) podioFields["beskrivelse-af-opgaven"] = task_description;
    if (task_start_preferred_date !== undefined) podioFields["hvornar-onskes-opgaven-udfort"] = task_start_preferred_date;
    if (subcontractor_conversation_note !== undefined) podioFields["ue-kommentar-til-samtalen"] = subcontractor_conversation_note;
    if (conversation_comment !== undefined) podioFields["kontor-kommentar-til-samtalen"] = conversation_comment;
    if (estimated_material_usage !== undefined) podioFields["estimeret-materiale-forbrug"] = estimated_material_usage;
    if (task_description_offer_pdf !== undefined) podioFields["opgavebeskrivelse-til-faktura"] = task_description_offer_pdf;
    if (estimated_days !== undefined) podioFields["estimeret-tid-for-opgaven"] = estimated_days;
    if (additional_services_text !== undefined) podioFields["fritekst-til-ekstraydelser"] = additional_services_text;
    if (not_included_in_offer !== undefined) podioFields["ikke-inkluderet-i-tilbuddet"] = not_included_in_offer;
    if (free_of_charge !== undefined) podioFields["uden-beregning"] = free_of_charge;
    if (task_description_offer2 !== undefined) podioFields["opgavebeskrivelse-til-faktura-tilbud-2"] = task_description_offer2;
    if (additional_info !== undefined) podioFields["supplerende-info"] = additional_info;

    // Category fields (need ID for Podio)
    // Handle null values: if explicitly null, clear the field in Podio
    if (housing_type !== undefined) {
      if (housing_type === null || housing_type.id === null || housing_type.id === undefined) {
        podioFields["boligtype-2"] = []; // Clear the field
      } else if (housing_type.id !== undefined) {
        podioFields["boligtype-2"] = [housing_type.id];
      }
    }
    if (followup_status !== undefined) {
      if (followup_status === null || followup_status.id === null || followup_status.id === undefined) {
        podioFields["status-pa-opfolgning"] = []; // Clear the field
      } else if (followup_status.id !== undefined) {
        podioFields["status-pa-opfolgning"] = [followup_status.id];
      }
    }
    // Status field is required - do not clear it, only update if a valid value is provided
    if (status !== undefined && status !== null) {
      if (status.id !== undefined) {
        podioFields["status"] = [status.id];
      }
    }
    // Note: Status field is not cleared when null - it's a required field
    if (painterkanon_employee !== undefined) {
      if (painterkanon_employee === null || painterkanon_employee.id === null || painterkanon_employee.id === undefined) {
        podioFields["gulvkanon-medarbejder"] = []; // Clear the field
      } else if (painterkanon_employee.id !== undefined) {
        podioFields["gulvkanon-medarbejder"] = [painterkanon_employee.id];
      }
    }
    if (region !== undefined) {
      if (region === null || region.id === null || region.id === undefined) {
        podioFields["landsdel"] = []; // Clear the field
      } else if (region.id !== undefined) {
        podioFields["landsdel"] = [region.id];
      }
    }
    if (rooms_to_paint !== undefined) {
      if (rooms_to_paint === null || rooms_to_paint.id === null || rooms_to_paint.id === undefined) {
        podioFields["antal-rum-der-skal-slibes"] = []; // Clear the field
      } else if (rooms_to_paint.id !== undefined) {
        podioFields["antal-rum-der-skal-slibes"] = [rooms_to_paint.id];
      }
    }

    // Date fields - convert ISO format to Podio format (YYYY-MM-DD HH:MM:SS)
    if (agreed_followup !== undefined) {
      podioFields["aftalt-opfolgning"] = agreed_followup 
        ? { start_date: formatDateForPodio(agreed_followup) } 
        : null;
    }
    if (estimate_sent_date !== undefined) {
      podioFields["prisoverslag-sendt-dato"] = estimate_sent_date 
        ? { start_date: formatDateForPodio(estimate_sent_date) } 
        : null;
    }
    if (offer_sent_date !== undefined) {
      podioFields["tilbud-sendtafgivet"] = offer_sent_date 
        ? { start_date: formatDateForPodio(offer_sent_date) } 
        : null;
    }
    if (offer_sent_date_2 !== undefined) {
      podioFields["tilbud-sendtafgivet-dato-tilbud-2"] = offer_sent_date_2 
        ? { start_date: formatDateForPodio(offer_sent_date_2) } 
        : null;
    }
    if (inspection_date !== undefined) {
      if (inspection_date && (inspection_date.start || inspection_date.end)) {
        podioFields["besigtigelsesdato"] = {
          start_date: inspection_date.start ? formatDateForPodio(inspection_date.start) : null,
          end_date: inspection_date.end ? formatDateForPodio(inspection_date.end) : null,
        };
      } else {
        podioFields["besigtigelsesdato"] = null;
      }
    }

    // Number fields
    if (task_price_estimate !== undefined) podioFields["pris-for-opgaven-inkl-moms-prisoverslag"] = task_price_estimate;
    if (hourly_rate !== undefined) podioFields["timepris"] = hourly_rate;
    if (estimated_hours !== undefined) podioFields["estimeret-timeforbrug"] = estimated_hours;
    if (price_for_the_task !== undefined) podioFields["pris-for-opgaven-inkl-moms"] = price_for_the_task;
    if (offer_price_2 !== undefined) podioFields["pris-for-opgaven-inkl-moms-tilbud-2"] = offer_price_2;

    // App reference field (partner)
    if (partner !== undefined) {
      if (partner?.item_id) {
        podioFields["partner"] = [partner.item_id];
      } else if (partner?.id) {
        // If only id is provided, assume it's the item_id
        podioFields["partner"] = [partner.id];
      } else if (partner === null || partner === '') {
        // Send empty array to Podio to clear the field
        podioFields["partner"] = [];
      }
    }

    // App reference field (add_product) - Product app reference
    // Podio external ID: "tilfoj-produkt-vaelg-et-produkt-ad-gangen"
    if (add_product !== undefined) {
      if (add_product && add_product !== null && add_product !== '') {
        // add_product can be a number (item_id) or an object with item_id
        const productItemId = typeof add_product === 'object' && add_product.item_id 
          ? add_product.item_id 
          : (typeof add_product === 'number' ? add_product : parseInt(add_product, 10));
        
        if (!isNaN(productItemId) && productItemId > 0) {
          podioFields["tilfoj-produkt-vaelg-et-produkt-ad-gangen"] = [productItemId];
        } else {
          // Send empty array to Podio to clear the field
          podioFields["tilfoj-produkt-vaelg-et-produkt-ad-gangen"] = [];
        }
      } else {
        // Send empty array to Podio to clear the field
        podioFields["tilfoj-produkt-vaelg-et-produkt-ad-gangen"] = [];
      }
    }

    // Remove null values from Podio fields (but keep empty arrays for clearing app reference fields)
    Object.keys(podioFields).forEach((key) => {
      if (podioFields[key] === null || podioFields[key] === undefined) {
        delete podioFields[key];
      }
    });

    // Authenticate with Podio
    const auth = await appAuthentication(
      process.env.INQUIRY_APP_ID,
      process.env.INQUIRY_APP_TOKEN
    );

    // Handle file uploads and deletions for images-from-partners field
    // Check if we have files to upload or deletions to process
    // Multer stores files in req.files when using upload.array("file")
    const hasFiles = req && req.files && (
      (Array.isArray(req.files) && req.files.length > 0) ||
      (req.files && typeof req.files === 'object' && Object.keys(req.files).length > 0)
    );
    const hasDeletion = data.deletion && (
      (typeof data.deletion === 'string' && data.deletion.trim() !== '' && data.deletion !== 'null' && data.deletion !== 'undefined') || 
      (Array.isArray(data.deletion) && data.deletion.length > 0)
    );
    
    Log.info("=== File upload check ===");
    Log.info("req exists:", !!req);
    Log.info("req.files:", req?.files);
    Log.info("req.files type:", Array.isArray(req.files) ? 'array' : typeof req.files);
    Log.info("req.files length:", Array.isArray(req.files) ? req.files.length : (req.files ? Object.keys(req.files).length : 0));
    Log.info("req.files is array:", Array.isArray(req.files));
    Log.info("hasFiles:", hasFiles);
    Log.info("data.deletion:", data.deletion);
    Log.info("data.deletion type:", typeof data.deletion);
    Log.info("hasDeletion:", hasDeletion);
    
    // Get files array - handle both array and object formats
    let filesArray = [];
    if (req && req.files) {
      if (Array.isArray(req.files)) {
        filesArray = req.files;
      } else if (typeof req.files === 'object') {
        // If it's an object, convert to array
        filesArray = Object.values(req.files).flat();
      }
    }
    Log.info("filesArray length:", filesArray.length);
    
    // Always process if req exists and has files, or if deletion is provided
    if (req && (filesArray.length > 0 || hasDeletion)) {
      Log.info("=== Starting image field update ===");
      
      // Get existing file_ids from database
      const existingFiles = await filesModel.findAll({
        where: {
          item_id: item_id,
          field: "images-from-partners",
          keyword: "office_partner",
        },
      });
      const existingFileIds = existingFiles.map((f) => f.file_id);
      Log.info("Existing file_ids from database:", existingFileIds);

      // Parse deletion array (can be string or array)
      let deletionIds = [];
      if (hasDeletion) {
        try {
          deletionIds = typeof data.deletion === 'string' 
            ? JSON.parse(data.deletion) 
            : data.deletion;
          if (!Array.isArray(deletionIds)) {
            deletionIds = [];
          }
        } catch (error) {
          Log.error("Error parsing deletion array:", error);
          deletionIds = [];
        }
      }
      Log.info("File IDs to delete:", deletionIds);

      // Upload new files
      let newFileIds = [];
      if (filesArray.length > 0) {
        Log.info(`Files received for upload: ${filesArray.length}`);
        Log.info("File details:", filesArray.map(f => ({ 
          name: f.originalname, 
          size: f.size, 
          mimetype: f.mimetype,
          fieldname: f.fieldname,
          buffer: f.buffer ? `Buffer(${f.buffer.length} bytes)` : 'no buffer'
        })));
        newFileIds = await uploadInquiryFiles(filesArray, item_id);
        Log.info("New file_ids from upload:", newFileIds);
      } else {
        Log.info("No files to upload - filesArray is empty");
      }

      // Calculate final file_ids: existing - deleted + new
      const finalFileIds = [
        ...existingFileIds.filter((id) => !deletionIds.includes(id)),
        ...newFileIds,
      ];
      Log.info("Final file_ids for Podio:", finalFileIds);

      // Delete files from database and server
      if (deletionIds.length > 0) {
        for (const fileId of deletionIds) {
          try {
            const fileToDelete = await filesModel.findOne({
              where: {
                item_id: item_id,
                file_id: fileId,
                field: "images-from-partners",
              },
            });
            if (fileToDelete) {
              // Delete from server
              const serverPath = `${filePath}${fileToDelete.filename}`;
              if (fs.existsSync(serverPath)) {
                fs.unlinkSync(serverPath);
                Log.info("File deleted from server:", serverPath);
              }
              // Delete from database
              await filesModel.destroy({
                where: {
                  id: fileToDelete.id,
                },
              });
              Log.info("File deleted from database:", fileId);
            }
          } catch (error) {
            Log.error(`Error deleting file ${fileId}:`, error);
          }
        }
      }

      // Update Podio with final file_ids
      // Always update if we processed files or deletions, even if finalFileIds is empty (to clear the field)
      if (newFileIds.length > 0 || deletionIds.length > 0 || filesArray.length > 0 || hasDeletion) {
        podioFields["images-from-partners"] = finalFileIds;
        Log.info("Updating Podio with file_ids:", finalFileIds);
      } else {
        Log.info("No file updates needed - skipping Podio file field update");
      }
    } else {
      Log.info("=== Skipping image field update ===");
      Log.info("Reason: No files to upload and no deletions to process");
      if (!req) Log.info("- req is not provided");
      if (req) {
        Log.info("- req.files:", req.files);
        Log.info("- req.files type:", typeof req.files);
        Log.info("- req.files is array:", Array.isArray(req.files));
        if (req.files) {
          Log.info("- req.files length:", Array.isArray(req.files) ? req.files.length : Object.keys(req.files).length);
        }
      }
      if (!hasDeletion) {
        Log.info("- No deletion data provided");
      }
    }

    // Update in Podio
    await Podio.api.item(auth).update(item_id, {
      fields: podioFields,
    });
    Log.info("Inquiry updated in Podio:", item_id);

    // Prepare database record (store values, not IDs for category fields)
    const dbRecord = {};

    // Text fields
    if (first_name !== undefined) dbRecord.first_name = first_name;
    if (last_name !== undefined) dbRecord.last_name = last_name;
    if (full_address !== undefined) dbRecord.full_address = full_address;
    if (phone_number !== undefined) dbRecord.phone_number = phone_number;
    if (email !== undefined) dbRecord.email = email;
    if (task_postal_code !== undefined) dbRecord.task_postal_code = task_postal_code;
    if (task_description !== undefined) dbRecord.task_description = task_description;
    if (task_start_preferred_date !== undefined) dbRecord.task_start_preferred_date = task_start_preferred_date;
    if (subcontractor_conversation_note !== undefined) dbRecord.subcontractor_conversation_note = subcontractor_conversation_note;
    if (conversation_comment !== undefined) dbRecord.conversation_comment = conversation_comment;
    if (estimated_material_usage !== undefined) dbRecord.estimated_material_usage = estimated_material_usage;
    if (task_description_offer_pdf !== undefined) dbRecord.task_description_offer_pdf = task_description_offer_pdf;
    if (estimated_days !== undefined) dbRecord.estimated_days = estimated_days;
    if (additional_services_text !== undefined) dbRecord.additional_services_text = additional_services_text;
    if (not_included_in_offer !== undefined) dbRecord.not_included_in_offer = not_included_in_offer;
    if (free_of_charge !== undefined) dbRecord.free_of_charge = free_of_charge;
    if (task_description_offer2 !== undefined) dbRecord.task_description_offer2 = task_description_offer2;
    if (additional_info !== undefined) dbRecord.additional_info = additional_info;

    // Category fields - store value in database
    // Handle null values: if explicitly null, set to null in database to clear the field
    if (housing_type !== undefined) {
      if (housing_type === null || housing_type.value === null || housing_type.value === '' || housing_type.value === undefined) {
        dbRecord.housing_type = null; // Clear the field
      } else if (housing_type.value !== undefined) {
        dbRecord.housing_type = housing_type.value;
      }
    }
    if (followup_status !== undefined) {
      if (followup_status === null || followup_status.value === null || followup_status.value === '' || followup_status.value === undefined) {
        dbRecord.followup_status = null; // Clear the field
      } else if (followup_status.value !== undefined) {
        dbRecord.followup_status = followup_status.value;
      }
    }
    // Support both name and value for backward compatibility
    // Status field: check name first (new format), then value (old format)
    // Status is required - do not clear it, only update if a valid value is provided
    if (status !== undefined && status !== null) {
      if (status.name !== undefined && status.name !== null && status.name !== '') {
        dbRecord.status = status.name;
        Log.info(`Status updated with name: ${status.name}`);
      } else if (status.value !== undefined && status.value !== null && status.value !== '') {
        dbRecord.status = status.value;
        Log.info(`Status updated with value: ${status.value}`);
      } else if (status.id !== undefined) {
        Log.warn(`Status has id (${status.id}) but no name or value. Status update skipped.`);
      }
    }
    // Note: Status field is not cleared when null - it's a required field
    if (painterkanon_employee !== undefined) {
      if (painterkanon_employee === null || painterkanon_employee.value === null || painterkanon_employee.value === '' || painterkanon_employee.value === undefined) {
        dbRecord.painterkanon_employee = null; // Clear the field
      } else if (painterkanon_employee.value !== undefined) {
        dbRecord.painterkanon_employee = painterkanon_employee.value;
      }
    }
    if (region !== undefined) {
      if (region === null || region.value === null || region.value === '' || region.value === undefined) {
        dbRecord.region = null; // Clear the field
      } else if (region.value !== undefined) {
        dbRecord.region = region.value;
      }
    }
    if (rooms_to_paint !== undefined) {
      if (rooms_to_paint === null || rooms_to_paint.value === null || rooms_to_paint.value === '' || rooms_to_paint.value === undefined) {
        dbRecord.rooms_to_paint = null; // Clear the field
      } else if (rooms_to_paint.value !== undefined) {
        dbRecord.rooms_to_paint = rooms_to_paint.value;
      }
    }

    // Date fields
    if (agreed_followup !== undefined) dbRecord.agreed_followup = agreed_followup ? new Date(agreed_followup) : null;
    if (estimate_sent_date !== undefined) dbRecord.estimate_sent_date = estimate_sent_date ? new Date(estimate_sent_date) : null;
    if (offer_sent_date !== undefined) dbRecord.offer_sent_date = offer_sent_date ? new Date(offer_sent_date) : null;
    if (offer_sent_date_2 !== undefined) dbRecord.offer_sent_date_2 = offer_sent_date_2 ? new Date(offer_sent_date_2) : null;
    if (inspection_date !== undefined) {
      if (inspection_date && inspection_date.start) {
        dbRecord.inspection_date_start = new Date(inspection_date.start);
      } else {
        dbRecord.inspection_date_start = null;
      }
      if (inspection_date && inspection_date.end) {
        dbRecord.inspection_date_end = new Date(inspection_date.end);
      } else {
        dbRecord.inspection_date_end = null;
      }
    }

    // Number fields
    if (task_price_estimate !== undefined) dbRecord.task_price_estimate = task_price_estimate ? parseFloat(task_price_estimate) : null;
    if (hourly_rate !== undefined) dbRecord.hourly_rate = hourly_rate ? parseFloat(hourly_rate) : null;
    if (estimated_hours !== undefined) dbRecord.estimated_hours = estimated_hours ? parseFloat(estimated_hours) : null;
    if (price_for_the_task !== undefined) dbRecord.price_for_the_task = price_for_the_task ? parseFloat(price_for_the_task) : null;
    if (offer_price_2 !== undefined) dbRecord.offer_price_2 = offer_price_2 ? parseFloat(offer_price_2) : null;

    // Partner field - store item_id
    if (partner !== undefined) {
      if (partner?.item_id) {
        dbRecord.partner = partner.item_id;
      } else if (partner?.id) {
        dbRecord.partner = partner.id;
      } else if (partner === null) {
        dbRecord.partner = null;
      }
    }

    // Add product field - store item_id
    if (add_product !== undefined) {
      if (add_product && add_product !== null && add_product !== '') {
        // add_product can be a number (item_id) or an object with item_id
        const productItemId = typeof add_product === 'object' && add_product.item_id 
          ? add_product.item_id 
          : (typeof add_product === 'number' ? add_product : parseInt(add_product, 10));
        
        dbRecord.add_product = (!isNaN(productItemId) && productItemId > 0) ? productItemId : null;
      } else {
        dbRecord.add_product = null;
      }
    }

    // Update in database
    await inquiry.update(dbRecord, {
      where: { item_id: item_id },
    });
    Log.info("Inquiry updated in database:", item_id);

    // Fetch and return updated inquiry
    const updatedInquiry = await inquiry.findOne({
      where: { item_id: item_id },
    });

    return updatedInquiry;
  } catch (error) {
    Log.error("Error updating inquiry:", error);
    throw error;
  }
};


Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists