magricambi/src/lib/macchine/index.ts

336 lines
9.7 KiB
TypeScript
Raw Normal View History

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<MacchinaWithDetails[]> {
const [rows] = await db.execute<MacchinaWithDetailsRow[]>(`
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<MacchinaWithDetails | null> {
const [rows] = await db.execute<MacchinaWithDetailsRow[]>(
`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<Macchina> {
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<MacchinaRow[]>(
"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<ResultSetHeader>(
`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<MacchinaWithDetails | null> {
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<MacchinaRow[]>(
"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<boolean> {
// 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<MacchinaRow[]>(
"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<ResultSetHeader>(
"DELETE FROM macchine_parti WHERE id = :id",
{ id }
);
return result.affectedRows > 0;
}
export async function cloneMacchina(
id: number,
newParentId: number | null = null
): Promise<Macchina | null> {
const original = await getMacchinaById(id);
if (!original) return null;
let copyName = `${original.nome} (copia)`;
let counter = 1;
const [existingRows] = await db.execute<MacchinaRow[]>(
"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<MacchinaRow[]>(
"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<MacchinaRow[]>(
"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<number> {
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<number[]> {
const ids: number[] = [];
const [children] = await db.execute<MacchinaRow[]>(
"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;
}