Files
Inventory/scripts/check-prod-orphaned-detail.php
Matthieu 38777b7de0 fix(custom-fields) : prevent data loss on ModelType save + restoration scripts
Backend: match existing CustomField by name as fallback when ID is not provided,
preventing deletion and recreation of field definitions (which cascade-deletes values).

Includes restoration/migration scripts for prod:
- restore-custom-field-values.php: restores piece values from audit logs
- migrate-orphaned-custom-fields.php: migrates values from orphaned CFs
- fix-prod-all.php: combined fix (migrate + restore + cleanup)
- fix-prod-recreate-and-migrate.php: full fix (recreate missing CFs + migrate + restore)
- check-prod-*.php: diagnostic scripts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 20:24:37 +01:00

84 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__.'/../vendor/autoload.php';
use Doctrine\DBAL\DriverManager;
$conn = DriverManager::getConnection([
'driver' => 'pdo_pgsql',
'host' => 'localhost',
'port' => 5432,
'dbname' => 'inventory',
'user' => 'ferme_user',
'password' => 'fermerecette',
]);
// Show a sample of orphaned CFValues for pieces
echo "--- Sample orphaned piece CFValues ---\n";
$rows = $conn->fetchAllAssociative("
SELECT cfv.id as cfv_id, cfv.value, cfv.pieceid,
cf.id as cf_id, cf.name as cf_name,
cf.typecomposantid, cf.typepieceid, cf.typeproductid,
p.name as piece_name, p.typepieceid as piece_modeltype
FROM custom_field_values cfv
JOIN custom_fields cf ON cf.id = cfv.customfieldid
JOIN pieces p ON p.id = cfv.pieceid
WHERE cfv.pieceid IS NOT NULL
AND cf.typepieceid IS NULL
ORDER BY p.name
LIMIT 10
");
echo sprintf("Found %d (limited to 10)\n\n", count($rows));
foreach ($rows as $r) {
echo sprintf(" Piece '%s' | field '%s' = '%s' | CF FK: composant=%s piece=%s product=%s\n",
$r['piece_name'], $r['cf_name'], $r['value'],
$r['typecomposantid'] ?? 'NULL',
$r['typepieceid'] ?? 'NULL',
$r['typeproductid'] ?? 'NULL'
);
}
// Show a sample of orphaned CFValues for composants
echo "\n--- Sample orphaned composant CFValues ---\n";
$rows = $conn->fetchAllAssociative("
SELECT cfv.id as cfv_id, cfv.value, cfv.composantid,
cf.id as cf_id, cf.name as cf_name,
cf.typecomposantid, cf.typepieceid, cf.typeproductid,
c.name as composant_name, c.typecomposantid as composant_modeltype
FROM custom_field_values cfv
JOIN custom_fields cf ON cf.id = cfv.customfieldid
JOIN composants c ON c.id = cfv.composantid
WHERE cfv.composantid IS NOT NULL
AND cf.typecomposantid IS NULL
ORDER BY c.name
LIMIT 10
");
echo sprintf("Found %d (limited to 10)\n\n", count($rows));
foreach ($rows as $r) {
echo sprintf(" Composant '%s' | field '%s' = '%s' | CF FK: composant=%s piece=%s product=%s\n",
$r['composant_name'], $r['cf_name'], $r['value'],
$r['typecomposantid'] ?? 'NULL',
$r['typepieceid'] ?? 'NULL',
$r['typeproductid'] ?? 'NULL'
);
}
// Check: are there CFs with ONLY typepieceid NULL but other FKs set?
echo "\n--- Orphaned CF FK patterns ---\n";
$rows = $conn->fetchAllAssociative("
SELECT
CASE WHEN typecomposantid IS NULL THEN 'NULL' ELSE 'SET' END as composant_fk,
CASE WHEN typepieceid IS NULL THEN 'NULL' ELSE 'SET' END as piece_fk,
CASE WHEN typeproductid IS NULL THEN 'NULL' ELSE 'SET' END as product_fk,
COUNT(*) as cnt
FROM custom_fields
GROUP BY composant_fk, piece_fk, product_fk
ORDER BY cnt DESC
");
foreach ($rows as $r) {
echo sprintf(" composant=%s piece=%s product=%s : %d CFs\n",
$r['composant_fk'], $r['piece_fk'], $r['product_fk'], $r['cnt']);
}