Files
Progetto-PHP/PHP/cliente/ordine.php
T
2026-08-23 15:34:23 +02:00

1024 lines
34 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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
$indirizzoPredefinito = null; // 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);
$stmt = $pdo->prepare("
SELECT
indirizzo.idIndirizzo,
indirizzo.via,
indirizzo.numeroCivico,
indirizzo.predefinito,
indirizzo.idComune
FROM
indirizzo
WHERE
indirizzo.idCliente = :idCliente
AND indirizzo.predefinito = 1
LIMIT 1
");
$stmt->execute([
"idCliente" => $idCliente
]);
$indirizzoPredefinito = $stmt->fetch(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([
"totale" => $totale,
"tempoPreparazione" => $tempoPreparazione,
"idOrdine" => $idOrdine,
"idCliente" => $idCliente
]);
$stmt = $pdo->prepare("
SELECT
MIN(so1.idStatoOrdine)
FROM
statoOrdine AS so1
WHERE
so1.nomeStato = 'inserito'
LIMIT
1
");
$stmt->execute();
$idStatoOrdine = $stmt->fetchColumn();
$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
>
<?php if ($indirizzoPredefinito === null): ?>
<option value="">Seleziona...</option>
<?php endif; ?>
<?php if ($indirizzoPredefinito !== null): ?>
<option
value="<?= e($indirizzoPredefinito["idIndirizzo"]) ?>"
select
>
<?= e($indirizzoPredefinito["via"] . " " . $indirizzoPredefinito["numeroCivico"]) ?>
<?= !empty($indirizzoPredefinito["predefinito"]) ? "(predefinito)" : "" ?>
</option>
<?php endif; ?>
// Rimuovo l'indirizzo dagli indirizzi
<?php
if (
is_array($indirizzoPredefinito) &&
isset($indirizzoPredefinito["idIndirizzo"]) &&
isset($indirizzoPredefinito["via"]) &&
isset($indirizzoPredefinito["numeroCivico"])
) {
foreach ($indirizzi as $key => $i) {
if ($i["idIndirizzo"] == $indirizzoPredefinito["idIndirizzo"]) {
unset($indirizzi[$key]);
break;
}
}
}
?>
<?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"; ?>