exec("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"); $ddl = [ "CREATE TABLE IF NOT EXISTS periodic_controls ( id INT NOT NULL AUTO_INCREMENT, organization_id INT NOT NULL, code VARCHAR(20) NULL, title VARCHAR(255) NOT NULL, description TEXT NULL, category VARCHAR(100) NULL, control_ref VARCHAR(32) NULL, owner_role_id INT NULL, frequency ENUM('giornaliero','settimanale','mensile','trimestrale','semestrale','annuale','custom') NOT NULL DEFAULT 'mensile', frequency_days INT NULL, method TEXT NULL, next_due_date DATE NULL, last_executed_at DATE NULL, status ENUM('active','suspended') NOT NULL DEFAULT 'active', created_by INT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_pctl_code (organization_id, code), KEY idx_pctl_org (organization_id), KEY idx_pctl_due (organization_id, next_due_date), CONSTRAINT fk_pctl_org FOREIGN KEY (organization_id) REFERENCES organizations (id) ON DELETE CASCADE, CONSTRAINT fk_pctl_role FOREIGN KEY (owner_role_id) REFERENCES org_roles (id) ON DELETE SET NULL, CONSTRAINT fk_pctl_user FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", "CREATE TABLE IF NOT EXISTS periodic_control_executions ( id INT NOT NULL AUTO_INCREMENT, control_id INT NOT NULL, executed_at DATE NOT NULL, executed_by INT NULL, outcome ENUM('conforme','non_conforme','parziale','non_applicabile') NOT NULL DEFAULT 'conforme', notes TEXT NULL, ncr_id INT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_pcexec_control (control_id), CONSTRAINT fk_pcexec_control FOREIGN KEY (control_id) REFERENCES periodic_controls (id) ON DELETE CASCADE, CONSTRAINT fk_pcexec_user FOREIGN KEY (executed_by) REFERENCES users (id) ON DELETE SET NULL, CONSTRAINT fk_pcexec_ncr FOREIGN KEY (ncr_id) REFERENCES non_conformities (id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", ]; foreach ($ddl as $stmt) { try { $pdo->exec($stmt); } catch (PDOException $e) { if (!in_array($e->errorInfo[1] ?? 0, [1050, 1061], true)) { throw $e; } } } // UNIQUE (organization_id, code) idempotente — la tabella su prod può preesistere senza // l'indice (creata prima del fix). Anti-doppione su CTL-NNN concorrenti (vedi retry in create()). try { $pdo->exec("ALTER TABLE periodic_controls ADD UNIQUE KEY uk_pctl_code (organization_id, code)"); } catch (PDOException $e) { $c = $e->errorInfo[1] ?? 0; if ($c === 1062) { fwrite(STDERR, "WARN uk_pctl_code: doppioni esistenti, indice NON aggiunto.\n"); } elseif (!in_array($c, [1061], true)) { throw $e; } // 1061 = indice già presente } // Estende non_conformities.source con 'monitoring' preservando i valori esistenti. try { $cur = (string) $pdo->query("SELECT COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'non_conformities' AND COLUMN_NAME = 'source'")->fetchColumn(); if (stripos($cur, "'monitoring'") === false) { $vals = []; if (preg_match_all("/'([^']+)'/", $cur, $m)) { $vals = $m[1]; } if (!in_array('monitoring', $vals, true)) { $vals[] = 'monitoring'; } if (!$vals) { $vals = ['assessment','audit','incident','supplier_review','management_review','external_audit','monitoring','other']; } $enum = implode(',', array_map(fn($v) => "'" . $v . "'", $vals)); $pdo->exec("ALTER TABLE non_conformities MODIFY COLUMN source ENUM($enum) NOT NULL DEFAULT 'assessment'"); } } catch (PDOException $e) { fwrite(STDERR, "WARN ALTER non_conformities.source: " . $e->getMessage() . "\n"); } $counts = []; foreach (['periodic_controls', 'periodic_control_executions'] as $t) { $counts[$t] = (int) $pdo->query("SELECT COUNT(*) FROM $t")->fetchColumn(); } $src = (string) $pdo->query("SELECT COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'non_conformities' AND COLUMN_NAME = 'source'")->fetchColumn(); echo "OK seed-periodic-controls — " . json_encode($counts, JSON_UNESCAPED_UNICODE) . "\n"; echo "non_conformities.source has monitoring: " . (str_contains($src, 'monitoring') ? 'YES' : 'NO') . "\n";