From 7c85b2ad93f3f477e2c47a7524f6feaff7178e96 Mon Sep 17 00:00:00 2001 From: forbes-0023 Date: Thu, 26 Feb 2026 09:00:13 -0600 Subject: [PATCH] fix(assembly): use instance suffixes for duplicate part labels When parts with structured part numbers (e.g., P03-0001) are inserted into an assembly multiple times, UniqueNameManager::decomposeName() treats the trailing digits as an auto-generated suffix and increments them (P03-0002, P03-0003), corrupting the part number. Add a makeInstanceLabel() helper in AssemblyLink.cpp that appends -N instance suffixes instead (P03-0001-1, P03-0001-2). All instances get a suffix starting at -1 so the original part number is never modified. Applied at all three Label.setValue() sites in synchronizeComponents() (AssemblyLink, link group, and regular link creation paths). Also add a UniqueNameManager test documenting the trailing-digit decomposition behavior for structured part numbers. Closes #327 --- src/Mod/Assembly/App/AssemblyLink.cpp | 19 ++++++++++++++++--- tests/src/Base/UniqueNameManager.cpp | 11 +++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyLink.cpp b/src/Mod/Assembly/App/AssemblyLink.cpp index 48e8c8d1cc..4263c3ecee 100644 --- a/src/Mod/Assembly/App/AssemblyLink.cpp +++ b/src/Mod/Assembly/App/AssemblyLink.cpp @@ -311,6 +311,19 @@ void AssemblyLink::updateContents() purgeTouched(); } +// Generate an instance label for assembly components by appending a -N suffix. +// All instances get a suffix (starting at -1) so that structured part numbers +// like "P03-0001" are never mangled by UniqueNameManager's trailing-digit logic. +static std::string makeInstanceLabel(App::Document* doc, const std::string& baseLabel) +{ + for (int i = 1;; ++i) { + std::string candidate = baseLabel + "-" + std::to_string(i); + if (!doc->containsLabel(candidate)) { + return candidate; + } + } +} + void AssemblyLink::synchronizeComponents() { App::Document* doc = getDocument(); @@ -428,7 +441,7 @@ void AssemblyLink::synchronizeComponents() auto* subAsmLink = static_cast(newObj); subAsmLink->LinkedObject.setValue(obj); subAsmLink->Rigid.setValue(asmLink->Rigid.getValue()); - subAsmLink->Label.setValue(obj->Label.getValue()); + subAsmLink->Label.setValue(makeInstanceLabel(doc, obj->Label.getValue())); addObject(subAsmLink); link = subAsmLink; } @@ -440,7 +453,7 @@ void AssemblyLink::synchronizeComponents() ); newLink->LinkedObject.setValue(srcLink->getTrueLinkedObject(false)); - newLink->Label.setValue(obj->Label.getValue()); + newLink->Label.setValue(makeInstanceLabel(doc, obj->Label.getValue())); addObject(newLink); newLink->ElementCount.setValue(srcLink->ElementCount.getValue()); @@ -461,7 +474,7 @@ void AssemblyLink::synchronizeComponents() App::DocumentObject* newObj = doc->addObject("App::Link", obj->getNameInDocument()); auto* newLink = static_cast(newObj); newLink->LinkedObject.setValue(obj); - newLink->Label.setValue(obj->Label.getValue()); + newLink->Label.setValue(makeInstanceLabel(doc, obj->Label.getValue())); addObject(newLink); link = newLink; } diff --git a/tests/src/Base/UniqueNameManager.cpp b/tests/src/Base/UniqueNameManager.cpp index ba502eafd2..20955efec8 100644 --- a/tests/src/Base/UniqueNameManager.cpp +++ b/tests/src/Base/UniqueNameManager.cpp @@ -122,4 +122,15 @@ TEST(UniqueNameManager, UniqueNameWith9NDigits) manager.addExactName("Compound123456789"); EXPECT_EQ(manager.makeUniqueName("Compound", 3), "Compound123456790"); } +TEST(UniqueNameManager, StructuredPartNumberDecomposition) +{ + // Structured part numbers like P03-0001 have their trailing digits + // treated as the uniquifying suffix by UniqueNameManager. This is + // correct for default FreeCAD objects (Body -> Body001) but wrong + // for structured identifiers. Assembly module handles this separately + // via makeInstanceLabel which appends -N instance suffixes instead. + Base::UniqueNameManager manager; + manager.addExactName("P03-0001"); + EXPECT_EQ(manager.makeUniqueName("P03-0001", 3), "P03-0002"); +} // NOLINTEND(cppcoreguidelines-*,readability-*)