getConnection();
// Verifica che iduserlogin sia definito
if (!isset($iduserlogin)) {
die("Errore: ID utente non definito.");
}
// Recupera i dati della scuola in base all'utente loggato
$stmt = $pdo->prepare("
SELECT id, name, website, email, phone, description, address_street, address_city, address_postal_code, address_province, address_country, logo, status
FROM schools
WHERE owner_id = ?
");
$stmt->execute([$iduserlogin]);
$school = $stmt->fetch();
if (!$school) {
die("Errore: Nessuna scuola trovata per l'utente loggato.");
}
$school_id = $school['id'];
$school_name = $school['name'];
// === RECUPERA IMPOSTAZIONI SCUOLA (valori predefiniti) ===
$stmt = $pdo->prepare("SELECT * FROM school_settings WHERE school_id = ?");
$stmt->execute([$school_id]);
$school_settings = $stmt->fetch() ?: [
'allow_freeze_global' => 1,
'freeze_max_days_global' => 30,
'auto_propagate_on_purchase' => 1,
'allow_full_access_rebooking' => 1,
'allowed_product_types' => 'subscription,carnet,drop_in'
];
// === PREPARA I TIPI DI PRODOTTO CONSENTITI ===
$allowed_types = array_filter(explode(',', $school_settings['allowed_product_types'] ?? 'subscription,carnet,drop_in'));
$types_map = [
'subscription' => 'Abbonamento',
'carnet' => 'Carnet',
'drop_in' => 'Lezione Singola'
];
// Recupera i prodotti della scuola con variazioni, classi e variazioni associate
$stmt = $pdo->prepare("
SELECT p.*,
p.auto_propagate_to_order AS product_auto_propagate,
pv.id AS variation_id, pv.name AS variation_name, pv.price, pv.duration_days, pv.max_entries, pv.weekly_limit, pv.max_recoveries, pv.recovery_validity_days, pv.allow_freeze, pv.freeze_max_days, pv.max_inventory, pv.current_inventory, pv.status AS variation_status, pv.auto_propagate_to_order AS variation_auto_propagate,
GROUP_CONCAT(DISTINCT CASE WHEN pct.variation_id IS NULL THEN c.name END) AS class_names,
GROUP_CONCAT(DISTINCT CASE WHEN pct.variation_id IS NULL THEN CONCAT(ct.level, ' (', ct.day_of_week, ')') END) AS variation_names,
GROUP_CONCAT(DISTINCT CASE WHEN pct.variation_id = pv.id THEN c.name END) AS variation_class_names,
GROUP_CONCAT(DISTINCT CASE WHEN pct.variation_id = pv.id THEN CONCAT(ct.level, ' (', ct.day_of_week, ')') END) AS variation_variation_names
FROM products p
LEFT JOIN product_variations pv ON p.id = pv.product_id
LEFT JOIN product_class_types pct ON (p.id = pct.product_id AND (pct.variation_id = pv.id OR pct.variation_id IS NULL))
LEFT JOIN class_types ct ON pct.class_type_id = ct.id
LEFT JOIN classes c ON ct.class_id = c.id
WHERE p.school_id = ?
GROUP BY p.id, pv.id
ORDER BY p.created_at DESC, pv.created_at DESC
");
$stmt->execute([$school_id]);
$raw_products = $stmt->fetchAll();
// Organizza i prodotti in una struttura gerarchica: prodotto -> variazioni
$products = [];
foreach ($raw_products as $row) {
$product_id = $row['id'];
if (!isset($products[$product_id])) {
$products[$product_id] = [
'id' => $row['id'],
'name' => $row['name'],
'type' => $row['type'],
'is_full_access' => $row['is_full_access'],
'status' => $row['status'],
'auto_propagate_to_order' => $row['product_auto_propagate'],
'class_names' => $row['class_names'],
'variation_names' => $row['variation_names'],
'variations' => []
];
}
if ($row['variation_id']) {
$products[$product_id]['variations'][] = [
'variation_id' => $row['variation_id'],
'variation_name' => $row['variation_name'],
'price' => $row['price'],
'duration_days' => $row['duration_days'],
'max_entries' => $row['max_entries'],
'weekly_limit' => $row['weekly_limit'],
'max_recoveries' => $row['max_recoveries'],
'recovery_validity_days' => $row['recovery_validity_days'],
'allow_freeze' => $row['allow_freeze'],
'freeze_max_days' => $row['freeze_max_days'],
'max_inventory' => $row['max_inventory'],
'current_inventory' => $row['current_inventory'],
'variation_status' => $row['variation_status'],
'auto_propagate_to_order' => $row['variation_auto_propagate'],
'class_names' => $row['variation_class_names'],
'variation_names' => $row['variation_variation_names']
];
}
}
// Recupera tutte le classi e le loro variazioni per il modale di aggiunta/modifica prodotti
$stmt = $pdo->prepare("
SELECT c.id AS class_id, c.name AS class_name, ct.id AS class_type_id, ct.level, ct.day_of_week
FROM classes c
LEFT JOIN class_types ct ON c.id = ct.class_id
WHERE c.school_id = ?
ORDER BY c.name, ct.level, ct.day_of_week
");
$stmt->execute([$school_id]);
$class_data = $stmt->fetchAll();
// Organizza i dati in una struttura gerarchica: classi -> variazioni
$all_classes = [];
foreach ($class_data as $row) {
$class_id = $row['class_id'];
if (!isset($all_classes[$class_id])) {
$all_classes[$class_id] = [
'name' => $row['class_name'],
'variations' => []
];
}
if ($row['class_type_id']) {
$all_classes[$class_id]['variations'][] = [
'id' => $row['class_type_id'],
'name' => ucfirst($row['level']) . ' (' . ucfirst($row['day_of_week']) . ')'
];
}
}
// Gestione delle azioni (aggiunta, modifica, eliminazione)
$success_message = '';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
// Aggiungi un prodotto
// Aggiungi un prodotto
if ($action === 'add_product') {
$name = $_POST['name'] ?? '';
$type = $_POST['type'] ?? '';
$variation_name = $_POST['variation_name'] ?? '';
$price = $_POST['price'] ?? 0;
$duration_days = $_POST['duration_days'] ?: null;
$max_entries = $_POST['max_entries'] ?: null;
$weekly_limit = $_POST['weekly_limit'] ?: null;
$max_recoveries = $_POST['max_recoveries'] ?: null;
$recovery_validity_days = $_POST['recovery_validity_days'] ?: null;
$allow_freeze = $_POST['allow_freeze'] ?? 0;
$freeze_max_days = $_POST['freeze_max_days'] ?: null;
$max_inventory = $_POST['max_inventory'] ?: null;
$is_full_access = $_POST['is_full_access'] ?? 0;
$auto_propagate_to_order = $_POST['auto_propagate_to_order'] ?? 0; // Nuovo campo
$class_types = $_POST['class_types'] ?? [];
if (empty($name) || empty($type) || empty($variation_name) || $price <= 0) {
$error = "Nome, tipo, nome variazione e prezzo sono obbligatori.";
} else {
// Inserisci il prodotto principale
$stmt = $pdo->prepare("
INSERT INTO products (school_id, name, type, is_full_access, auto_propagate_to_order)
VALUES (?, ?, ?, ?, ?)
");
$stmt->execute([$school_id, $name, $type, $is_full_access, $auto_propagate_to_order]);
$product_id = $pdo->lastInsertId();
// Inserisci la variazione del prodotto
$current_inventory = $max_inventory;
$stmt = $pdo->prepare("
INSERT INTO product_variations (product_id, name, price, duration_days, max_entries, weekly_limit, max_recoveries, recovery_validity_days, allow_freeze, freeze_max_days, max_inventory, current_inventory, auto_propagate_to_order)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$product_id,
$variation_name,
$price,
$duration_days,
$max_entries,
$weekly_limit,
$max_recoveries,
$recovery_validity_days,
$allow_freeze,
$freeze_max_days,
$max_inventory,
$current_inventory,
$auto_propagate_to_order // Propaga lo stesso valore del prodotto
]);
// Associa le classi selezionate (se non è full access)
if (!$is_full_access && !empty($class_types)) {
$stmt = $pdo->prepare("INSERT INTO product_class_types (product_id, class_type_id, entry_cost) VALUES (?, ?, 1)");
foreach ($class_types as $class_type_id) {
$stmt->execute([$product_id, $class_type_id]);
}
}
$success_message = "Prodotto aggiunto con successo!";
header("Location: products.php");
exit;
}
}
// add product variation
// Aggiungi una variazione
// Aggiungi una variazione
if ($action === 'add_variation') {
$product_id = $_POST['product_id'] ?? 0;
$variation_name = $_POST['variation_name'] ?? '';
$price = $_POST['price'] ?? 0;
$duration_days = $_POST['duration_days'] ?: null;
$max_entries = $_POST['max_entries'] ?: null;
$weekly_limit = $_POST['weekly_limit'] ?: null;
$max_recoveries = $_POST['max_recoveries'] ?: null;
$recovery_validity_days = $_POST['recovery_validity_days'] ?: null;
$allow_freeze = $_POST['allow_freeze'] ?? 0;
$freeze_max_days = $_POST['freeze_max_days'] ?: null;
$max_inventory = $_POST['max_inventory'] ?: null;
$current_inventory = $_POST['current_inventory'] ?? $max_inventory;
$status = $_POST['status'] ?? 'active';
$auto_propagate_to_order = $_POST['auto_propagate_to_order'] ?? 0; // Nuovo campo
$class_types = $_POST['class_types'] ?? [];
if ($product_id <= 0 || empty($variation_name) || $price <= 0) {
$error = "ID prodotto, nome variazione e prezzo sono obbligatori.";
} else {
// Verifica che il prodotto appartenga alla scuola e recupera is_full_access
$stmt = $pdo->prepare("SELECT id, is_full_access FROM products WHERE id = ? AND school_id = ?");
$stmt->execute([$product_id, $school_id]);
$product = $stmt->fetch();
if (!$product) {
$error = "Prodotto non trovato.";
} else {
$is_full_access = $product['is_full_access'];
// Inserisci la nuova variazione
$stmt = $pdo->prepare("
INSERT INTO product_variations (product_id, name, price, duration_days, max_entries, weekly_limit, max_recoveries, recovery_validity_days, allow_freeze, freeze_max_days, max_inventory, current_inventory, status, auto_propagate_to_order)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$product_id,
$variation_name,
$price,
$duration_days,
$max_entries,
$weekly_limit,
$max_recoveries,
$recovery_validity_days,
$allow_freeze,
$freeze_max_days,
$max_inventory,
$current_inventory,
$status,
$auto_propagate_to_order
]);
// Recupera l'ID della variazione appena creata
$variation_id = $pdo->lastInsertId();
// Associa le classi alla variazione (se non è full access)
if (!$is_full_access) {
if (!empty($class_types)) {
// Usa le classi selezionate nel modale
$stmt = $pdo->prepare("INSERT INTO product_class_types (product_id, variation_id, class_type_id, entry_cost) VALUES (?, ?, ?, 1)");
foreach ($class_types as $class_type_id) {
$stmt->execute([$product_id, $variation_id, $class_type_id]);
}
} else {
// Se non sono state selezionate classi, eredita quelle del prodotto
$stmt = $pdo->prepare("SELECT class_type_id FROM product_class_types WHERE product_id = ? AND variation_id IS NULL");
$stmt->execute([$product_id]);
$inherited_class_types = $stmt->fetchAll(PDO::FETCH_COLUMN);
if (!empty($inherited_class_types)) {
$stmt = $pdo->prepare("INSERT INTO product_class_types (product_id, variation_id, class_type_id, entry_cost) VALUES (?, ?, ?, 1)");
foreach ($inherited_class_types as $class_type_id) {
$stmt->execute([$product_id, $variation_id, $class_type_id]);
}
}
}
}
$success_message = "Variazione aggiunta con successo!";
header("Location: products.php");
exit;
}
}
}
// Modifica un prodotto
// Modifica un prodotto
if ($action === 'edit_product') {
$id = $_POST['id'] ?? 0;
$variation_id = $_POST['variation_id'] ?? 0;
$name = $_POST['name'] ?? '';
$type = $_POST['type'] ?? '';
$variation_name = $_POST['variation_name'] ?? '';
$price = $_POST['price'] ?? 0;
$duration_days = $_POST['duration_days'] ?: null;
$max_entries = $_POST['max_entries'] ?: null;
$weekly_limit = $_POST['weekly_limit'] ?: null;
$max_recoveries = $_POST['max_recoveries'] ?: null;
$recovery_validity_days = $_POST['recovery_validity_days'] ?: null;
$allow_freeze = $_POST['allow_freeze'] ?? 0;
$freeze_max_days = $_POST['freeze_max_days'] ?: null;
$max_inventory = $_POST['max_inventory'] ?: null;
$current_inventory = $_POST['current_inventory'] ?: null;
$is_full_access = $_POST['is_full_access'] ?? 0;
$status = $_POST['status'] ?? 'active';
$auto_propagate_to_order = $_POST['auto_propagate_to_order'] ?? 0; // Nuovo campo
$class_types = $_POST['class_types'] ?? [];
if ($id <= 0 || empty($name) || empty($type)) {
$error = "ID, nome e tipo sono obbligatori.";
} elseif ($variation_id > 0 && (empty($variation_name) || $price <= 0)) {
$error = "Nome variazione e prezzo sono obbligatori per la variazione.";
} else {
// Verifica che il prodotto appartenga alla scuola
$stmt = $pdo->prepare("SELECT id FROM products WHERE id = ? AND school_id = ?");
$stmt->execute([$id, $school_id]);
if (!$stmt->fetch()) {
$error = "Prodotto non trovato.";
} else {
// Aggiorna il prodotto principale
$stmt = $pdo->prepare("
UPDATE products
SET name = ?, type = ?, is_full_access = ?, status = ?, auto_propagate_to_order = ?
WHERE id = ?
");
$stmt->execute([$name, $type, $is_full_access, $status, $auto_propagate_to_order, $id]);
// Aggiorna la variazione, se presente
if ($variation_id > 0) {
$stmt = $pdo->prepare("
UPDATE product_variations
SET name = ?, price = ?, duration_days = ?, max_entries = ?, weekly_limit = ?, max_recoveries = ?, recovery_validity_days = ?, allow_freeze = ?, freeze_max_days = ?, max_inventory = ?, current_inventory = ?, status = ?, auto_propagate_to_order = ?
WHERE id = ? AND product_id = ?
");
$stmt->execute([
$variation_name,
$price,
$duration_days,
$max_entries,
$weekly_limit,
$max_recoveries,
$recovery_validity_days,
$allow_freeze,
$freeze_max_days,
$max_inventory,
$current_inventory,
$status,
$auto_propagate_to_order,
$variation_id,
$id
]);
}
// Aggiorna le classi associate
if ($variation_id > 0) {
// Se stiamo modificando una variazione, aggiorna le classi associate alla variazione
$stmt = $pdo->prepare("DELETE FROM product_class_types WHERE product_id = ? AND variation_id = ?");
$stmt->execute([$id, $variation_id]);
} else {
// Se stiamo modificando il prodotto, aggiorna le classi associate al prodotto
$stmt = $pdo->prepare("DELETE FROM product_class_types WHERE product_id = ? AND variation_id IS NULL");
$stmt->execute([$id]);
}
if (!$is_full_access && !empty($class_types)) {
$stmt = $pdo->prepare("INSERT INTO product_class_types (product_id, variation_id, class_type_id, entry_cost) VALUES (?, ?, ?, 1)");
foreach ($class_types as $class_type_id) {
$class_type_id = (int)$class_type_id;
if ($class_type_id > 0) {
// Verifica che il class_type_id esista
$checkStmt = $pdo->prepare("SELECT id FROM class_types WHERE id = ?");
$checkStmt->execute([$class_type_id]);
if ($checkStmt->fetch()) {
$stmt->execute([$id, $variation_id > 0 ? $variation_id : null, $class_type_id]);
}
}
}
}
$success_message = "Prodotto modificato con successo!";
header("Location: products.php");
exit;
}
}
}
// Elimina un prodotto
if ($action === 'delete_product') {
$id = $_POST['id'] ?? 0;
if ($id <= 0) {
$error = "ID non valido.";
} else {
$stmt = $pdo->prepare("DELETE FROM products WHERE id = ? AND school_id = ?");
$stmt->execute([$id, $school_id]);
$success_message = "Prodotto eliminato con successo!";
header("Location: products.php");
exit;
}
}
//delete variations product
if ($action === 'delete_variation') {
$variation_id = $_POST['variation_id'] ?? 0;
if ($variation_id <= 0) {
$error = "ID variazione non valido.";
} else {
// Verifica che la variazione appartenga a un prodotto della scuola
$stmt = $pdo->prepare("
SELECT pv.id
FROM product_variations pv
JOIN products p ON pv.product_id = p.id
WHERE pv.id = ? AND p.school_id = ?
");
$stmt->execute([$variation_id, $school_id]);
if (!$stmt->fetch()) {
$error = "Variazione non trovata.";
} else {
$stmt = $pdo->prepare("DELETE FROM product_variations WHERE id = ?");
$stmt->execute([$variation_id]);
$success_message = "Variazione eliminata con successo!";
header("Location: products.php");
exit;
}
}
}
}
?>
| Nome |
Variazione |
Tipo |
Prezzo |
Durata (giorni) |
Ingressi |
Inventario |
Accesso Completo |
Classi Associate |
Azioni |
| Nessun prodotto trovato. |
|
- |
'Carnet',
'subscription' => 'Abbonamento',
'drop_in' => 'Lezione Singola'
];
echo $type_labels[$product['type']] ?? $product['type'];
?>
|
- |
- |
- |
- |
|
' . htmlspecialchars($variation_names) . '';
}
} else {
echo 'Nessuna classe associata';
}
?>
|
|
|
|
|
€ |
|
|
|
|
' . htmlspecialchars($variation_names) . '';
}
} else {
echo 'Nessuna classe associata';
}
?>
|
|