Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28394ce1b4 | ||
|
|
8cfcb41a39 |
@@ -1,2 +1,2 @@
|
||||
parameters:
|
||||
app.version: '1.9.27'
|
||||
app.version: '1.9.28'
|
||||
|
||||
320
src/Command/ConvertMoteurPieceToComponentCommand.php
Normal file
320
src/Command/ConvertMoteurPieceToComponentCommand.php
Normal file
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Throwable;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:convert-moteur-piece-to-component',
|
||||
description: 'Convertit la catégorie "Moteur" (PIECE) en COMPONENT et migre toutes les pièces liées en composants.',
|
||||
)]
|
||||
class ConvertMoteurPieceToComponentCommand extends Command
|
||||
{
|
||||
private const MODEL_TYPE_ID = 'cmgytewe0002447ffup09bscr';
|
||||
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Affiche les actions sans les exécuter');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$dryRun = $input->getOption('dry-run');
|
||||
|
||||
$io->title('Conversion catégorie "Moteur" : PIECE → COMPONENT');
|
||||
|
||||
// ── 1. Vérifications ──────────────────────────────────────────────
|
||||
|
||||
$modelType = $this->connection->fetchAssociative(
|
||||
'SELECT id, name, code, category FROM model_types WHERE id = :id',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
|
||||
if (!$modelType) {
|
||||
$io->error('ModelType "Moteur" introuvable (id: '.self::MODEL_TYPE_ID.')');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if ('PIECE' !== $modelType['category']) {
|
||||
$io->error(sprintf('Le ModelType "Moteur" est déjà de catégorie %s — rien à faire.', $modelType['category']));
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$pieces = $this->connection->fetchAllAssociative(
|
||||
'SELECT id, name, reference FROM pieces WHERE typepieceid = :id ORDER BY name',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
|
||||
$pieceCount = count($pieces);
|
||||
$io->info(sprintf('Pièces à convertir : %d', $pieceCount));
|
||||
|
||||
if ($pieceCount > 0) {
|
||||
$io->table(
|
||||
['ID', 'Nom', 'Référence'],
|
||||
array_map(fn (array $p) => [$p['id'], $p['name'], $p['reference'] ?? '—'], $pieces),
|
||||
);
|
||||
}
|
||||
|
||||
// Check blockers
|
||||
$blockers = [];
|
||||
|
||||
$machineLinked = (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM machine_piece_links mpl
|
||||
JOIN pieces p ON mpl.pieceid = p.id
|
||||
WHERE p.typepieceid = :id',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
|
||||
if ($machineLinked > 0) {
|
||||
$blockers[] = sprintf('%d pièce(s) liée(s) à des machines — conversion impossible.', $machineLinked);
|
||||
}
|
||||
|
||||
$nameCollisions = $this->connection->fetchFirstColumn(
|
||||
'SELECT p.name FROM pieces p
|
||||
WHERE p.typepieceid = :id
|
||||
AND p.name IN (SELECT c.name FROM composants c)',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
|
||||
if ([] !== $nameCollisions) {
|
||||
$blockers[] = sprintf('Collision de noms avec des composants existants : %s', implode(', ', $nameCollisions));
|
||||
}
|
||||
|
||||
$categoryCollision = (int) $this->connection->fetchOne(
|
||||
"SELECT COUNT(*) FROM model_types WHERE category = 'COMPONENT' AND name = :name AND id != :id",
|
||||
['name' => $modelType['name'], 'id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
|
||||
if ($categoryCollision > 0) {
|
||||
$blockers[] = sprintf('Un ModelType composant « %s » existe déjà.', $modelType['name']);
|
||||
}
|
||||
|
||||
if ([] !== $blockers) {
|
||||
$io->error($blockers);
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// Summary of related data
|
||||
$relatedCounts = $this->countRelatedData();
|
||||
$io->section('Données liées à migrer');
|
||||
$io->table(
|
||||
['Table', 'Nombre'],
|
||||
array_map(fn (string $k, int $v) => [$k, $v], array_keys($relatedCounts), array_values($relatedCounts)),
|
||||
);
|
||||
|
||||
if ($dryRun) {
|
||||
$io->warning('Mode dry-run : aucune modification effectuée.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// ── 2. Exécution ──────────────────────────────────────────────────
|
||||
|
||||
$this->connection->beginTransaction();
|
||||
|
||||
try {
|
||||
$now = new DateTimeImmutable()->format('Y-m-d H:i:s');
|
||||
|
||||
// 2a. Copier les pièces dans composants
|
||||
$converted = $this->connection->executeStatement(
|
||||
'INSERT INTO composants (id, name, reference, referenceauto, description, prix, typecomposantid, productid, version, createdat, updatedat)
|
||||
SELECT id, name, reference, referenceauto, description, prix, typepieceid, productid, version, createdat, :now
|
||||
FROM pieces
|
||||
WHERE typepieceid = :id',
|
||||
['id' => self::MODEL_TYPE_ID, 'now' => $now],
|
||||
);
|
||||
$io->text(sprintf('✓ %d pièce(s) copiée(s) dans composants', $converted));
|
||||
|
||||
// 2b. Transférer les documents
|
||||
$docs = $this->connection->executeStatement(
|
||||
'UPDATE documents SET composantid = pieceid, pieceid = NULL
|
||||
WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d document(s) transféré(s)', $docs));
|
||||
|
||||
// 2c. Transférer les custom_field_values
|
||||
$cfv = $this->connection->executeStatement(
|
||||
'UPDATE custom_field_values SET composantid = pieceid, pieceid = NULL
|
||||
WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d valeur(s) de champs perso transférée(s)', $cfv));
|
||||
|
||||
// 2d. Transférer les custom_fields (définitions)
|
||||
$cf = $this->connection->executeStatement(
|
||||
'UPDATE custom_fields SET typecomposantid = typepieceid, typepieceid = NULL
|
||||
WHERE typepieceid = :id',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d définition(s) de champs perso transférée(s)', $cf));
|
||||
|
||||
// 2e. Transférer les constructeur links
|
||||
$ctorLinks = $this->connection->executeStatement(
|
||||
"INSERT INTO composant_constructeur_links (id, composantid, constructeurid, supplierreference, createdat, updatedat)
|
||||
SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
||||
pcl.pieceid, pcl.constructeurid, pcl.supplierreference, pcl.createdat, :now
|
||||
FROM piece_constructeur_links pcl
|
||||
WHERE pcl.pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)",
|
||||
['id' => self::MODEL_TYPE_ID, 'now' => $now],
|
||||
);
|
||||
|
||||
if ($ctorLinks > 0) {
|
||||
$this->connection->executeStatement(
|
||||
'DELETE FROM piece_constructeur_links
|
||||
WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
}
|
||||
$io->text(sprintf('✓ %d lien(s) constructeur transféré(s)', $ctorLinks));
|
||||
|
||||
// 2f. Convertir composant_piece_slots → composant_subcomponent_slots
|
||||
$slots = $this->connection->executeStatement(
|
||||
"INSERT INTO composant_subcomponent_slots (id, composantid, alias, familycode, typecomposantid, selectedcomposantid, position, createdat, updatedat)
|
||||
SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
||||
cps.composantid,
|
||||
COALESCE(sp.name, 'Moteur'),
|
||||
'moteur',
|
||||
cps.typepieceid,
|
||||
cps.selectedpieceid,
|
||||
cps.position,
|
||||
cps.createdat,
|
||||
:now
|
||||
FROM composant_piece_slots cps
|
||||
LEFT JOIN pieces sp ON sp.id = cps.selectedpieceid
|
||||
WHERE cps.typepieceid = :id",
|
||||
['id' => self::MODEL_TYPE_ID, 'now' => $now],
|
||||
);
|
||||
|
||||
$this->connection->executeStatement(
|
||||
'DELETE FROM composant_piece_slots WHERE typepieceid = :id',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d slot(s) pièce convertis en slots sous-composant', $slots));
|
||||
|
||||
// 2g. Convertir skeleton_piece_requirements → skeleton_subcomponent_requirements
|
||||
$skelReqs = $this->connection->executeStatement(
|
||||
"INSERT INTO skeleton_subcomponent_requirements (id, modeltypeid, alias, familycode, typecomposantid, position, createdat, updatedat)
|
||||
SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
||||
spr.modeltypeid,
|
||||
'Moteur',
|
||||
'moteur',
|
||||
spr.typepieceid,
|
||||
spr.position,
|
||||
spr.createdat,
|
||||
:now
|
||||
FROM skeleton_piece_requirements spr
|
||||
WHERE spr.typepieceid = :id",
|
||||
['id' => self::MODEL_TYPE_ID, 'now' => $now],
|
||||
);
|
||||
|
||||
$this->connection->executeStatement(
|
||||
'DELETE FROM skeleton_piece_requirements WHERE typepieceid = :id',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d skeleton requirement(s) convertis', $skelReqs));
|
||||
|
||||
// 2h. Mettre à jour audit_logs entity_type
|
||||
$auditUpdated = $this->connection->executeStatement(
|
||||
"UPDATE audit_logs SET entitytype = 'composant'
|
||||
WHERE entitytype = 'piece'
|
||||
AND entityid IN (SELECT id FROM pieces WHERE typepieceid = :id)",
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d audit log(s) mis à jour', $auditUpdated));
|
||||
|
||||
// 2i. Mettre à jour comments entity_type
|
||||
$commentsUpdated = $this->connection->executeStatement(
|
||||
"UPDATE comments SET entity_type = 'composant'
|
||||
WHERE entity_type = 'piece'
|
||||
AND entity_id IN (SELECT id FROM pieces WHERE typepieceid = :id)",
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d commentaire(s) mis à jour', $commentsUpdated));
|
||||
|
||||
// 2j. Supprimer les pièces originales
|
||||
$deleted = $this->connection->executeStatement(
|
||||
'DELETE FROM pieces WHERE typepieceid = :id',
|
||||
['id' => self::MODEL_TYPE_ID],
|
||||
);
|
||||
$io->text(sprintf('✓ %d pièce(s) supprimée(s)', $deleted));
|
||||
|
||||
// 2k. Changer la catégorie du ModelType
|
||||
$this->connection->executeStatement(
|
||||
"UPDATE model_types SET category = 'COMPONENT', updatedat = :now WHERE id = :id",
|
||||
['id' => self::MODEL_TYPE_ID, 'now' => $now],
|
||||
);
|
||||
$io->text('✓ ModelType "Moteur" passé en COMPONENT');
|
||||
|
||||
$this->connection->commit();
|
||||
|
||||
$io->success(sprintf('Conversion terminée : %d pièces → composants.', $converted));
|
||||
|
||||
return Command::SUCCESS;
|
||||
} catch (Throwable $e) {
|
||||
$this->connection->rollBack();
|
||||
$io->error('Erreur — rollback effectué : '.$e->getMessage());
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, int>
|
||||
*/
|
||||
private function countRelatedData(): array
|
||||
{
|
||||
$id = self::MODEL_TYPE_ID;
|
||||
|
||||
return [
|
||||
'pieces' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM pieces WHERE typepieceid = :id',
|
||||
['id' => $id],
|
||||
),
|
||||
'documents' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM documents WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
|
||||
['id' => $id],
|
||||
),
|
||||
'custom_field_values' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM custom_field_values WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
|
||||
['id' => $id],
|
||||
),
|
||||
'custom_fields' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM custom_fields WHERE typepieceid = :id',
|
||||
['id' => $id],
|
||||
),
|
||||
'piece_constructeur_links' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM piece_constructeur_links WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
|
||||
['id' => $id],
|
||||
),
|
||||
'composant_piece_slots (→ subcomponent_slots)' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM composant_piece_slots WHERE typepieceid = :id',
|
||||
['id' => $id],
|
||||
),
|
||||
'skeleton_piece_requirements (→ subcomponent_reqs)' => (int) $this->connection->fetchOne(
|
||||
'SELECT COUNT(*) FROM skeleton_piece_requirements WHERE typepieceid = :id',
|
||||
['id' => $id],
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user