res.partner.x_fc_default_coc_contact_id (single Many2one) becomes x_fc_default_coc_contact_ids (self-referential Many2many 'Default CoC Contacts') so a customer can list several contacts who need the CoC. - res.partner: M2m field (rel fp_default_coc_contact_rel) + many2many_tags. - Cert: contact_partner_id (primary addressee printed on the cert) is set to the FIRST CoC contact at job creation + lazy-filled at issue. - Send: action_send_to_customer pre-fills the email composer with ALL the customer's CoC contacts (primary + the rest), falling back to the company. - fp.job cert-default resolution + the action_issue gate wording updated. - Migration 19.0.10.2.0: copies each partner's old single value into the new M2m, then drops the orphaned column. Deployed + verified on entech: migration copied 2 existing values, old column dropped, field is M2m, send pre-fills all contacts. entech-only part_line_ids / multi-part resolver preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
2.2 KiB
Python
58 lines
2.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright 2026 Nexa Systems Inc.
|
|
# License OPL-1
|
|
"""Migrate the single Default CoC Contact (Many2one) to the multi-contact
|
|
Many2many.
|
|
|
|
The field x_fc_default_coc_contact_id (Many2one column on res_partner) was
|
|
renamed to x_fc_default_coc_contact_ids (self-referential Many2many, rel
|
|
table fp_default_coc_contact_rel). Odoo creates the new rel table during the
|
|
schema-update phase but leaves the old column orphaned. Copy each partner's
|
|
single contact into the new M2m so existing per-customer CoC routing carries
|
|
over, then drop the dead column. Idempotent + guarded on column existence.
|
|
"""
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
def migrate(cr, version):
|
|
# Old column still present (Odoo doesn't drop removed-field columns)?
|
|
cr.execute("""
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'res_partner'
|
|
AND column_name = 'x_fc_default_coc_contact_id'
|
|
""")
|
|
if not cr.fetchone():
|
|
return
|
|
# New M2m rel table created by the schema update before this runs.
|
|
cr.execute("""
|
|
SELECT 1 FROM information_schema.tables
|
|
WHERE table_name = 'fp_default_coc_contact_rel'
|
|
""")
|
|
if not cr.fetchone():
|
|
_logger.warning(
|
|
'fp_default_coc_contact_rel missing — skipping CoC contact '
|
|
'migration (rel table not created yet).')
|
|
return
|
|
# Copy the single value into the M2m (skip rows already present so a
|
|
# re-run is harmless).
|
|
cr.execute("""
|
|
INSERT INTO fp_default_coc_contact_rel (partner_id, contact_id)
|
|
SELECT p.id, p.x_fc_default_coc_contact_id
|
|
FROM res_partner p
|
|
WHERE p.x_fc_default_coc_contact_id IS NOT NULL
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM fp_default_coc_contact_rel r
|
|
WHERE r.partner_id = p.id
|
|
AND r.contact_id = p.x_fc_default_coc_contact_id)
|
|
""")
|
|
moved = cr.rowcount
|
|
cr.execute(
|
|
"ALTER TABLE res_partner DROP COLUMN IF EXISTS "
|
|
"x_fc_default_coc_contact_id")
|
|
_logger.info(
|
|
'CoC contact migration: copied %s single Default-CoC-Contact value(s) '
|
|
'into the new x_fc_default_coc_contact_ids M2m, dropped old column.',
|
|
moved)
|