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; } } } } ?>
Prodotti
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'; } ?>