Duffer Derek
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