import type { RowDataPacket, ResultSetHeader } from "mysql2"; import { db } from "@/src/lib/db"; import type { Macchina, MacchinaWithDetails, MacchinaCreateInput, MacchinaUpdateInput, TipoMacchina, } from "@/src/types/macchina"; type MacchinaRow = Macchina & RowDataPacket; type MacchinaWithDetailsRow = MacchinaWithDetails & RowDataPacket; export async function listMacchine(): Promise { const [rows] = await db.execute(` SELECT m.id, m.nome, m.tipo, m.id_padre, m.id_stabilimento, m.colore_testo, s.descrizione AS stabilimento_nome, p.nome AS padre_nome FROM macchine_parti m LEFT JOIN stabilimenti s ON s.id = m.id_stabilimento LEFT JOIN macchine_parti p ON p.id = m.id_padre ORDER BY ISNULL(m.id_padre) DESC, m.id_padre ASC, m.nome ASC `); return rows.map((row) => ({ id: row.id, nome: row.nome, tipo: row.tipo as TipoMacchina, id_padre: row.id_padre, id_stabilimento: row.id_stabilimento, colore_testo: row.colore_testo || "#000000", stabilimento_nome: row.stabilimento_nome, padre_nome: row.padre_nome, })); } export async function getMacchinaById(id: number): Promise { const [rows] = await db.execute( `SELECT m.id, m.nome, m.tipo, m.id_padre, m.id_stabilimento, m.colore_testo, s.descrizione AS stabilimento_nome, p.nome AS padre_nome FROM macchine_parti m LEFT JOIN stabilimenti s ON s.id = m.id_stabilimento LEFT JOIN macchine_parti p ON p.id = m.id_padre WHERE m.id = :id LIMIT 1`, { id } ); const row = rows[0]; if (!row) return null; return { id: row.id, nome: row.nome, tipo: row.tipo as TipoMacchina, id_padre: row.id_padre, id_stabilimento: row.id_stabilimento, colore_testo: row.colore_testo || "#000000", stabilimento_nome: row.stabilimento_nome, padre_nome: row.padre_nome, }; } export async function createMacchina(input: MacchinaCreateInput): Promise { const { nome, tipo, id_padre, id_stabilimento, colore_testo = "#000000" } = input; let stabilimentoId = id_stabilimento; if (tipo === "macchina" && !id_padre && !stabilimentoId) { throw new Error("Stabilimento obbligatorio per macchine di livello root"); } if (id_padre && !stabilimentoId) { const [parentRows] = await db.execute( "SELECT id_stabilimento FROM macchine_parti WHERE id = :id_padre LIMIT 1", { id_padre } ); const parent = parentRows[0]; if (parent?.id_stabilimento) { stabilimentoId = parent.id_stabilimento; } } const [result] = await db.execute( `INSERT INTO macchine_parti (nome, tipo, id_padre, id_stabilimento, colore_testo) VALUES (:nome, :tipo, :id_padre, :id_stabilimento, :colore_testo)`, { nome, tipo, id_padre: id_padre || null, id_stabilimento: stabilimentoId || null, colore_testo, } ); return { id: result.insertId, nome, tipo, id_padre: id_padre || null, id_stabilimento: stabilimentoId || null, colore_testo, }; } export async function updateMacchina( id: number, input: MacchinaUpdateInput ): Promise { const current = await getMacchinaById(id); if (!current) return null; const nome = input.nome ?? current.nome; const tipo = input.tipo ?? current.tipo; const id_padre = input.id_padre !== undefined ? input.id_padre : current.id_padre; const colore_testo = input.colore_testo ?? current.colore_testo; let id_stabilimento = input.id_stabilimento !== undefined ? input.id_stabilimento : current.id_stabilimento; if (id_padre && !id_stabilimento) { const [parentRows] = await db.execute( "SELECT id_stabilimento FROM macchine_parti WHERE id = :id_padre LIMIT 1", { id_padre } ); const parent = parentRows[0]; if (parent?.id_stabilimento) { id_stabilimento = parent.id_stabilimento; } } await db.execute( `UPDATE macchine_parti SET nome = :nome, tipo = :tipo, id_padre = :id_padre, id_stabilimento = :id_stabilimento, colore_testo = :colore_testo WHERE id = :id`, { id, nome, tipo, id_padre: id_padre || null, id_stabilimento: id_stabilimento || null, colore_testo, } ); return getMacchinaById(id); } export async function deleteMacchina( id: number, idMagazzinoRientro?: number, user: string = "system" ): Promise { // Recupera gli articoli assegnati a questa macchina const [articoliRows] = await db.execute<(RowDataPacket & { idart: number; qtain: number; qtaout: number; descrizione: string; codice: string; })[]>( `SELECT idart, qtain, qtaout, descrizione, codice FROM art_macchine_parti WHERE idmacchina = :id AND (qtain - qtaout) > 0`, { id } ); // Se c'è un magazzino di rientro, fai il carico degli articoli if (idMagazzinoRientro && articoliRows.length > 0) { for (const art of articoliRows) { const quantita = art.qtain - art.qtaout; if (quantita <= 0) continue; // Verifica se l'articolo esiste ancora nel catalogo const [existsRows] = await db.execute<(RowDataPacket & { id: number })[]>( "SELECT id FROM articoli WHERE id = :idart LIMIT 1", { idart: art.idart } ); if (existsRows.length > 0) { // L'articolo esiste, registra il movimento di carico await db.execute( `INSERT INTO movimenti (id_articolo, articolo_codice, articolo_descrizione, quantita, tipo_movimento, causale, utente, id_magazzino_destinazione) VALUES (:id_articolo, :articolo_codice, :articolo_descrizione, :quantita, 1, :causale, :utente, :id_magazzino_destinazione)`, { id_articolo: art.idart, articolo_codice: art.codice, articolo_descrizione: art.descrizione, quantita, causale: "Rientro da eliminazione macchina", utente: user, id_magazzino_destinazione: idMagazzinoRientro, } ); // Aggiorna la giacenza const [existingGiacenza] = await db.execute<(RowDataPacket & { id: number })[]>( "SELECT id FROM giacenze WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId", { articoloId: art.idart, magazzinoId: idMagazzinoRientro } ); if (existingGiacenza.length > 0) { await db.execute( "UPDATE giacenze SET quantita = quantita + :quantita WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId", { quantita, articoloId: art.idart, magazzinoId: idMagazzinoRientro } ); } else { await db.execute( "INSERT INTO giacenze (articolo_id, id_magazzino, quantita) VALUES (:articoloId, :magazzinoId, :quantita)", { articoloId: art.idart, magazzinoId: idMagazzinoRientro, quantita } ); } } // Se l'articolo non esiste più, non facciamo nulla (gli articoli vengono persi) } } // Elimina gli articoli assegnati a questa macchina await db.execute( "DELETE FROM art_macchine_parti WHERE idmacchina = :id", { id } ); // Elimina ricorsivamente i figli const [childRows] = await db.execute( "SELECT id FROM macchine_parti WHERE id_padre = :id", { id } ); if (childRows.length > 0) { for (const child of childRows) { await deleteMacchina(child.id, idMagazzinoRientro, user); } } const [result] = await db.execute( "DELETE FROM macchine_parti WHERE id = :id", { id } ); return result.affectedRows > 0; } export async function cloneMacchina( id: number, newParentId: number | null = null ): Promise { const original = await getMacchinaById(id); if (!original) return null; let copyName = `${original.nome} (copia)`; let counter = 1; const [existingRows] = await db.execute( "SELECT nome FROM macchine_parti WHERE nome LIKE :pattern", { pattern: `${original.nome} (copia%` } ); const existingNames = new Set(existingRows.map((r) => r.nome)); while (existingNames.has(copyName)) { copyName = `${original.nome} (copia ${counter})`; counter++; } let stabilimentoId = original.id_stabilimento; if (newParentId) { const [parentRows] = await db.execute( "SELECT id_stabilimento FROM macchine_parti WHERE id = :id LIMIT 1", { id: newParentId } ); if (parentRows[0]?.id_stabilimento) { stabilimentoId = parentRows[0].id_stabilimento; } } const newMacchina = await createMacchina({ nome: copyName, tipo: original.tipo, id_padre: newParentId ?? original.id_padre, id_stabilimento: stabilimentoId, colore_testo: original.colore_testo, }); const [children] = await db.execute( "SELECT id FROM macchine_parti WHERE id_padre = :id", { id } ); for (const child of children) { await cloneMacchina(child.id, newMacchina.id); } return newMacchina; } export async function getChildrenCount(id: number): Promise { const [rows] = await db.execute<(RowDataPacket & { count: number })[]>( "SELECT COUNT(*) as count FROM macchine_parti WHERE id_padre = :id", { id } ); return rows[0]?.count ?? 0; } export async function getAllDescendantIds(id: number): Promise { const ids: number[] = []; const [children] = await db.execute( "SELECT id FROM macchine_parti WHERE id_padre = :id", { id } ); for (const child of children) { ids.push(child.id); const descendantIds = await getAllDescendantIds(child.id); ids.push(...descendantIds); } return ids; }