From 0ff3c42d35079a7523b6574989ce1f9690c59536 Mon Sep 17 00:00:00 2001 From: "fefe.clochette" Date: Thu, 4 Jun 2026 13:09:47 +0200 Subject: [PATCH] =?UTF-8?q?fix=20b=C3=AAtises=20et=20update=20schema=20sql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/init/00-setup.sql | 3 +- docker/schema_family.sql | 125 +++++---- migrate.php | 271 +++++++++++-------- modules/budget/includes/api/save-budget.php | 162 +++++------ modules/budget/views/budget_prev.php | 182 +++++++------ modules/budget/views/suivi.php | 52 +++- schema.sql | 284 ++++++++++++++++++-- 7 files changed, 712 insertions(+), 367 deletions(-) diff --git a/docker/init/00-setup.sql b/docker/init/00-setup.sql index 206a992..b94191d 100644 --- a/docker/init/00-setup.sql +++ b/docker/init/00-setup.sql @@ -36,6 +36,7 @@ CREATE TABLE IF NOT EXISTS user_calendar_integrations ( calendar_url VARCHAR(1024) DEFAULT NULL, status VARCHAR(30) DEFAULT 'connected', last_sync_at DATETIME DEFAULT NULL, + calendar_prefs_json TEXT DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uq_user_provider (user_id, provider), @@ -44,4 +45,4 @@ CREATE TABLE IF NOT EXISTS user_calendar_integrations ( -- Donner au user applicatif le droit de créer des DBs famille GRANT ALL PRIVILEGES ON `househub_f%`.* TO 'househub'@'%'; -FLUSH PRIVILEGES; +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/docker/schema_family.sql b/docker/schema_family.sql index 34413ca..0684f5e 100644 --- a/docker/schema_family.sql +++ b/docker/schema_family.sql @@ -1,18 +1,8 @@ --- HouseHub — Schéma MySQL --- Créer la base : CREATE DATABASE househub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - +-- HouseHub — Schéma MySQL (Modèle pour une base famille) SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; --- ─── Utilisateurs ───────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS pf_users ( - id INT AUTO_INCREMENT PRIMARY KEY, - username VARCHAR(100) NOT NULL UNIQUE, - password_hash VARCHAR(255) NOT NULL, - display_name VARCHAR(100) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - --- ─── Familles ───────────────────────────────────────────────────────────── +-- ─── Configuration du foyer ─────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_foyer_settings ( id INT AUTO_INCREMENT PRIMARY KEY, currency VARCHAR(10) NOT NULL DEFAULT '€', @@ -20,17 +10,25 @@ CREATE TABLE IF NOT EXISTS pf_foyer_settings ( updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- Initialisation de la ligne unique par défaut INSERT IGNORE INTO pf_foyer_settings (id, currency, zone_scolaire) VALUES (1, '€', 'C'); +-- ─── Utilisateurs (Legacy) ──────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(100) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + display_name VARCHAR(100) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- ─── Personnes ──────────────────────────────────────────────────────────────── CREATE TABLE `pf_people` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `user_id` int(11) DEFAULT NULL, + `name` varchar(100) NOT NULL, + `user_id` int(11) DEFAULT NULL, `role` varchar(50) DEFAULT NULL, + `color` varchar(7) DEFAULT '#0891b2', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ─── Calendrier familial ────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_events ( @@ -115,20 +113,22 @@ CREATE TABLE IF NOT EXISTS pf_expenses ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS pf_alloc_categories ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(100) NOT NULL, - target DECIMAL(10,2) DEFAULT 0, - holiday_id INT DEFAULT NULL, - sort_order INT DEFAULT 0 + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + target DECIMAL(10,2) DEFAULT 0.00, + transfer_dest VARCHAR(50) DEFAULT NULL, + holiday_id INT DEFAULT NULL, + sort_order INT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS pf_alloc_values ( id INT AUTO_INCREMENT PRIMARY KEY, month_date VARCHAR(10) NOT NULL, cat_id INT NOT NULL, - amount_alex DECIMAL(10,2) DEFAULT 0, - amount_laia DECIMAL(10,2) DEFAULT 0, - UNIQUE KEY uq_alloc (month_date, cat_id) + person_id INT NOT NULL, + amount DECIMAL(10,2) DEFAULT 0.00, + UNIQUE KEY uq_alloc_person (month_date, cat_id, person_id), + FOREIGN KEY (person_id) REFERENCES pf_people(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS pf_savings ( @@ -161,9 +161,22 @@ CREATE TABLE IF NOT EXISTS pf_salary_config ( CREATE TABLE IF NOT EXISTS pf_import_rules ( id INT AUTO_INCREMENT PRIMARY KEY, keyword VARCHAR(255) NOT NULL UNIQUE, - category VARCHAR(100) NOT NULL + category VARCHAR(100) NOT NULL, + budget_item_id INT(11) NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS pf_advances ( + id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + advance_date date NOT NULL, + payer varchar(50) NOT NULL, + description varchar(255) NOT NULL, + amount decimal(10,2) DEFAULT 0.00, + from_savings tinyint(1) DEFAULT 0, + is_resolved tinyint(1) DEFAULT 0, + created_at datetime DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ─── Notes / Memo ───────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_notes ( id INT AUTO_INCREMENT PRIMARY KEY, note_type VARCHAR(100) NOT NULL, @@ -172,6 +185,29 @@ CREATE TABLE IF NOT EXISTS pf_notes ( UNIQUE KEY uq_note (note_type, reference_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS pf_memo_notes ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + content LONGTEXT DEFAULT '', + tags VARCHAR(1000) DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FULLTEXT KEY ft_notes (title, content, tags) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_memo_attachments ( + id INT AUTO_INCREMENT PRIMARY KEY, + note_id INT NOT NULL, + type ENUM('image','file','url') NOT NULL DEFAULT 'file', + filename VARCHAR(255) DEFAULT NULL, + original_name VARCHAR(255) DEFAULT NULL, + url TEXT DEFAULT NULL, + label VARCHAR(255) DEFAULT NULL, + size INT DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (note_id) REFERENCES pf_memo_notes(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- ─── Voyages / Holidays ─────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_holidays ( id INT AUTO_INCREMENT PRIMARY KEY, @@ -239,14 +275,6 @@ CREATE TABLE IF NOT EXISTS pf_gifts ( amount DECIMAL(8,2) DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -SET FOREIGN_KEY_CHECKS = 1; - --- ─── Données initiales ──────────────────────────────────────────────────────── --- Utilisateur admin (mot de passe : changeme → à modifier !) -INSERT IGNORE INTO pf_users (id, username, password_hash, display_name) -VALUES (1, 'admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Admin'); - - -- ─── Garage Manager ─────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_vehicles ( id INT AUTO_INCREMENT PRIMARY KEY, @@ -321,30 +349,6 @@ CREATE TABLE IF NOT EXISTS pf_garage_documents ( FOREIGN KEY (maintenance_id) REFERENCES pf_maintenances(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- ─── Notes / Memo ───────────────────────────────────────────────────────────── -CREATE TABLE IF NOT EXISTS pf_memo_notes ( - id INT AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(255) NOT NULL, - content LONGTEXT DEFAULT '', - tags VARCHAR(1000) DEFAULT '', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FULLTEXT KEY ft_notes (title, content, tags) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -CREATE TABLE IF NOT EXISTS pf_memo_attachments ( - id INT AUTO_INCREMENT PRIMARY KEY, - note_id INT NOT NULL, - type ENUM('image','file','url') NOT NULL DEFAULT 'file', - filename VARCHAR(255) DEFAULT NULL, - original_name VARCHAR(255) DEFAULT NULL, - url TEXT DEFAULT NULL, - label VARCHAR(255) DEFAULT NULL, - size INT DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (note_id) REFERENCES pf_memo_notes(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - -- ─── Todo ───────────────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_todo_lists ( id INT AUTO_INCREMENT PRIMARY KEY, @@ -372,7 +376,7 @@ CREATE TABLE IF NOT EXISTS pf_todos ( FOREIGN KEY (list_id) REFERENCES pf_todo_lists(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- ─── Module Liste (multi-listes) ────────────────────────────────────────────── +-- ─── Module Liste (Courses partagées) ───────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_lists ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL DEFAULT 'Ma liste', @@ -382,7 +386,7 @@ CREATE TABLE IF NOT EXISTS pf_lists ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -INSERT INTO pf_lists (id, name, position) VALUES (1, 'Ma liste', 0); +INSERT IGNORE INTO pf_lists (id, name, position) VALUES (1, 'Ma liste', 0); CREATE TABLE IF NOT EXISTS pf_grocery_items ( id INT AUTO_INCREMENT PRIMARY KEY, @@ -447,6 +451,7 @@ CREATE TABLE IF NOT EXISTS pf_calendar_event_links ( external_uid VARCHAR(255) NOT NULL, external_etag VARCHAR(255) DEFAULT NULL, calendar_url VARCHAR(1024) DEFAULT NULL, + external_href VARCHAR(2048) DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uq_calendar_event (calendar_event_id), @@ -498,3 +503,5 @@ CREATE TABLE IF NOT EXISTS pf_planka_config ( updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/migrate.php b/migrate.php index f177197..50b5079 100644 --- a/migrate.php +++ b/migrate.php @@ -1,9 +1,26 @@ 🚀 Début de la migration Globale ..."; +// ========================================== +// 0. MISE À JOUR DE LA META DB +// ========================================== +echo "

Mise à jour Meta DB (househub_meta)

"; + +// ========================================== +// MIGRATIONS PAR FAMILLE +// ========================================== $stmt = $meta_pdo->query("SELECT id, name, db_name FROM families WHERE db_name != ''"); $families = $stmt->fetchAll(); @@ -15,153 +32,187 @@ foreach ($families as $f) { $db_name = $f['db_name']; $family_id = $f['id']; echo "

Mise à jour de {$f['name']} ($db_name)

"; + + // ========================================== + // 8. SEPARATION CIBLE BUDGET / DESTINATION VIREMENT + // ========================================== + echo "
  • Table pf_alloc_categories (Fix Collision) : "; + try { + $fam_pdo->exec("ALTER TABLE pf_alloc_categories ADD COLUMN transfer_dest VARCHAR(50) DEFAULT NULL AFTER target"); + echo "Succès ! Colonne 'transfer_dest' ajoutée pour préserver vos objectifs chiffrés.
  • "; + } catch (\PDOException $e) { + echo "La colonne transfer_dest existe déjà."; + } + + // ========================================== + // 9. TRANSFERT DES DONNÉES (Cible -> Destination) + // ========================================== + echo "
  • Table pf_alloc_categories (Récupération des données) : "; + try { + // Déplacer les textes (qui commencent par "vers") dans transfer_dest + $updated = $fam_pdo->exec("UPDATE pf_alloc_categories SET transfer_dest = target, target = '0' WHERE target LIKE 'vers %'"); + echo "$updated destinations récupérées et déplacées proprement.
  • "; + + // Remettre la colonne target en format numérique maintenant qu'elle est propre + $fam_pdo->exec("ALTER TABLE pf_alloc_categories MODIFY target DECIMAL(10,2) DEFAULT 0.00"); + echo "
  • Colonne 'target' re-sécurisée en format monétaire DECIMAL(10,2).
  • "; + } catch (\PDOException $e) { + echo "Erreur : " . $e->getMessage() . ""; + } } + + echo "

    🎉 Migration terminée avec succès !

    "; ?> \ No newline at end of file diff --git a/modules/budget/includes/api/save-budget.php b/modules/budget/includes/api/save-budget.php index 1aaf4d4..71db02a 100644 --- a/modules/budget/includes/api/save-budget.php +++ b/modules/budget/includes/api/save-budget.php @@ -60,59 +60,85 @@ if ($action === 'update_salary_config') { // 2. MISE A JOUR TABLEAU REPARTITION (AJAX) if ($action === 'update_allocation') { - header('Content-Type: application/json'); - $date = $_POST['month_date']; - $catId = $_POST['cat_id']; - $person = $_POST['person']; // 'amount_alex' ou 'amount_laia' - $value = floatval($_POST['value']); + header('Content-Type: application/json'); + $date = $_POST['month_date']; + $catId = (int)$_POST['cat_id']; + $personId = (int)$_POST['person_id']; // 🟢 Reçoit l'ID de la personne directement ! + $value = floatval($_POST['value']); - $stmt = $pdo->prepare("INSERT INTO pf_alloc_values (month_date, cat_id, $person) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE $person = VALUES($person)"); - $stmt->execute([$date, $catId, $value]); + if ($catId <= 0 || $personId <= 0) { + echo json_encode(['success' => false, 'error' => 'Données invalides.']); + exit; + } + + $stmt = $pdo->prepare("INSERT INTO pf_alloc_values (month_date, cat_id, person_id, amount) + VALUES (?, ?, ?, ?) + ON DUPLICATE KEY UPDATE amount = VALUES(amount)"); + $stmt->execute([$date, $catId, $personId, $value]); + echo json_encode(['success' => true]); exit; } // 3. GESTION DES CATEGORIES (Ajout) if ($action === 'add_category') { - $name = trim($_POST['name']); - $target = trim($_POST['target']); - $holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null; - - if (!empty($name)) { - $stmt = $pdo->prepare("INSERT INTO pf_alloc_categories (name, target, holiday_id) VALUES (?, ?, ?)"); - $stmt->execute([$name, $target, $holiday_id]); - } - - // --- NOUVEAU : Réponse AJAX --- - if (isset($_POST['ajax'])) { - header('Content-Type: application/json'); - echo json_encode(['success' => true]); + try { + $name = trim($_POST['name']); + $transfer_dest = trim($_POST['transfer_dest'] ?? ''); + $target = floatval($_POST['target'] ?? 0); + $holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null; + + if (!empty($name)) { + $stmt = $pdo->prepare("INSERT INTO pf_alloc_categories (name, target, transfer_dest, holiday_id) VALUES (?, ?, ?, ?)"); + $stmt->execute([$name, $target, $transfer_dest, $holiday_id]); + } + + if (isset($_POST['ajax'])) { + header('Content-Type: application/json'); + echo json_encode(['success' => true]); + exit; + } + header("Location: " . $_SERVER['HTTP_REFERER']); exit; + } catch (Exception $e) { + if (isset($_POST['ajax'])) { + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); + exit; + } + die($e->getMessage()); } - header("Location: " . $_SERVER['HTTP_REFERER']); - exit; } // 4. MODIFICATION D'UNE CATEGORIE if ($action === 'update_category') { - $id = (int)$_POST['cat_id']; - $name = trim($_POST['name']); - $target = trim($_POST['target']); - $holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null; + try { + $id = (int)$_POST['cat_id']; + $name = trim($_POST['name']); + $transfer_dest = trim($_POST['transfer_dest'] ?? ''); + $target = floatval($_POST['target'] ?? 0); + $holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null; - if ($id > 0 && !empty($name)) { - $stmt = $pdo->prepare("UPDATE pf_alloc_categories SET name = ?, target = ?, holiday_id = ? WHERE id = ?"); - $stmt->execute([$name, $target, $holiday_id, $id]); - } + if ($id > 0 && !empty($name)) { + $stmt = $pdo->prepare("UPDATE pf_alloc_categories SET name = ?, target = ?, transfer_dest = ?, holiday_id = ? WHERE id = ?"); + $stmt->execute([$name, $target, $transfer_dest, $holiday_id, $id]); + } - // --- NOUVEAU : Réponse AJAX --- - if (isset($_POST['ajax'])) { - header('Content-Type: application/json'); - echo json_encode(['success' => true]); + if (isset($_POST['ajax'])) { + header('Content-Type: application/json'); + echo json_encode(['success' => true]); + exit; + } + header("Location: " . $_SERVER['HTTP_REFERER']); exit; + } catch (Exception $e) { + if (isset($_POST['ajax'])) { + header('Content-Type: application/json'); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); + exit; + } + die($e->getMessage()); } - header("Location: " . $_SERVER['HTTP_REFERER']); - exit; } // 5. SUPPRESSION CATEGORIE @@ -136,43 +162,48 @@ if ($action === 'delete_category') { // 6. VALIDATION DES VIREMENTS (Complex Business Logic) if ($action === 'validate_transfers') { header('Content-Type: application/json'); - $person = $_POST['person']; + $personId = (int)$_POST['person_id']; // Reçoit l'ID parent $monthDate = $_POST['month_date']; try { $pdo->beginTransaction(); + $stmtP = $pdo->prepare("SELECT name FROM pf_people WHERE id = ?"); + $stmtP->execute([$personId]); + $dbPersonName = $stmtP->fetchColumn(); + if (!$dbPersonName) throw new Exception("Parent introuvable."); + $stmt = $pdo->prepare(" - SELECT v.*, c.name as cat_name, c.target, c.holiday_id - FROM pf_alloc_values v + SELECT v.amount, c.name as cat_name, c.transfer_dest, c.holiday_id + FROM pf_alloc_values v JOIN pf_alloc_categories c ON v.cat_id = c.id - WHERE v.month_date = ? + WHERE v.month_date = ? AND v.person_id = ? "); - $stmt->execute([$monthDate]); + $stmt->execute([$monthDate, $personId]); $budgetLines = $stmt->fetchAll(PDO::FETCH_ASSOC); $transfersToDo = []; foreach ($budgetLines as $line) { - $amount = ($person === 'Alex') ? $line['amount_alex'] : $line['amount_laia']; - if ($amount <= 0) continue; + $amount = (float)$line['amount']; + if ($amount <= 0) continue; - $target = trim($line['target']); + $dest = trim($line['transfer_dest']); $catName = trim($line['cat_name']); - $holidayId = $line['holiday_id']; + $holidayId = $line['holiday_id']; $targetOwner = null; - if ($target === 'vers L.Perso') { $targetOwner = $person; } - elseif ($target === 'vers L.Pol') { $targetOwner = 'Pol'; } - elseif ($target === 'vers L.Pep') { $targetOwner = 'Pep'; } - elseif ($target === 'vers commune') { continue; } + if ($dest === 'vers L.Perso') { $targetOwner = $dbPersonName; } + elseif ($dest === 'vers L.Pol') { $targetOwner = 'Pol'; } + elseif ($dest === 'vers L.Pep') { $targetOwner = 'Pep'; } + elseif ($dest === 'vers commune') { continue; } if ($targetOwner) { if (!isset($transfersToDo[$targetOwner])) { $transfersToDo[$targetOwner] = ['total_add' => 0, 'cats' => []]; } $transfersToDo[$targetOwner]['total_add'] += $amount; - + if (!isset($transfersToDo[$targetOwner]['cats'][$catName])) { $transfersToDo[$targetOwner]['cats'][$catName] = ['amount' => 0, 'holiday_id' => $holidayId]; } @@ -181,7 +212,6 @@ if ($action === 'validate_transfers') { } foreach ($transfersToDo as $owner => $data) { - // A. VERIFIER EXISTENCE (Inchangé) $stmtCheck = $pdo->prepare("SELECT COUNT(*) FROM pf_savings WHERE owner = ? AND month_date = ? AND category = 'TOTAL_BANQUE'"); $stmtCheck->execute([$owner, $monthDate]); $exists = $stmtCheck->fetchColumn() > 0; @@ -200,54 +230,38 @@ if ($action === 'validate_transfers') { } } - // B. UPDATE TOTAL (Inchangé) $stmtUpdTotal = $pdo->prepare("UPDATE pf_savings SET amount = amount + ? WHERE owner = ? AND month_date = ? AND category = 'TOTAL_BANQUE'"); $stmtUpdTotal->execute([$data['total_add'], $owner, $monthDate]); - // C. UPDATE CATÉGORIES (Modifié pour gérer le holiday_id) foreach ($data['cats'] as $catName => $catInfo) { $catAmount = $catInfo['amount']; - $catHolidayId = $catInfo['holiday_id']; // NOUVEAU + $catHolidayId = $catInfo['holiday_id']; - if ($catName === 'Eco Alex' || $catName === 'Eco Laia') { continue; } + if (strpos($catName, 'Eco P') === 0) { continue; } $stmtCheckCat = $pdo->prepare("SELECT id FROM pf_savings WHERE owner = ? AND month_date = ? AND category = ?"); $stmtCheckCat->execute([$owner, $monthDate, $catName]); $catId = $stmtCheckCat->fetchColumn(); if ($catId) { - // Update : On actualise aussi le holiday_id au cas où il aurait changé $stmtUpdateCat = $pdo->prepare("UPDATE pf_savings SET amount = amount + ?, holiday_id = ? WHERE id = ?"); $stmtUpdateCat->execute([$catAmount, $catHolidayId, $catId]); } else { - // Insert $stmtInsertCat = $pdo->prepare("INSERT INTO pf_savings (month_date, owner, category, amount, holiday_id) VALUES (?, ?, ?, ?, ?)"); $stmtInsertCat->execute([$monthDate, $owner, $catName, $catAmount, $catHolidayId]); } } } - // 3. ENREGISTRER LA VALIDATION (Mise à jour table existante) - - // a. Trouver l'ID de la catégorie système $stmtSys = $pdo->prepare("SELECT id FROM pf_alloc_categories WHERE name = 'SYSTEM_VALIDATION' LIMIT 1"); $stmtSys->execute(); $sysCatId = $stmtSys->fetchColumn(); if ($sysCatId) { - - if ($person === 'Alex') { - $sql = "INSERT INTO pf_alloc_values (month_date, cat_id, amount_alex, amount_laia) - VALUES (?, ?, 1, 0) - ON DUPLICATE KEY UPDATE amount_alex = 1"; - } else { - $sql = "INSERT INTO pf_alloc_values (month_date, cat_id, amount_alex, amount_laia) - VALUES (?, ?, 0, 1) - ON DUPLICATE KEY UPDATE amount_laia = 1"; - } - - $stmtVal = $pdo->prepare($sql); - $stmtVal->execute([$monthDate, $sysCatId]); + $stmtVal = $pdo->prepare("INSERT INTO pf_alloc_values (month_date, cat_id, person_id, amount) + VALUES (?, ?, ?, 1) + ON DUPLICATE KEY UPDATE amount = 1"); + $stmtVal->execute([$monthDate, $sysCatId, $personId]); } $pdo->commit(); @@ -258,6 +272,4 @@ if ($action === 'validate_transfers') { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } exit; - - } \ No newline at end of file diff --git a/modules/budget/views/budget_prev.php b/modules/budget/views/budget_prev.php index 0dce674..74a2a94 100644 --- a/modules/budget/views/budget_prev.php +++ b/modules/budget/views/budget_prev.php @@ -16,7 +16,6 @@ foreach ($budgetParents as $index => $parent) { $parentMapping[] = [ 'id' => (int)$parent['id'], 'name' => $parent['name'], - 'db_field' => 'amount_p' . $num, 'css' => 'p' . $num, 'color' => $parent['color'] ?? (($num === 1) ? '#0891b2' : '#f59e0b') ]; @@ -52,7 +51,6 @@ for ($i = 0; $i < 6; $i++) { $prevMonthLink = date('Y-m-01', strtotime("-1 month", $focusTs)); $nextMonthLink = date('Y-m-01', strtotime("+1 month", $focusTs)); -// Récupération des Cycles configurés dans pf_notes $cycleConfigs = []; $stmtNotes = $pdo->query("SELECT reference_id, content FROM pf_notes WHERE note_type = 'month_config'"); while ($row = $stmtNotes->fetch(PDO::FETCH_ASSOC)) { @@ -63,14 +61,14 @@ while ($row = $stmtNotes->fetch(PDO::FETCH_ASSOC)) { } } -// 5. Récupération Valeurs Répartition +// 5. Récupération Valeurs Répartition Relationnelles $inQuery = implode(',', array_fill(0, count($months), '?')); $stmt = $pdo->prepare("SELECT * FROM pf_alloc_values WHERE month_date IN ($inQuery)"); $stmt->execute($months); $allocs = []; while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { - $allocs[$row['month_date']][$row['cat_id']] = $row; + $allocs[$row['month_date']][$row['cat_id']][$row['person_id']] = (float)$row['amount']; } // 6. Récupération de l'ID de la catégorie système @@ -78,7 +76,7 @@ $sysCatId = null; foreach ($cats as $key => $c) { if ($c['name'] === 'SYSTEM_VALIDATION') { $sysCatId = $c['id']; - unset($cats[$key]); + unset($cats[$key]); break; } } @@ -86,14 +84,10 @@ foreach ($cats as $key => $c) { // Statuts de validation $focusDate = $months[0]; $isValidated = []; -if ($sysCatId && isset($allocs[$focusDate][$sysCatId])) { - $row = $allocs[$focusDate][$sysCatId]; - foreach ($parentMapping as $map) { - $isValidated[$map['css']] = ($row[$map['db_field']] == 1); - } -} else { - foreach ($parentMapping as $map) { - $isValidated[$map['css']] = false; +foreach ($parentMapping as $map) { + $isValidated[$map['css']] = false; + if ($sysCatId && isset($allocs[$focusDate][$sysCatId][$map['id']])) { + $isValidated[$map['css']] = ($allocs[$focusDate][$sysCatId][$map['id']] == 1); } } @@ -110,13 +104,13 @@ function getTranslatedMonthName($dateString) { } ?> -
    +

    - + @@ -130,9 +124,9 @@ function getTranslatedMonthName($dateString) { - @@ -180,14 +174,14 @@ function getTranslatedMonthName($dateString) { - - "> - + - - @@ -282,21 +280,24 @@ function getTranslatedMonthName($dateString) {

    📝

    - + - + @@ -306,7 +307,7 @@ function getTranslatedMonthName($dateString) {
    -
    - +
    - " . sprintf(tr('bud_sav_from_date'), $cStart) . ""; @@ -227,11 +221,11 @@ function getTranslatedMonthName($dateString) {
    - + Info
    - + 0) echo 'Obj: ' . round((float)$cat['target']) . '€ '; ?> +
    - +
    - +
    0 - + data-transfer-dest="" + onchange="updateAlloc('', , , this)">
    @@ -364,9 +365,13 @@ function getTranslatedMonthName($dateString) {
    - - +
    +
    + +
    - - +
    +
    + +
    @@ -792,10 +811,14 @@ function openEditModal(e) { else if (e.category === 'Income') document.getElementById('incomeSelect').value = e.budget_item_id; } -function handleLineCatChange(select) { +function handleLineCatChange(select, isInit = false) { const row = select.closest('tr'); const fSel = row.querySelector('.select-frais'); const iSel = row.querySelector('.select-income'); - fSel.style.display = 'none'; iSel.style.display = 'none'; fSel.value = ''; iSel.value = ''; fSel.disabled = true; iSel.disabled = true; + + fSel.style.display = 'none'; iSel.style.display = 'none'; + if (!isInit) { fSel.value = ''; iSel.value = ''; } + fSel.disabled = true; iSel.disabled = true; + if (select.value === 'Frais') { fSel.style.display = 'block'; fSel.disabled = false; } else if (select.value === 'Income') { iSel.style.display = 'block'; iSel.disabled = false; } checkValidation(); @@ -815,7 +838,10 @@ function checkValidation() { else { btn.disabled = false; btn.style.opacity=1; msg.style.display='none'; } } -if(document.getElementById('formMapping')) { document.querySelectorAll('.line-select').forEach(s => handleLineCatChange(s)); checkValidation(); } +if(document.getElementById('formMapping')) { + document.querySelectorAll('.line-select').forEach(s => handleLineCatChange(s, true)); + checkValidation(); +} window.addEventListener('click', (e) => { if (e.target.classList.contains('pf-modal')) { @@ -826,7 +852,7 @@ window.addEventListener('click', (e) => { // --- 2. SUPPRESSION ASYNCHRONE --- async function deleteExpense(id) { - const confirmed = await pachaConfirm("Suppression", tr('bud_confirm_delete')); + const confirmed = await pachaConfirm("Suppression", window.I18N['bud_confirm_delete']); if (!confirmed) return; const formData = new FormData(); diff --git a/schema.sql b/schema.sql index 0371f76..0684f5e 100644 --- a/schema.sql +++ b/schema.sql @@ -1,10 +1,18 @@ --- HouseHub — Schéma MySQL --- Créer la base : CREATE DATABASE househub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - +-- HouseHub — Schéma MySQL (Modèle pour une base famille) SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; --- ─── Utilisateurs ───────────────────────────────────────────────────────────── +-- ─── Configuration du foyer ─────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_foyer_settings ( + id INT AUTO_INCREMENT PRIMARY KEY, + currency VARCHAR(10) NOT NULL DEFAULT '€', + zone_scolaire VARCHAR(5) NOT NULL DEFAULT 'C', + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT IGNORE INTO pf_foyer_settings (id, currency, zone_scolaire) VALUES (1, '€', 'C'); + +-- ─── Utilisateurs (Legacy) ──────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(100) NOT NULL UNIQUE, @@ -15,11 +23,12 @@ CREATE TABLE IF NOT EXISTS pf_users ( -- ─── Personnes ──────────────────────────────────────────────────────────────── CREATE TABLE `pf_people` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(50) NOT NULL, - `user_id` int(11) DEFAULT NULL, + `name` varchar(100) NOT NULL, + `user_id` int(11) DEFAULT NULL, `role` varchar(50) DEFAULT NULL, + `color` varchar(7) DEFAULT '#0891b2', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ─── Calendrier familial ────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_events ( @@ -94,7 +103,7 @@ CREATE TABLE IF NOT EXISTS pf_budget_items ( CREATE TABLE IF NOT EXISTS pf_expenses ( id INT AUTO_INCREMENT PRIMARY KEY, date_exp DATE NOT NULL, - gestion_month VARCHAR(7) NOT NULL, + gestion_month VARCHAR(10) NOT NULL, category VARCHAR(100), label VARCHAR(255), amount DECIMAL(10,2) DEFAULT 0, @@ -104,26 +113,28 @@ CREATE TABLE IF NOT EXISTS pf_expenses ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS pf_alloc_categories ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(100) NOT NULL, - target DECIMAL(10,2) DEFAULT 0, - holiday_id INT DEFAULT NULL, - sort_order INT DEFAULT 0 + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + target DECIMAL(10,2) DEFAULT 0.00, + transfer_dest VARCHAR(50) DEFAULT NULL, + holiday_id INT DEFAULT NULL, + sort_order INT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS pf_alloc_values ( id INT AUTO_INCREMENT PRIMARY KEY, - month_date VARCHAR(7) NOT NULL, + month_date VARCHAR(10) NOT NULL, cat_id INT NOT NULL, - amount_alex DECIMAL(10,2) DEFAULT 0, - amount_laia DECIMAL(10,2) DEFAULT 0, - UNIQUE KEY uq_alloc (month_date, cat_id) + person_id INT NOT NULL, + amount DECIMAL(10,2) DEFAULT 0.00, + UNIQUE KEY uq_alloc_person (month_date, cat_id, person_id), + FOREIGN KEY (person_id) REFERENCES pf_people(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS pf_savings ( id INT AUTO_INCREMENT PRIMARY KEY, owner VARCHAR(50) NOT NULL, - month_date VARCHAR(7) NOT NULL, + month_date VARCHAR(10) NOT NULL, category VARCHAR(100) NOT NULL, amount DECIMAL(10,2) DEFAULT 0, holiday_id INT DEFAULT NULL @@ -150,9 +161,22 @@ CREATE TABLE IF NOT EXISTS pf_salary_config ( CREATE TABLE IF NOT EXISTS pf_import_rules ( id INT AUTO_INCREMENT PRIMARY KEY, keyword VARCHAR(255) NOT NULL UNIQUE, - category VARCHAR(100) NOT NULL + category VARCHAR(100) NOT NULL, + budget_item_id INT(11) NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS pf_advances ( + id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + advance_date date NOT NULL, + payer varchar(50) NOT NULL, + description varchar(255) NOT NULL, + amount decimal(10,2) DEFAULT 0.00, + from_savings tinyint(1) DEFAULT 0, + is_resolved tinyint(1) DEFAULT 0, + created_at datetime DEFAULT current_timestamp() +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ─── Notes / Memo ───────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_notes ( id INT AUTO_INCREMENT PRIMARY KEY, note_type VARCHAR(100) NOT NULL, @@ -161,6 +185,29 @@ CREATE TABLE IF NOT EXISTS pf_notes ( UNIQUE KEY uq_note (note_type, reference_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS pf_memo_notes ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + content LONGTEXT DEFAULT '', + tags VARCHAR(1000) DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FULLTEXT KEY ft_notes (title, content, tags) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_memo_attachments ( + id INT AUTO_INCREMENT PRIMARY KEY, + note_id INT NOT NULL, + type ENUM('image','file','url') NOT NULL DEFAULT 'file', + filename VARCHAR(255) DEFAULT NULL, + original_name VARCHAR(255) DEFAULT NULL, + url TEXT DEFAULT NULL, + label VARCHAR(255) DEFAULT NULL, + size INT DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (note_id) REFERENCES pf_memo_notes(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- ─── Voyages / Holidays ─────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_holidays ( id INT AUTO_INCREMENT PRIMARY KEY, @@ -228,13 +275,153 @@ CREATE TABLE IF NOT EXISTS pf_gifts ( amount DECIMAL(8,2) DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -SET FOREIGN_KEY_CHECKS = 1; +-- ─── Garage Manager ─────────────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_vehicles ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + brand VARCHAR(100) NOT NULL, + model VARCHAR(100) NOT NULL, + year INT DEFAULT NULL, + license_plate VARCHAR(50) DEFAULT NULL, + vin VARCHAR(100) DEFAULT NULL, + fuel_type VARCHAR(50) DEFAULT 'Essence', + color VARCHAR(50) DEFAULT NULL, + purchase_date DATE DEFAULT NULL, + purchase_price DECIMAL(10,2) DEFAULT NULL, + current_km INT DEFAULT 0, + photo VARCHAR(255) DEFAULT NULL, + notes TEXT DEFAULT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; --- ─── Données initiales ──────────────────────────────────────────────────────── --- Utilisateur admin (mot de passe : changeme → à modifier !) -INSERT IGNORE INTO pf_users (id, username, password_hash, display_name) -VALUES (1, 'admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Admin'); +CREATE TABLE IF NOT EXISTS pf_maintenances ( + id INT AUTO_INCREMENT PRIMARY KEY, + vehicle_id INT NOT NULL, + type VARCHAR(100) NOT NULL, + description TEXT DEFAULT NULL, + date DATE NOT NULL, + km INT DEFAULT NULL, + cost DECIMAL(10,2) DEFAULT 0, + mechanic VARCHAR(100) DEFAULT NULL, + garage_name VARCHAR(100) DEFAULT NULL, + next_km INT DEFAULT NULL, + next_date DATE DEFAULT NULL, + invoice_photo VARCHAR(255) DEFAULT NULL, + notes TEXT DEFAULT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (vehicle_id) REFERENCES pf_vehicles(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE IF NOT EXISTS pf_parts ( + id INT AUTO_INCREMENT PRIMARY KEY, + vehicle_id INT DEFAULT NULL, + maintenance_id INT DEFAULT NULL, + brand VARCHAR(100) DEFAULT NULL, + reference VARCHAR(100) DEFAULT NULL, + name VARCHAR(255) NOT NULL, + category VARCHAR(100) DEFAULT 'Autre', + price DECIMAL(10,2) DEFAULT 0, + quantity INT DEFAULT 1, + unit VARCHAR(50) DEFAULT 'pièce', + supplier VARCHAR(100) DEFAULT NULL, + purchase_date DATE DEFAULT NULL, + photo VARCHAR(255) DEFAULT NULL, + notes TEXT DEFAULT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (vehicle_id) REFERENCES pf_vehicles(id) ON DELETE SET NULL, + FOREIGN KEY (maintenance_id) REFERENCES pf_maintenances(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_garage_documents ( + id INT AUTO_INCREMENT PRIMARY KEY, + vehicle_id INT NOT NULL, + maintenance_id INT DEFAULT NULL, + label VARCHAR(255) DEFAULT NULL, + filename VARCHAR(255) NOT NULL, + original_name VARCHAR(255) DEFAULT NULL, + mime VARCHAR(120) DEFAULT NULL, + size INT DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + KEY idx_gd_vehicle (vehicle_id), + KEY idx_gd_maint (maintenance_id), + FOREIGN KEY (vehicle_id) REFERENCES pf_vehicles(id) ON DELETE CASCADE, + FOREIGN KEY (maintenance_id) REFERENCES pf_maintenances(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ─── Todo ───────────────────────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_todo_lists ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + color VARCHAR(20) DEFAULT '#3b82f6', + icon VARCHAR(10) DEFAULT '📋', + position INT DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_todos ( + id INT AUTO_INCREMENT PRIMARY KEY, + list_id INT DEFAULT NULL, + title VARCHAR(500) NOT NULL, + notes TEXT DEFAULT NULL, + due_date DATE DEFAULT NULL, + due_time TIME DEFAULT NULL, + notified TINYINT(1) DEFAULT 0, + notified_date DATE DEFAULT NULL, + priority ENUM('none','low','medium','high') DEFAULT 'none', + done TINYINT(1) DEFAULT 0, + done_at DATETIME DEFAULT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (list_id) REFERENCES pf_todo_lists(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ─── Module Liste (Courses partagées) ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_lists ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL DEFAULT 'Ma liste', + color VARCHAR(20) DEFAULT NULL, + list_type VARCHAR(50) DEFAULT NULL, + position INT NOT NULL DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT IGNORE INTO pf_lists (id, name, position) VALUES (1, 'Ma liste', 0); + +CREATE TABLE IF NOT EXISTS pf_grocery_items ( + id INT AUTO_INCREMENT PRIMARY KEY, + list_id INT NOT NULL DEFAULT 1, + category_id INT DEFAULT NULL, + label VARCHAR(500) NOT NULL, + in_cart TINYINT(1) NOT NULL DEFAULT 0, + position INT NOT NULL DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + KEY idx_items_list (list_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_grocery_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + label_hash CHAR(64) NOT NULL, + label_display VARCHAR(500) NOT NULL, + last_used_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uq_grocery_hist_hash (label_hash), + KEY idx_hist_last (last_used_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_list_categories ( + id INT AUTO_INCREMENT PRIMARY KEY, + icon VARCHAR(10) NOT NULL DEFAULT '🏷️', + name VARCHAR(100) NOT NULL, + position INT NOT NULL DEFAULT 0 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_item_category_rules ( + id INT AUTO_INCREMENT PRIMARY KEY, + keyword VARCHAR(255) NOT NULL, + category_id INT NOT NULL, + UNIQUE KEY uq_keyword (keyword) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ─── Calendar iOS ───────────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS pf_calendar_events ( @@ -264,8 +451,57 @@ CREATE TABLE IF NOT EXISTS pf_calendar_event_links ( external_uid VARCHAR(255) NOT NULL, external_etag VARCHAR(255) DEFAULT NULL, calendar_url VARCHAR(1024) DEFAULT NULL, + external_href VARCHAR(2048) DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uq_calendar_event (calendar_event_id), UNIQUE KEY uq_external_link (external_uid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ─── PrintVault ─────────────────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_pv_models ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT DEFAULT '', + category VARCHAR(100) DEFAULT 'Non classé', + tags VARCHAR(500) DEFAULT '', + file_type VARCHAR(20) NOT NULL, + filename VARCHAR(255) NOT NULL, + original_name VARCHAR(255) NOT NULL, + file_size INT DEFAULT 0, + dim_x DECIMAL(10,3) DEFAULT 0, + dim_y DECIMAL(10,3) DEFAULT 0, + dim_z DECIMAL(10,3) DEFAULT 0, + volume DECIMAL(10,3) DEFAULT 0, + gcode_time VARCHAR(50) DEFAULT '', + gcode_filament VARCHAR(50) DEFAULT '', + gcode_nozzle VARCHAR(20) DEFAULT '', + gcode_bed VARCHAR(20) DEFAULT '', + thumb VARCHAR(255) DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS pf_pv_categories ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL UNIQUE, + color VARCHAR(20) DEFAULT '#8b5cf6' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT IGNORE INTO pf_pv_categories (name, color) VALUES + ('Non classé','#64748b'),('Déco','#ec4899'),('Fonctionnel','#3b82f6'), + ('Mécanique','#f59e0b'),('Jouets','#10b981'),('Outils','#ef4444'), + ('Architecture','#8b5cf6'),('Art','#06b6d4'); + +-- ─── Planka ─────────────────────────────────────────────────────────────────── +CREATE TABLE IF NOT EXISTS pf_planka_config ( + id INT NOT NULL DEFAULT 1, + project_id VARCHAR(32), + admin_token TEXT, + token_expires_at DATETIME, + active_board_id VARCHAR(32), + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file