correta associazione macchine-stabilimento
All checks were successful
Deploy to VPS / build (push) Successful in 39s
Deploy to VPS / deploy (push) Successful in 28s

This commit is contained in:
AV77web 2026-05-30 00:15:39 +02:00
parent 42ad52e344
commit 819cee2bec
3 changed files with 97 additions and 98 deletions

View file

@ -2,11 +2,12 @@
import { useState, useEffect } from "react";
import type { ArticoloAssegnatoMacchina } from "@/src/types/artMacchineParti";
import type { Stabilimento, Magazzino } from "@/src/types/stabilimento";
import type { Magazzino } from "@/src/types/stabilimento";
interface ArticoliMacchinaModalProps {
macchinaId: number;
macchinaNome: string;
macchinaStabilimentoId: number | null;
articoli: ArticoloAssegnatoMacchina[];
onClose: () => void;
onArticoloRimosso: () => void;
@ -22,6 +23,7 @@ interface RimuoviModalState {
export default function ArticoliMacchinaModal({
macchinaId,
macchinaNome,
macchinaStabilimentoId,
articoli,
onClose,
onArticoloRimosso,
@ -29,33 +31,28 @@ export default function ArticoliMacchinaModal({
const [rimuoviModal, setRimuoviModal] = useState<RimuoviModalState | null>(null);
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
const [stabilimenti, setStabilimenti] = useState<Stabilimento[]>([]);
const [magazzini, setMagazzini] = useState<Magazzino[]>([]);
const [selectedStabilimento, setSelectedStabilimento] = useState<number | null>(null);
useEffect(() => {
async function fetchData() {
async function fetchMagazzini() {
if (!macchinaStabilimentoId) return;
try {
const [stabRes, magRes] = await Promise.all([
fetch("/api/stabilimenti"),
fetch("/api/magazzini"),
]);
if (stabRes.ok && magRes.ok) {
const stabData = await stabRes.json();
const magData = await magRes.json();
setStabilimenti(stabData.stabilimenti);
setMagazzini(magData.magazzini);
const res = await fetch("/api/magazzini");
if (res.ok) {
const data = await res.json();
// Filtra solo i magazzini dello stabilimento della macchina
const filtered = data.magazzini.filter(
(m: Magazzino) => m.id_stabilimento === macchinaStabilimentoId
);
setMagazzini(filtered);
}
} catch (err) {
console.error("Errore caricamento stabilimenti/magazzini:", err);
console.error("Errore caricamento magazzini:", err);
}
}
fetchData();
}, []);
const filteredMagazzini = selectedStabilimento
? magazzini.filter((m) => m.id_stabilimento === selectedStabilimento)
: magazzini;
fetchMagazzini();
}, [macchinaStabilimentoId]);
const handleRimuoviClick = (articolo: ArticoloAssegnatoMacchina) => {
setRimuoviModal({
@ -64,7 +61,6 @@ export default function ArticoliMacchinaModal({
opzione: "carico",
idMagazzino: null,
});
setSelectedStabilimento(null);
setError(null);
};
@ -255,26 +251,12 @@ export default function ArticoliMacchinaModal({
<div className="space-y-3 p-4 rounded-lg bg-teal-900/20 border border-teal-700/30">
<p className="text-sm font-medium text-teal-300">Seleziona il magazzino di destinazione</p>
<div>
<label className="block text-xs text-zinc-400 mb-1">Stabilimento</label>
<select
value={selectedStabilimento || ""}
onChange={(e) => {
const stabId = e.target.value ? parseInt(e.target.value) : null;
setSelectedStabilimento(stabId);
setRimuoviModal({ ...rimuoviModal, idMagazzino: null });
}}
className="w-full px-3 py-2 bg-zinc-800 border border-zinc-700 rounded-lg text-white text-sm focus:outline-none focus:ring-2 focus:ring-teal-500"
>
<option value="">Seleziona stabilimento...</option>
{stabilimenti.map((stab) => (
<option key={stab.id} value={stab.id}>
{stab.descrizione}
</option>
))}
</select>
</div>
{magazzini.length === 0 ? (
<p className="text-xs text-yellow-400">
Nessun magazzino disponibile per lo stabilimento di questa macchina
</p>
) : (
<>
<div>
<label className="block text-xs text-zinc-400 mb-1">Magazzino</label>
<select
@ -285,13 +267,10 @@ export default function ArticoliMacchinaModal({
idMagazzino: e.target.value ? parseInt(e.target.value) : null,
})
}
disabled={!selectedStabilimento}
className="w-full px-3 py-2 bg-zinc-800 border border-zinc-700 rounded-lg text-white text-sm focus:outline-none focus:ring-2 focus:ring-teal-500 disabled:opacity-50"
className="w-full px-3 py-2 bg-zinc-800 border border-zinc-700 rounded-lg text-white text-sm focus:outline-none focus:ring-2 focus:ring-teal-500"
>
<option value="">
{selectedStabilimento ? "Seleziona magazzino..." : "Prima seleziona uno stabilimento"}
</option>
{filteredMagazzini.map((mag) => (
<option value="">Seleziona magazzino...</option>
{magazzini.map((mag) => (
<option key={mag.id} value={mag.id}>
{mag.descrizione}
</option>
@ -304,6 +283,8 @@ export default function ArticoliMacchinaModal({
Devi selezionare un magazzino per confermare il rientro
</p>
)}
</>
)}
</div>
)}
</div>

View file

@ -51,6 +51,7 @@ export default function MacchineTree() {
const [showArticlesModal, setShowArticlesModal] = useState<{
macchinaId: number;
macchinaNome: string;
macchinaStabilimentoId: number | null;
articoli: ArticoloAssegnatoMacchina[];
} | null>(null);
@ -243,6 +244,7 @@ export default function MacchineTree() {
setShowArticlesModal({
macchinaId: nodeId,
macchinaNome: macchina.nome,
macchinaStabilimentoId: macchina.id_stabilimento,
articoli: allArticoli,
});
};
@ -301,14 +303,34 @@ export default function MacchineTree() {
};
const filteredNodes = useMemo(() => {
if (!selectedStabilimento) return nodes;
if (!selectedStabilimento) return [];
return nodes.filter((n) => n.id_stabilimento === selectedStabilimento.id);
}, [nodes, selectedStabilimento]);
const rootNodes = filteredNodes.filter((n) => n.id_padre === null);
const filteredCount = filteredNodes.length;
const totalCount = nodes.length;
// Se non c'è stabilimento selezionato, mostra messaggio
if (!selectedStabilimento) {
return (
<div className="flex flex-col h-full">
<div className="mb-4">
<h2 className="text-lg font-semibold text-white">Macchine</h2>
<p className="text-xs text-zinc-500">Seleziona uno stabilimento per visualizzare le macchine</p>
</div>
<div className="flex-1 flex flex-col items-center justify-center text-center p-8 border-2 border-dashed border-zinc-800 rounded-lg">
<svg className="w-12 h-12 text-zinc-700 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
<p className="text-zinc-500 text-sm mb-2">Nessuno stabilimento selezionato</p>
<p className="text-zinc-600 text-xs">
Seleziona uno stabilimento dal menu in alto per visualizzare e gestire le macchine
</p>
</div>
</div>
);
}
return (
<div className="flex flex-col h-full">
@ -317,10 +339,7 @@ export default function MacchineTree() {
<div>
<h2 className="text-lg font-semibold text-white">Macchine</h2>
<p className="text-xs text-zinc-500">
{selectedStabilimento
? `${filteredCount} di ${totalCount} elementi`
: `${totalCount} elementi nell'albero`
}
{filteredCount} elementi in {selectedStabilimento.descrizione}
</p>
</div>
<button
@ -335,13 +354,6 @@ export default function MacchineTree() {
</button>
</div>
{/* Info selezione corrente */}
{selectedStabilimento && (
<div className="mb-3 px-3 py-2 rounded-lg bg-blue-900/20 border border-blue-700/30 text-xs text-blue-300">
Filtro attivo: {selectedStabilimento.descrizione}
</div>
)}
{/* Indicatore clonazione */}
{isCloning && (
<div className="mb-3 px-3 py-2 rounded-lg bg-blue-900/30 border border-blue-700/50 flex items-center gap-2">
@ -391,16 +403,10 @@ export default function MacchineTree() {
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<p className="text-zinc-500 text-sm mb-2">
{selectedStabilimento
? `Nessuna macchina in "${selectedStabilimento.descrizione}"`
: "Nessuna macchina presente"
}
Nessuna macchina in &quot;{selectedStabilimento.descrizione}&quot;
</p>
<p className="text-zinc-600 text-xs mb-4">
{selectedStabilimento
? "Seleziona un altro stabilimento o aggiungi una nuova macchina"
: "Clicca sul pulsante + per aggiungere la prima macchina"
}
Clicca sul pulsante + per aggiungere la prima macchina
</p>
<button
onClick={handleAddMachine}
@ -483,6 +489,7 @@ export default function MacchineTree() {
<ArticoliMacchinaModal
macchinaId={showArticlesModal.macchinaId}
macchinaNome={showArticlesModal.macchinaNome}
macchinaStabilimentoId={showArticlesModal.macchinaStabilimentoId}
articoli={showArticlesModal.articoli}
onClose={() => setShowArticlesModal(null)}
onArticoloRimosso={() => {

View file

@ -143,15 +143,9 @@ export async function rimuoviArticoloDaMacchina(
);
const articolo = articoloRows[0];
const [magazzinoRows] = await db.execute<(RowDataPacket & { descrizione: string })[]>(
"SELECT descrizione FROM magazzini WHERE id = :idMagazzino LIMIT 1",
{ idMagazzino }
);
const magazzinoNome = magazzinoRows[0]?.descrizione || "";
await db.execute(
`INSERT INTO movimenti (id_articolo, articolo_codice, articolo_descrizione, quantita, tipo_movimento, causale, utente, id_magazzino, nome_magazzino)
VALUES (:id_articolo, :articolo_codice, :articolo_descrizione, :quantita, 1, :causale, :utente, :id_magazzino, :nome_magazzino)`,
`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: idart,
articolo_codice: articolo?.codice || "",
@ -159,10 +153,27 @@ export async function rimuoviArticoloDaMacchina(
quantita,
causale: "Rientro da macchina",
utente: user,
id_magazzino: idMagazzino,
nome_magazzino: magazzinoNome,
id_magazzino_destinazione: idMagazzino,
}
);
// Aggiorna la giacenza nel magazzino
const [existingGiacenza] = await db.execute<(RowDataPacket & { id: number; quantita: number })[]>(
"SELECT id, quantita FROM giacenze WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId",
{ articoloId: idart, magazzinoId: idMagazzino }
);
if (existingGiacenza.length > 0) {
await db.execute(
"UPDATE giacenze SET quantita = quantita + :quantita WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId",
{ quantita, articoloId: idart, magazzinoId: idMagazzino }
);
} else {
await db.execute(
"INSERT INTO giacenze (articolo_id, id_magazzino, quantita) VALUES (:articoloId, :magazzinoId, :quantita)",
{ articoloId: idart, magazzinoId: idMagazzino, quantita }
);
}
}
return { success: true, message: opzione === "carico" ? "Articolo rientrato in magazzino" : "Articolo rimosso" };