corretto il carico dell'articolo eliminato dalla macchina in ArticoliMacchinaModale.tsx
All checks were successful
Deploy to VPS / build (push) Successful in 38s
Deploy to VPS / deploy (push) Successful in 27s

This commit is contained in:
AV77web 2026-05-30 00:06:30 +02:00
parent 15312ddb33
commit 42ad52e344
4 changed files with 124 additions and 9 deletions

View file

@ -24,7 +24,7 @@ export async function DELETE(request: Request, { params }: RouteParams) {
try { try {
const body = await request.json(); const body = await request.json();
const { quantita, opzione } = body; const { quantita, opzione, idMagazzino } = body;
if (!quantita || quantita <= 0) { if (!quantita || quantita <= 0) {
return NextResponse.json( return NextResponse.json(
@ -40,12 +40,20 @@ export async function DELETE(request: Request, { params }: RouteParams) {
); );
} }
if (opzione === "carico" && !idMagazzino) {
return NextResponse.json(
{ error: "Seleziona un magazzino per il rientro" },
{ status: 400 }
);
}
const result = await rimuoviArticoloDaMacchina( const result = await rimuoviArticoloDaMacchina(
articoloId, articoloId,
macchinaId, macchinaId,
quantita, quantita,
opzione, opzione,
session.user.email || session.user.name || "unknown" session.user.email || session.user.name || "unknown",
idMagazzino
); );
return NextResponse.json(result); return NextResponse.json(result);

View file

@ -1,7 +1,8 @@
"use client"; "use client";
import { useState } from "react"; import { useState, useEffect } from "react";
import type { ArticoloAssegnatoMacchina } from "@/src/types/artMacchineParti"; import type { ArticoloAssegnatoMacchina } from "@/src/types/artMacchineParti";
import type { Stabilimento, Magazzino } from "@/src/types/stabilimento";
interface ArticoliMacchinaModalProps { interface ArticoliMacchinaModalProps {
macchinaId: number; macchinaId: number;
@ -15,6 +16,7 @@ interface RimuoviModalState {
articolo: ArticoloAssegnatoMacchina; articolo: ArticoloAssegnatoMacchina;
quantita: number; quantita: number;
opzione: "carico" | "elimina"; opzione: "carico" | "elimina";
idMagazzino: number | null;
} }
export default function ArticoliMacchinaModal({ export default function ArticoliMacchinaModal({
@ -27,19 +29,53 @@ export default function ArticoliMacchinaModal({
const [rimuoviModal, setRimuoviModal] = useState<RimuoviModalState | null>(null); const [rimuoviModal, setRimuoviModal] = useState<RimuoviModalState | null>(null);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null); 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() {
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);
}
} catch (err) {
console.error("Errore caricamento stabilimenti/magazzini:", err);
}
}
fetchData();
}, []);
const filteredMagazzini = selectedStabilimento
? magazzini.filter((m) => m.id_stabilimento === selectedStabilimento)
: magazzini;
const handleRimuoviClick = (articolo: ArticoloAssegnatoMacchina) => { const handleRimuoviClick = (articolo: ArticoloAssegnatoMacchina) => {
setRimuoviModal({ setRimuoviModal({
articolo, articolo,
quantita: 1, quantita: 1,
opzione: "carico", opzione: "carico",
idMagazzino: null,
}); });
setSelectedStabilimento(null);
setError(null); setError(null);
}; };
const handleConfermaRimozione = async () => { const handleConfermaRimozione = async () => {
if (!rimuoviModal) return; if (!rimuoviModal) return;
if (rimuoviModal.opzione === "carico" && !rimuoviModal.idMagazzino) {
setError("Seleziona un magazzino per il rientro");
return;
}
setSaving(true); setSaving(true);
setError(null); setError(null);
@ -52,6 +88,7 @@ export default function ArticoliMacchinaModal({
body: JSON.stringify({ body: JSON.stringify({
quantita: rimuoviModal.quantita, quantita: rimuoviModal.quantita,
opzione: rimuoviModal.opzione, opzione: rimuoviModal.opzione,
idMagazzino: rimuoviModal.idMagazzino,
}), }),
} }
); );
@ -203,7 +240,7 @@ export default function ArticoliMacchinaModal({
name="opzione" name="opzione"
value="elimina" value="elimina"
checked={rimuoviModal.opzione === "elimina"} checked={rimuoviModal.opzione === "elimina"}
onChange={() => setRimuoviModal({ ...rimuoviModal, opzione: "elimina" })} onChange={() => setRimuoviModal({ ...rimuoviModal, opzione: "elimina", idMagazzino: null })}
className="w-4 h-4 text-red-500" className="w-4 h-4 text-red-500"
/> />
<div> <div>
@ -213,6 +250,62 @@ export default function ArticoliMacchinaModal({
</label> </label>
</div> </div>
</div> </div>
{rimuoviModal.opzione === "carico" && (
<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>
<div>
<label className="block text-xs text-zinc-400 mb-1">Magazzino</label>
<select
value={rimuoviModal.idMagazzino || ""}
onChange={(e) =>
setRimuoviModal({
...rimuoviModal,
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"
>
<option value="">
{selectedStabilimento ? "Seleziona magazzino..." : "Prima seleziona uno stabilimento"}
</option>
{filteredMagazzini.map((mag) => (
<option key={mag.id} value={mag.id}>
{mag.descrizione}
</option>
))}
</select>
</div>
{!rimuoviModal.idMagazzino && (
<p className="text-xs text-yellow-400">
Devi selezionare un magazzino per confermare il rientro
</p>
)}
</div>
)}
</div> </div>
<div className="px-5 py-4 border-t border-zinc-800 flex justify-end gap-3"> <div className="px-5 py-4 border-t border-zinc-800 flex justify-end gap-3">
@ -225,7 +318,7 @@ export default function ArticoliMacchinaModal({
</button> </button>
<button <button
onClick={handleConfermaRimozione} onClick={handleConfermaRimozione}
disabled={saving} disabled={saving || (rimuoviModal.opzione === "carico" && !rimuoviModal.idMagazzino)}
className="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-lg disabled:opacity-50 flex items-center gap-2" className="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-lg disabled:opacity-50 flex items-center gap-2"
> >
{saving ? ( {saving ? (

View file

@ -106,7 +106,8 @@ export async function rimuoviArticoloDaMacchina(
idmacchina: number, idmacchina: number,
quantita: number, quantita: number,
opzione: "carico" | "elimina", opzione: "carico" | "elimina",
user: string = "system" user: string = "system",
idMagazzino?: number
): Promise<{ success: boolean; message: string }> { ): Promise<{ success: boolean; message: string }> {
const [existingRows] = await db.execute<(RowDataPacket & { id: number; qtain: number; qtaout: number })[]>( const [existingRows] = await db.execute<(RowDataPacket & { id: number; qtain: number; qtaout: number })[]>(
"SELECT id, qtain, qtaout FROM art_macchine_parti WHERE idart = :idart AND idmacchina = :idmacchina LIMIT 1", "SELECT id, qtain, qtaout FROM art_macchine_parti WHERE idart = :idart AND idmacchina = :idmacchina LIMIT 1",
@ -124,6 +125,10 @@ export async function rimuoviArticoloDaMacchina(
throw new Error(`Quantità richiesta (${quantita}) supera quella disponibile (${quantitaDisponibile})`); throw new Error(`Quantità richiesta (${quantita}) supera quella disponibile (${quantitaDisponibile})`);
} }
if (opzione === "carico" && !idMagazzino) {
throw new Error("Seleziona un magazzino per il rientro");
}
await db.execute( await db.execute(
`UPDATE art_macchine_parti `UPDATE art_macchine_parti
SET qtaout = qtaout + :quantita, updated_at = NOW() SET qtaout = qtaout + :quantita, updated_at = NOW()
@ -131,16 +136,22 @@ export async function rimuoviArticoloDaMacchina(
{ quantita, idart, idmacchina } { quantita, idart, idmacchina }
); );
if (opzione === "carico") { if (opzione === "carico" && idMagazzino) {
const [articoloRows] = await db.execute<(RowDataPacket & { descrizione: string; codice: string })[]>( const [articoloRows] = await db.execute<(RowDataPacket & { descrizione: string; codice: string })[]>(
"SELECT descrizione, codice FROM articoli WHERE id = :idart LIMIT 1", "SELECT descrizione, codice FROM articoli WHERE id = :idart LIMIT 1",
{ idart } { idart }
); );
const articolo = articoloRows[0]; 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( await db.execute(
`INSERT INTO movimenti (id_articolo, articolo_codice, articolo_descrizione, quantita, tipo_movimento, causale, utente) `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)`, VALUES (:id_articolo, :articolo_codice, :articolo_descrizione, :quantita, 1, :causale, :utente, :id_magazzino, :nome_magazzino)`,
{ {
id_articolo: idart, id_articolo: idart,
articolo_codice: articolo?.codice || "", articolo_codice: articolo?.codice || "",
@ -148,6 +159,8 @@ export async function rimuoviArticoloDaMacchina(
quantita, quantita,
causale: "Rientro da macchina", causale: "Rientro da macchina",
utente: user, utente: user,
id_magazzino: idMagazzino,
nome_magazzino: magazzinoNome,
} }
); );
} }

View file

@ -33,5 +33,6 @@ export interface AssegnaArticoloInput {
export interface RimuoviArticoloInput { export interface RimuoviArticoloInput {
quantita: number; quantita: number;
opzione: "carico" | "elimina"; opzione: "carico" | "elimina";
idMagazzino?: number;
user?: string; user?: string;
} }