import prisma from '../config/database'; import { generateSharedNumber } from './numbering.service'; import { NasFileManager } from './nas-file-manager'; const nasFileManager = new NasFileManager(); const ALLOWED_SORT_FIELDS = ['id', 'project_number', 'name', 'status', 'created_at']; interface ListProjectsParams { page: number; limit: number; skip: number; sort: string; order: 'asc' | 'desc'; search: string; status?: string; customer_id?: number; } export async function listProjects(params: ListProjectsParams) { const { page, limit, skip, sort, order, search, status, customer_id } = params; const sortField = ALLOWED_SORT_FIELDS.includes(sort) ? sort : 'id'; const where: Record = {}; if (status) where.status = status; if (customer_id) where.customer_id = customer_id; if (search) where.OR = [{ name: { contains: search } }, { project_number: { contains: search } }]; const [projects, total] = await Promise.all([ prisma.projects.findMany({ where, skip, take: limit, orderBy: { [sortField]: order }, include: { customers: { select: { id: true, name: true } }, users: { select: { id: true, first_name: true, last_name: true } }, orders: { select: { order_number: true } }, }, }), prisma.projects.count({ where }), ]); const enriched = projects.map(p => ({ ...p, customer_name: p.customers?.name || null, responsible_user_name: p.users ? `${p.users.first_name} ${p.users.last_name}`.trim() : null, order_number: (p.orders as any)?.order_number || null, })); return { data: enriched, total, page, limit }; } export async function getProject(id: number) { const project = await prisma.projects.findUnique({ where: { id }, include: { customers: true, users: true, quotations: true, orders: true, project_notes: { orderBy: { created_at: 'desc' } } }, }); if (!project) return null; return { ...project, has_nas_folder: project.project_number ? nasFileManager.projectFolderExists(project.project_number) : false, }; } export async function createProject(body: Record) { const project = await prisma.projects.create({ data: { project_number: body.project_number ? String(body.project_number) : null, name: body.name ? String(body.name) : null, customer_id: body.customer_id ? Number(body.customer_id) : null, responsible_user_id: body.responsible_user_id ? Number(body.responsible_user_id) : null, quotation_id: body.quotation_id ? Number(body.quotation_id) : null, order_id: body.order_id ? Number(body.order_id) : null, status: body.status ? String(body.status) : 'aktivni', start_date: body.start_date ? new Date(String(body.start_date)) : null, end_date: body.end_date ? new Date(String(body.end_date)) : null, notes: body.notes ? String(body.notes) : null, }, }); if (project.project_number && nasFileManager.isConfigured()) { nasFileManager.createProjectFolder(project.project_number, project.name || ''); } return project; } export async function updateProject(id: number, body: Record) { const existing = await prisma.projects.findUnique({ where: { id } }); if (!existing) return null; const data: Record = { modified_at: new Date() }; const strFields = ['project_number', 'name', 'status', 'notes']; for (const f of strFields) if (body[f] !== undefined) data[f] = body[f] ? String(body[f]) : null; if (body.customer_id !== undefined) data.customer_id = body.customer_id ? Number(body.customer_id) : null; if (body.responsible_user_id !== undefined) data.responsible_user_id = body.responsible_user_id ? Number(body.responsible_user_id) : null; if (body.quotation_id !== undefined) data.quotation_id = body.quotation_id ? Number(body.quotation_id) : null; if (body.order_id !== undefined) data.order_id = body.order_id ? Number(body.order_id) : null; if (body.start_date !== undefined) data.start_date = body.start_date ? new Date(String(body.start_date)) : null; if (body.end_date !== undefined) data.end_date = body.end_date ? new Date(String(body.end_date)) : null; await prisma.projects.update({ where: { id }, data }); if (existing.name !== data.name && existing.project_number && nasFileManager.isConfigured()) { nasFileManager.renameProjectFolder(existing.project_number, String(data.name || '')); } return existing; } export async function deleteProject(id: number, deleteFiles: boolean = false) { const existing = await prisma.projects.findUnique({ where: { id } }); if (!existing) return { error: 'not_found' as const }; if (existing.order_id) return { error: 'has_order' as const }; if (deleteFiles && existing.project_number && nasFileManager.isConfigured()) { await nasFileManager.deleteProjectFolder(existing.project_number); } await prisma.projects.delete({ where: { id } }); return existing; } export async function createProjectNote(projectId: number, data: { userId: number; firstName: string; lastName: string; content?: string }) { const note = await prisma.project_notes.create({ data: { project_id: projectId, user_id: data.userId, user_name: `${data.firstName} ${data.lastName}`, content: data.content ? String(data.content) : null, }, }); return note; } export async function deleteProjectNote(projectId: number, noteId: number) { const note = await prisma.project_notes.findFirst({ where: { id: noteId, project_id: projectId } }); if (!note) return null; await prisma.project_notes.delete({ where: { id: noteId } }); return note; } export async function getNextProjectNumber() { return generateSharedNumber(); }