// /components/contactInquiries/contactInquiryService.js const ContactInquiry = require('./contactInquiryModel'); const Course = require('../courses/courseModel'); const AppError = require('../../utils/AppError'); const { parsePaginationAndSort, buildFilterQuery, calculateMeta } = require('../../utils/pagination'); const normalizePhone = (value) => { if (!value) return undefined; return String(value).replace(/[\s\-()]/g, '').trim(); }; const createInquiry = async (data) => { const preferredContactMethods = Array.isArray(data.preferredContactMethods) ? [...new Set(data.preferredContactMethods)] : []; if (!preferredContactMethods.length) { throw new AppError('VALIDATION_FAILED', { preferredContactMethods: 'At least one preferred contact method is required' }, 'حداقل یک روش تماس را انتخاب کنید.'); } const phoneNumber = normalizePhone(data.phoneNumber || data.phone); const email = data.email ? String(data.email).trim().toLowerCase() : undefined; if (!phoneNumber && !email) { throw new AppError('VALIDATION_FAILED', { contact: 'Phone number or email is required' }, 'شماره تلفن یا ایمیل الزامی است.'); } const courseIds = Array.isArray(data.courses) ? [...new Set(data.courses.filter(Boolean).map(String))] : []; if (courseIds.length) { const foundCount = await Course.countDocuments({ _id: { $in: courseIds }, showOnFrontend: { $ne: false } }); if (foundCount !== courseIds.length) { throw new AppError('COURSE_NOT_FOUND', null, 'یکی از دوره‌های انتخاب‌شده یافت نشد.'); } } const inquiry = await ContactInquiry.create({ name: data.name || data.firstName, surname: data.surname || data.lastName, nationalIdCode: data.nationalIdCode || data.nationalId || undefined, phoneNumber, email, message: data.message || undefined, preferredContactMethods, courses: courseIds }); return inquiry.populate('courses', 'title type price'); }; const getAllInquiries = async (queryParams) => { const { page, limit, skip, sort } = parsePaginationAndSort(queryParams, 'createdAt', 'desc'); const filter = buildFilterQuery( queryParams, ['name', 'surname', 'nationalIdCode', 'phoneNumber', 'email', 'notes', 'message'], ['page', 'limit', 'sortBy', 'sortOrder', 'q', 'lang', 'course', 'courseId', 'courses'] ); const courseFilter = queryParams.course || queryParams.courseId || queryParams.courses; if (courseFilter) { const ids = String(courseFilter).split(',').map((id) => id.trim()).filter(Boolean); if (ids.length === 1) filter.courses = ids[0]; else if (ids.length > 1) filter.courses = { $in: ids }; } const [data, totalCount] = await Promise.all([ ContactInquiry.find(filter) .populate('courses', 'title type price') .sort(sort) .skip(skip) .limit(limit), ContactInquiry.countDocuments(filter) ]); return { data, meta: calculateMeta(totalCount, page, limit) }; }; const getInquiryById = async (id) => { const inquiry = await ContactInquiry.findById(id).populate('courses', 'title type price'); if (!inquiry) { throw new AppError('NOT_FOUND', null, 'درخواست تماس یافت نشد.'); } return inquiry; }; const updateInquiry = async (id, updateData) => { const inquiry = await ContactInquiry.findById(id); if (!inquiry) { throw new AppError('NOT_FOUND', null, 'درخواست تماس یافت نشد.'); } if (updateData.status !== undefined) { if (!ContactInquiry.CONTACT_STATUSES.includes(updateData.status)) { throw new AppError('VALIDATION_FAILED', { status: 'Invalid status' }, 'وضعیت نامعتبر است.'); } inquiry.status = updateData.status; } if (updateData.notes !== undefined) { inquiry.notes = String(updateData.notes).slice(0, 5000); } await inquiry.save(); return getInquiryById(id); }; module.exports = { createInquiry, getAllInquiries, getInquiryById, updateInquiry };