fixed cancel lesson
This commit is contained in:
@@ -0,0 +1,172 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancellazione di una prenotazione da parte della scuola (dettaglio lezioni in Statistiche).
|
||||||
|
*
|
||||||
|
* POST: id = idbookingclass
|
||||||
|
* send_email = Y/N invia email di conferma all'allieva
|
||||||
|
* count_reprog = Y/N conta come riprogrammazione (orderbook.reprogrammed + 1)
|
||||||
|
*
|
||||||
|
* Copia la prenotazione nello storico (bookingclass_cancellations), poi la elimina.
|
||||||
|
* Tutto in una transazione; l'email parte solo dopo il commit.
|
||||||
|
*/
|
||||||
|
ob_start();
|
||||||
|
require_once('include/headscript.php');
|
||||||
|
|
||||||
|
function jsonOut(array $payload, int $code = 200): void
|
||||||
|
{
|
||||||
|
if (ob_get_length() !== false) ob_end_clean();
|
||||||
|
http_response_code($code);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
jsonOut(['success' => false, 'message' => 'Metodo non consentito.'], 405);
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int) ($_POST['id'] ?? 0);
|
||||||
|
$sendEmail = ($_POST['send_email'] ?? 'N') === 'Y';
|
||||||
|
$countReprog = ($_POST['count_reprog'] ?? 'N') === 'Y';
|
||||||
|
$staffId = (int) $iduserlogin; // chi cancella: sempre l'utente della sessione
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
jsonOut(['success' => false, 'message' => 'Prenotazione non valida.'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$bk = null;
|
||||||
|
$incremented = 'N';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// Prenotazione + dati per l'email, riga bloccata contro doppi click
|
||||||
|
$st = $pdo->prepare("
|
||||||
|
SELECT bc.idbookingclass, bc.idorder, bc.bookingstart,
|
||||||
|
s.servicename, ss.dateschedule,
|
||||||
|
u.email, u.first_name
|
||||||
|
FROM bookingclass bc
|
||||||
|
LEFT JOIN service s ON s.idservice = bc.idservice
|
||||||
|
LEFT JOIN serviceschedule ss ON ss.idserviceschedule = bc.idserviceschedule
|
||||||
|
LEFT JOIN auth_users u ON u.id = bc.iduser
|
||||||
|
WHERE bc.idbookingclass = ?
|
||||||
|
FOR UPDATE
|
||||||
|
");
|
||||||
|
$st->execute([$id]);
|
||||||
|
$bk = $st->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$bk) {
|
||||||
|
throw new RuntimeException('notfound');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conta come riprogrammazione (solo se c'è un ordine collegato)
|
||||||
|
if ($countReprog && $bk['idorder'] !== null) {
|
||||||
|
$up = $pdo->prepare("UPDATE orderbook SET reprogrammed = reprogrammed + 1 WHERE idorderbook = ?");
|
||||||
|
$up->execute([(int) $bk['idorder']]);
|
||||||
|
if ($up->rowCount() === 1) {
|
||||||
|
$incremented = 'Y';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copia nello storico
|
||||||
|
$ins = $pdo->prepare("
|
||||||
|
INSERT INTO bookingclass_cancellations
|
||||||
|
(idbookingclass, idserviceschedule, bookingstart, status, idservice, iduser, idorder,
|
||||||
|
prevbookingstart, is_reprogrammed, is_gift, booking_created_at,
|
||||||
|
cancelled_at, cancelled_by, reprogrammed_incremented)
|
||||||
|
SELECT idbookingclass, idserviceschedule, bookingstart, status, idservice, iduser, idorder,
|
||||||
|
prevbookingstart, is_reprogrammed, is_gift, created_at,
|
||||||
|
NOW(), ?, ?
|
||||||
|
FROM bookingclass
|
||||||
|
WHERE idbookingclass = ?
|
||||||
|
");
|
||||||
|
$ins->execute([$staffId, $incremented, $id]);
|
||||||
|
|
||||||
|
// Eliminazione
|
||||||
|
$del = $pdo->prepare("DELETE FROM bookingclass WHERE idbookingclass = ?");
|
||||||
|
$del->execute([$id]);
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
error_log("admin_cancel_booking #{$id} (staff #{$staffId}): " . $e->getMessage());
|
||||||
|
jsonOut(['success' => false, 'message' => 'Errore durante la cancellazione. Riprova.'], 500);
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
jsonOut(['success' => false, 'message' => 'Prenotazione non trovata (forse già cancellata).'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Email di conferma (dopo il commit, solo se richiesta)
|
||||||
|
* ---------------------------------------------------------------------- */
|
||||||
|
$emailNote = '';
|
||||||
|
|
||||||
|
if ($sendEmail) {
|
||||||
|
$to = trim((string) ($bk['email'] ?? ''));
|
||||||
|
|
||||||
|
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$emailNote = ' Email non inviata: indirizzo dell\'allieva mancante o non valido.';
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
require_once(__DIR__ . '/class/mailer.php');
|
||||||
|
|
||||||
|
$clean = fn($v) => trim((string) $v, "\" \t\n\r\0\x0B");
|
||||||
|
$enc = strtolower($clean($_ENV['MAIL_ENCRYPTION'] ?? 'ssl'));
|
||||||
|
|
||||||
|
$mail = new \PHPMailer\PHPMailer\PHPMailer(true);
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = $clean($_ENV['MAIL_HOST'] ?? '');
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = $_ENV['MAIL_USERNAME'] ?? '';
|
||||||
|
$mail->Password = $_ENV['MAIL_PASSWORD'] ?? '';
|
||||||
|
$mail->Port = (int) ($_ENV['MAIL_PORT'] ?? 465);
|
||||||
|
if (in_array($enc, ['tls', 'ssl'], true)) {
|
||||||
|
$mail->SMTPSecure = $enc;
|
||||||
|
}
|
||||||
|
$mail->Timeout = 20;
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
|
|
||||||
|
$fromName = $clean($_ENV['MAIL_FROM_NAME'] ?? 'YogiBook');
|
||||||
|
$mail->setFrom($clean($_ENV['MAIL_FROM_ADDRESS'] ?? ''), $fromName);
|
||||||
|
|
||||||
|
$nome = trim((string) ($bk['first_name'] ?? ''));
|
||||||
|
$className = $bk['servicename'] ?: 'Lezione';
|
||||||
|
$dateRaw = $bk['dateschedule'] ?: $bk['bookingstart'];
|
||||||
|
$dateTxt = $dateRaw ? date('d/m/Y H:i', strtotime($dateRaw)) : '';
|
||||||
|
|
||||||
|
$h = fn($v) => htmlspecialchars((string) $v, ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
|
$mail->addAddress($to, $nome);
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = 'Lezione cancellata - ' . $className;
|
||||||
|
$mail->Body = "
|
||||||
|
<div style='font-family:Arial,sans-serif;font-size:14px;color:#222'>
|
||||||
|
<p>Ciao " . $h($nome !== '' ? $nome : 'Yogi') . ",</p>
|
||||||
|
<p>ti confermiamo che la tua lezione è stata <b>cancellata</b>.</p>
|
||||||
|
<p>
|
||||||
|
<b>Lezione:</b> " . $h($className) . "<br>
|
||||||
|
<b>Data/ora:</b> " . $h($dateTxt) . "
|
||||||
|
</p>
|
||||||
|
<p>Se hai domande, rispondi pure a questa email.</p>
|
||||||
|
<p>A presto,<br>" . $h($fromName) . "</p>
|
||||||
|
</div>";
|
||||||
|
$mail->AltBody = "Ciao " . ($nome !== '' ? $nome : 'Yogi') . ",\n"
|
||||||
|
. "ti confermiamo che la tua lezione è stata cancellata.\n"
|
||||||
|
. "Lezione: {$className}\nData/ora: {$dateTxt}\n\n{$fromName}";
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
$emailNote = ' Email di conferma inviata.';
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
error_log("admin_cancel_booking #{$id}: email non inviata: " . $e->getMessage());
|
||||||
|
$emailNote = ' Attenzione: la lezione è stata cancellata ma l\'email non è partita.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$reprogNote = $incremented === 'Y' ? ' Conteggiata come riprogrammazione.' : '';
|
||||||
|
|
||||||
|
jsonOut(['success' => true, 'message' => 'Lezione cancellata.' . $reprogNote . $emailNote]);
|
||||||
+46
-19
@@ -617,7 +617,8 @@ Ti ringraziamo per la collaborazione e per l’attenzione alla sicurezza di tutt
|
|||||||
actionTd.append($('<button>').addClass('btn btn-sm btn-danger remove-class action-icon')
|
actionTd.append($('<button>').addClass('btn btn-sm btn-danger remove-class action-icon')
|
||||||
.attr('title', 'Rimuovi')
|
.attr('title', 'Rimuovi')
|
||||||
.html('<i class="fas fa-trash"></i>')
|
.html('<i class="fas fa-trash"></i>')
|
||||||
.data('id', row.idbookingclass));
|
.data('id', row.idbookingclass)
|
||||||
|
.data('order', row.idorder || ''));
|
||||||
} else {
|
} else {
|
||||||
actionTd.text('-');
|
actionTd.text('-');
|
||||||
}
|
}
|
||||||
@@ -639,44 +640,70 @@ Ti ringraziamo per la collaborazione e per l’attenzione alla sicurezza di tutt
|
|||||||
refreshTable(id, nome);
|
refreshTable(id, nome);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rimuovi lezione
|
// Rimuovi lezione: l'insegnante sceglie email e conteggio riprogrammazione
|
||||||
$(document).on('click', '.remove-class', function() {
|
$(document).on('click', '.remove-class', function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
|
var hasOrder = String($(this).data('order') || '') !== '';
|
||||||
var idU = $('#classModal').data('current-id'),
|
var idU = $('#classModal').data('current-id'),
|
||||||
nomeU = $('#userName').text();
|
nomeU = $('#userName').text();
|
||||||
|
|
||||||
|
var html = '<p>La lezione verrà rimossa e salvata nello storico delle cancellazioni.</p>' +
|
||||||
|
'<div class="text-start" style="max-width:360px;margin:0 auto;">' +
|
||||||
|
'<div class="form-check mb-2">' +
|
||||||
|
'<input class="form-check-input" type="checkbox" id="swSendEmail" checked>' +
|
||||||
|
'<label class="form-check-label" for="swSendEmail">Invia email di conferma all\'allieva</label>' +
|
||||||
|
'</div>' +
|
||||||
|
(hasOrder ?
|
||||||
|
'<div class="form-check">' +
|
||||||
|
'<input class="form-check-input" type="checkbox" id="swCountReprog">' +
|
||||||
|
'<label class="form-check-label" for="swCountReprog">Conta come riprogrammazione</label>' +
|
||||||
|
'</div>' :
|
||||||
|
'<div class="text-muted" style="font-size:13px;">Lezione omaggio: nessuna riprogrammazione da conteggiare.</div>') +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
alertBox({
|
alertBox({
|
||||||
title: 'Sei sicuro?',
|
title: 'Cancellare la lezione?',
|
||||||
text: 'Questa lezione verrà rimossa definitivamente.',
|
html: html,
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonColor: '#d33',
|
confirmButtonColor: '#d33',
|
||||||
confirmButtonText: 'Sì, rimuovi',
|
confirmButtonText: 'Sì, cancella',
|
||||||
cancelButtonText: 'Annulla'
|
cancelButtonText: 'Annulla',
|
||||||
|
preConfirm: () => ({
|
||||||
|
send: $('#swSendEmail').is(':checked') ? 'Y' : 'N',
|
||||||
|
count: $('#swCountReprog').is(':checked') ? 'Y' : 'N'
|
||||||
|
})
|
||||||
}).then((r) => {
|
}).then((r) => {
|
||||||
if (r.isConfirmed) {
|
if (!r.isConfirmed) return;
|
||||||
$.post('deleteclass.php', {
|
$.post('admin_cancel_booking.php', {
|
||||||
id: id
|
id: id,
|
||||||
|
send_email: r.value.send,
|
||||||
|
count_reprog: r.value.count
|
||||||
}, null, 'json')
|
}, null, 'json')
|
||||||
.done(function(resp) {
|
.done(function(resp) {
|
||||||
if (resp.success) {
|
if (resp && resp.success) {
|
||||||
alertBox({
|
alertBox({
|
||||||
title: 'Fatto',
|
title: 'Fatto',
|
||||||
text: 'Lezione rimossa.',
|
text: resp.message,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
});
|
});
|
||||||
refreshTable(idU, nomeU);
|
refreshTable(idU, nomeU);
|
||||||
} else alertBox({
|
} else {
|
||||||
|
alertBox({
|
||||||
title: 'Errore',
|
title: 'Errore',
|
||||||
text: resp.message || 'Errore durante la rimozione.',
|
text: (resp && resp.message) || 'Errore durante la cancellazione.',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.fail(() => alertBox({
|
|
||||||
title: 'Errore',
|
|
||||||
text: 'Errore durante la rimozione.',
|
|
||||||
icon: 'error'
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.fail(function(xhr) {
|
||||||
|
var msg = (xhr.responseJSON && xhr.responseJSON.message) || 'Errore durante la cancellazione.';
|
||||||
|
alertBox({
|
||||||
|
title: 'Errore',
|
||||||
|
text: msg,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user