import prisma from "../config/database"; import { releaseSharedNumber } 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(({ customers, users, orders, ...p }) => ({ ...p, customer_name: customers?.name || null, responsible_user_name: users ? `${users.first_name} ${users.last_name}`.trim() : null, order_number: orders?.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: { select: { id: true, name: true } }, users: { select: { id: true, first_name: true, last_name: true } }, quotations: { select: { id: true, quotation_number: true } }, orders: { select: { id: true, order_number: true, status: true } }, project_notes: { orderBy: { created_at: "desc" } }, }, }); if (!project) return null; const { orders, quotations, customers, users, ...rest } = project; return { ...rest, customer_name: customers?.name ?? null, responsible_user_name: users ? `${users.first_name} ${users.last_name}` : null, order_number: orders?.order_number ?? null, order_status: orders?.status ?? null, quotation_number: quotations?.quotation_number ?? null, has_nas_folder: project.project_number ? nasFileManager.projectFolderExists(project.project_number) : false, }; } export async function updateProject(id: number, body: Record) { const existing = await prisma.projects.findUnique({ where: { id } }); if (!existing) return null; if ( body.project_number !== undefined && String(body.project_number) !== existing.project_number ) { return { error: "Číslo projektu nelze změnit", status: 400 }; } const data: Record = { modified_at: new Date() }; const strFields = ["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 != null ? Number(body.customer_id) : null; if (body.responsible_user_id !== undefined) data.responsible_user_id = body.responsible_user_id != null ? Number(body.responsible_user_id) : null; if (body.quotation_id !== undefined) data.quotation_id = body.quotation_id != null ? Number(body.quotation_id) : null; if (body.order_id !== undefined) data.order_id = body.order_id != null ? 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; const updated = await prisma.projects.update({ where: { id }, data }); if ( body.name !== undefined && existing.name !== body.name && existing.project_number && nasFileManager.isConfigured() ) { nasFileManager.renameProjectFolder( existing.project_number, String(body.name || ""), ); } return updated; } 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 } }); const year = existing.created_at ? new Date(existing.created_at).getFullYear() : new Date().getFullYear(); await releaseSharedNumber(year, existing.project_number ?? undefined); return existing; } export async function createProjectNote( projectId: number, data: { userId: number; firstName: string; lastName: string; content?: string; }, ) { const project = await prisma.projects.findUnique({ where: { id: projectId }, select: { id: true }, }); if (!project) { return { error: "Projekt nenalezen" as const, status: 404 }; } 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; }