feat(configurator): Part cell rows 2-3 now editable — type to save
Customer feedback: rows 2 (description) and 3 (serials) in the Part cell rendered as read-only spans. User wanted to edit directly. New writable computed fields on fp.direct.order.line: - part_name_editable: compute reads part_catalog_id.name, inverse writes back to part.name on the linked catalog record - serials_text: compute joins serial_ids names with commas; inverse parses the typed string and find-or-creates fp.serial records, updates the line's serial_ids M2M Removed the redundant rev separator (display_name already includes '(Rev X)' so showing it twice was clutter). Rev edits happen by editing the part record directly via the OPEN button. OWL widget templates updated: - Row 2: <input> bound to part_name_editable, t-on-change saves - Row 3: <input> bound to serials_text, t-on-change parses + saves SCSS: - Row 2 input: italic, transparent border, focus tints background yellow - Row 3 input: small grey text, comma-separated friendly placeholder - Both disabled-look when no part is picked Both inputs trigger the inverse method on blur. The G4 sync chain takes over from there to push line.line_description etc. back to the part as before — so editing in the line keeps the part defaults fresh for future orders.
This commit is contained in:
@@ -478,6 +478,66 @@ class FpDirectOrderLine(models.Model):
|
||||
string='Part Name (display)',
|
||||
readonly=True,
|
||||
)
|
||||
# Writable bridges so the Part cell widget's row-2 description input
|
||||
# and row-3 serials input save back to the underlying records
|
||||
# (part.name and the line's serial_ids M2M) on blur.
|
||||
part_name_editable = fields.Char(
|
||||
string='Part Name (editable)',
|
||||
compute='_compute_part_name_editable',
|
||||
inverse='_inverse_part_name_editable',
|
||||
store=False,
|
||||
)
|
||||
serials_text = fields.Char(
|
||||
string='Serials (text)',
|
||||
compute='_compute_serials_text',
|
||||
inverse='_inverse_serials_text',
|
||||
store=False,
|
||||
help='Comma-separated list of serial numbers — typing here parses, '
|
||||
'creates new fp.serial records as needed, and updates the M2M.',
|
||||
)
|
||||
|
||||
@api.depends('part_catalog_id', 'part_catalog_id.name')
|
||||
def _compute_part_name_editable(self):
|
||||
for rec in self:
|
||||
rec.part_name_editable = rec.part_catalog_id.name or ''
|
||||
|
||||
def _inverse_part_name_editable(self):
|
||||
for rec in self:
|
||||
if rec.part_catalog_id:
|
||||
new_name = (rec.part_name_editable or '').strip()
|
||||
if new_name and new_name != rec.part_catalog_id.name:
|
||||
rec.part_catalog_id.sudo().write({'name': new_name})
|
||||
|
||||
@api.depends('serial_ids', 'serial_ids.name')
|
||||
def _compute_serials_text(self):
|
||||
for rec in self:
|
||||
rec.serials_text = ', '.join(rec.serial_ids.mapped('name'))
|
||||
|
||||
def _inverse_serials_text(self):
|
||||
Serial = self.env['fp.serial']
|
||||
company_id = self.env.company.id
|
||||
for rec in self:
|
||||
raw = (rec.serials_text or '').strip()
|
||||
if not raw:
|
||||
rec.serial_ids = [(5, 0)]
|
||||
continue
|
||||
names = [n.strip() for n in raw.replace(';', ',').split(',') if n.strip()]
|
||||
if not names:
|
||||
rec.serial_ids = [(5, 0)]
|
||||
continue
|
||||
existing = Serial.search([
|
||||
('name', 'in', names),
|
||||
('company_id', '=', company_id),
|
||||
])
|
||||
existing_by_name = {s.name: s for s in existing}
|
||||
ids = []
|
||||
for name in names:
|
||||
if name in existing_by_name:
|
||||
ids.append(existing_by_name[name].id)
|
||||
else:
|
||||
new = Serial.sudo().create({'name': name})
|
||||
ids.append(new.id)
|
||||
rec.serial_ids = [(6, 0, ids)]
|
||||
# Anchor field for the FpExpressActionBtns widget — renders the
|
||||
# stacked DWG / OPEN buttons in one list column. The widget reads
|
||||
# part_catalog_id from the line; this field's value is unused.
|
||||
|
||||
Reference in New Issue
Block a user