mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-11 04:39:33 +00:00
BOMValidator: Validierung für rekursive Baugruppen-Eintragsprüfung ergänzt
Es wurde eine neue Validierung hinzugefügt, um sicherzustellen, dass keine Baugruppe in ihrer eigenen Hierarchie als Unterbaugruppe referenziert wird. Diese Logik wurde in die entsprechenden Dateien integriert und unterstützt Mehrsprachigkeit durch neue Übersetzungen.
This commit is contained in:
parent
9acca25ac7
commit
a62866dfe3
17 changed files with 302 additions and 21 deletions
|
|
@ -24,6 +24,7 @@ namespace App\Entity\AssemblySystem;
|
|||
|
||||
use App\Repository\AssemblyRepository;
|
||||
use App\Validator\Constraints\AssemblySystem\AssemblyCycle;
|
||||
use App\Validator\Constraints\AssemblySystem\AssemblyInvalidBomEntry;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
|
|
@ -111,11 +112,12 @@ class Assembly extends AbstractStructuralDBElement
|
|||
* @var Collection<int, AssemblyBOMEntry>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
#[AssemblyCycle]
|
||||
#[AssemblyInvalidBomEntry]
|
||||
#[UniqueReferencedAssembly]
|
||||
#[Groups(['extended', 'full', 'import'])]
|
||||
#[ORM\OneToMany(targetEntity: AssemblyBOMEntry::class, mappedBy: 'assembly', cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
#[UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part'])]
|
||||
#[AssemblyCycle]
|
||||
#[UniqueReferencedAssembly]
|
||||
#[UniqueObjectCollection(message: 'assembly.bom_entry.project_already_in_bom', fields: ['project'])]
|
||||
#[UniqueObjectCollection(message: 'assembly.bom_entry.name_already_in_bom', fields: ['name'])]
|
||||
protected Collection $bom_entries;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ use App\Entity\Contracts\TimeStampableInterface;
|
|||
use App\Entity\ProjectSystem\Project;
|
||||
use App\Repository\DBElementRepository;
|
||||
use App\Validator\Constraints\AssemblySystem\AssemblyCycle;
|
||||
use App\Validator\Constraints\AssemblySystem\AssemblyInvalidBomEntry;
|
||||
use App\Validator\UniqueValidatableInterface;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
|
|
@ -142,6 +143,7 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt
|
|||
message: 'validator.assembly.bom_entry.only_part_or_assembly_allowed'
|
||||
)]
|
||||
#[AssemblyCycle]
|
||||
#[AssemblyInvalidBomEntry]
|
||||
#[ORM\ManyToOne(targetEntity: Assembly::class)]
|
||||
#[ORM\JoinColumn(name: 'id_referenced_assembly', nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['bom_entry:read', 'bom_entry:write', ])]
|
||||
|
|
|
|||
|
|
@ -49,49 +49,78 @@ class AssemblyCycleValidator extends ConstraintValidator
|
|||
return;
|
||||
}
|
||||
|
||||
$bomEntries = $value->getBomEntries()->toArray();
|
||||
$availableViolations = $this->context->getViolations();
|
||||
if (count($availableViolations) > 0) {
|
||||
//already violations given, currently no more needed to check
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$bomEntries = [];
|
||||
|
||||
// Consider additional entries from the form
|
||||
if ($this->context->getRoot() instanceof Form && $this->context->getRoot()->has('bom_entries')) {
|
||||
$formBomEntries = $this->context->getRoot()->get('bom_entries')->getData();
|
||||
if ($formBomEntries) {
|
||||
$given = is_array($formBomEntries) ? $formBomEntries : iterator_to_array($formBomEntries);
|
||||
foreach ($given as $givenIdx => $entry) {
|
||||
if (in_array($entry, $bomEntries, true)) {
|
||||
continue;
|
||||
} else {
|
||||
$bomEntries[$givenIdx] = $entry;
|
||||
}
|
||||
}
|
||||
$bomEntries = $this->context->getRoot()->get('bom_entries')->getData();
|
||||
$bomEntries = is_array($bomEntries) ? $bomEntries : iterator_to_array($bomEntries);
|
||||
} elseif ($this->context->getRoot() instanceof Assembly) {
|
||||
$bomEntries = $value->getBomEntries()->toArray();
|
||||
}
|
||||
|
||||
$relevantEntries = [];
|
||||
|
||||
foreach ($bomEntries as $bomEntry) {
|
||||
if ($bomEntry->getReferencedAssembly() !== null) {
|
||||
$relevantEntries[$bomEntry->getId()] = $bomEntry;
|
||||
}
|
||||
}
|
||||
|
||||
$visitedAssemblies = [];
|
||||
foreach ($bomEntries as $bomEntry) {
|
||||
foreach ($relevantEntries as $bomEntry) {
|
||||
if ($this->hasCycle($bomEntry->getReferencedAssembly(), $value, $visitedAssemblies)) {
|
||||
$this->addViolation($value, $constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function hasCycle(?Assembly $currentAssembly, Assembly $originalAssembly, array &$visitedAssemblies): bool
|
||||
/**
|
||||
* Determines if there is a cyclic dependency in the assembly hierarchy.
|
||||
*
|
||||
* This method checks if a cycle exists in the hierarchy of referenced assemblies starting
|
||||
* from a given assembly. It traverses through the Bill of Materials (BOM) entries of each
|
||||
* assembly recursively and keeps track of visited assemblies to detect cycles.
|
||||
*
|
||||
* @param Assembly|null $currentAssembly The current assembly being checked for cycles.
|
||||
* @param Assembly $originalAssembly The original assembly from where the cycle detection started.
|
||||
* @param Assembly[] $visitedAssemblies A list of assemblies that have been visited during the current traversal.
|
||||
*
|
||||
* @return bool True if a cycle is detected, false otherwise.
|
||||
*/
|
||||
private function hasCycle(?Assembly $currentAssembly, Assembly $originalAssembly, array $visitedAssemblies = []): bool
|
||||
{
|
||||
//No referenced assembly → no cycle
|
||||
if ($currentAssembly === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($currentAssembly, $visitedAssemblies, true)) {
|
||||
//If the assembly has already been visited, there is a cycle
|
||||
if (in_array($currentAssembly->getId(), array_map(fn($a) => $a->getId(), $visitedAssemblies), true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Add the current assembly to the visited
|
||||
$visitedAssemblies[] = $currentAssembly;
|
||||
|
||||
//Go through the bom entries of the current assembly
|
||||
foreach ($currentAssembly->getBomEntries() as $bomEntry) {
|
||||
if ($this->hasCycle($bomEntry->getReferencedAssembly(), $originalAssembly, $visitedAssemblies)) {
|
||||
$referencedAssembly = $bomEntry->getReferencedAssembly();
|
||||
|
||||
if ($referencedAssembly !== null && $this->hasCycle($referencedAssembly, $originalAssembly, $visitedAssemblies)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Remove the current assembly from the list of visit (recursion completed)
|
||||
array_pop($visitedAssemblies);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -102,11 +131,11 @@ class AssemblyCycleValidator extends ConstraintValidator
|
|||
* already exists in the context. If such a violation is found, the current violation is not added again.
|
||||
* The process involves reflection to access private or protected properties of violation objects.
|
||||
*
|
||||
* @param mixed $value The value that triggered the violation.
|
||||
* @param Constraint $constraint The constraint containing the validation details.
|
||||
* @param mixed $value The value that triggered the violation.
|
||||
* @param Constraint $constraint The constraint containing the validation details.
|
||||
*
|
||||
*/
|
||||
private function addViolation($value, Constraint $constraint): void
|
||||
private function addViolation(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
/** @var ConstraintViolationBuilder $buildViolation */
|
||||
$buildViolation = $this->context->buildViolation($constraint->message)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Validator\Constraints\AssemblySystem;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* This constraint ensures that no BOM entries in the assembly reference its own children.
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||
class AssemblyInvalidBomEntry extends Constraint
|
||||
{
|
||||
public string $message = 'assembly.bom_entry.invalid_child_entry';
|
||||
|
||||
public function validatedBy(): string
|
||||
{
|
||||
return AssemblyInvalidBomEntryValidator::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Validator\Constraints\AssemblySystem;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Validator to check that no child assemblies are referenced in BOM entries.
|
||||
*/
|
||||
class AssemblyInvalidBomEntryValidator extends ConstraintValidator
|
||||
{
|
||||
public function validate($value, Constraint $constraint): void
|
||||
{
|
||||
if (!$constraint instanceof AssemblyInvalidBomEntry) {
|
||||
throw new UnexpectedTypeException($constraint, AssemblyInvalidBomEntry::class);
|
||||
}
|
||||
|
||||
if (!$value instanceof Assembly) {
|
||||
return;
|
||||
}
|
||||
|
||||
$availableViolations = $this->context->getViolations();
|
||||
if (count($availableViolations) > 0) {
|
||||
//already violations given, currently no more needed to check
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$bomEntries = [];
|
||||
|
||||
if ($this->context->getRoot() instanceof Form && $this->context->getRoot()->has('bom_entries')) {
|
||||
$bomEntries = $this->context->getRoot()->get('bom_entries')->getData();
|
||||
$bomEntries = is_array($bomEntries) ? $bomEntries : iterator_to_array($bomEntries);
|
||||
} elseif ($this->context->getRoot() instanceof Assembly) {
|
||||
$bomEntries = $value->getBomEntries()->toArray();
|
||||
}
|
||||
|
||||
$relevantEntries = [];
|
||||
|
||||
foreach ($bomEntries as $bomEntry) {
|
||||
if ($bomEntry->getReferencedAssembly() !== null) {
|
||||
$relevantEntries[$bomEntry->getId()] = $bomEntry;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($relevantEntries as $bomEntry) {
|
||||
$referencedAssembly = $bomEntry->getReferencedAssembly();
|
||||
|
||||
if ($bomEntry->getAssembly()->getParent()?->getId() === $referencedAssembly->getParent()?->getId()) {
|
||||
//Save on the same assembly level
|
||||
continue;
|
||||
} elseif ($this->isInvalidBomEntry($referencedAssembly, $bomEntry->getAssembly())) {
|
||||
$this->addViolation($value, $constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a Bill of Materials (BOM) entry is invalid based on the relationship
|
||||
* between the current assembly and the parent assembly.
|
||||
*
|
||||
* @param Assembly|null $currentAssembly The current assembly being analyzed. Null indicates no assembly is referenced.
|
||||
* @param Assembly $parentAssembly The parent assembly to check against the current assembly.
|
||||
*
|
||||
* @return bool Returns
|
||||
*/
|
||||
private function isInvalidBomEntry(?Assembly $currentAssembly, Assembly $parentAssembly): bool
|
||||
{
|
||||
//No assembly referenced -> no problems
|
||||
if ($currentAssembly === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check: is the current assembly a descendant of the parent assembly?
|
||||
if ($currentAssembly->isChildOf($parentAssembly)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Recursive check: Analyze the current assembly list
|
||||
foreach ($currentAssembly->getBomEntries() as $bomEntry) {
|
||||
$referencedAssembly = $bomEntry->getReferencedAssembly();
|
||||
|
||||
if ($this->isInvalidBomEntry($referencedAssembly, $parentAssembly)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
private function isOnSameLevel(Assembly $assembly1, Assembly $assembly2): bool
|
||||
{
|
||||
$parent1 = $assembly1->getParent();
|
||||
$parent2 = $assembly2->getParent();
|
||||
|
||||
if ($parent1 === null || $parent2 === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Beide Assemblies teilen denselben Parent
|
||||
return $parent1 !== null && $parent2 !== null && $parent1->getId() === $parent2->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a violation to the current context if it hasn’t already been added.
|
||||
*
|
||||
* This method checks whether a violation with the same property path as the current violation
|
||||
* already exists in the context. If such a violation is found, the current violation is not added again.
|
||||
* The process involves reflection to access private or protected properties of violation objects.
|
||||
*
|
||||
* @param mixed $value The value that triggered the violation.
|
||||
* @param Constraint $constraint The constraint containing the validation details.
|
||||
*
|
||||
*/
|
||||
private function addViolation($value, Constraint $constraint): void
|
||||
{
|
||||
/** @var ConstraintViolationBuilder $buildViolation */
|
||||
$buildViolation = $this->context->buildViolation($constraint->message)
|
||||
->setParameter('%name%', $value->getName());
|
||||
|
||||
$alreadyAdded = false;
|
||||
|
||||
try {
|
||||
$reflectionClass = new ReflectionClass($buildViolation);
|
||||
$property = $reflectionClass->getProperty('propertyPath');
|
||||
$propertyPath = $property->getValue($buildViolation);
|
||||
|
||||
$availableViolations = $this->context->getViolations();
|
||||
|
||||
foreach ($availableViolations as $tmpViolation) {
|
||||
$tmpReflectionClass = new ReflectionClass($tmpViolation);
|
||||
$tmpProperty = $tmpReflectionClass->getProperty('propertyPath');
|
||||
$tmpPropertyPath = $tmpProperty->getValue($tmpViolation);
|
||||
|
||||
if ($tmpPropertyPath === $propertyPath) {
|
||||
$alreadyAdded = true;
|
||||
}
|
||||
}
|
||||
} catch (\ReflectionException) {
|
||||
}
|
||||
|
||||
if (!$alreadyAdded) {
|
||||
$buildViolation->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -401,6 +401,12 @@
|
|||
<target>Byl zjištěn cyklus: Sestava "%name%" nepřímo odkazuje sama na sebe.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Sestava nesmí ve svém seznamu materiálů (BOM) odkazovat na podskupinu, která je součástí její vlastní hierarchie.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -377,6 +377,12 @@
|
|||
<target>En cyklus blev opdaget: Samlingen "%name%" refererer indirekte til sig selv.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>En samling må ikke referere til en undergruppe fra sin egen hierarki i BOM-listerne.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -401,6 +401,12 @@
|
|||
<target>Ein Zyklus wurde entdeckt: Die Baugruppe "%name%" referenziert sich indirekt selbst.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Eine Baugruppe darf keine Unterbaugruppe aus seiner eigenen Hierarchie in den BOM-Einträgen referenzieren.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@
|
|||
<target>Εντοπίστηκε κύκλος: Η συναρμολόγηση "%name%" αναφέρεται έμμεσα στον εαυτό της.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Μία συναρμολόγηση δεν πρέπει να αναφέρεται σε μία υποσυναρμολόγηση από την ίδια την ιεραρχία της στη λίστα BOM.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -401,6 +401,12 @@
|
|||
<target>A cycle was detected: the assembly "%name%" indirectly references itself.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>An assembly must not reference a subassembly from its own hierarchy in the BOM entries.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -233,6 +233,12 @@
|
|||
<target>Un cycle a été détecté : L'assemblage "%name%" se réfère indirectement à lui-même.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Un assemblage ne doit pas référencer un sous-assemblage de sa propre hiérarchie dans les entrées de la nomenclature (BOM).</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -395,6 +395,12 @@
|
|||
<target>Otkriven je ciklus: Sklop "%name%" neizravno referencira samog sebe.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Sklop ne smije referencirati podsklop iz vlastite hijerarhije u unosima BOM-a.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -395,6 +395,12 @@
|
|||
<target>È stato rilevato un ciclo: L'assemblaggio "%name%" fa riferimento indirettamente a sé stesso.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Un assemblaggio non deve fare riferimento a un sottoassemblaggio nella propria gerarchia nelle voci della distinta base (BOM).</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -233,6 +233,12 @@
|
|||
<target>循環が検出されました: アセンブリ「%name%」が間接的に自身を参照しています。</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>アセンブリは、BOMエントリで自身の階層内のサブアセンブリを参照してはいけません。</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -395,6 +395,12 @@
|
|||
<target>循環が検出されました: アセンブリ「%name%」が間接的に自身を参照しています。</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Zespół nie może odwoływać się do podzespołu w swojej własnej hierarchii w wpisach BOM.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -395,6 +395,12 @@
|
|||
<target>Обнаружен цикл: Сборка «%name%» косвенно ссылается на саму себя.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Сборка не должна ссылаться на подсборку внутри своей собственной иерархии в записях спецификации (BOM).</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
|
|
@ -383,6 +383,12 @@
|
|||
<target>检测到循环:装配体“%name%”间接引用了其自身。</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="cEtB87a" name="assembly.bom_entry.invalid_child_entry">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.invalid_child_entry</source>
|
||||
<target>Сборка не должна ссылаться на подсборку внутри своей собственной иерархии в записях спецификации (BOM).</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="6bkQ3bo" name="assembly.bom_entry.project_already_in_bom">
|
||||
<segment>
|
||||
<source>assembly.bom_entry.project_already_in_bom</source>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue