added modal inser lesson maually by teacher
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Aggiunge la colonna is_gift a bookingclass.
|
||||
*
|
||||
* is_gift = 'Y' indica una lezione OMAGGIO inserita dall'insegnante:
|
||||
* - non consuma ticket dell'ordine (esclusa dai conteggi residui)
|
||||
* - non è riprogrammabile (legata alla data della classe)
|
||||
* - tipicamente ha idorder = NULL
|
||||
*
|
||||
* Default 'N' = prenotazione normale.
|
||||
*/
|
||||
final class AddIsGiftToBookingclass extends AbstractMigration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$table = $this->table('bookingclass');
|
||||
$table->addColumn('is_gift', 'char', [
|
||||
'limit' => 1,
|
||||
'null' => false,
|
||||
'default' => 'N',
|
||||
'after' => 'is_reprogrammed',
|
||||
])->update();
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$table = $this->table('bookingclass');
|
||||
$table->removeColumn('is_gift')->update();
|
||||
}
|
||||
}
|
||||
+169
-1
@@ -479,6 +479,17 @@ $italianMonths = [
|
||||
<?php endif;
|
||||
endif; ?>
|
||||
|
||||
<?php if (isset($_GET['error'])) :
|
||||
$errMap = [
|
||||
'duplicate' => 'Questo utente è già prenotato in questa classe.',
|
||||
'insert' => 'Errore durante l\'inserimento. Riprova.',
|
||||
'missingdata' => 'Dati mancanti per l\'inserimento.',
|
||||
];
|
||||
$em = $errMap[$_GET['error']] ?? 'Si è verificato un errore.';
|
||||
?>
|
||||
<div class="alert alert-danger" role="alert"><?php echo e($em); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="month-navigation">
|
||||
<a href="?prev_month=<?php echo e(date('Y-m', strtotime('-1 month', strtotime($currentMonthStart)))); ?>"><i class="fas fa-chevron-left fa-2x"></i></a>
|
||||
<h2><?php echo e($italianMonths[date("F", strtotime($currentMonthStart))] . ' ' . date("Y", strtotime($currentMonthStart))); ?></h2>
|
||||
@@ -542,7 +553,7 @@ $italianMonths = [
|
||||
$ini = initials($b['first_name'] ?? '', $b['last_name'] ?? '');
|
||||
$avColor = avatarColor($fullName);
|
||||
?>
|
||||
<div class="booking-item <?php echo $isLost ? 'is-lost' : ''; ?>">
|
||||
<div class="booking-item <?php echo $isLost ? 'is-lost' : ''; ?>" data-iduser="<?php echo (int) $b['iduser']; ?>">
|
||||
<div class="avatar" style="background: <?php echo $isLost ? '#7f8c8d' : $avColor; ?>;"><?php echo e($ini); ?></div>
|
||||
<div class="booking-info">
|
||||
<span class="booking-name"><?php echo e($fullName); ?></span>
|
||||
@@ -755,6 +766,163 @@ $italianMonths = [
|
||||
});
|
||||
});
|
||||
|
||||
// === Intercetta submit "Aggiungi partecipante" ===
|
||||
$(document).on('submit', '.add-booking-form', function(e) {
|
||||
e.preventDefault();
|
||||
const form = $(this);
|
||||
const userid = form.find('.userid').val();
|
||||
const name = form.find('.name').val().trim();
|
||||
const surname = form.find('.surname').val().trim();
|
||||
|
||||
if (!name || !surname) {
|
||||
Swal.fire('Dati mancanti', 'Inserisci nome e cognome.', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const hasUser = userid && parseInt(userid) > 0;
|
||||
|
||||
// Controllo anti-duplicato PRIMA di aprire il modale:
|
||||
// l'utente è già presente in questa classe?
|
||||
if (hasUser) {
|
||||
const panel = form.closest('.bookings-panel');
|
||||
const already = panel.find('.booking-item[data-iduser="' + parseInt(userid) + '"]').length > 0;
|
||||
if (already) {
|
||||
Swal.fire(
|
||||
'Già prenotato',
|
||||
'Questa persona è già presente in questa classe.',
|
||||
'info'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUser) {
|
||||
$.getJSON('get_user_orders.php', {
|
||||
userid: userid
|
||||
}).done(function(data) {
|
||||
showBookingModal(form, data.orders || []);
|
||||
}).fail(function() {
|
||||
showBookingModal(form, []);
|
||||
});
|
||||
} else {
|
||||
showBookingModal(form, []);
|
||||
}
|
||||
});
|
||||
|
||||
function showBookingModal(form, orders) {
|
||||
const usableOrders = orders.filter(o => o.remaining > 0);
|
||||
let orderOptions = '';
|
||||
usableOrders.forEach(function(o) {
|
||||
const exp = o.expireon ? ' - scade ' + o.expireon : '';
|
||||
orderOptions += '<option value="' + o.idorderbook + '">' +
|
||||
o.service_name + ' (' + o.remaining + '/' + o.tickets + ' residui)' + exp +
|
||||
'</option>';
|
||||
});
|
||||
|
||||
const hasUsableOrders = usableOrders.length > 0;
|
||||
|
||||
const d = new Date();
|
||||
d.setMonth(d.getMonth() + 3);
|
||||
const defExpiry = d.toISOString().slice(0, 10);
|
||||
|
||||
const html =
|
||||
'<div style="text-align:left;">' +
|
||||
'<label style="display:block;margin:8px 0;">' +
|
||||
'<input type="radio" name="bkmode" value="scala" ' + (hasUsableOrders ? 'checked' : 'disabled') + '> ' +
|
||||
'<strong>Scala da un pacchetto</strong>' + (hasUsableOrders ? '' : ' (nessun pacchetto con residui)') +
|
||||
'</label>' +
|
||||
'<select id="bk-order" class="form-control" style="margin:6px 0 14px;" ' + (hasUsableOrders ? '' : 'disabled') + '>' +
|
||||
orderOptions +
|
||||
'</select>' +
|
||||
'<label style="display:block;margin:8px 0;">' +
|
||||
'<input type="radio" name="bkmode" value="omaggio" ' + (hasUsableOrders ? '' : 'checked') + '> ' +
|
||||
'<strong>Aggiungi come omaggio</strong> <span style="color:#6b7280;font-size:12px;">(non consuma pacchetti, non spostabile)</span>' +
|
||||
'</label>' +
|
||||
'<label style="display:block;margin:8px 0;">' +
|
||||
'<input type="radio" name="bkmode" value="nuovo"> ' +
|
||||
'<strong>Crea nuovo ordine</strong> <span style="color:#6b7280;font-size:12px;">(1 lezione)</span>' +
|
||||
'</label>' +
|
||||
'<div id="bk-nuovo-fields" style="display:none;margin:6px 0 0;padding-left:22px;">' +
|
||||
'<label style="font-size:13px;">Scadenza ordine:</label>' +
|
||||
'<input type="date" id="bk-expiry" class="form-control" value="' + defExpiry + '">' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
Swal.fire({
|
||||
title: 'Aggiungi partecipante',
|
||||
html: html,
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Inserisci',
|
||||
cancelButtonText: 'Annulla',
|
||||
confirmButtonColor: '#1ebf73',
|
||||
didOpen: () => {
|
||||
document.querySelectorAll('input[name="bkmode"]').forEach(function(r) {
|
||||
r.addEventListener('change', function() {
|
||||
document.getElementById('bk-nuovo-fields').style.display =
|
||||
(this.value === 'nuovo') ? 'block' : 'none';
|
||||
});
|
||||
});
|
||||
},
|
||||
preConfirm: () => {
|
||||
const mode = document.querySelector('input[name="bkmode"]:checked');
|
||||
if (!mode) {
|
||||
Swal.showValidationMessage('Seleziona una modalità');
|
||||
return false;
|
||||
}
|
||||
const result = {
|
||||
mode: mode.value
|
||||
};
|
||||
if (mode.value === 'scala') {
|
||||
const sel = document.getElementById('bk-order').value;
|
||||
if (!sel) {
|
||||
Swal.showValidationMessage('Seleziona un pacchetto');
|
||||
return false;
|
||||
}
|
||||
result.idorder = sel;
|
||||
}
|
||||
if (mode.value === 'nuovo') {
|
||||
const exp = document.getElementById('bk-expiry').value;
|
||||
if (!exp) {
|
||||
Swal.showValidationMessage('Inserisci la scadenza');
|
||||
return false;
|
||||
}
|
||||
result.expiry = exp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}).then((res) => {
|
||||
if (!res.isConfirmed) return;
|
||||
submitBooking(form, res.value);
|
||||
});
|
||||
}
|
||||
|
||||
function submitBooking(form, choice) {
|
||||
form.find('input[name="bkmode"]').remove();
|
||||
form.find('input[name="new_expiry"]').remove();
|
||||
|
||||
$('<input>').attr({
|
||||
type: 'hidden',
|
||||
name: 'bkmode',
|
||||
value: choice.mode
|
||||
}).appendTo(form);
|
||||
|
||||
if (choice.mode === 'scala') {
|
||||
form.find('input[name="idorder"]').val(choice.idorder);
|
||||
} else if (choice.mode === 'omaggio') {
|
||||
form.find('input[name="idorder"]').val('');
|
||||
} else if (choice.mode === 'nuovo') {
|
||||
form.find('input[name="idorder"]').val('');
|
||||
$('<input>').attr({
|
||||
type: 'hidden',
|
||||
name: 'new_expiry',
|
||||
value: choice.expiry
|
||||
}).appendTo(form);
|
||||
}
|
||||
|
||||
form.off('submit');
|
||||
form[0].submit();
|
||||
}
|
||||
|
||||
let searchTimeout;
|
||||
$(document).on('input', '.surname, .name', function() {
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
@@ -88,6 +88,7 @@ $stmt = $db->prepare("
|
||||
ON bc.idserviceschedule = ss.idserviceschedule
|
||||
WHERE bc.iduser = :uid
|
||||
AND bc.status != 'cancelled'
|
||||
AND bc.is_gift = 'N'
|
||||
");
|
||||
$stmt->execute([
|
||||
':now1' => $now,
|
||||
@@ -128,6 +129,7 @@ $stmt = $db->prepare("
|
||||
bc.idservice,
|
||||
bc.idorder,
|
||||
bc.lostlesson,
|
||||
bc.is_gift,
|
||||
ss.dateschedule,
|
||||
s.servicename,
|
||||
s.colorclass,
|
||||
@@ -192,10 +194,17 @@ foreach ($rows as $r) {
|
||||
$reprogrammed = (int) ($r['reprogrammed'] ?? 0);
|
||||
$canReprogram = $reprogrammed < $maxreschedule;
|
||||
|
||||
// Nella webapp i bottoni "Riprogramma" e "Cancella" compaiono entrambi
|
||||
// solo se ($canByTime && $canReprogram). Riproduciamo la stessa condizione.
|
||||
$isGift = ($r['is_gift'] ?? 'N') === 'Y';
|
||||
|
||||
if ($isGift) {
|
||||
// Omaggio: mai riprogrammabile, cancellabile in base al tempo.
|
||||
$canReschedule = false;
|
||||
$canDelete = $canByTime;
|
||||
} else {
|
||||
// Lezione normale: entrambi soggetti a tempo + limite riprogrammazioni.
|
||||
$canReschedule = $canByTime && $canReprogram;
|
||||
$canDelete = $canByTime && $canReprogram;
|
||||
}
|
||||
|
||||
$lessons[] = [
|
||||
'booking_id' => (int) $r['idbookingclass'],
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
// Bufferizziamo da subito così la risposta resta JSON puro
|
||||
// (headscript.php può emettere HTML).
|
||||
ob_start();
|
||||
|
||||
require_once('include/headscript.php');
|
||||
|
||||
/**
|
||||
* get_user_orders.php
|
||||
* --------------------------------------------------------------------------
|
||||
* Restituisce gli ordini ATTIVI (non scaduti) di un utente, con il numero
|
||||
* di ticket residui, per popolare il modale "Aggiungi partecipante"
|
||||
* nell'adminpanel.
|
||||
*
|
||||
* Pattern: webapp (sessione + PDO), come searchemail.php.
|
||||
* Metodo: GET o POST
|
||||
* Param: userid (int)
|
||||
*
|
||||
* Residui = nticket - (prenotazioni non cancellate e NON omaggio)
|
||||
* -> is_gift = 'N' esclude gli omaggi dal consumo.
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
function jsonOut(array $payload): void
|
||||
{
|
||||
if (ob_get_length() !== false) {
|
||||
ob_end_clean();
|
||||
}
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$userid = isset($_REQUEST['userid']) ? (int) $_REQUEST['userid'] : 0;
|
||||
|
||||
if ($userid <= 0) {
|
||||
jsonOut(['error' => 'Utente non valido', 'orders' => []]);
|
||||
}
|
||||
|
||||
try {
|
||||
$today = date('Y-m-d');
|
||||
|
||||
// Ordini dell'utente NON scaduti
|
||||
$sql = "SELECT ob.idorderbook, ob.idservice, ob.nticket, ob.quantityclass,
|
||||
ob.expireon, ob.maxreschedule, ob.reprogrammed,
|
||||
s.servicename
|
||||
FROM orderbook ob
|
||||
LEFT JOIN service s ON ob.idservice = s.idservice
|
||||
WHERE ob.iduser = :uid
|
||||
AND (ob.expireon IS NULL OR ob.expireon >= :today)
|
||||
ORDER BY ob.idorderbook DESC";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([':uid' => $userid, ':today' => $today]);
|
||||
$rows = $stmt->fetchAll();
|
||||
|
||||
// Prepared per contare le prenotazioni che consumano ticket
|
||||
// (non cancellate e non omaggio)
|
||||
$usedStmt = $pdo->prepare(
|
||||
"SELECT COUNT(*) AS n
|
||||
FROM bookingclass
|
||||
WHERE idorder = :oid
|
||||
AND status != 'cancelled'
|
||||
AND is_gift = 'N'"
|
||||
);
|
||||
|
||||
$orders = [];
|
||||
foreach ($rows as $o) {
|
||||
$orderId = (int) $o['idorderbook'];
|
||||
|
||||
// tickets totali: quantityclass se valorizzato, altrimenti nticket
|
||||
$tickets = (int) (($o['quantityclass'] !== null && $o['quantityclass'] !== '')
|
||||
? $o['quantityclass']
|
||||
: ($o['nticket'] ?? 0));
|
||||
|
||||
$usedStmt->execute([':oid' => $orderId]);
|
||||
$used = (int) ($usedStmt->fetch()['n'] ?? 0);
|
||||
|
||||
$remaining = $tickets - $used;
|
||||
if ($remaining < 0) {
|
||||
$remaining = 0;
|
||||
}
|
||||
|
||||
$orders[] = [
|
||||
'idorderbook' => $orderId,
|
||||
'service_name' => (string) ($o['servicename'] ?? ''),
|
||||
'tickets' => $tickets,
|
||||
'used' => $used,
|
||||
'remaining' => $remaining,
|
||||
'expireon' => $o['expireon'] ?? null,
|
||||
'maxreschedule' => (int) ($o['maxreschedule'] ?? 0),
|
||||
'reprogrammed' => (int) ($o['reprogrammed'] ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
jsonOut(['orders' => $orders]);
|
||||
} catch (Throwable $ex) {
|
||||
error_log('get_user_orders error: ' . $ex->getMessage());
|
||||
jsonOut(['error' => 'Errore durante il recupero degli ordini.', 'orders' => []]);
|
||||
}
|
||||
+139
-11
@@ -3,7 +3,17 @@ require_once('include/headscript.php');
|
||||
|
||||
/**
|
||||
* Inserisce un partecipante in una classe (bookingclass).
|
||||
* Se l'utente non esiste ancora (userid mancante), lo crea in auth_users
|
||||
*
|
||||
* Modalità (campo bkmode dal modale):
|
||||
* - 'scala' : aggancia a un ordine esistente (idorder valorizzato), consuma ticket
|
||||
* - 'omaggio' : idorder NULL, is_gift='Y' (non consuma, non spostabile)
|
||||
* - 'nuovo' : crea un nuovo orderbook (1 ticket, maxreschedule 0, scadenza da new_expiry)
|
||||
* e vi aggancia la prenotazione
|
||||
*
|
||||
* Nota FK: bookingclass.idorder ha una foreign key verso orderbook.
|
||||
* Quindi idorder deve essere NULL o un idorderbook reale, MAI 0.
|
||||
*
|
||||
* Se l'utente non esiste (userid mancante), lo crea in auth_users
|
||||
* riusando un eventuale account con la stessa email. Tutto in transazione.
|
||||
*/
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
@@ -15,10 +25,14 @@ $email = trim($_POST['email'] ?? '');
|
||||
$userid = isset($_POST['userid']) ? (int) $_POST['userid'] : 0;
|
||||
$idserviceschedule = isset($_POST['idserviceschedule']) ? (int) $_POST['idserviceschedule'] : 0;
|
||||
$status = trim($_POST['status'] ?? 'booked');
|
||||
$idorder = isset($_POST['idorder']) ? (int) $_POST['idorder'] : 0;
|
||||
$idservice = isset($_POST['idservice']) ? (int) $_POST['idservice'] : 0;
|
||||
$bookingstart = trim($_POST['bookingstart'] ?? '');
|
||||
|
||||
// Nuovi campi dal modale
|
||||
$bkmode = trim($_POST['bkmode'] ?? 'scala'); // scala | omaggio | nuovo
|
||||
$idorderRaw = $_POST['idorder'] ?? '';
|
||||
$newExpiry = trim($_POST['new_expiry'] ?? '');
|
||||
|
||||
// --- Validazione minima ---
|
||||
if ($idserviceschedule <= 0 || $idservice <= 0) {
|
||||
header('Location: adminpanel.php?error=missingdata');
|
||||
@@ -28,10 +42,10 @@ if ($idserviceschedule <= 0 || $idservice <= 0) {
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Se non abbiamo un userid valido, dobbiamo risolvere/creare l'utente
|
||||
// ------------------------------------------------------------------
|
||||
// 1) Risolvi / crea utente
|
||||
// ------------------------------------------------------------------
|
||||
if ($userid <= 0) {
|
||||
|
||||
// 1) Se è stata fornita un'email, controlliamo se l'utente esiste già
|
||||
if ($email !== '') {
|
||||
$check = $pdo->prepare("SELECT id FROM auth_users WHERE email = :email LIMIT 1");
|
||||
$check->execute([':email' => $email]);
|
||||
@@ -41,9 +55,7 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Se ancora non c'è, creiamo un nuovo utente
|
||||
if ($userid <= 0) {
|
||||
// Password casuale hashata (questi account non usano una password condivisa)
|
||||
$randomPassword = bin2hex(random_bytes(8));
|
||||
$hashed = password_hash($randomPassword, PASSWORD_BCRYPT);
|
||||
|
||||
@@ -67,20 +79,136 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Inserimento della prenotazione
|
||||
// ------------------------------------------------------------------
|
||||
// 2) Determina idorder e is_gift in base alla modalità
|
||||
// ------------------------------------------------------------------
|
||||
$idorder = null; // default NULL (rispetta la FK)
|
||||
$isGift = 'N';
|
||||
|
||||
if ($bkmode === 'omaggio') {
|
||||
// Omaggio: nessun ordine, flag gift
|
||||
$idorder = null;
|
||||
$isGift = 'Y';
|
||||
} elseif ($bkmode === 'nuovo') {
|
||||
// Crea un nuovo ordine con 1 ticket, maxreschedule 0
|
||||
if ($newExpiry === '') {
|
||||
throw new RuntimeException('Scadenza mancante per il nuovo ordine.');
|
||||
}
|
||||
|
||||
// Recupera i dati reali dell'utente (email, nome, cognome)
|
||||
$uStmt = $pdo->prepare(
|
||||
"SELECT first_name, last_name, email FROM auth_users WHERE id = :uid LIMIT 1"
|
||||
);
|
||||
$uStmt->execute([':uid' => $userid]);
|
||||
$uRow = $uStmt->fetch() ?: [];
|
||||
$uEmail = $uRow['email'] ?? ($email !== '' ? $email : null);
|
||||
$uFirst = $uRow['first_name'] ?? $name;
|
||||
$uLast = $uRow['last_name'] ?? $surname;
|
||||
|
||||
// Recupera il nome del servizio (per cod / product_name)
|
||||
$sStmt = $pdo->prepare(
|
||||
"SELECT servicename FROM service WHERE idservice = :sid LIMIT 1"
|
||||
);
|
||||
$sStmt->execute([':sid' => $idservice]);
|
||||
$serviceName = (string) ($sStmt->fetchColumn() ?: '');
|
||||
|
||||
// first_lesson_date = data della lezione (solo parte data di bookingstart)
|
||||
$firstLessonDate = null;
|
||||
if ($bookingstart !== '') {
|
||||
try {
|
||||
$firstLessonDate = (new DateTime($bookingstart))->format('Y-m-d');
|
||||
} catch (Throwable $e) {
|
||||
$firstLessonDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
$insOrder = $pdo->prepare(
|
||||
"INSERT INTO orderbook
|
||||
(iduser, idservice, nticket, quantity, quantityclass,
|
||||
expireon, maxreschedule, reprogrammed, status,
|
||||
order_billing_email, cod, product_name,
|
||||
first_name, last_name, first_lesson_date, order_date_created)
|
||||
VALUES
|
||||
(:uid, :service, 1, 1, 1,
|
||||
:expire, 0, 0, 'booked',
|
||||
:bemail, :cod, :pname,
|
||||
:first, :last, :firstlesson, :created)"
|
||||
);
|
||||
$insOrder->execute([
|
||||
':uid' => $userid,
|
||||
':service' => $idservice,
|
||||
':expire' => $newExpiry,
|
||||
':bemail' => $uEmail,
|
||||
':cod' => $serviceName,
|
||||
':pname' => $serviceName,
|
||||
':first' => $uFirst,
|
||||
':last' => $uLast,
|
||||
':firstlesson' => $firstLessonDate,
|
||||
':created' => date('Y-m-d'),
|
||||
]);
|
||||
$idorder = (int) $pdo->lastInsertId();
|
||||
$isGift = 'N';
|
||||
} else {
|
||||
// 'scala': aggancia a ordine esistente
|
||||
$idorder = ($idorderRaw !== '' && (int) $idorderRaw > 0) ? (int) $idorderRaw : null;
|
||||
|
||||
// Se non è stato passato un ordine valido in modalità scala,
|
||||
// per sicurezza NON forziamo 0 (romperebbe la FK): resta NULL.
|
||||
if ($idorder !== null) {
|
||||
// Verifica che l'ordine sia dell'utente e non scaduto
|
||||
$chk = $pdo->prepare(
|
||||
"SELECT expireon FROM orderbook
|
||||
WHERE idorderbook = :oid AND iduser = :uid LIMIT 1"
|
||||
);
|
||||
$chk->execute([':oid' => $idorder, ':uid' => $userid]);
|
||||
$row = $chk->fetch();
|
||||
if (!$row) {
|
||||
throw new RuntimeException('Ordine non valido per questo utente.');
|
||||
}
|
||||
$exp = $row['expireon'] ?? null;
|
||||
if ($exp !== null && $exp < date('Y-m-d')) {
|
||||
throw new RuntimeException('Il pacchetto selezionato è scaduto.');
|
||||
}
|
||||
}
|
||||
$isGift = 'N';
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 2b) Controllo anti-duplicato: utente già in questa classe?
|
||||
// ------------------------------------------------------------------
|
||||
$dup = $pdo->prepare(
|
||||
"SELECT COUNT(*) FROM bookingclass
|
||||
WHERE iduser = :uid
|
||||
AND idserviceschedule = :idschedule
|
||||
AND status != 'cancelled'"
|
||||
);
|
||||
$dup->execute([
|
||||
':uid' => $userid,
|
||||
':idschedule' => $idserviceschedule,
|
||||
]);
|
||||
if ((int) $dup->fetchColumn() > 0) {
|
||||
$pdo->rollBack();
|
||||
header('Location: adminpanel.php?error=duplicate');
|
||||
exit;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 3) Inserimento prenotazione
|
||||
// ------------------------------------------------------------------
|
||||
$insBooking = $pdo->prepare(
|
||||
"INSERT INTO bookingclass
|
||||
(iduser, idserviceschedule, status, idorder, idservice, bookingstart)
|
||||
(iduser, idserviceschedule, status, idorder, idservice, bookingstart, is_gift)
|
||||
VALUES
|
||||
(:iduser, :idschedule, :status, :idorder, :idservice, :bookingstart)"
|
||||
(:iduser, :idschedule, :status, :idorder, :idservice, :bookingstart, :isgift)"
|
||||
);
|
||||
$insBooking->execute([
|
||||
':iduser' => $userid,
|
||||
':idschedule' => $idserviceschedule,
|
||||
':status' => $status,
|
||||
':idorder' => $idorder,
|
||||
':idorder' => $idorder, // NULL o id reale, mai 0
|
||||
':idservice' => $idservice,
|
||||
':bookingstart' => $bookingstart !== '' ? $bookingstart : null,
|
||||
':isgift' => $isGift,
|
||||
]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
Reference in New Issue
Block a user