335 lines
9.7 KiB
TypeScript
335 lines
9.7 KiB
TypeScript
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;
|
|
}
|