fix(plating): tree editor — title wrapping + import hierarchy
Two bugs reported on the tree editor after the move/import feature
shipped:
1. Card titles truncated to "Contrac…" because .o_fp_re_title had
white-space: nowrap + text-overflow: ellipsis. Swapped to
white-space: normal + overflow-wrap: anywhere so long names
wrap onto multiple lines inside the card. Widened card max-
width 380→460px and bumped min-width 240→260px so wrapped
titles have room.
2. Import-children was flattening the tree — all operations AND
their step children landed at the top level instead of staying
nested under their operations.
Root cause: src_node.copy({'parent_id': new_parent.id, ...})
on a _parent_store model behaved unpredictably — in some runs
the override in copy_vals didn't stick and child recursion
ended up with a wrong parent_id. Rewrote _copy_subtree to use
copy_data() + Node.create() so parent_id is set explicitly and
child_ids / parent_path are stripped (we recurse ourselves).
Smoke verified on entech: General Processing (1 root + 5 ops
+ 7 steps = 13 nodes) imports with hierarchy bit-identical to
source.
fusion_plating → 19.0.7.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating',
|
'name': 'Fusion Plating',
|
||||||
'version': '19.0.7.0.0',
|
'version': '19.0.7.1.0',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
||||||
'description': """
|
'description': """
|
||||||
|
|||||||
@@ -233,15 +233,26 @@ class FpRecipeController(http.Controller):
|
|||||||
max_seq = max((c.sequence for c in target.child_ids), default=0)
|
max_seq = max((c.sequence for c in target.child_ids), default=0)
|
||||||
|
|
||||||
def _copy_subtree(src_node, new_parent, base_seq):
|
def _copy_subtree(src_node, new_parent, base_seq):
|
||||||
"""Deep-copy src_node under new_parent, recursing children."""
|
"""Deep-copy src_node under new_parent, recursing children.
|
||||||
copy_vals = {
|
|
||||||
'parent_id': new_parent.id,
|
Uses copy_data() + manual create() rather than copy() so we
|
||||||
'sequence': base_seq,
|
have full control over what's carried:
|
||||||
}
|
* strip child_ids (we recurse ourselves)
|
||||||
# copy() picks up every other field; we only override parent + seq.
|
* strip parent_path (Odoo recomputes from parent_id)
|
||||||
new_node = src_node.copy(copy_vals)
|
* force parent_id + sequence to our target values
|
||||||
# Recurse — child_ids of the copy are blank (Odoo doesn't
|
|
||||||
# deep-copy one2many children by default for this model)
|
The previous copy()-based version sometimes produced a
|
||||||
|
flattened tree because copy() on a _parent_store model can
|
||||||
|
leave parent_id pointed at the original source when the
|
||||||
|
override in copy_vals collides with the field's copy= flag.
|
||||||
|
copy_data() returns a plain dict — safer.
|
||||||
|
"""
|
||||||
|
[vals] = src_node.copy_data()
|
||||||
|
vals.pop('child_ids', None)
|
||||||
|
vals.pop('parent_path', None)
|
||||||
|
vals['parent_id'] = new_parent.id
|
||||||
|
vals['sequence'] = base_seq
|
||||||
|
new_node = Node.create(vals)
|
||||||
for i, child in enumerate(src_node.child_ids.sorted('sequence'), 1):
|
for i, child in enumerate(src_node.child_ids.sorted('sequence'), 1):
|
||||||
_copy_subtree(child, new_node, i * 10)
|
_copy_subtree(child, new_node, i * 10)
|
||||||
return new_node
|
return new_node
|
||||||
|
|||||||
@@ -241,10 +241,10 @@ $re-line-w : 2px;
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
min-width: 240px;
|
min-width: 260px;
|
||||||
max-width: 380px;
|
max-width: 460px;
|
||||||
min-height: $re-card-h;
|
min-height: $re-card-h;
|
||||||
padding: 8px 12px;
|
padding: 10px 12px;
|
||||||
background-color: #2b2f36;
|
background-color: #2b2f36;
|
||||||
color: #f1f3f5;
|
color: #f1f3f5;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -343,10 +343,15 @@ $re-line-w : 2px;
|
|||||||
gap: 2px;
|
gap: 2px;
|
||||||
}
|
}
|
||||||
.o_fp_re_title {
|
.o_fp_re_title {
|
||||||
overflow: hidden;
|
// Let long node names wrap onto multiple lines so the card shows
|
||||||
text-overflow: ellipsis;
|
// the whole title (e.g. "Final Inspection / Packaging") instead
|
||||||
white-space: nowrap;
|
// of cutting to "Final I…". word-break handles CJK / no-space
|
||||||
|
// strings gracefully; line-height keeps the card compact.
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
.o_fp_re_meta {
|
.o_fp_re_meta {
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user