fix(custom-fields) : match by orderIndex to prevent value loss on rename

When a ModelType's custom field was renamed without sending the field ID,
the service would create a new CustomField instead of reusing the existing
one, orphaning all CustomFieldValues. Now matches by orderIndex as fallback
before name, preserving the link to existing values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-24 08:36:59 +01:00
parent 509c4d2247
commit 4468fd7cdf
3 changed files with 63 additions and 5 deletions

View File

@@ -382,6 +382,8 @@ abstract class AbstractApiTestCase extends ApiTestCase
string $value = 'test value',
?Machine $machine = null,
?Composant $composant = null,
?Piece $piece = null,
?Product $product = null,
): CustomFieldValue {
$cfv = new CustomFieldValue();
$cfv->setValue($value);
@@ -392,6 +394,12 @@ abstract class AbstractApiTestCase extends ApiTestCase
if (null !== $composant) {
$cfv->setComposant($composant);
}
if (null !== $piece) {
$cfv->setPiece($piece);
}
if (null !== $product) {
$cfv->setProduct($product);
}
$em = $this->getEntityManager();
$em->persist($cfv);

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Tests\Api\Entity;
use App\Entity\CustomField;
use App\Entity\CustomFieldValue;
use App\Enum\ModelCategory;
use App\Tests\AbstractApiTestCase;
@@ -280,4 +282,42 @@ class ModelTypeTest extends AbstractApiTestCase
$this->assertSame($productType->getId(), $data['structure']['products'][0]['typeProductId']);
$this->assertSame('GR', $data['structure']['products'][0]['familyCode']);
}
public function testPatchPieceStructureRenameKeepsCustomFieldValuesWhenIdIsMissing(): void
{
$type = $this->createModelType('Arbre', 'ARB-001', ModelCategory::PIECE);
$piece = $this->createPiece('Arbre de test', 'ARB-TEST', $type);
$field = $this->createCustomField('diamètre', 'text', typePiece: $type, orderIndex: 0);
$this->createCustomFieldValue($field, '35 mm', piece: $piece);
$client = $this->createGestionnaireClient();
$client->request('PATCH', self::iri('model_types', $type->getId()), [
'headers' => ['Content-Type' => 'application/merge-patch+json'],
'json' => [
'structure' => [
'customFields' => [[
'name' => 'Diamètre',
'type' => 'text',
'required' => false,
'orderIndex' => 0,
]],
],
],
]);
$this->assertResponseIsSuccessful();
$em = $this->getEntityManager();
$em->clear();
$fields = $em->getRepository(CustomField::class)->findBy(['typePiece' => $type], ['orderIndex' => 'ASC']);
$this->assertCount(1, $fields);
$this->assertSame($field->getId(), $fields[0]->getId(), 'Renaming must reuse the existing CustomField');
$this->assertSame('Diamètre', $fields[0]->getName());
$values = $em->getRepository(CustomFieldValue::class)->findBy(['piece' => $piece]);
$this->assertCount(1, $values);
$this->assertSame('35 mm', $values[0]->getValue(), 'Existing custom field value must be preserved');
$this->assertSame($field->getId(), $values[0]->getCustomField()->getId());
}
}