first commit

This commit is contained in:
Emanuele Slusarz
2026-08-22 11:15:29 +02:00
commit ec30fb40b9
38 changed files with 3723 additions and 0 deletions
+972
View File
@@ -0,0 +1,972 @@
<?php
require_once __DIR__ . "/../includes/auth.php";
requireRole("cliente");
require_once __DIR__ . "/../config/database.php";
$idCliente = (int) $_SESSION["idUtente"];
$idProdotto = filter_input(INPUT_POST, "idProdotto", FILTER_VALIDATE_INT);
$quantita = filter_input(INPUT_POST, "quantita", FILTER_VALIDATE_INT);
$caratteristiche = array_values(
array_unique(
array_filter(
array_map("intval", (array) ($_POST["caratteristiche"] ?? []))
)
)
);
$azione = $_POST["azione"] ?? null;
$orarioConsegna = $_POST["orarioConsegna"] ?? null;
$idIndirizzo = filter_input(INPUT_POST, "idIndirizzo", FILTER_VALIDATE_INT);
$idOrdine = null; // CARICATO
$prodotti = []; // CARICATO
$caratteristicheDisponibili = []; // CARICATO
$indirizzi = []; // CARICATO
$righeOrdine = []; // CARICATO
$riepilogo = null; // CARICATO
$messaggio = $_SESSION["messaggio"] ?? null; // CARICATO
unset($_SESSION["messaggio"]);
/* ============================================================
* INDIRIZZI DEL CLIENTE
* ============================================================
*/
$stmt = $pdo->prepare("
SELECT indirizzo.idIndirizzo, indirizzo.via, indirizzo.numeroCivico,
indirizzo.predefinito, indirizzo.idComune
FROM indirizzo
WHERE indirizzo.idCliente = :idCliente
");
$stmt->execute([
"idCliente" => $idCliente
]);
$indirizzi = $stmt->fetchAll(PDO::FETCH_ASSOC);
/* ============================================================
* EVENTUALE ORDINE IN BOZZA
* ============================================================
*/
$stmt = $pdo->prepare("
SELECT ordine.idOrdine
FROM ordine
WHERE ordine.idCliente = :idCliente
AND ordine.statoConferma = 'bozza'
ORDER BY ordine.dataOraCreazione DESC
LIMIT 1
");
$stmt->execute([
"idCliente" => $idCliente
]);
$idOrdine = $stmt->fetchColumn();
if ($idOrdine === false) {
$idOrdine = null;
}
/* ============================================================
* PRODOTTI DISPONIBILI
* ============================================================
*/
$stmt = $pdo->prepare("
SELECT prodotto.idProdotto, prodotto.nome, prodotto.descrizione,
prodotto.prezzoBase, prodotto.tempoPreparazione,
prodotto.proceduraPreparazione, prodotto.disponibile
FROM prodotto
WHERE prodotto.disponibile = 1
ORDER BY prodotto.nome
");
$stmt->execute();
$prodotti = $stmt->fetchAll(PDO::FETCH_ASSOC);
/* ============================================================
* CARATTERISTICHE DISPONIBILI
* ============================================================
*/
$stmt = $pdo->prepare("
SELECT caratteristica.idCaratteristica, caratteristica.nome,
caratteristica.descrizione, caratteristica.differenzaPrezzo,
caratteristica.predefinita, caratteristica.disponibile,
caratteristica.idProdotto, caratteristica.idGruppoEsclusione,
gruppoEsclusione.nome AS nomeGruppo
FROM caratteristica
LEFT JOIN gruppoEsclusione
ON caratteristica.idGruppoEsclusione = gruppoEsclusione.idGruppoEsclusione
WHERE caratteristica.disponibile = 1
ORDER BY caratteristica.idProdotto,
caratteristica.idGruppoEsclusione,
caratteristica.idCaratteristica
");
$stmt->execute();
$caratteristicheDisponibili = $stmt->fetchAll(PDO::FETCH_ASSOC);
/* ============================================================
* QUERY 3
*
* Inserimento di un prodotto in un ordine
* con le caratteristiche selezionate.
* ============================================================
*/
if (postMethod() && $azione === "aggiungi") {
if (
$idCliente <= 0 ||
!$idProdotto ||
!$quantita ||
$quantita < 1 ||
$quantita > 255
) {
$messaggio = "Dati in input non validi";
} else {
try {
/* Verifico che il prodotto esista e sia disponibile. */
$stmt = $pdo->prepare("
SELECT prodotto.prezzoBase
FROM prodotto
WHERE prodotto.idProdotto = :idProdotto
AND prodotto.disponibile = 1
LIMIT 1
");
$stmt->execute([
"idProdotto" => $idProdotto
]);
$prezzoBaseApplicato = $stmt->fetchColumn();
if ($prezzoBaseApplicato === false) {
$messaggio = "Prodotto inesistente o non disponibile";
} else {
/* Se non esiste una bozza recupero l'indirizzo predefinito. */
if (!$idOrdine) {
$stmt = $pdo->prepare("
SELECT indirizzo.idIndirizzo
FROM indirizzo
WHERE indirizzo.idCliente = :idCliente
AND indirizzo.predefinito = 1
LIMIT 1
");
$stmt->execute([
"idCliente" => $idCliente
]);
$idIndirizzo = $stmt->fetchColumn();
if ($idIndirizzo === false) {
$messaggio = "Prima di creare un ordine devi impostare un indirizzo predefinito.";
}
}
if ($idOrdine || $idIndirizzo) {
$pdo->beginTransaction();
/* ========================================================
* CREAZIONE EVENTUALE BOZZA
* ========================================================
*/
if (!$idOrdine) {
$stmt = $pdo->prepare("
INSERT INTO ordine (
dataOraCreazione,
dataOraConferma,
dataOraAnnullamento,
statoConferma,
orarioConsegna,
totale,
tempoStimatoConsegna,
idCliente,
idIndirizzo
)
VALUES (
NOW(),
NULL,
NULL,
'bozza',
NULL,
0.00,
NULL,
:idCliente,
:idIndirizzo
)
");
$stmt->execute([
"idCliente" => $idCliente,
"idIndirizzo" => $idIndirizzo
]);
$idOrdine = (int) $pdo->lastInsertId();
}
/* ========================================================
* INSERIMENTO RIGA ORDINE
* ========================================================
*/
$stmt = $pdo->prepare("
INSERT INTO rigaOrdine (
quantita,
prezzoBaseApplicato,
idOrdine,
idProdotto
)
VALUES (
:quantita,
:prezzoBaseApplicato,
:idOrdine,
:idProdotto
)
");
$stmt->execute([
"quantita" => $quantita,
"prezzoBaseApplicato" => $prezzoBaseApplicato,
"idOrdine" => $idOrdine,
"idProdotto" => $idProdotto
]);
$idRigaOrdine = (int) $pdo->lastInsertId();
/* ========================================================
* CARATTERISTICHE DELLA RIGA
* ========================================================
*/
$stmtCaratteristica = $pdo->prepare("
SELECT caratteristica.idCaratteristica,
caratteristica.differenzaPrezzo,
caratteristica.idGruppoEsclusione
FROM caratteristica
WHERE caratteristica.idCaratteristica = :idCaratteristica
AND caratteristica.idProdotto = :idProdotto
AND caratteristica.disponibile = 1
LIMIT 1
");
$stmtInserisciCaratteristica = $pdo->prepare("
INSERT INTO rigaOrdineSelezionaCaratteristica (
idCaratteristica,
idRigaOrdine,
differenzaPrezzoApplicata
)
VALUES (
:idCaratteristica,
:idRigaOrdine,
:differenzaPrezzoApplicata
)
");
$gruppiSelezionati = [];
foreach ($caratteristiche as $idCaratteristica) {
$stmtCaratteristica->execute([
"idCaratteristica" => $idCaratteristica,
"idProdotto" => $idProdotto
]);
$caratteristica = $stmtCaratteristica->fetch(PDO::FETCH_ASSOC);
if (!$caratteristica) {
throw new RuntimeException(
"Caratteristica non valida per il prodotto selezionato."
);
}
$idGruppoEsclusione = $caratteristica["idGruppoEsclusione"];
if ($idGruppoEsclusione !== null) {
$idGruppoEsclusione = (int) $idGruppoEsclusione;
if (isset($gruppiSelezionati[$idGruppoEsclusione])) {
throw new RuntimeException(
"Sono state selezionate due caratteristiche incompatibili."
);
}
$gruppiSelezionati[$idGruppoEsclusione] = true;
}
$stmtInserisciCaratteristica->execute([
"idCaratteristica" => $idCaratteristica,
"idRigaOrdine" => $idRigaOrdine,
"differenzaPrezzoApplicata" => $caratteristica["differenzaPrezzo"]
]);
}
/* ========================================================
* FINE QUERY 3
* ========================================================
*/
$pdo->commit();
// POST -> REDIRECT -> GET
header("Location: ordine.php");
exit;
}
}
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
die(
"Errore durante l'aggiunta del prodotto all'ordine: "
. $e->getMessage()
);
}
}
}
/* ============================================================
* RIGHE DELLA BOZZA
* ============================================================
*/
if ($idOrdine) {
$stmt = $pdo->prepare("
SELECT rigaOrdine.idRigaOrdine,
rigaOrdine.quantita,
rigaOrdine.prezzoBaseApplicato,
rigaOrdine.idOrdine,
rigaOrdine.idProdotto,
prodotto.nome AS nomeProdotto,
(
rigaOrdine.quantita *
(
rigaOrdine.prezzoBaseApplicato +
COALESCE(
(
SELECT SUM(
rigaOrdineSelezionaCaratteristica.differenzaPrezzoApplicata
)
FROM rigaOrdineSelezionaCaratteristica
WHERE rigaOrdineSelezionaCaratteristica.idRigaOrdine =
rigaOrdine.idRigaOrdine
),
0
)
)
) AS subtotale
FROM rigaOrdine
JOIN prodotto
ON rigaOrdine.idProdotto = prodotto.idProdotto
WHERE rigaOrdine.idOrdine = :idOrdine
ORDER BY rigaOrdine.idRigaOrdine
");
$stmt->execute([
"idOrdine" => $idOrdine
]);
$righeOrdine = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/* ============================================================
* QUERY 4
*
* Calcolo totale e tempo stimato di consegna
* ============================================================
*/
if ($idOrdine) {
$stmt = $pdo->prepare("
SELECT
SUM(
(
rigaOrdine.prezzoBaseApplicato +
COALESCE(
(
SELECT SUM(
rigaOrdineSelezionaCaratteristica.differenzaPrezzoApplicata
)
FROM rigaOrdineSelezionaCaratteristica
WHERE
rigaOrdineSelezionaCaratteristica.idRigaOrdine =
rigaOrdine.idRigaOrdine
),
0
)
) * rigaOrdine.quantita
) AS totale,
SUM(
prodotto.tempoPreparazione * rigaOrdine.quantita
) AS tempoStimatoConsegna
FROM rigaOrdine JOIN prodotto ON prodotto.idProdotto = rigaOrdine.idProdotto
WHERE rigaOrdine.idOrdine = :idOrdine
");
$stmt->execute([
"idOrdine" => $idOrdine
]);
$riepilogo = $stmt->fetch(PDO::FETCH_ASSOC);
}
/* ============================================================
* CONFERMA / ANNULLA
* ============================================================
*/
if (postMethod() && in_array($azione, ["conferma", "annulla"], true)) {
// Annullamento ordine
if ($azione === "annulla") {
// Registrazione dell'annullamento dell'ordine
$stmt = $pdo->prepare("
UPDATE
ordine
SET
ordine.dataOraAnnullamento = NOW(),
ordine.statoConferma = 'annullato',
ordine.orarioConsegna = NULL,
ordine.tempoStimatoConsegna = NULL
WHERE
ordine.idOrdine = :idOrdine
AND ordine.idCliente = :idCliente
AND ordine.statoConferma = 'bozza'
");
$stmt->execute([
"idOrdine" => $idOrdine,
"idCliente" => $idCliente
]);
// Pulizia del riepilogo
header("Location: ordine.php");
// Messaggio
$_SESSION["messaggio"] = "Ordine annullato con successo";
exit;
}
// Conferma ordine
if ($azione === "conferma") {
// Calcolo del tempo di preparazione
$stmt = $pdo->prepare("
SELECT
COALESCE(
SUM(rigaOrdine.quantita * prodotto.tempoPreparazione),
0
) AS tempoPreparazione
FROM
ordine
JOIN rigaOrdine
ON rigaOrdine.idOrdine = ordine.idOrdine
JOIN prodotto
ON rigaOrdine.idProdotto = prodotto.idProdotto
WHERE
ordine.idOrdine = :idOrdine
");
$stmt->execute([
"idOrdine" => $idOrdine
]);
$tempoPreparazione = $stmt->fetchColumn();
// Calcolo del totale
$stmt = $pdo->prepare("
SELECT
COALESCE(
SUM(totali.totaleRigaOrdine),
0
)
FROM
rigaOrdine AS ro1
LEFT JOIN (
SELECT
ro2.idRigaOrdine AS idRigaOrdine,
ro2.quantita * (
ro2.prezzoBaseApplicato
+
COALESCE(
SUM(rosc2.differenzaPrezzoApplicata),
0
)
) AS totaleRigaOrdine
FROM
rigaOrdine AS ro2
LEFT JOIN rigaOrdineSelezionaCaratteristica AS rosc2
ON ro2.idRigaOrdine = rosc2.idRigaOrdine
WHERE
ro2.idOrdine = :idOrdine
GROUP BY
ro2.idRigaOrdine
) AS totali
ON ro1.idRigaOrdine = totali.idRigaOrdine
WHERE
ro1.idOrdine = :idOrdine
");
$stmt->execute([
"idOrdine" => $idOrdine
]);
$totale = $stmt->fetchColumn();
// Registrazione della conferma dell'ordine
$stmt = $pdo->prepare("
UPDATE
ordine
SET
ordine.dataOraConferma = NOW(),
ordine.statoConferma = 'confermato',
ordine.totale = :totale,
ordine.tempoStimatoConsegna = :tempoPreparazione
WHERE
ordine.idOrdine = :idOrdine
AND ordine.idCliente = :idCliente
AND ordine.statoConferma = 'bozza'
");
$stmt->execute([
"idOrdine" => $idOrdine,
"idCliente" => $idCliente,
"totale" => $totale,
"tempoPreparazione" => $tempoPreparazione
]);
// Creazione dello storicoStato e statoOrdine
$stmt = $pdo->prepare("
INSERT
INTO statoOrdine
(
nomeStato,
posizione
)
VALUES (
'IN LAVORAZIONE',
1
)
");
$stmt->execute();
$idStatoOrdine = $pdo->lastInsertId();
$stmt = $pdo->prepare("
INSERT
INTO storicoStato
(
timestamp,
idOrdine,
idStatoOrdine
)
VALUES (
NOW(),
:idOrdine,
:idStatoOrdine
)
");
$stmt->execute([
"idOrdine" => $idOrdine,
"idStatoOrdine" => $idStatoOrdine
]);
// Pulizia del riepilogo
header("Location: ordine.php");
// Messaggio
$_SESSION["messaggio"] = "Ordine confermato con successo";
exit;
}
}
/* ============================================================
* PAGINA
* ============================================================
*/
$pageTitle = "Componi ordine";
$basePath = "..";
require __DIR__ . "/../includes/header.php";
?>
<h1 class="h3">Componi ordine</h1>
<?php if ($messaggio): ?>
<div class="alert alert-warning">
<?= e($messaggio) ?>
</div>
<?php endif; ?>
<div class="row g-4">
<!-- ======================================================
AGGIUNTA PRODOTTO
====================================================== -->
<div class="col-lg-7">
<div class="card shadow-sm">
<div class="card-body">
<h2 class="h5">Aggiungi un prodotto</h2>
<form method="post">
<input type="hidden" name="azione" value="aggiungi">
<!-- PRODOTTO -->
<div class="mb-3">
<label class="form-label" for="idProdotto">Prodotto</label>
<select class="form-select" id="idProdotto" name="idProdotto" required>
<option value="">Seleziona...</option>
<?php foreach ($prodotti as $p): ?>
<option value="<?= e($p["idProdotto"]) ?>">
<?= e($p["nome"]) ?> — € <?= e($p["prezzoBase"]) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- QUANTITA -->
<div class="mb-3">
<label class="form-label" for="quantita">Quantità</label>
<input
class="form-control"
type="number"
min="1"
max="255"
value="1"
id="quantita"
name="quantita"
required
>
</div>
<!-- CARATTERISTICHE -->
<fieldset class="mb-3">
<legend class="h6">Caratteristiche</legend>
<p id="messaggioCaratteristiche" class="text-muted">
Seleziona prima un prodotto.
</p>
<?php foreach ($caratteristicheDisponibili as $c): ?>
<div
class="caratteristica-item align-items-center gap-2"
style="display: none; width: 100%;"
data-id-prodotto="<?= e($c["idProdotto"]) ?>"
>
<input
class="form-check-input caratteristica-checkbox mt-0"
style="float: none; margin-left: 0;"
type="checkbox"
name="caratteristiche[]"
value="<?= e($c["idCaratteristica"]) ?>"
id="c<?= e($c["idCaratteristica"]) ?>"
data-gruppo-esclusione="<?= e($c["idGruppoEsclusione"] ?? "") ?>"
<?= !empty($c["predefinita"]) ? "checked" : "" ?>
disabled
>
<label
class="form-check-label"
for="c<?= e($c["idCaratteristica"]) ?>"
>
<?= e($c["nome"]) ?>
<?php if ($c["idGruppoEsclusione"] !== null): ?>
<small class="text-muted">
(<?= e($c["nomeGruppo"]) ?>)
</small>
<?php endif; ?>
<?= $c["differenzaPrezzo"] >= 0 ? "+" : "" ?>
€ <?= e($c["differenzaPrezzo"]) ?>
</label>
</div>
<?php endforeach; ?>
</fieldset>
<button class="btn btn-primary" type="submit">
Aggiungi all'ordine
</button>
</form>
</div>
</div>
</div>
<!-- ======================================================
RIEPILOGO
====================================================== -->
<div class="col-lg-5">
<div class="card shadow-sm mb-3">
<div class="card-body">
<h2 class="h5">Riepilogo</h2>
<?php if (!$righeOrdine): ?>
<p class="text-muted">La bozza è vuota.</p>
<?php endif; ?>
<?php foreach ($righeOrdine as $r): ?>
<div class="d-flex justify-content-between">
<span>
<?= e($r["quantita"]) ?> × <?= e($r["nomeProdotto"]) ?>
</span>
<span>
€ <?= e($r["subtotale"]) ?>
</span>
</div>
<?php endforeach; ?>
<hr>
<!-- QUERY 4 non ancora implementata. -->
<p>
<strong>Totale:</strong> €
<?= e($riepilogo["totale"] ?? "-")?>
<br>
<strong>Tempo stimato:</strong>
<?= e($riepilogo["tempoStimatoConsegna"] ?? "—") ?> min
</p>
</div>
</div>
<!-- ==================================================
CONFERMA / ANNULLA
================================================== -->
<div class="card shadow-sm">
<div class="card-body">
<h2 class="h5">Conferma o annulla</h2>
<form method="post">
<input type="hidden" name="idOrdine" value="<?= e($idOrdine) ?>">
<!-- INDIRIZZO -->
<div class="mb-3">
<label class="form-label" for="idIndirizzo">
Indirizzo
</label>
<select
class="form-select"
id="idIndirizzo"
name="idIndirizzo"
required
>
<option value="">Seleziona...</option>
<?php foreach ($indirizzi as $i): ?>
<option value="<?= e($i["idIndirizzo"]) ?>">
<?= e($i["via"] . " " . $i["numeroCivico"]) ?>
<?= !empty($i["predefinito"]) ? "(predefinito)" : "" ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- ORARIO -->
<div class="mb-3">
<label class="form-label" for="orarioConsegna">
Orario di consegna
</label>
<input
class="form-control"
type="datetime-local"
id="orarioConsegna"
name="orarioConsegna"
required
>
</div>
<button
class="btn btn-success"
name="azione"
value="conferma"
>
Conferma
</button>
<button
class="btn btn-outline-danger"
name="azione"
value="annulla"
formnovalidate
>
Annulla
</button>
</form>
</div>
</div>
</div>
</div>
<!-- ==========================================================
JAVASCRIPT CARATTERISTICHE
========================================================== -->
<script>
document.addEventListener("DOMContentLoaded", function () {
const selectProdotto = document.getElementById("idProdotto");
const caratteristiche = document.querySelectorAll(".caratteristica-item");
const messaggio = document.getElementById("messaggioCaratteristiche");
function aggiornaCaratteristiche() {
const idProdotto = selectProdotto.value;
let trovata = false;
caratteristiche.forEach(function (elemento) {
const appartiene = elemento.dataset.idProdotto === idProdotto;
elemento.style.display = appartiene ? "flex" : "none";
const input = elemento.querySelector("input");
input.disabled = !appartiene;
if (appartiene) {
trovata = true;
}
});
if (!idProdotto) {
messaggio.textContent = "Seleziona prima un prodotto.";
messaggio.classList.remove("d-none");
} else if (!trovata) {
messaggio.textContent = "Questo prodotto non ha caratteristiche disponibili.";
messaggio.classList.remove("d-none");
} else {
messaggio.classList.add("d-none");
}
}
/*
* Se seleziono una caratteristica appartenente ad un gruppo
* deseleziono le altre dello stesso gruppo.
*/
document.querySelectorAll(".caratteristica-checkbox").forEach(function (checkbox) {
checkbox.addEventListener("change", function () {
const gruppo = this.dataset.gruppoEsclusione;
if (!this.checked || !gruppo) {
return;
}
document.querySelectorAll(".caratteristica-checkbox").forEach(function (altra) {
if (altra === checkbox) {
return;
}
if (altra.dataset.gruppoEsclusione === gruppo) {
altra.checked = false;
}
});
});
});
selectProdotto.addEventListener("change", aggiornaCaratteristiche);
aggiornaCaratteristiche();
});
</script>
<?php require __DIR__ . "/../includes/footer.php"; ?>