Deploiement staging #5
@@ -36,6 +36,7 @@ CREATE TABLE IF NOT EXISTS user_calendar_integrations (
|
|||||||
calendar_url VARCHAR(1024) DEFAULT NULL,
|
calendar_url VARCHAR(1024) DEFAULT NULL,
|
||||||
status VARCHAR(30) DEFAULT 'connected',
|
status VARCHAR(30) DEFAULT 'connected',
|
||||||
last_sync_at DATETIME DEFAULT NULL,
|
last_sync_at DATETIME DEFAULT NULL,
|
||||||
|
calendar_prefs_json TEXT DEFAULT NULL,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
UNIQUE KEY uq_user_provider (user_id, provider),
|
UNIQUE KEY uq_user_provider (user_id, provider),
|
||||||
|
|||||||
+61
-54
@@ -1,18 +1,8 @@
|
|||||||
-- HouseHub — Schéma MySQL
|
-- HouseHub — Schéma MySQL (Modèle pour une base famille)
|
||||||
-- Créer la base : CREATE DATABASE househub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
SET FOREIGN_KEY_CHECKS = 0;
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
-- ─── Utilisateurs ─────────────────────────────────────────────────────────────
|
-- ─── Configuration du foyer ───────────────────────────────────────────────────
|
||||||
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 ─────────────────────────────────────────────────────────────
|
|
||||||
CREATE TABLE IF NOT EXISTS pf_foyer_settings (
|
CREATE TABLE IF NOT EXISTS pf_foyer_settings (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
currency VARCHAR(10) NOT NULL DEFAULT '€',
|
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
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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');
|
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 ────────────────────────────────────────────────────────────────
|
-- ─── Personnes ────────────────────────────────────────────────────────────────
|
||||||
CREATE TABLE `pf_people` (
|
CREATE TABLE `pf_people` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`name` varchar(50) NOT NULL,
|
`name` varchar(100) NOT NULL,
|
||||||
`user_id` int(11) DEFAULT NULL,
|
`user_id` int(11) DEFAULT NULL,
|
||||||
`role` varchar(50) DEFAULT NULL,
|
`role` varchar(50) DEFAULT NULL,
|
||||||
|
`color` varchar(7) DEFAULT '#0891b2',
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
-- ─── Calendrier familial ──────────────────────────────────────────────────────
|
-- ─── Calendrier familial ──────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_events (
|
CREATE TABLE IF NOT EXISTS pf_events (
|
||||||
@@ -117,7 +115,8 @@ CREATE TABLE IF NOT EXISTS pf_expenses (
|
|||||||
CREATE TABLE IF NOT EXISTS pf_alloc_categories (
|
CREATE TABLE IF NOT EXISTS pf_alloc_categories (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
name VARCHAR(100) NOT NULL,
|
name VARCHAR(100) NOT NULL,
|
||||||
target DECIMAL(10,2) DEFAULT 0,
|
target DECIMAL(10,2) DEFAULT 0.00,
|
||||||
|
transfer_dest VARCHAR(50) DEFAULT NULL,
|
||||||
holiday_id INT DEFAULT NULL,
|
holiday_id INT DEFAULT NULL,
|
||||||
sort_order INT DEFAULT 0
|
sort_order INT DEFAULT 0
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
@@ -126,9 +125,10 @@ CREATE TABLE IF NOT EXISTS pf_alloc_values (
|
|||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
month_date VARCHAR(10) NOT NULL,
|
month_date VARCHAR(10) NOT NULL,
|
||||||
cat_id INT NOT NULL,
|
cat_id INT NOT NULL,
|
||||||
amount_alex DECIMAL(10,2) DEFAULT 0,
|
person_id INT NOT NULL,
|
||||||
amount_laia DECIMAL(10,2) DEFAULT 0,
|
amount DECIMAL(10,2) DEFAULT 0.00,
|
||||||
UNIQUE KEY uq_alloc (month_date, cat_id)
|
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;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pf_savings (
|
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 (
|
CREATE TABLE IF NOT EXISTS pf_import_rules (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
keyword VARCHAR(255) NOT NULL UNIQUE,
|
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;
|
) 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 (
|
CREATE TABLE IF NOT EXISTS pf_notes (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
note_type VARCHAR(100) NOT NULL,
|
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)
|
UNIQUE KEY uq_note (note_type, reference_id)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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 ───────────────────────────────────────────────────────
|
-- ─── Voyages / Holidays ───────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_holidays (
|
CREATE TABLE IF NOT EXISTS pf_holidays (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
@@ -239,14 +275,6 @@ CREATE TABLE IF NOT EXISTS pf_gifts (
|
|||||||
amount DECIMAL(8,2) DEFAULT 0
|
amount DECIMAL(8,2) DEFAULT 0
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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 ───────────────────────────────────────────────────────────
|
-- ─── Garage Manager ───────────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_vehicles (
|
CREATE TABLE IF NOT EXISTS pf_vehicles (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
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
|
FOREIGN KEY (maintenance_id) REFERENCES pf_maintenances(id) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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 ─────────────────────────────────────────────────────────────────────
|
-- ─── Todo ─────────────────────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_todo_lists (
|
CREATE TABLE IF NOT EXISTS pf_todo_lists (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
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
|
FOREIGN KEY (list_id) REFERENCES pf_todo_lists(id) ON DELETE SET NULL
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
-- ─── Module Liste (multi-listes) ──────────────────────────────────────────────
|
-- ─── Module Liste (Courses partagées) ─────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_lists (
|
CREATE TABLE IF NOT EXISTS pf_lists (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
name VARCHAR(255) NOT NULL DEFAULT 'Ma liste',
|
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
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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 (
|
CREATE TABLE IF NOT EXISTS pf_grocery_items (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
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_uid VARCHAR(255) NOT NULL,
|
||||||
external_etag VARCHAR(255) DEFAULT NULL,
|
external_etag VARCHAR(255) DEFAULT NULL,
|
||||||
calendar_url VARCHAR(1024) DEFAULT NULL,
|
calendar_url VARCHAR(1024) DEFAULT NULL,
|
||||||
|
external_href VARCHAR(2048) DEFAULT NULL,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
UNIQUE KEY uq_calendar_event (calendar_event_id),
|
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,
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
+153
-102
@@ -1,9 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
// Script de migration : Refonte globale Multi-Tenant (pf_people & Budget)
|
// Script de migration : Refonte globale Multi-Tenant & Synchronisation des schémas
|
||||||
require_once __DIR__ . '/includes/meta_db.php';
|
require_once __DIR__ . '/includes/meta_db.php';
|
||||||
|
|
||||||
echo "<h1>🚀 Début de la migration Globale ...</h1>";
|
echo "<h1>🚀 Début de la migration Globale ...</h1>";
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 0. MISE À JOUR DE LA META DB
|
||||||
|
// ==========================================
|
||||||
|
echo "<h3>Mise à jour Meta DB (househub_meta)</h3><ul>";
|
||||||
|
try {
|
||||||
|
$meta_pdo->exec("ALTER TABLE user_calendar_integrations ADD COLUMN calendar_prefs_json TEXT DEFAULT NULL");
|
||||||
|
echo "<li><span style='color:green'>Colonne 'calendar_prefs_json' ajoutée à user_calendar_integrations.</span></li>";
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
if ($e->getCode() == '42S21' || strpos($e->getMessage(), '1060') !== false) {
|
||||||
|
echo "<li><span style='color:gray'>Colonne 'calendar_prefs_json' déjà présente.</span></li>";
|
||||||
|
} else { throw $e; }
|
||||||
|
}
|
||||||
|
echo "</ul>";
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// MIGRATIONS PAR FAMILLE
|
||||||
|
// ==========================================
|
||||||
$stmt = $meta_pdo->query("SELECT id, name, db_name FROM families WHERE db_name != ''");
|
$stmt = $meta_pdo->query("SELECT id, name, db_name FROM families WHERE db_name != ''");
|
||||||
$families = $stmt->fetchAll();
|
$families = $stmt->fetchAll();
|
||||||
|
|
||||||
@@ -16,152 +33,186 @@ foreach ($families as $f) {
|
|||||||
$family_id = $f['id'];
|
$family_id = $f['id'];
|
||||||
echo "<h3>Mise à jour de <strong>{$f['name']}</strong> ($db_name)</h3><ul>";
|
echo "<h3>Mise à jour de <strong>{$f['name']}</strong> ($db_name)</h3><ul>";
|
||||||
|
|
||||||
// 🟢 SÉCURITÉ : On vide les variables à chaque famille pour éviter les fuites (le bug f2 vers f1)
|
|
||||||
$p1_name = null;
|
$p1_name = null;
|
||||||
$p2_name = null;
|
$p2_name = null;
|
||||||
$parents = [];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$fam_pdo = new PDO("mysql:host=$host;dbname=$db_name;charset=utf8mb4", $user, $pass, [
|
$fam_pdo = new PDO("mysql:host=$host;dbname=$db_name;charset=utf8mb4", $user, $pass, [
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ==========================================
|
// 1. Table pf_foyer_settings
|
||||||
// 1. GESTION DE PF_PEOPLE (user_id, role, color)
|
$fam_pdo->exec("CREATE TABLE IF NOT EXISTS pf_foyer_settings (
|
||||||
// ==========================================
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
echo "<li><strong>Table pf_people :</strong> ";
|
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;");
|
||||||
|
$fam_pdo->exec("INSERT IGNORE INTO pf_foyer_settings (id, currency, zone_scolaire) VALUES (1, '€', 'C');");
|
||||||
|
echo "<li><span style='color:green'>Table pf_foyer_settings vérifiée/créée.</span></li>";
|
||||||
|
|
||||||
// user_id
|
// 2. Colonne external_href pour le Calendrier iOS
|
||||||
try {
|
try {
|
||||||
$fam_pdo->exec("ALTER TABLE pf_people ADD COLUMN user_id INT NULL DEFAULT NULL");
|
$fam_pdo->exec("ALTER TABLE pf_calendar_event_links ADD COLUMN external_href VARCHAR(2048) DEFAULT NULL");
|
||||||
echo "<span style='color:green'>user_id OK</span> - ";
|
echo "<li><span style='color:green'>Colonne 'external_href' ajoutée à pf_calendar_event_links.</span></li>";
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
if ($e->getCode() == '42S21' || strpos($e->getMessage(), '1060') !== false) {
|
if ($e->getCode() != '42S21' && strpos($e->getMessage(), '1060') === false) throw $e;
|
||||||
echo "<span style='color:gray'>user_id déjà présent</span> - ";
|
|
||||||
} else { throw $e; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🟢 CORRECTION DE L'AUTO-MAPPING : Si pf_people est vide, on la remplit !
|
// 3. Colonne budget_item_id pour les règles d'import
|
||||||
|
try {
|
||||||
|
$fam_pdo->exec("ALTER TABLE pf_import_rules ADD COLUMN budget_item_id INT(11) DEFAULT NULL");
|
||||||
|
echo "<li><span style='color:green'>Colonne 'budget_item_id' ajoutée à pf_import_rules.</span></li>";
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
if ($e->getCode() != '42S21' && strpos($e->getMessage(), '1060') === false) throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Uniformisation des VARCHAR de dates pour le budget (YYYY-MM-01 = 10 chars)
|
||||||
|
$fam_pdo->exec("ALTER TABLE pf_expenses MODIFY gestion_month VARCHAR(10) NOT NULL");
|
||||||
|
$fam_pdo->exec("ALTER TABLE pf_alloc_values MODIFY month_date VARCHAR(10) NOT NULL");
|
||||||
|
$fam_pdo->exec("ALTER TABLE pf_savings MODIFY month_date VARCHAR(10) NOT NULL");
|
||||||
|
echo "<li><span style='color:green'>Formats de dates (VARCHAR 10) uniformisés pour le budget.</span></li>";
|
||||||
|
|
||||||
|
// 5. GESTION DE PF_PEOPLE (user_id, role, color)
|
||||||
|
echo "<li><strong>Mise à jour pf_people :</strong> ";
|
||||||
|
try { $fam_pdo->exec("ALTER TABLE pf_people ADD COLUMN user_id INT NULL DEFAULT NULL"); } catch (\Exception $e) {}
|
||||||
|
try { $fam_pdo->exec("ALTER TABLE pf_people ADD COLUMN role VARCHAR(50) DEFAULT NULL"); } catch (\Exception $e) {}
|
||||||
|
try { $fam_pdo->exec("ALTER TABLE pf_people ADD COLUMN color VARCHAR(7) DEFAULT '#0891b2'"); } catch (\Exception $e) {}
|
||||||
|
|
||||||
$stmtUsers = $meta_pdo->prepare("SELECT id, username, display_name FROM users WHERE family_id = ? ORDER BY id ASC");
|
$stmtUsers = $meta_pdo->prepare("SELECT id, username, display_name FROM users WHERE family_id = ? ORDER BY id ASC");
|
||||||
$stmtUsers->execute([$family_id]);
|
$stmtUsers->execute([$family_id]);
|
||||||
$users = $stmtUsers->fetchAll();
|
$users = $stmtUsers->fetchAll();
|
||||||
|
|
||||||
$mappedCount = 0;
|
|
||||||
foreach ($users as $u) {
|
foreach ($users as $u) {
|
||||||
// On vérifie si l'utilisateur existe déjà dans pf_people (via user_id ou son nom)
|
|
||||||
$stmtCheck = $fam_pdo->prepare("SELECT id FROM pf_people WHERE user_id = ? OR LOWER(name) = LOWER(?)");
|
$stmtCheck = $fam_pdo->prepare("SELECT id FROM pf_people WHERE user_id = ? OR LOWER(name) = LOWER(?)");
|
||||||
$stmtCheck->execute([$u['id'], $u['username']]);
|
$stmtCheck->execute([$u['id'], $u['username']]);
|
||||||
$exists = $stmtCheck->fetchColumn();
|
$exists = $stmtCheck->fetchColumn();
|
||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
// S'il existe, on s'assure que son user_id est bien renseigné
|
$fam_pdo->prepare("UPDATE pf_people SET user_id = ? WHERE id = ?")->execute([$u['id'], $exists]);
|
||||||
$stmtUpdate = $fam_pdo->prepare("UPDATE pf_people SET user_id = ? WHERE id = ?");
|
|
||||||
$stmtUpdate->execute([$u['id'], $exists]);
|
|
||||||
$mappedCount += $stmtUpdate->rowCount();
|
|
||||||
} else {
|
} else {
|
||||||
// S'il n'existe pas du tout (base f1 vide), on le crée !
|
$fam_pdo->prepare("INSERT INTO pf_people (name, user_id) VALUES (?, ?)")->execute([$u['display_name'] ?: $u['username'], $u['id']]);
|
||||||
$stmtInsert = $fam_pdo->prepare("INSERT INTO pf_people (name, user_id) VALUES (?, ?)");
|
|
||||||
$stmtInsert->execute([$u['display_name'] ?: $u['username'], $u['id']]);
|
|
||||||
$mappedCount++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo "<span style='color:blue'>$mappedCount profils liés ou créés</span> - ";
|
$fam_pdo->exec("UPDATE pf_people SET role = 'parent' WHERE user_id IS NOT NULL AND role IS NULL;");
|
||||||
|
$fam_pdo->exec("UPDATE pf_people SET role = 'nounou' WHERE LOWER(name) = 'carole';");
|
||||||
|
|
||||||
// role
|
$pIds = $fam_pdo->query("SELECT id FROM pf_people WHERE role = 'parent' ORDER BY id ASC")->fetchAll(PDO::FETCH_COLUMN);
|
||||||
try {
|
if (isset($pIds[0])) $fam_pdo->exec("UPDATE pf_people SET color = '#0891b2' WHERE id = " . (int)$pIds[0] . " AND color IS NULL");
|
||||||
$fam_pdo->exec("ALTER TABLE pf_people ADD COLUMN role VARCHAR(50) DEFAULT NULL");
|
if (isset($pIds[1])) $fam_pdo->exec("UPDATE pf_people SET color = '#f59e0b' WHERE id = " . (int)$pIds[1] . " AND color IS NULL");
|
||||||
echo "<span style='color:green'>role OK</span> - ";
|
echo "<span style='color:blue'>OK</span></li>";
|
||||||
} catch (\PDOException $e) {
|
|
||||||
if ($e->getCode() == '42S21' || strpos($e->getMessage(), '1060') !== false) {
|
|
||||||
echo "<span style='color:gray'>role déjà présent</span> - ";
|
|
||||||
} else { throw $e; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// color
|
|
||||||
try {
|
|
||||||
$fam_pdo->exec("ALTER TABLE pf_people ADD COLUMN color VARCHAR(7) DEFAULT '#0891b2'");
|
|
||||||
echo "<span style='color:green'>colonne 'color' ajoutée</span> - ";
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
if ($e->getCode() == '42S21' || strpos($e->getMessage(), '1060') !== false) {
|
|
||||||
echo "<span style='color:gray'>colonne 'color' déjà présente</span> - ";
|
|
||||||
} else { throw $e; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configuration des valeurs par défaut et des rôles
|
|
||||||
$fam_pdo->exec("
|
|
||||||
UPDATE pf_people SET role = 'parent' WHERE user_id IS NOT NULL;
|
|
||||||
UPDATE pf_people SET role = 'nounou' WHERE LOWER(name) = 'carole';
|
|
||||||
");
|
|
||||||
|
|
||||||
// On donne des couleurs distinctes par défaut aux deux parents (P1 = Cyan, P2 = Orange)
|
|
||||||
$stmtP = $fam_pdo->query("SELECT id FROM pf_people WHERE role = 'parent' ORDER BY id ASC");
|
|
||||||
$pIds = $stmtP->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
if (isset($pIds[0])) {
|
|
||||||
$fam_pdo->exec("UPDATE pf_people SET color = '#0891b2' WHERE id = " . (int)$pIds[0]);
|
|
||||||
}
|
|
||||||
if (isset($pIds[1])) {
|
|
||||||
$fam_pdo->exec("UPDATE pf_people SET color = '#f59e0b' WHERE id = " . (int)$pIds[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "<span style='color:blue'>Rôles et Couleurs initialisés.</span></li>";
|
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 2. GESTION DU BUDGET (pf_alloc_values)
|
// 6. REFONTE RELATIONNELLE : pf_alloc_values (Colonnes -> Lignes)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
echo "<li><strong>Table pf_alloc_values :</strong> ";
|
echo "<li><strong>Table pf_alloc_values (Normalisation) :</strong> ";
|
||||||
|
|
||||||
$checkAlex = $fam_pdo->query("SHOW COLUMNS FROM pf_alloc_values LIKE 'amount_alex'")->rowCount();
|
// Vérifier si la table est déjà convertie
|
||||||
if ($checkAlex > 0) {
|
$checkNewFormat = $fam_pdo->query("SHOW COLUMNS FROM pf_alloc_values LIKE 'person_id'")->rowCount();
|
||||||
$fam_pdo->exec("ALTER TABLE pf_alloc_values CHANGE amount_alex amount_p1 FLOAT DEFAULT 0");
|
|
||||||
echo "<span style='color:green'>amount_alex -> amount_p1</span> - ";
|
if ($checkNewFormat > 0) {
|
||||||
|
echo "<span style='color:gray'>Déjà convertie au format relationnel.</span></li>";
|
||||||
} else {
|
} else {
|
||||||
echo "<span style='color:gray'>amount_p1 OK</span> - ";
|
// A. Récupérer l'ordre des parents réels pour faire le mapping d'index
|
||||||
|
$stmtParents = $fam_pdo->query("SELECT id, name FROM pf_people WHERE role = 'parent' ORDER BY id ASC");
|
||||||
|
$orderedParents = $stmtParents->fetchAll();
|
||||||
|
|
||||||
|
// B. Sauvegarder les anciennes données à migrer
|
||||||
|
$oldAllocations = $fam_pdo->query("SELECT * FROM pf_alloc_values")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// C. Supprimer l'ancienne table
|
||||||
|
$fam_pdo->exec("DROP TABLE IF EXISTS pf_alloc_values");
|
||||||
|
|
||||||
|
// D. Créer la nouvelle table normalisée
|
||||||
|
$fam_pdo->exec("CREATE TABLE pf_alloc_values (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
month_date VARCHAR(10) NOT NULL,
|
||||||
|
cat_id INT NOT NULL,
|
||||||
|
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;");
|
||||||
|
|
||||||
|
// E. Migration des données
|
||||||
|
$stmtInsert = $fam_pdo->prepare("INSERT INTO pf_alloc_values (month_date, cat_id, person_id, amount) VALUES (?, ?, ?, ?)");
|
||||||
|
$migratedRows = 0;
|
||||||
|
|
||||||
|
foreach ($oldAllocations as $oldRow) {
|
||||||
|
// Mapping des anciennes colonnes potentielles vers les index de parents
|
||||||
|
$possibleColumns = [
|
||||||
|
0 => ['amount_p1', 'amount_alex'],
|
||||||
|
1 => ['amount_p2', 'amount_laia'],
|
||||||
|
2 => ['amount_p3'],
|
||||||
|
3 => ['amount_p4']
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($possibleColumns as $parentIndex => $colNames) {
|
||||||
|
if (!isset($orderedParents[$parentIndex])) continue;
|
||||||
|
$targetParentId = $orderedParents[$parentIndex]['id'];
|
||||||
|
|
||||||
|
foreach ($colNames as $col) {
|
||||||
|
if (isset($oldRow[$col]) && (float)$oldRow[$col] > 0) {
|
||||||
|
$stmtInsert->execute([
|
||||||
|
$oldRow['month_date'],
|
||||||
|
$oldRow['cat_id'],
|
||||||
|
$targetParentId,
|
||||||
|
(float)$oldRow[$col]
|
||||||
|
]);
|
||||||
|
$migratedRows++;
|
||||||
|
break; // Passer à l'index parent suivant dès qu'on a trouvé une valeur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "<span style='color:green'>Succès ! Nouvelle table créée, $migratedRows lignes migrées.</span></li>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$checkLaia = $fam_pdo->query("SHOW COLUMNS FROM pf_alloc_values LIKE 'amount_laia'")->rowCount();
|
// 7. GESTION DU BUDGET (pf_alloc_categories & pf_salary_config)
|
||||||
if ($checkLaia > 0) {
|
$fam_pdo->exec("UPDATE pf_alloc_categories SET name = 'Eco P1' WHERE name LIKE 'Eco Alex%'");
|
||||||
$fam_pdo->exec("ALTER TABLE pf_alloc_values CHANGE amount_laia amount_p2 FLOAT DEFAULT 0");
|
$fam_pdo->exec("UPDATE pf_alloc_categories SET name = 'Eco P2' WHERE name LIKE 'Eco Laia%'");
|
||||||
echo "<span style='color:green'>amount_laia -> amount_p2</span></li>";
|
|
||||||
} else {
|
|
||||||
echo "<span style='color:gray'>amount_p2 OK</span></li>";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 3. GESTION DU BUDGET (pf_alloc_categories)
|
|
||||||
// ==========================================
|
|
||||||
echo "<li><strong>Table pf_alloc_categories :</strong> ";
|
|
||||||
$stmtCats1 = $fam_pdo->exec("UPDATE pf_alloc_categories SET name = 'Eco P1' WHERE name LIKE 'Eco Alex%'");
|
|
||||||
$stmtCats2 = $fam_pdo->exec("UPDATE pf_alloc_categories SET name = 'Eco P2' WHERE name LIKE 'Eco Laia%'");
|
|
||||||
echo "<span style='color:green'>Catégories 'Eco' génériques mises à jour (" . ($stmtCats1 + $stmtCats2) . " lignes).</span></li>";
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// 4. GESTION DU BUDGET (pf_salary_config)
|
|
||||||
// ==========================================
|
|
||||||
echo "<li><strong>Table pf_salary_config :</strong> ";
|
|
||||||
// On récupère les vrais prénoms fraîchement créés/mappés
|
|
||||||
$stmtParents = $fam_pdo->query("SELECT name FROM pf_people WHERE role = 'parent' ORDER BY id ASC");
|
$stmtParents = $fam_pdo->query("SELECT name FROM pf_people WHERE role = 'parent' ORDER BY id ASC");
|
||||||
$parents = $stmtParents->fetchAll();
|
$parents = $stmtParents->fetchAll();
|
||||||
|
|
||||||
if (count($parents) >= 2) {
|
if (count($parents) >= 2) {
|
||||||
$p1_name = $parents[0]['name'];
|
$fam_pdo->prepare("UPDATE pf_salary_config SET person = ? WHERE person = 'Alex'")->execute([$parents[0]['name']]);
|
||||||
$p2_name = $parents[1]['name'];
|
$fam_pdo->prepare("UPDATE pf_salary_config SET person = ? WHERE person = 'Laia'")->execute([$parents[1]['name']]);
|
||||||
|
|
||||||
$stmtSal1 = $fam_pdo->prepare("UPDATE pf_salary_config SET person = ? WHERE person = 'Alex'");
|
|
||||||
$stmtSal1->execute([$p1_name]);
|
|
||||||
|
|
||||||
$stmtSal2 = $fam_pdo->prepare("UPDATE pf_salary_config SET person = ? WHERE person = 'Laia'");
|
|
||||||
$stmtSal2->execute([$p2_name]);
|
|
||||||
|
|
||||||
echo "<span style='color:green'>Salaires mappés vers $p1_name et $p2_name.</span></li>";
|
|
||||||
} else {
|
|
||||||
echo "<span style='color:orange'>Pas assez de parents trouvés pour mapper les salaires.</span></li>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
echo "<li style='color:red'>❌ Erreur : " . $e->getMessage() . "</li>";
|
echo "<li style='color:red'>❌ Erreur : " . $e->getMessage() . "</li>";
|
||||||
}
|
}
|
||||||
echo "</ul>";
|
echo "</ul>";
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 8. SEPARATION CIBLE BUDGET / DESTINATION VIREMENT
|
||||||
|
// ==========================================
|
||||||
|
echo "<li><strong>Table pf_alloc_categories (Fix Collision) :</strong> ";
|
||||||
|
try {
|
||||||
|
$fam_pdo->exec("ALTER TABLE pf_alloc_categories ADD COLUMN transfer_dest VARCHAR(50) DEFAULT NULL AFTER target");
|
||||||
|
echo "<span style='color:green'>Succès ! Colonne 'transfer_dest' ajoutée pour préserver vos objectifs chiffrés.</span></li>";
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
echo "<span style='color:gray'>La colonne transfer_dest existe déjà.</span></li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 9. TRANSFERT DES DONNÉES (Cible -> Destination)
|
||||||
|
// ==========================================
|
||||||
|
echo "<li><strong>Table pf_alloc_categories (Récupération des données) :</strong> ";
|
||||||
|
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 "<span style='color:green'>$updated destinations récupérées et déplacées proprement.</span></li>";
|
||||||
|
|
||||||
|
// 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 "<li><span style='color:green'>Colonne 'target' re-sécurisée en format monétaire DECIMAL(10,2).</span></li>";
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
echo "<span style='color:red'>Erreur : " . $e->getMessage() . "</span></li>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
echo "<h2>🎉 Migration terminée avec succès !</h2>";
|
echo "<h2>🎉 Migration terminée avec succès !</h2>";
|
||||||
?>
|
?>
|
||||||
@@ -62,28 +62,37 @@ if ($action === 'update_salary_config') {
|
|||||||
if ($action === 'update_allocation') {
|
if ($action === 'update_allocation') {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
$date = $_POST['month_date'];
|
$date = $_POST['month_date'];
|
||||||
$catId = $_POST['cat_id'];
|
$catId = (int)$_POST['cat_id'];
|
||||||
$person = $_POST['person']; // 'amount_alex' ou 'amount_laia'
|
$personId = (int)$_POST['person_id']; // 🟢 Reçoit l'ID de la personne directement !
|
||||||
$value = floatval($_POST['value']);
|
$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)");
|
if ($catId <= 0 || $personId <= 0) {
|
||||||
$stmt->execute([$date, $catId, $value]);
|
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]);
|
echo json_encode(['success' => true]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. GESTION DES CATEGORIES (Ajout)
|
// 3. GESTION DES CATEGORIES (Ajout)
|
||||||
if ($action === 'add_category') {
|
if ($action === 'add_category') {
|
||||||
|
try {
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name']);
|
||||||
$target = trim($_POST['target']);
|
$transfer_dest = trim($_POST['transfer_dest'] ?? '');
|
||||||
|
$target = floatval($_POST['target'] ?? 0);
|
||||||
$holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null;
|
$holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null;
|
||||||
|
|
||||||
if (!empty($name)) {
|
if (!empty($name)) {
|
||||||
$stmt = $pdo->prepare("INSERT INTO pf_alloc_categories (name, target, holiday_id) VALUES (?, ?, ?)");
|
$stmt = $pdo->prepare("INSERT INTO pf_alloc_categories (name, target, transfer_dest, holiday_id) VALUES (?, ?, ?, ?)");
|
||||||
$stmt->execute([$name, $target, $holiday_id]);
|
$stmt->execute([$name, $target, $transfer_dest, $holiday_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NOUVEAU : Réponse AJAX ---
|
|
||||||
if (isset($_POST['ajax'])) {
|
if (isset($_POST['ajax'])) {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
@@ -91,21 +100,30 @@ if ($action === 'add_category') {
|
|||||||
}
|
}
|
||||||
header("Location: " . $_SERVER['HTTP_REFERER']);
|
header("Location: " . $_SERVER['HTTP_REFERER']);
|
||||||
exit;
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. MODIFICATION D'UNE CATEGORIE
|
// 4. MODIFICATION D'UNE CATEGORIE
|
||||||
if ($action === 'update_category') {
|
if ($action === 'update_category') {
|
||||||
|
try {
|
||||||
$id = (int)$_POST['cat_id'];
|
$id = (int)$_POST['cat_id'];
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name']);
|
||||||
$target = trim($_POST['target']);
|
$transfer_dest = trim($_POST['transfer_dest'] ?? '');
|
||||||
|
$target = floatval($_POST['target'] ?? 0);
|
||||||
$holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null;
|
$holiday_id = !empty($_POST['holiday_id']) ? (int)$_POST['holiday_id'] : null;
|
||||||
|
|
||||||
if ($id > 0 && !empty($name)) {
|
if ($id > 0 && !empty($name)) {
|
||||||
$stmt = $pdo->prepare("UPDATE pf_alloc_categories SET name = ?, target = ?, holiday_id = ? WHERE id = ?");
|
$stmt = $pdo->prepare("UPDATE pf_alloc_categories SET name = ?, target = ?, transfer_dest = ?, holiday_id = ? WHERE id = ?");
|
||||||
$stmt->execute([$name, $target, $holiday_id, $id]);
|
$stmt->execute([$name, $target, $transfer_dest, $holiday_id, $id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NOUVEAU : Réponse AJAX ---
|
|
||||||
if (isset($_POST['ajax'])) {
|
if (isset($_POST['ajax'])) {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
@@ -113,6 +131,14 @@ if ($action === 'update_category') {
|
|||||||
}
|
}
|
||||||
header("Location: " . $_SERVER['HTTP_REFERER']);
|
header("Location: " . $_SERVER['HTTP_REFERER']);
|
||||||
exit;
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. SUPPRESSION CATEGORIE
|
// 5. SUPPRESSION CATEGORIE
|
||||||
@@ -136,36 +162,41 @@ if ($action === 'delete_category') {
|
|||||||
// 6. VALIDATION DES VIREMENTS (Complex Business Logic)
|
// 6. VALIDATION DES VIREMENTS (Complex Business Logic)
|
||||||
if ($action === 'validate_transfers') {
|
if ($action === 'validate_transfers') {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
$person = $_POST['person'];
|
$personId = (int)$_POST['person_id']; // Reçoit l'ID parent
|
||||||
$monthDate = $_POST['month_date'];
|
$monthDate = $_POST['month_date'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdo->beginTransaction();
|
$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("
|
$stmt = $pdo->prepare("
|
||||||
SELECT v.*, c.name as cat_name, c.target, c.holiday_id
|
SELECT v.amount, c.name as cat_name, c.transfer_dest, c.holiday_id
|
||||||
FROM pf_alloc_values v
|
FROM pf_alloc_values v
|
||||||
JOIN pf_alloc_categories c ON v.cat_id = c.id
|
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);
|
$budgetLines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$transfersToDo = [];
|
$transfersToDo = [];
|
||||||
|
|
||||||
foreach ($budgetLines as $line) {
|
foreach ($budgetLines as $line) {
|
||||||
$amount = ($person === 'Alex') ? $line['amount_alex'] : $line['amount_laia'];
|
$amount = (float)$line['amount'];
|
||||||
if ($amount <= 0) continue;
|
if ($amount <= 0) continue;
|
||||||
|
|
||||||
$target = trim($line['target']);
|
$dest = trim($line['transfer_dest']);
|
||||||
$catName = trim($line['cat_name']);
|
$catName = trim($line['cat_name']);
|
||||||
$holidayId = $line['holiday_id'];
|
$holidayId = $line['holiday_id'];
|
||||||
|
|
||||||
$targetOwner = null;
|
$targetOwner = null;
|
||||||
if ($target === 'vers L.Perso') { $targetOwner = $person; }
|
if ($dest === 'vers L.Perso') { $targetOwner = $dbPersonName; }
|
||||||
elseif ($target === 'vers L.Pol') { $targetOwner = 'Pol'; }
|
elseif ($dest === 'vers L.Pol') { $targetOwner = 'Pol'; }
|
||||||
elseif ($target === 'vers L.Pep') { $targetOwner = 'Pep'; }
|
elseif ($dest === 'vers L.Pep') { $targetOwner = 'Pep'; }
|
||||||
elseif ($target === 'vers commune') { continue; }
|
elseif ($dest === 'vers commune') { continue; }
|
||||||
|
|
||||||
if ($targetOwner) {
|
if ($targetOwner) {
|
||||||
if (!isset($transfersToDo[$targetOwner])) {
|
if (!isset($transfersToDo[$targetOwner])) {
|
||||||
@@ -181,7 +212,6 @@ if ($action === 'validate_transfers') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($transfersToDo as $owner => $data) {
|
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 = $pdo->prepare("SELECT COUNT(*) FROM pf_savings WHERE owner = ? AND month_date = ? AND category = 'TOTAL_BANQUE'");
|
||||||
$stmtCheck->execute([$owner, $monthDate]);
|
$stmtCheck->execute([$owner, $monthDate]);
|
||||||
$exists = $stmtCheck->fetchColumn() > 0;
|
$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 = $pdo->prepare("UPDATE pf_savings SET amount = amount + ? WHERE owner = ? AND month_date = ? AND category = 'TOTAL_BANQUE'");
|
||||||
$stmtUpdTotal->execute([$data['total_add'], $owner, $monthDate]);
|
$stmtUpdTotal->execute([$data['total_add'], $owner, $monthDate]);
|
||||||
|
|
||||||
// C. UPDATE CATÉGORIES (Modifié pour gérer le holiday_id)
|
|
||||||
foreach ($data['cats'] as $catName => $catInfo) {
|
foreach ($data['cats'] as $catName => $catInfo) {
|
||||||
$catAmount = $catInfo['amount'];
|
$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 = $pdo->prepare("SELECT id FROM pf_savings WHERE owner = ? AND month_date = ? AND category = ?");
|
||||||
$stmtCheckCat->execute([$owner, $monthDate, $catName]);
|
$stmtCheckCat->execute([$owner, $monthDate, $catName]);
|
||||||
$catId = $stmtCheckCat->fetchColumn();
|
$catId = $stmtCheckCat->fetchColumn();
|
||||||
|
|
||||||
if ($catId) {
|
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 = $pdo->prepare("UPDATE pf_savings SET amount = amount + ?, holiday_id = ? WHERE id = ?");
|
||||||
$stmtUpdateCat->execute([$catAmount, $catHolidayId, $catId]);
|
$stmtUpdateCat->execute([$catAmount, $catHolidayId, $catId]);
|
||||||
} else {
|
} else {
|
||||||
// Insert
|
|
||||||
$stmtInsertCat = $pdo->prepare("INSERT INTO pf_savings (month_date, owner, category, amount, holiday_id) VALUES (?, ?, ?, ?, ?)");
|
$stmtInsertCat = $pdo->prepare("INSERT INTO pf_savings (month_date, owner, category, amount, holiday_id) VALUES (?, ?, ?, ?, ?)");
|
||||||
$stmtInsertCat->execute([$monthDate, $owner, $catName, $catAmount, $catHolidayId]);
|
$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 = $pdo->prepare("SELECT id FROM pf_alloc_categories WHERE name = 'SYSTEM_VALIDATION' LIMIT 1");
|
||||||
$stmtSys->execute();
|
$stmtSys->execute();
|
||||||
$sysCatId = $stmtSys->fetchColumn();
|
$sysCatId = $stmtSys->fetchColumn();
|
||||||
|
|
||||||
if ($sysCatId) {
|
if ($sysCatId) {
|
||||||
|
$stmtVal = $pdo->prepare("INSERT INTO pf_alloc_values (month_date, cat_id, person_id, amount)
|
||||||
if ($person === 'Alex') {
|
VALUES (?, ?, ?, 1)
|
||||||
$sql = "INSERT INTO pf_alloc_values (month_date, cat_id, amount_alex, amount_laia)
|
ON DUPLICATE KEY UPDATE amount = 1");
|
||||||
VALUES (?, ?, 1, 0)
|
$stmtVal->execute([$monthDate, $sysCatId, $personId]);
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$pdo->commit();
|
$pdo->commit();
|
||||||
@@ -258,6 +272,4 @@ if ($action === 'validate_transfers') {
|
|||||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,6 @@ foreach ($budgetParents as $index => $parent) {
|
|||||||
$parentMapping[] = [
|
$parentMapping[] = [
|
||||||
'id' => (int)$parent['id'],
|
'id' => (int)$parent['id'],
|
||||||
'name' => $parent['name'],
|
'name' => $parent['name'],
|
||||||
'db_field' => 'amount_p' . $num,
|
|
||||||
'css' => 'p' . $num,
|
'css' => 'p' . $num,
|
||||||
'color' => $parent['color'] ?? (($num === 1) ? '#0891b2' : '#f59e0b')
|
'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));
|
$prevMonthLink = date('Y-m-01', strtotime("-1 month", $focusTs));
|
||||||
$nextMonthLink = 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 = [];
|
$cycleConfigs = [];
|
||||||
$stmtNotes = $pdo->query("SELECT reference_id, content FROM pf_notes WHERE note_type = 'month_config'");
|
$stmtNotes = $pdo->query("SELECT reference_id, content FROM pf_notes WHERE note_type = 'month_config'");
|
||||||
while ($row = $stmtNotes->fetch(PDO::FETCH_ASSOC)) {
|
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), '?'));
|
$inQuery = implode(',', array_fill(0, count($months), '?'));
|
||||||
$stmt = $pdo->prepare("SELECT * FROM pf_alloc_values WHERE month_date IN ($inQuery)");
|
$stmt = $pdo->prepare("SELECT * FROM pf_alloc_values WHERE month_date IN ($inQuery)");
|
||||||
$stmt->execute($months);
|
$stmt->execute($months);
|
||||||
|
|
||||||
$allocs = [];
|
$allocs = [];
|
||||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
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
|
// 6. Récupération de l'ID de la catégorie système
|
||||||
@@ -86,14 +84,10 @@ foreach ($cats as $key => $c) {
|
|||||||
// Statuts de validation
|
// Statuts de validation
|
||||||
$focusDate = $months[0];
|
$focusDate = $months[0];
|
||||||
$isValidated = [];
|
$isValidated = [];
|
||||||
if ($sysCatId && isset($allocs[$focusDate][$sysCatId])) {
|
foreach ($parentMapping as $map) {
|
||||||
$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;
|
$isValidated[$map['css']] = false;
|
||||||
|
if ($sysCatId && isset($allocs[$focusDate][$sysCatId][$map['id']])) {
|
||||||
|
$isValidated[$map['css']] = ($allocs[$focusDate][$sysCatId][$map['id']] == 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +104,7 @@ function getTranslatedMonthName($dateString) {
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="prev-container" style="--p1-main: <?= $parentMapping[0]['color'] ?>; --p2-main: <?= $parentMapping[1]['color'] ?>;">
|
<div class="prev-container" style="--p1-main: <?= $parentMapping[0]['color'] ?>; --p2-main: <?= $parentMapping[1]['color'] ?? '#f59e0b' ?>;">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="prev-section-header">
|
<div class="prev-section-header">
|
||||||
@@ -244,29 +238,33 @@ function getTranslatedMonthName($dateString) {
|
|||||||
<?php if($isIndicative): ?><span>Info</span><?php endif; ?>
|
<?php if($isIndicative): ?><span>Info</span><?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="cat-target-label">
|
<div class="cat-target-label">
|
||||||
<?= htmlspecialchars($cat['target']) ?>
|
<?php if((float)$cat['target'] > 0) echo 'Obj: ' . round((float)$cat['target']) . '€ '; ?>
|
||||||
|
<?php if(!empty($cat['transfer_dest'])) echo '➔ ' . htmlspecialchars($cat['transfer_dest']); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row-actions">
|
<div class="row-actions">
|
||||||
<button type="button" class="btn-icon-action edit" title="<?= tr('edit') ?>" data-id="<?= $cat['id'] ?>" data-name="<?= htmlspecialchars($cat['name']) ?>" data-target="<?= htmlspecialchars($cat['target']) ?>" data-holiday="<?= $cat['holiday_id'] ?? '' ?>" onclick="openEditModal(this)">✎</button>
|
<button type="button" class="btn-icon-action edit" title="<?= tr('edit') ?>"
|
||||||
|
data-id="<?= $cat['id'] ?>"
|
||||||
|
data-name="<?= htmlspecialchars($cat['name']) ?>"
|
||||||
|
data-target="<?= htmlspecialchars($cat['target'] ?? 0) ?>"
|
||||||
|
data-transfer-dest="<?= htmlspecialchars($cat['transfer_dest'] ?? '') ?>"
|
||||||
|
data-holiday="<?= $cat['holiday_id'] ?? '' ?>"
|
||||||
|
onclick="openEditModal(this)">✎</button>
|
||||||
<button type="button" onclick="deleteCategory(<?= $cat['id'] ?>)" class="btn-icon-action delete" title="<?= tr('delete') ?>">🗑️</button>
|
<button type="button" onclick="deleteCategory(<?= $cat['id'] ?>)" class="btn-icon-action delete" title="<?= tr('delete') ?>">🗑️</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<?php foreach ($months as $m):
|
<?php foreach ($months as $m): ?>
|
||||||
$val = $allocs[$m][$cat['id']] ?? [];
|
|
||||||
?>
|
|
||||||
<td class="txt-global sum-target" id="g_<?= $m ?>_<?= $cat['id'] ?>">0</td>
|
<td class="txt-global sum-target" id="g_<?= $m ?>_<?= $cat['id'] ?>">0</td>
|
||||||
|
|
||||||
<?php foreach ($parentMapping as $map):
|
<?php foreach ($parentMapping as $map):
|
||||||
$dbField = $map['db_field'];
|
$cellValue = $allocs[$m][$cat['id']][$map['id']] ?? 0;
|
||||||
$cellValue = isset($val[$dbField]) ? $val[$dbField] : 0;
|
|
||||||
?>
|
?>
|
||||||
<td>
|
<td>
|
||||||
<input type="number" step="1" class="prev-input txt-<?= $map['css'] ?> inp-<?= $map['css'] ?>-<?= $m ?> <?= $inputClass ?>"
|
<input type="number" step="1" class="prev-input txt-<?= $map['css'] ?> inp-<?= $map['css'] ?>-<?= $m ?> <?= $inputClass ?>"
|
||||||
value="<?= $cellValue == 0 ? '' : round($cellValue) ?>"
|
value="<?= $cellValue == 0 ? '' : round($cellValue) ?>"
|
||||||
placeholder="-"
|
placeholder="-"
|
||||||
data-target="<?= htmlspecialchars($cat['target']) ?>"
|
data-transfer-dest="<?= htmlspecialchars($cat['transfer_dest'] ?? '') ?>"
|
||||||
onchange="updateAlloc('<?= $m ?>', <?= $cat['id'] ?>, '<?= $dbField ?>', this)">
|
onchange="updateAlloc('<?= $m ?>', <?= $cat['id'] ?>, <?= $map['id'] ?>, this)">
|
||||||
</td>
|
</td>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@@ -293,10 +291,13 @@ function getTranslatedMonthName($dateString) {
|
|||||||
<?php
|
<?php
|
||||||
$focusMonth = $months[0];
|
$focusMonth = $months[0];
|
||||||
$targetsOrder = ['vers commune', 'vers L.Pol', 'vers L.Pep', 'vers L.Perso'];
|
$targetsOrder = ['vers commune', 'vers L.Pol', 'vers L.Pep', 'vers L.Perso'];
|
||||||
|
|
||||||
$allTargets = $targetsOrder;
|
$allTargets = $targetsOrder;
|
||||||
foreach($cats as $c) {
|
foreach($cats as $c) {
|
||||||
$t = trim($c['target']);
|
$t = trim($c['transfer_dest'] ?? '');
|
||||||
if(!empty($t) && !in_array($t, $allTargets)) { $allTargets[] = $t; }
|
if(!empty($t) && !in_array($t, $allTargets)) {
|
||||||
|
$allTargets[] = $t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$allTargets = array_unique($allTargets);
|
$allTargets = array_unique($allTargets);
|
||||||
?>
|
?>
|
||||||
@@ -364,9 +365,13 @@ function getTranslatedMonthName($dateString) {
|
|||||||
<input type="text" name="name" class="pf-input" required>
|
<input type="text" name="name" class="pf-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="pf-label"><?= tr('bud_prev_label_target') ?></label>
|
<label class="pf-label">Objectif Mensuel (€)</label>
|
||||||
<select name="target" class="pf-input" required>
|
<input type="number" step="1" name="target" class="pf-input" placeholder="Ex: 150">
|
||||||
<option value="" disabled selected>-- <?= tr('bud_prev_choose') ?> --</option>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="pf-label">Destination Virement (Optionnel)</label>
|
||||||
|
<select name="transfer_dest" class="pf-input">
|
||||||
|
<option value="" selected>-- Aucune --</option>
|
||||||
<option value="vers L.Pol">vers L.Pol</option>
|
<option value="vers L.Pol">vers L.Pol</option>
|
||||||
<option value="vers L.Pep">vers L.Pep</option>
|
<option value="vers L.Pep">vers L.Pep</option>
|
||||||
<option value="vers L.Perso">vers L.Perso</option>
|
<option value="vers L.Perso">vers L.Perso</option>
|
||||||
@@ -404,8 +409,13 @@ function getTranslatedMonthName($dateString) {
|
|||||||
<input type="text" name="name" id="edit_cat_name" class="pf-input" required>
|
<input type="text" name="name" id="edit_cat_name" class="pf-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="pf-label"><?= tr('bud_prev_label_target') ?></label>
|
<label class="pf-label">Objectif Mensuel (€)</label>
|
||||||
<select name="target" id="edit_cat_target" class="pf-input" required>
|
<input type="number" step="1" name="target" id="edit_cat_target" class="pf-input" placeholder="Ex: 150">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="pf-label">Destination Virement (Optionnel)</label>
|
||||||
|
<select name="transfer_dest" id="edit_cat_transfer_dest" class="pf-input">
|
||||||
|
<option value="">-- Aucune --</option>
|
||||||
<option value="vers L.Pol">vers L.Pol</option>
|
<option value="vers L.Pol">vers L.Pol</option>
|
||||||
<option value="vers L.Pep">vers L.Pep</option>
|
<option value="vers L.Pep">vers L.Pep</option>
|
||||||
<option value="vers L.Perso">vers L.Perso</option>
|
<option value="vers L.Perso">vers L.Perso</option>
|
||||||
@@ -452,6 +462,7 @@ window.I18N = {
|
|||||||
|
|
||||||
window.CONFIG = window.CONFIG || {};
|
window.CONFIG = window.CONFIG || {};
|
||||||
window.CONFIG.parentMapping = <?= json_encode($parentMapping) ?>;
|
window.CONFIG.parentMapping = <?= json_encode($parentMapping) ?>;
|
||||||
|
window.CONFIG.CURRENCY = '<?= defined('CURRENCY') ? CURRENCY : "€" ?>';
|
||||||
|
|
||||||
const currentYear = <?= $currentYear ?>;
|
const currentYear = <?= $currentYear ?>;
|
||||||
const months = <?= json_encode($months) ?>;
|
const months = <?= json_encode($months) ?>;
|
||||||
@@ -460,6 +471,7 @@ function openEditModal(btn) {
|
|||||||
document.getElementById('edit_cat_id').value = btn.getAttribute('data-id');
|
document.getElementById('edit_cat_id').value = btn.getAttribute('data-id');
|
||||||
document.getElementById('edit_cat_name').value = btn.getAttribute('data-name');
|
document.getElementById('edit_cat_name').value = btn.getAttribute('data-name');
|
||||||
document.getElementById('edit_cat_target').value = btn.getAttribute('data-target');
|
document.getElementById('edit_cat_target').value = btn.getAttribute('data-target');
|
||||||
|
document.getElementById('edit_cat_transfer_dest').value = btn.getAttribute('data-transfer-dest');
|
||||||
document.getElementById('edit_cat_holiday').value = btn.getAttribute('data-holiday');
|
document.getElementById('edit_cat_holiday').value = btn.getAttribute('data-holiday');
|
||||||
document.getElementById('editCatModal').style.display = 'flex';
|
document.getElementById('editCatModal').style.display = 'flex';
|
||||||
document.body.classList.add('no-scroll');
|
document.body.classList.add('no-scroll');
|
||||||
@@ -484,8 +496,8 @@ function updateSalary(person, input) {
|
|||||||
recalcAllAllocations();
|
recalcAllAllocations();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAlloc(month, catId, personField, input) {
|
function updateAlloc(month, catId, personId, input) {
|
||||||
saveData('update_allocation', { month_date: month, cat_id: catId, person: personField, value: input.value || 0 });
|
saveData('update_allocation', { month_date: month, cat_id: catId, person_id: personId, value: input.value || 0 });
|
||||||
recalcAllAllocations();
|
recalcAllAllocations();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +534,7 @@ function duplicateMonth() {
|
|||||||
const tInp = row.querySelector('.inp-' + map.css + '-' + targetDateStr);
|
const tInp = row.querySelector('.inp-' + map.css + '-' + targetDateStr);
|
||||||
if(sInp && tInp) {
|
if(sInp && tInp) {
|
||||||
tInp.value = sInp.value;
|
tInp.value = sInp.value;
|
||||||
updateAlloc(targetDateStr, catId, map.db_field, tInp);
|
updateAlloc(targetDateStr, catId, map.id, tInp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -593,10 +605,10 @@ function updateSummaryTable() {
|
|||||||
dataByTarget[map.css] = {};
|
dataByTarget[map.css] = {};
|
||||||
|
|
||||||
document.querySelectorAll('.inp-' + map.css + '-' + focusMonth).forEach(inp => {
|
document.querySelectorAll('.inp-' + map.css + '-' + focusMonth).forEach(inp => {
|
||||||
const target = inp.getAttribute('data-target');
|
const dest = inp.getAttribute('data-transfer-dest');
|
||||||
if(target) {
|
if(dest) {
|
||||||
if(!dataByTarget[map.css][target]) dataByTarget[map.css][target] = 0;
|
if(!dataByTarget[map.css][dest]) dataByTarget[map.css][dest] = 0;
|
||||||
dataByTarget[map.css][target] += (parseFloat(inp.value) || 0);
|
dataByTarget[map.css][dest] += (parseFloat(inp.value) || 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -637,15 +649,15 @@ function saveData(action, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateTransfers(personCss, month) {
|
function validateTransfers(personCss, month) {
|
||||||
const msg = window.I18N['bud_prev_confirm_transfers'].replace('%p', personCss).replace('%m', month);
|
|
||||||
if (!confirm(msg)) return;
|
|
||||||
|
|
||||||
const parentMap = window.CONFIG.parentMapping.find(m => m.css === personCss);
|
const parentMap = window.CONFIG.parentMapping.find(m => m.css === personCss);
|
||||||
const dbPersonName = parentMap ? parentMap.name : personCss;
|
if (!parentMap) return;
|
||||||
|
|
||||||
|
const msg = window.I18N['bud_prev_confirm_transfers'].replace('%p', parentMap.name).replace('%m', month);
|
||||||
|
if (!confirm(msg)) return;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'validate_transfers');
|
formData.append('action', 'validate_transfers');
|
||||||
formData.append('person', dbPersonName);
|
formData.append('person_id', parentMap.id);
|
||||||
formData.append('month_date', month);
|
formData.append('month_date', month);
|
||||||
|
|
||||||
fetch('modules/budget/includes/api/save-budget.php', { method: 'POST', body: formData })
|
fetch('modules/budget/includes/api/save-budget.php', { method: 'POST', body: formData })
|
||||||
|
|||||||
@@ -57,12 +57,11 @@ if (isset($_POST['action']) && $_POST['action'] === 'reopen_month') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (isset($_POST['action']) && $_POST['action'] === 'save_import') {
|
if (isset($_POST['action']) && $_POST['action'] === 'save_import') {
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$stmtExp = $pdo->prepare("INSERT INTO pf_expenses (date_exp, gestion_month, category, label, amount, import_ref, budget_item_id, holiday_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
$stmtExp = $pdo->prepare("INSERT INTO pf_expenses (date_exp, gestion_month, category, label, amount, import_ref, budget_item_id, holiday_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
$stmtRule = $pdo->prepare("INSERT INTO pf_import_rules (keyword, category) VALUES (?, ?) ON DUPLICATE KEY UPDATE category = VALUES(category)");
|
// Mémorisation du mapping étendu (incluant budget_item_id)
|
||||||
|
$stmtRule = $pdo->prepare("INSERT INTO pf_import_rules (keyword, category, budget_item_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE category = VALUES(category), budget_item_id = VALUES(budget_item_id)");
|
||||||
|
|
||||||
if (isset($_POST['lines']) && is_array($_POST['lines'])) {
|
if (isset($_POST['lines']) && is_array($_POST['lines'])) {
|
||||||
foreach ($_POST['lines'] as $line) {
|
foreach ($_POST['lines'] as $line) {
|
||||||
@@ -81,7 +80,7 @@ if (isset($_POST['action']) && $_POST['action'] === 'save_import') {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$stmtExp->execute([$dateToSave, $gestionMonthLine, $cat, $line['label'], $finalAmount, $line['ref'], $budgetItemId, $holidayId]);
|
$stmtExp->execute([$dateToSave, $gestionMonthLine, $cat, $line['label'], $finalAmount, $line['ref'], $budgetItemId, $holidayId]);
|
||||||
$stmtRule->execute([$line['label'], $cat]);
|
$stmtRule->execute([$line['label'], $cat, $budgetItemId]);
|
||||||
$count++;
|
$count++;
|
||||||
} catch (Exception $e) { continue; }
|
} catch (Exception $e) { continue; }
|
||||||
}
|
}
|
||||||
@@ -110,7 +109,15 @@ $showPreview = false;
|
|||||||
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
||||||
$file = $_FILES['csv_file']['tmp_name'];
|
$file = $_FILES['csv_file']['tmp_name'];
|
||||||
$handle = fopen($file, "r");
|
$handle = fopen($file, "r");
|
||||||
$rules = []; try { $rules = $pdo->query("SELECT keyword, category FROM pf_import_rules")->fetchAll(PDO::FETCH_KEY_PAIR); } catch(Exception $e){}
|
|
||||||
|
$rules = [];
|
||||||
|
try {
|
||||||
|
$stmtRules = $pdo->query("SELECT keyword, category, budget_item_id FROM pf_import_rules");
|
||||||
|
while ($r = $stmtRules->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
$rules[$r['keyword']] = ['cat' => $r['category'], 'budget_item_id' => $r['budget_item_id']];
|
||||||
|
}
|
||||||
|
} catch(Exception $e){}
|
||||||
|
|
||||||
$existingRefs = []; try { $existingRefs = $pdo->query("SELECT import_ref FROM pf_expenses WHERE import_ref IS NOT NULL")->fetchAll(PDO::FETCH_COLUMN); } catch(Exception $e){}
|
$existingRefs = []; try { $existingRefs = $pdo->query("SELECT import_ref FROM pf_expenses WHERE import_ref IS NOT NULL")->fetchAll(PDO::FETCH_COLUMN); } catch(Exception $e){}
|
||||||
fgetcsv($handle, 1000, ";", "\"", "\\");
|
fgetcsv($handle, 1000, ";", "\"", "\\");
|
||||||
|
|
||||||
@@ -129,9 +136,16 @@ if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] == 0) {
|
|||||||
$isDuplicate = in_array($uniqueKey, $existingRefs);
|
$isDuplicate = in_array($uniqueKey, $existingRefs);
|
||||||
|
|
||||||
$suggestedCat = '';
|
$suggestedCat = '';
|
||||||
foreach ($rules as $kw => $c) { if (stripos($label, $kw) !== false) { $suggestedCat = $c; break; } }
|
$suggestedItemId = null;
|
||||||
|
foreach ($rules as $kw => $ruleData) {
|
||||||
|
if (stripos($label, $kw) !== false) {
|
||||||
|
$suggestedCat = $ruleData['cat'];
|
||||||
|
$suggestedItemId = $ruleData['budget_item_id'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$csvData[] = ['date'=>$dateSql, 'label'=>$label, 'amount'=>$amount, 'cat'=>$suggestedCat, 'ref'=>$uniqueKey, 'is_duplicate'=>$isDuplicate, 'is_credit'=>$isCredit];
|
$csvData[] = ['date'=>$dateSql, 'label'=>$label, 'amount'=>$amount, 'cat'=>$suggestedCat, 'suggested_item_id'=>$suggestedItemId, 'ref'=>$uniqueKey, 'is_duplicate'=>$isDuplicate, 'is_credit'=>$isCredit];
|
||||||
}
|
}
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
$showPreview = true;
|
$showPreview = true;
|
||||||
@@ -224,6 +238,7 @@ $categoriesConfig = [
|
|||||||
'School' => ['type'=>'debit', 'label'=>tr('cat_school'), 'budget'=>$budget_school, 'color'=>'#10b981', 'suggestions'=>[]],
|
'School' => ['type'=>'debit', 'label'=>tr('cat_school'), 'budget'=>$budget_school, 'color'=>'#10b981', 'suggestions'=>[]],
|
||||||
'Frais' => ['type'=>'debit', 'label'=>tr('cat_fixed'), 'budget'=>$budget_frais, 'color'=>'#ef4444', 'suggestions'=>[]],
|
'Frais' => ['type'=>'debit', 'label'=>tr('cat_fixed'), 'budget'=>$budget_frais, 'color'=>'#ef4444', 'suggestions'=>[]],
|
||||||
'Autres' => ['type'=>'debit', 'label'=>tr('cat_others'), 'budget'=>$budget_autres, 'color'=>'#64748b', 'suggestions'=>['Restaurant', 'Cadeau']],
|
'Autres' => ['type'=>'debit', 'label'=>tr('cat_others'), 'budget'=>$budget_autres, 'color'=>'#64748b', 'suggestions'=>['Restaurant', 'Cadeau']],
|
||||||
|
'Apports' => ['type'=>'debit', 'label'=>tr('cat_contributions') ?? 'Apports & Projets', 'budget'=>0, 'color'=>'#0ea5e9', 'suggestions'=>['Alex', 'Laia', 'Remboursement']],
|
||||||
'LivretA' => ['type'=>'debit', 'label'=>tr('cat_savings'),'budget'=>0, 'color'=>'#8b5cf6', 'suggestions'=>['Virement']]
|
'LivretA' => ['type'=>'debit', 'label'=>tr('cat_savings'),'budget'=>0, 'color'=>'#8b5cf6', 'suggestions'=>['Virement']]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -695,11 +710,15 @@ $monthName = $monthNames[(int)$viewM] . ' ' . $viewY;
|
|||||||
</select>
|
</select>
|
||||||
<select name="lines[<?= $idx ?>][budget_item_id]" class="pf-input select-frais" onchange="checkValidation()" style="display:none; padding:4px; font-size:0.85rem; flex:1;" disabled>
|
<select name="lines[<?= $idx ?>][budget_item_id]" class="pf-input select-frais" onchange="checkValidation()" style="display:none; padding:4px; font-size:0.85rem; flex:1;" disabled>
|
||||||
<option value="">-- <?= tr('bud_is_charge') ?> --</option>
|
<option value="">-- <?= tr('bud_is_charge') ?> --</option>
|
||||||
<?php foreach ($fixedChargesList as $fc): ?><option value="<?= $fc['id'] ?>"><?= htmlspecialchars($fc['name']) ?></option><?php endforeach; ?>
|
<?php foreach ($fixedChargesList as $fc): ?>
|
||||||
|
<option value="<?= $fc['id'] ?>" <?= ($row['suggested_item_id'] == $fc['id']) ? 'selected' : '' ?>><?= htmlspecialchars($fc['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
<select name="lines[<?= $idx ?>][budget_item_id]" class="pf-input select-income" onchange="checkValidation()" style="display:none; padding:4px; font-size:0.85rem; flex:1;" disabled>
|
<select name="lines[<?= $idx ?>][budget_item_id]" class="pf-input select-income" onchange="checkValidation()" style="display:none; padding:4px; font-size:0.85rem; flex:1;" disabled>
|
||||||
<option value="">-- <?= tr('bud_is_income') ?> --</option>
|
<option value="">-- <?= tr('bud_is_income') ?> --</option>
|
||||||
<?php foreach ($incomeList as $inc): ?><option value="<?= $inc['id'] ?>"><?= htmlspecialchars($inc['name']) ?></option><?php endforeach; ?>
|
<?php foreach ($incomeList as $inc): ?>
|
||||||
|
<option value="<?= $inc['id'] ?>" <?= ($row['suggested_item_id'] == $inc['id']) ? 'selected' : '' ?>><?= htmlspecialchars($inc['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -792,10 +811,14 @@ function openEditModal(e) {
|
|||||||
else if (e.category === 'Income') document.getElementById('incomeSelect').value = e.budget_item_id;
|
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 row = select.closest('tr');
|
||||||
const fSel = row.querySelector('.select-frais'); const iSel = row.querySelector('.select-income');
|
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; }
|
if (select.value === 'Frais') { fSel.style.display = 'block'; fSel.disabled = false; }
|
||||||
else if (select.value === 'Income') { iSel.style.display = 'block'; iSel.disabled = false; }
|
else if (select.value === 'Income') { iSel.style.display = 'block'; iSel.disabled = false; }
|
||||||
checkValidation();
|
checkValidation();
|
||||||
@@ -815,7 +838,10 @@ function checkValidation() {
|
|||||||
else { btn.disabled = false; btn.style.opacity=1; msg.style.display='none'; }
|
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) => {
|
window.addEventListener('click', (e) => {
|
||||||
if (e.target.classList.contains('pf-modal')) {
|
if (e.target.classList.contains('pf-modal')) {
|
||||||
@@ -826,7 +852,7 @@ window.addEventListener('click', (e) => {
|
|||||||
|
|
||||||
// --- 2. SUPPRESSION ASYNCHRONE ---
|
// --- 2. SUPPRESSION ASYNCHRONE ---
|
||||||
async function deleteExpense(id) {
|
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;
|
if (!confirmed) return;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|||||||
+255
-19
@@ -1,10 +1,18 @@
|
|||||||
-- HouseHub — Schéma MySQL
|
-- HouseHub — Schéma MySQL (Modèle pour une base famille)
|
||||||
-- Créer la base : CREATE DATABASE househub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
SET FOREIGN_KEY_CHECKS = 0;
|
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 (
|
CREATE TABLE IF NOT EXISTS pf_users (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
username VARCHAR(100) NOT NULL UNIQUE,
|
username VARCHAR(100) NOT NULL UNIQUE,
|
||||||
@@ -15,11 +23,12 @@ CREATE TABLE IF NOT EXISTS pf_users (
|
|||||||
-- ─── Personnes ────────────────────────────────────────────────────────────────
|
-- ─── Personnes ────────────────────────────────────────────────────────────────
|
||||||
CREATE TABLE `pf_people` (
|
CREATE TABLE `pf_people` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`name` varchar(50) NOT NULL,
|
`name` varchar(100) NOT NULL,
|
||||||
`user_id` int(11) DEFAULT NULL,
|
`user_id` int(11) DEFAULT NULL,
|
||||||
`role` varchar(50) DEFAULT NULL,
|
`role` varchar(50) DEFAULT NULL,
|
||||||
|
`color` varchar(7) DEFAULT '#0891b2',
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
-- ─── Calendrier familial ──────────────────────────────────────────────────────
|
-- ─── Calendrier familial ──────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_events (
|
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 (
|
CREATE TABLE IF NOT EXISTS pf_expenses (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
date_exp DATE NOT NULL,
|
date_exp DATE NOT NULL,
|
||||||
gestion_month VARCHAR(7) NOT NULL,
|
gestion_month VARCHAR(10) NOT NULL,
|
||||||
category VARCHAR(100),
|
category VARCHAR(100),
|
||||||
label VARCHAR(255),
|
label VARCHAR(255),
|
||||||
amount DECIMAL(10,2) DEFAULT 0,
|
amount DECIMAL(10,2) DEFAULT 0,
|
||||||
@@ -106,24 +115,26 @@ CREATE TABLE IF NOT EXISTS pf_expenses (
|
|||||||
CREATE TABLE IF NOT EXISTS pf_alloc_categories (
|
CREATE TABLE IF NOT EXISTS pf_alloc_categories (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
name VARCHAR(100) NOT NULL,
|
name VARCHAR(100) NOT NULL,
|
||||||
target DECIMAL(10,2) DEFAULT 0,
|
target DECIMAL(10,2) DEFAULT 0.00,
|
||||||
|
transfer_dest VARCHAR(50) DEFAULT NULL,
|
||||||
holiday_id INT DEFAULT NULL,
|
holiday_id INT DEFAULT NULL,
|
||||||
sort_order INT DEFAULT 0
|
sort_order INT DEFAULT 0
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pf_alloc_values (
|
CREATE TABLE IF NOT EXISTS pf_alloc_values (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
month_date VARCHAR(7) NOT NULL,
|
month_date VARCHAR(10) NOT NULL,
|
||||||
cat_id INT NOT NULL,
|
cat_id INT NOT NULL,
|
||||||
amount_alex DECIMAL(10,2) DEFAULT 0,
|
person_id INT NOT NULL,
|
||||||
amount_laia DECIMAL(10,2) DEFAULT 0,
|
amount DECIMAL(10,2) DEFAULT 0.00,
|
||||||
UNIQUE KEY uq_alloc (month_date, cat_id)
|
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;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pf_savings (
|
CREATE TABLE IF NOT EXISTS pf_savings (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
owner VARCHAR(50) NOT NULL,
|
owner VARCHAR(50) NOT NULL,
|
||||||
month_date VARCHAR(7) NOT NULL,
|
month_date VARCHAR(10) NOT NULL,
|
||||||
category VARCHAR(100) NOT NULL,
|
category VARCHAR(100) NOT NULL,
|
||||||
amount DECIMAL(10,2) DEFAULT 0,
|
amount DECIMAL(10,2) DEFAULT 0,
|
||||||
holiday_id INT DEFAULT NULL
|
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 (
|
CREATE TABLE IF NOT EXISTS pf_import_rules (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
keyword VARCHAR(255) NOT NULL UNIQUE,
|
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;
|
) 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 (
|
CREATE TABLE IF NOT EXISTS pf_notes (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
note_type VARCHAR(100) NOT NULL,
|
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)
|
UNIQUE KEY uq_note (note_type, reference_id)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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 ───────────────────────────────────────────────────────
|
-- ─── Voyages / Holidays ───────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_holidays (
|
CREATE TABLE IF NOT EXISTS pf_holidays (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
@@ -228,13 +275,153 @@ CREATE TABLE IF NOT EXISTS pf_gifts (
|
|||||||
amount DECIMAL(8,2) DEFAULT 0
|
amount DECIMAL(8,2) DEFAULT 0
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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 ────────────────────────────────────────────────────────
|
CREATE TABLE IF NOT EXISTS pf_maintenances (
|
||||||
-- Utilisateur admin (mot de passe : changeme → à modifier !)
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
INSERT IGNORE INTO pf_users (id, username, password_hash, display_name)
|
vehicle_id INT NOT NULL,
|
||||||
VALUES (1, 'admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Admin');
|
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 ─────────────────────────────────────────────────────────────
|
-- ─── Calendar iOS ─────────────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_calendar_events (
|
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_uid VARCHAR(255) NOT NULL,
|
||||||
external_etag VARCHAR(255) DEFAULT NULL,
|
external_etag VARCHAR(255) DEFAULT NULL,
|
||||||
calendar_url VARCHAR(1024) DEFAULT NULL,
|
calendar_url VARCHAR(1024) DEFAULT NULL,
|
||||||
|
external_href VARCHAR(2048) DEFAULT NULL,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
UNIQUE KEY uq_calendar_event (calendar_event_id),
|
UNIQUE KEY uq_calendar_event (calendar_event_id),
|
||||||
UNIQUE KEY uq_external_link (external_uid)
|
UNIQUE KEY uq_external_link (external_uid)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) 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;
|
||||||
Reference in New Issue
Block a user