Skip to content

[OU-ADD] crm: migrate CRM and close modules from 19.0 to 20.0 - #2

Open
adekock11 wants to merge 7 commits into
20.0from
cursor/crm-190-200-91bd
Open

adekock11 wants to merge 7 commits into
20.0from
cursor/crm-190-200-91bd

Conversation

@adekock11

Copy link
Copy Markdown
Owner

Summary

Migration scripts and annotated analysis for the CRM family from Odoo 19.0 to 20.0:

Module Version directory Script? Status
crm 20.0.1.9 pre + post Done
crm_iap_mine 20.0.1.2 pre + post Done
crm_livechat 20.0.1.0 pre + post Done
crm_mail_plugin 20.0.1.0 post Done
crm_sms 20.0.1.1 analysis only Nothing to do
sales_team 20.0.1.1 analysis only Nothing to do
crm_iap_enrich — no layout change Nothing to do
im_livechat 20.0.1.0 pre only Partial. See below

upgrade_analysis.txt files are copied from OCA/OpenUpgrade#6068 (hbrunn/OpenUpgrade 20.0-base, 2d9eb6bab7e7e579953e38d7ad3b96aad6fcc44d). upgrade_analysis_work.txt annotates every line. docsource/modules190-200.rst is that same file with the rows above filled in. This branch does not contain the OCA#6068 commits. It depends on them: base renames ir.model.access / ir.rule xmlids and builds ir.access for custom rules. Standard access rows are then recreated from each module's ir.access.csv.

crm

Sources: addons/crm on odoo/odoo 19.0 vs 20.0.

crm.lead.email_cc deleted

crm.lead no longer inherits mail.thread.cc. The mixin module was removed in odoo/odoo@3c26c0553754 ([IMP] mail,*: remove MailThreadCC, part of odoo/odoo#238694, related odoo/upgrade#9007). Carbon-copy addresses stored on the lead have nowhere to go.

Without a script the column is left behind unread (Odoo 20 does not DROP removed columns) and disappears from the form.

Pre-migration HTML-escapes a non-empty email_cc, appends it to description (Email cc preserved from previous version: …), then drops the column.

duration_tracking is now stored

Defined on mail.tracking.duration.mixin. v19 computed a non-stored {stage_id: seconds} map from mail.tracking.value on every read. v20 stores JSON and the compute only rolls that dict forward: odoo/odoo@d5e8a68f4b ([IMP] (test_, mail), crm, project: store stage duration in duration tracking). The stored shape, confirmed by mail/tests/common_tracking.py, is:

  • s: current stage_id (0 if empty)
  • d: YYYY-MM-DD HH:MM:SS when the current stage was entered
  • <stage id>: minutes spent on completed visits only

The open visit is not stored under the stage key. The web client adds now - d. Field.update_db recomputes only when the column is missing (odoo/orm/fields.py). A recompute of NULL sets s/d to "now" and drops history, which is what the rainbow-man check and the stage-duration widget read.

Pre-migration adds crm_lead.duration_tracking jsonb and fills NULL rows from mail.tracking.value (field stage_id), converting each completed interval to minutes. d is date_last_stage_update, then create_date. A second run does not add the minutes again.

The same mixin change hits every model that uses it (project, helpdesk, …). Those belong in their own module scripts, not here. If the mail migration later fills crm_lead the same way, running both is safe only while both skip non-NULL values. This script already does.

assignment_optout removed, assignment_rules added

odoo/odoo@553fbd1afa ([IMP] crm,sales_team: update lead assignment for sales teams). assignment_rules is a non-stored compute of assignment_max:

  • >= 30000 → unlimited (new default for new members)
  • == 0 → opt-out
  • else → limited

v19 stored opt-out as a boolean beside a cap (default 30). Leaving opted-out members at cap 30 would put them back into rotation.

Pre-migration sets assignment_max = 0 where assignment_optout is true, then drops the boolean. Everyone else keeps their cap, so they stay "limited". The unlimited sentinel is not written onto existing rows.

commercial_partner_id deleted

Non-stored compute used only to pick a company on the lead form. No column. Nothing to do.

utm_reference new

New optional Reference on utm.mixin (present on 20.0 utm/models/utm_mixin.py, absent on the 19.0 branch; introduced historically by odoo/odoo@d2cc73ebe7bb). No backfill in crm. crm_livechat fills it for chatbot leads.

website_message_ids deleted

Non-stored one2many of mail.message. It is not declared on current 19.0 or 20.0 mail.thread / crm.lead, so there is no column. Messages stay on mail.message. Nothing to do.

crm.stage.color deleted

odoo/odoo@3cce5fbeba01 ([IMP] project, crm: remove the color field from stages). crm_stage_data.xml no longer sets colors. No successor field. The integer column is left in the table by the ORM and is not read. Nothing to do.

Obsolete crm.lead2opportunity.partner

Transient wizard. The mass wizard remains and no longer inherits it. No stored rows. Base pre-migration already calls clean_transient_models. Nothing to do.

XML: access, rules, actions, views

Every NEW ir.access / DEL ir.model.access / DEL ir.rule line is the base merge of access and rules into ir.access. Nothing to do in crm, including the noupdate lead and activity rules. Their v20 definitions (manager crud on all leads, salesman cru on own leads, activity-plan cud limited to crm.lead) are loaded from crm/security/ir.access.csv because base renames the old xmlids first. Customizations of those noupdate rules are the known base gap, not reimplemented here.

Post-migration deletes xmlids whose files left the manifest, before view validation:

  • crm.action_your_pipeline and crm.action_opportunity_forecast (data/ir_action_data.xml removed). They call methods that no longer exist.
  • crm.action_crm_lead2opportunity_partner and crm.view_crm_lead2opportunity_partner (wizard file removed). The view's model is the deleted transient.
  • crm.crm_lead_all_leads_view_calendar (calendar binding removed from a file that still loads; delete is a no-op if already gone).

New views (crm_lead_creation_message, simplified list, access-request template) load from module data.

crm_iap_mine

odoo/odoo@57b8a7d360 ([IMP] crm_iap_mine, website_crm_iap_reveal: restructure crm_iap_lead_industry, odoo/odoo#246985, related odoo/upgrade#9405).

v19 industries are IAP reveal ids (reveal_ids, names like "Consumer Discretionary"). v20 industries are SIC major groups (sic_group required) grouped by a new crm.iap.lead.industry.division (division_id required). The commit does not publish a mapping. Sending old reveal ids as sic_groups would call IAP with the wrong catalog.

Pre-migration, only while reveal_ids still exists (so a retry after the new CSV has loaded does not delete the SIC rows):

  1. Follow foreign keys to crm_iap_lead_industry.
  2. Many2many links are deleted (mining requests lose their industry filter; the next manual run searches all industries until the user picks SIC groups).
  3. crm.reveal.rule rows that had an industry filter are set active = False before the link is removed. An empty industry_tag_ids means "match every industry" (website_crm_iap_reveal), which would widen a paid cron. Rules that already had no industry filter stay active.
  4. Delete ir.model.data for crm.iap.lead.industry and the rows themselves, then DROP COLUMN reveal_ids.

Module data then loads crm.iap.lead.industry.division.csv and the new industry CSV. User-created industries are removed too: they cannot satisfy the required SIC columns.

People-search fields on crm.iap.lead.mining.request (search_type, contact_number, contact_filter_type, preferred_role_id, role_ids, seniority_id) are gone. v20 always sends search_type = companies. There is no target field. Odoo 20 does not drop the columns, so the values remain on the table for a DBA. Generated leads are untouched. crm.iap.lead.role and crm.iap.lead.seniority master data stay.

_order is not stored. Division rows and the new unique constraint come from module data / the ORM. Access xmlids are the base ir.access merge.

Post-migration deletes the lead views from crm_lead_views.xml, which left the manifest (crm_case_kanban_view_leads, both lead trees, view_crm_lead_kanban), before view validation.

company_size_max default changed from 1000 to 100 in Python only. Existing rows keep 1000. Not in the analysis. Left as user data.

crm_livechat

create_lead_and_forward removed

odoo/odoo@895154fd1725 ([REM] crm_livechat, im_livechat: remove "create lead & forward" step). The 20.0 selection has no ondelete for that key, and _process_ondelete skips keys that are not in field.ondelete, so the old value would stay in chatbot_script_step.step_type and break the next read.

Pre-migration sets those steps to create_lead. The lead is still created and assigned. The automatic forward-to-operator half is not rebuilt: inserting a step would rewrite triggering_answer_ids and sequences. Add a forward_operator step by hand if that behavior is still needed.

utm_source_livechat moved to utm

v19 data: crm_livechat.utm_source_livechat (noupdate, name "Livechat"). v20 data: utm.utm_source_livechat in utm/data/utm_source_data.xml (noupdate). utm is updated before crm_livechat. If the old xmlid is still there, utm creates a second source. utm.source names are unique, so the new row is typically "Livechat [2]", and new /lead commands would tag that duplicate.

Post-migration keeps the historical row, repoints foreign keys (and other xmlids) from the duplicate, deletes the duplicate, and renames the xmlid to utm.utm_source_livechat with noupdate = true. A dangling old xmlid is deleted instead. A second run finds no crm_livechat xmlid and returns.

Doing the rename in utm pre-migration would avoid creating the duplicate. That belongs with the utm PR. This post-migration still cleans up if utm did not rename it.

Chatbot leads and utm_reference

v19 chatbot.script inherits utm.source.mixin. Leads stored that per-script source_id. v20 sets source_id to the shared im_livechat.utm_source_chatbot and utm_reference to chatbot.script,<id>, and the lead counter filters on utm_reference.

source_id is dropped when im_livechat updates, which is before crm_livechat. The map is saved in im_livechat's pre-migration (below). Post-migration sets utm_reference where it is empty and source_id matches a saved script source (lowest script id if two scripts shared a source). The lead's source_id is not rewritten to the shared Chatbot source: the old per-script source is still a valid utm.source and is more specific. New leads use the shared source. The map table is then dropped.

Discuss-channel /lead rows use the Livechat source, not a script source, so they are not given a chatbot reference.

Record rules (ir_rule_discuss_channel_*) moved to ir.access.csv with the same domains: channel r, member cr, salesman group. Nothing to do beyond base.

im_livechat (partial, on purpose)

chatbot.script drops utm.source.mixin, so source_id is removed during this module's update, before crm_livechat runs.

Pre-migration runs only when crm_livechat is installed, to upgrade, or to install, and chatbot_script.source_id still exists. It copies (id, source_id) into ou_crm_livechat_chatbot_source. It does not delete utm.source rows.

Everything else in im_livechat/20.0.1.0/upgrade_analysis.txt is marked DEFERRED, not "nothing to do": obsolete im_livechat.conversation.tag, livechat_operator_id removed, rating changed from float to selection, livechat_status lost waiting, stored livechat_looking_for_help_since_dt, and the access/menu/view churn. That is a livechat migration and should be its own PR. If that PR adds pre-migration.py, merge _preserve_chatbot_script_source into it.

docsource leaves the im_livechat status blank and notes "Partial: chatbot source map only."

crm_mail_plugin

20.0 manifest has no data files. v19 had:

  • lead_creation_prefilled_action (server action, deprecated since saas-14.3) calling crm.lead._form_view_auto_fill(), which is gone
  • crm_lead_action_form_edit (window action opening the lead form)

Post-migration deletes both xmlids. No model or column changes.

crm_sms

Only the access merge. v19 gave sale managers full CRUD on every sms.template. v20's CSV gives them read on all templates and create/write/unlink only when the template's model is crm.lead or res.partner. That is what the new file loads after base renames the old xmlids. No script.

sales_team

No column changes. crm_rule_all_salesteam moves from group_sale_salesman_all_leads to group_sale_manager with operation crud and no domain. Writes were already manager-only (access_crm_team_manager); salesman access was read-only. v20 grants crm.team read to base.group_user. sale_team_comp_rule keeps [('company_id', 'in', company_ids + [False])]. Both are loaded from ir.access.csv.

sales_team.crm_team_action_sales was removed from crm_team_views.xml, which is still loaded, and it is not noupdate. End-of-registry cleanup removes it. It is a window action, not a call into a deleted method. Nothing to do.

crm_iap_enrich

Python-only (get_param → get_str, enrich batch helper). No field or XML change in the OCA#6068 analysis. Nothing to do.

Deliberately not in this PR

These showed up next to CRM in the 19→20 analysis and should be their own PRs:

  • im_livechat remainder (see above). Real data: conversation tags, operator, ratings.
  • website_crm, website_crm_iap_reveal, website_crm_partner_assign, website_crm_livechat, website_crm_sms. Partner-assign moves res.partner.grade website fields to website_partnership. This PR does touch reveal rules, but only to deactivate ones whose industry filter cannot be mapped.
  • sale_crm. Access xmlid only. Belongs with sale.
  • event_crm. Access xmlids and two removed window actions. Belongs with event.
  • survey_crm. Deletes crm.lead.origin_survey_id and the team/survey inverses. Real data loss; belongs with survey.
  • crm_sale_project. New module (project.project.lead_id, crm.lead.project_ids). Belongs with sale/project. Nothing to migrate until those modules exist on the upgraded database; new columns start empty.
  • gamification_sale_crm, mass_mailing_crm, mass_mailing_crm_sms, iap_crm, event_crm_sale, website_event_crm. Analysis says no DB layout changes.

Dependency on OCA#6068

No commit from OCA#6068 is in this branch. Required behavior from that PR:

  • base pre-migration suffixes ir.model.access and ir.rule xmlids (name || '_' || model) so the new ir.access xmlids can be created.
  • base post-migration builds ir.access for manual access and rules only.
  • openupgrade_framework loads these scripts.

Do not merge this ahead of that framework if you expect the scripts to run inside Odoo. The SQL itself does not import the framework.

Tests

Not a full Odoo 19 database upgraded with Odoo 20. That needs OCA#6068's framework on the branch and both Odoo trees installed. What was run:

  • ruff check on the migration scripts (clean). ruff format applied.

  • PostgreSQL 16 fixture that loads the migration functions (Odoo import stubbed) and runs them on hand-built tables:

    • email_cc Ann <a@x.com> appended and escaped; column dropped.
    • duration_tracking: no history → {s, d} only; one day in stage 10 → {"10": 1440, ...}; 10 then 20 then back to 10 → 10: 1440, 20: 2880, open visit not added to 10. Second run does not double the minutes.
    • Opted-out member cap becomes 0; a cap of 45 and a NULL opt-out stay. Boolean column dropped.
    • v19 industry row, mining-request link, and one of two reveal rules removed/deactivated. The rule with no industry link stays active. reveal_ids dropped. Second run is a no-op.
    • create_lead_and_forward → create_lead.
    • Duplicate utm.utm_source_livechat merged into the historical source, including a lead FK that pointed at the duplicate. Chatbot lead gets utm_reference = chatbot.script,<id>. Livechat-source lead does not. Map table dropped. Second post-migration is a no-op.

Known gaps

  • Historical stage duration depends on mail.tracking.value still pointing at crm.lead / stage_id. Tracking that was never written (or whose field_id was rewritten) is not reconstructed. d then falls back to date_last_stage_update.
  • Sub-minute visits become 0 and are omitted, matching the v20 integer-minute compute.
  • No reveal-id → SIC map. Mining requests lose their industry filter. Reveal rules that had one are deactivated rather than widened. Re-pick SIC industries and re-enable the rules.
  • create_lead_and_forward no longer forwards to an operator.
  • Chatbot leads keep their old per-script source_id. Counts and the "view leads" action use utm_reference, which is backfilled. Reporting that filters on the new shared "Chatbot" source will not see old rows.
  • If two chatbots shared one source_id, leads are attributed to the lowest script id.
  • People-search criteria on mining requests are not shown in the UI. The columns are still in the table.
  • Stage colors are not shown. The integer column is still in crm_stage if you need to read it by SQL.
  • Noupdate edits of standard CRM record rules are not copied onto the new ir.access rows. That is the base migration's gap. Un-xmlid'd custom rules are handled by base.
  • im_livechat data other than source_id is not migrated.
Open in Web Open in Cursor 

cursoragent and others added 7 commits October 30, 2026 20:44
Preserve email_cc on the lead description, store stage duration in the
v20 duration_tracking shape, and turn assignment opt-out into a zero cap.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
Drop the v19 reveal-id industries before the new CSV loads. Deactivate
website reveal rules that filtered on those industries so an empty filter
does not match every visitor.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
Map create_lead_and_forward to create_lead, keep the historical Livechat
utm.source under utm.utm_source_livechat, and backfill lead utm_reference
from the chatbot source map.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
Copy chatbot.script.source_id before this module drops the column. The
rest of the im_livechat 20.0 analysis is left for a dedicated migration.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
The 20.0 module ships no data files. Delete the server action that called
the removed crm.lead._form_view_auto_fill method.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
ir.model.access and ir.rule rows are recreated as ir.access by the base
migration and the modules' new CSV files. No further script is required.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
Status lines for crm and the modules migrated with it. The table itself
comes from OCA#6068.

Co-authored-by: André de Kock <adekock11@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants