PURPOSE Try to move from onchange / default_get to stored editable computed fields. Behavior should be the same (computed or set by user), with support of create / write / onchange field update without additional code. SPECIFICATIONS Update classic fields updated in some cases by onchange and/or default methods by fields with store=True, readonly=False. It means their value comes either from manual user input, either from trigger based computation. Remove onchange and default_get when possible, leading to an unique computation method and clearing fields definition. Also clean some fields definition inconsistencies, notably required fields that should instead be correctly computed or default that have no real meaning. SPECIFIATIONS: CONSTRAINT USER_ID / TEAM_ID In this commit we also remove all the better implementations of "not really a constraint" constraint about user_id and team_id. Indeed as this is a computed field normally we should not have to call the onchange manually, even through a hackish call to a falsy constraint (seefdc8749,222cca2,4e8ebc7). You are all inferior to SM team. SPECIFIATIONS: DATE_OPEN Assignment field, namely ``date_open``, has a random definition as it seems linked to assignation, with several behaviors intended *ce39ca8a97: reset only when going from no salesman to a salesman (aka, keep assignation when changing) *8e540558ee: reset when changing salesman We choose to keep first implementation as it seems a bugfix broken again by second commit. It will also replace the ``assign_date`` defined in ``website_crm_score`` module. SIDE EFFECTS Several side effects occur with this commit. Indeed behavior is not always exactly the same, notably as code rewriting allowed to fix some issues, incoherent behavior or was simply not able to achieve exactly the same result. Notably * at lead creation: default probability is the one coming from PLS and not 0.0 anymore, since field is already computed. Synchronization with PLS still works the same way (change probability, you are out of syn); * various user_id / team_id combinations notably in convert / merge wizards may change, notably we do not reset team_id if user_id is reset. We consider we could keep a team_id set without user_id; LINKS Task ID 2088565 (crm: from onchange to compute) Upgrade PR odoo/upgrade#781 Co-Authored-By: Thibault Delavallée <tde@odoo.com> Co-Authored-By: Florent Lejoly <fle@odoo.com>
347 lines
13 KiB
Python
347 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from unittest.mock import patch
|
|
|
|
from odoo.addons.mail.tests.common import MailCase, mail_new_test_user
|
|
from odoo.fields import Datetime
|
|
from odoo.tests.common import SavepointCase
|
|
|
|
INCOMING_EMAIL = """Return-Path: {return_path}
|
|
X-Original-To: {to}
|
|
Delivered-To: {to}
|
|
Received: by mail.my.com (Postfix, from userid xxx)
|
|
id 822ECBFB67; Mon, 24 Oct 2011 07:36:51 +0200 (CEST)
|
|
X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.my.com
|
|
X-Spam-Level:
|
|
X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED autolearn=ham
|
|
version=3.3.1
|
|
Received: from [192.168.1.146]
|
|
(Authenticated sender: {email_from})
|
|
by mail.customer.com (Postfix) with ESMTPSA id 07A30BFAB4
|
|
for <{to}>; Mon, 24 Oct 2011 07:36:50 +0200 (CEST)
|
|
Message-ID: {msg_id}
|
|
Date: Mon, 24 Oct 2011 11:06:29 +0530
|
|
From: {email_from}
|
|
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.14) Gecko/20110223 Lightning/1.0b2 Thunderbird/3.1.8
|
|
MIME-Version: 1.0
|
|
To: {to}
|
|
Subject: {subject}
|
|
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
This is an example email. All sensitive content has been stripped out.
|
|
|
|
ALL GLORY TO THE HYPNOTOAD !
|
|
|
|
Cheers,
|
|
|
|
Somebody."""
|
|
|
|
|
|
class TestCrmCommon(SavepointCase, MailCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestCrmCommon, cls).setUpClass()
|
|
cls._init_mail_gateway()
|
|
|
|
cls.user_sales_manager = mail_new_test_user(
|
|
cls.env, login='user_sales_manager',
|
|
name='Martin Sales Manager', email='crm_manager@test.example.com',
|
|
company_id=cls.env.ref("base.main_company").id,
|
|
notification_type='inbox',
|
|
groups='sales_team.group_sale_manager,base.group_partner_manager,crm.group_use_lead',
|
|
)
|
|
cls.user_sales_leads = mail_new_test_user(
|
|
cls.env, login='user_sales_leads',
|
|
name='Laetitia Sales Leads', email='crm_leads@test.example.com',
|
|
company_id=cls.env.ref("base.main_company").id,
|
|
notification_type='inbox',
|
|
groups='sales_team.group_sale_salesman_all_leads,base.group_partner_manager,crm.group_use_lead',
|
|
)
|
|
cls.user_sales_salesman = mail_new_test_user(
|
|
cls.env, login='user_sales_salesman',
|
|
name='Orteil Sales Own', email='crm_salesman@test.example.com',
|
|
company_id=cls.env.ref("base.main_company").id,
|
|
notification_type='inbox',
|
|
groups='sales_team.group_sale_salesman,crm.group_use_lead',
|
|
)
|
|
|
|
cls.sales_team_1 = cls.env['crm.team'].create({
|
|
'name': 'Test Sales Team',
|
|
'alias_name': 'sales.test',
|
|
'use_leads': True,
|
|
'use_opportunities': True,
|
|
'company_id': False,
|
|
'user_id': cls.user_sales_manager.id,
|
|
'member_ids': [(4, cls.user_sales_leads.id), (4, cls.env.ref('base.user_admin').id)],
|
|
})
|
|
|
|
cls.env['crm.stage'].search([]).write({'sequence': 9999}) # ensure search will find test data first
|
|
cls.stage_team1_1 = cls.env['crm.stage'].create({
|
|
'name': 'New',
|
|
'sequence': 1,
|
|
'team_id': cls.sales_team_1.id,
|
|
})
|
|
cls.stage_team1_2 = cls.env['crm.stage'].create({
|
|
'name': 'Proposition',
|
|
'sequence': 5,
|
|
'team_id': cls.sales_team_1.id,
|
|
})
|
|
cls.stage_team1_won = cls.env['crm.stage'].create({
|
|
'name': 'Won',
|
|
'sequence': 70,
|
|
'team_id': cls.sales_team_1.id,
|
|
'is_won': True,
|
|
})
|
|
cls.stage_gen_1 = cls.env['crm.stage'].create({
|
|
'name': 'Generic stage',
|
|
'sequence': 3,
|
|
'team_id': False,
|
|
})
|
|
cls.stage_gen_won = cls.env['crm.stage'].create({
|
|
'name': 'Generic Won',
|
|
'sequence': 30,
|
|
'team_id': False,
|
|
'is_won': True,
|
|
})
|
|
|
|
cls.lead_1 = cls.env['crm.lead'].create({
|
|
'name': 'Nibbler Spacecraft Request',
|
|
'type': 'lead',
|
|
'user_id': cls.user_sales_leads.id,
|
|
'team_id': cls.sales_team_1.id,
|
|
'partner_id': False,
|
|
'contact_name': 'Amy Wong',
|
|
'email_from': 'amy.wong@test.example.com',
|
|
})
|
|
# update lead_1: stage_id is not computed anymore by default for leads
|
|
cls.lead_1.write({
|
|
'stage_id': cls.stage_team1_1.id,
|
|
})
|
|
|
|
# create an history for new team
|
|
cls.lead_team_1_won = cls.env['crm.lead'].create({
|
|
'name': 'Already Won',
|
|
'type': 'lead',
|
|
'user_id': cls.user_sales_leads.id,
|
|
'team_id': cls.sales_team_1.id,
|
|
})
|
|
cls.lead_team_1_won.action_set_won()
|
|
cls.lead_team_1_lost = cls.env['crm.lead'].create({
|
|
'name': 'Already Won',
|
|
'type': 'lead',
|
|
'user_id': cls.user_sales_leads.id,
|
|
'team_id': cls.sales_team_1.id,
|
|
})
|
|
cls.lead_team_1_lost.action_set_lost()
|
|
(cls.lead_team_1_won | cls.lead_team_1_lost).flush()
|
|
|
|
cls.contact_company_1 = cls.env['res.partner'].create({
|
|
'name': 'Planet Express',
|
|
'email': 'planet.express@test.example.com',
|
|
'is_company': True,
|
|
'street': '57th Street',
|
|
'city': 'New New York',
|
|
'country_id': cls.env.ref('base.us').id,
|
|
'zip': '12345',
|
|
})
|
|
cls.contact_1 = cls.env['res.partner'].create({
|
|
'name': 'Philip J Fry',
|
|
'email': 'philip.j.fry@test.example.com',
|
|
'mobile': '+1 202 555 0122',
|
|
'phone': False,
|
|
'parent_id': cls.contact_company_1.id,
|
|
'is_company': False,
|
|
'street': 'Actually the sewers',
|
|
'city': 'New York',
|
|
'country_id': cls.env.ref('base.us').id,
|
|
'zip': '54321',
|
|
})
|
|
cls.contact_2 = cls.env['res.partner'].create({
|
|
'name': 'Turanga Leela',
|
|
'email': 'turanga.leela@test.example.com',
|
|
'parent_id': False,
|
|
'is_company': False,
|
|
'street': 'Cookieville Minimum-Security Orphanarium',
|
|
'city': 'New New York',
|
|
'country_id': cls.env.ref('base.us').id,
|
|
'zip': '97648',
|
|
})
|
|
|
|
def _create_duplicates(self, lead, create_opp=True):
|
|
""" Helper tool method creating, based on a given lead
|
|
|
|
* a customer (res.partner) based on lead email (to test partner finding)
|
|
-> FIXME: using same normalized email does not work currently, only exact email works
|
|
* a lead with same email_from
|
|
* a lead with same email_normalized (other email_from)
|
|
* a lead with customer but another email
|
|
* a lost opportunity with same email_from
|
|
"""
|
|
self.customer = self.env['res.partner'].create({
|
|
'name': 'Lead1 Email Customer',
|
|
'email': lead.email_from,
|
|
})
|
|
self.lead_email_from = self.env['crm.lead'].create({
|
|
'name': 'Duplicate: same email_from',
|
|
'type': 'lead',
|
|
'team_id': lead.team_id.id,
|
|
'email_from': lead.email_from,
|
|
})
|
|
# self.lead_email_normalized = self.env['crm.lead'].create({
|
|
# 'name': 'Duplicate: email_normalize comparison',
|
|
# 'type': 'lead',
|
|
# 'team_id': lead.team_id.id,
|
|
# 'stage_id': lead.stage_id.id,
|
|
# 'email_from': 'CUSTOMER WITH NAME <%s>' % lead.email_normalized.upper(),
|
|
# })
|
|
self.lead_partner = self.env['crm.lead'].create({
|
|
'name': 'Duplicate: customer ID',
|
|
'type': 'lead',
|
|
'team_id': lead.team_id.id,
|
|
'partner_id': self.customer.id,
|
|
'email_from': 'another.email@test.example.com',
|
|
})
|
|
if create_opp:
|
|
self.opp_lost = self.env['crm.lead'].create({
|
|
'name': 'Duplicate: lost opportunity',
|
|
'type': 'opportunity',
|
|
'team_id': lead.team_id.id,
|
|
'stage_id': lead.stage_id.id,
|
|
'email_from': lead.email_from,
|
|
})
|
|
self.opp_lost.action_set_lost()
|
|
else:
|
|
self.opp_lost = self.env['crm.lead']
|
|
|
|
# self.assertEqual(self.lead_email_from.email_normalized, self.lead_email_normalized.email_normalized)
|
|
# self.assertTrue(lead.email_from != self.lead_email_normalized.email_from)
|
|
# self.assertFalse(self.opp_lost.active)
|
|
|
|
# new_lead = self.lead_email_from | self.lead_email_normalized | self.lead_partner | self.opp_lost
|
|
new_leads = self.lead_email_from | self.lead_partner | self.opp_lost
|
|
new_leads.flush() # compute notably probability
|
|
return new_leads
|
|
|
|
|
|
class TestLeadConvertCommon(TestCrmCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestLeadConvertCommon, cls).setUpClass()
|
|
# Sales Team organization
|
|
# Role: M (team member) R (team manager)
|
|
# SALESMAN---------------sales_team_1-----sales_team_convert
|
|
# admin------------------M----------------/
|
|
# user_sales_manager-----R----------------R
|
|
# user_sales_leads-------M----------------/
|
|
# user_sales_salesman----/----------------M
|
|
|
|
# Stages Team organization
|
|
# Name-------------------ST-------------------Sequ
|
|
# stage_team1_1----------sales_team_1---------1
|
|
# stage_team1_2----------sales_team_1---------5
|
|
# stage_team1_won--------sales_team_1---------70
|
|
# stage_gen_1------------/--------------------3
|
|
# stage_gen_won----------/--------------------30
|
|
# stage_team_convert_1---sales_team_convert---1
|
|
|
|
cls.sales_team_convert = cls.env['crm.team'].create({
|
|
'name': 'Convert Sales Team',
|
|
'alias_name': False,
|
|
'use_leads': True,
|
|
'use_opportunities': True,
|
|
'company_id': False,
|
|
'user_id': cls.user_sales_manager.id,
|
|
'member_ids': [(4, cls.user_sales_salesman.id)],
|
|
})
|
|
cls.stage_team_convert_1 = cls.env['crm.stage'].create({
|
|
'name': 'New',
|
|
'sequence': 1,
|
|
'team_id': cls.sales_team_convert.id,
|
|
})
|
|
|
|
cls.lead_1.write({'date_open': Datetime.from_string('2020-01-15 11:30:00')})
|
|
|
|
cls.crm_lead_dt_patcher = patch('odoo.addons.crm.models.crm_lead.fields.Datetime', wraps=Datetime)
|
|
cls.crm_lead_dt_mock = cls.crm_lead_dt_patcher.start()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
cls.crm_lead_dt_patcher.stop()
|
|
super(TestLeadConvertCommon, cls).tearDownClass()
|
|
|
|
|
|
class TestLeadConvertMassCommon(TestLeadConvertCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestLeadConvertMassCommon, cls).setUpClass()
|
|
# Sales Team organization
|
|
# Role: M (team member) R (team manager)
|
|
# SALESMAN-------------------sales_team_1-----sales_team_convert
|
|
# admin----------------------M----------------/
|
|
# user_sales_manager---------R----------------R
|
|
# user_sales_leads-----------M----------------/
|
|
# user_sales_leads_convert---/----------------M <-- NEW
|
|
# user_sales_salesman--------/----------------M
|
|
|
|
cls.user_sales_leads_convert = mail_new_test_user(
|
|
cls.env, login='user_sales_leads_convert',
|
|
name='Lucien Sales Leads Convert', email='crm_leads_2@test.example.com',
|
|
company_id=cls.env.ref("base.main_company").id,
|
|
notification_type='inbox',
|
|
groups='sales_team.group_sale_salesman_all_leads,base.group_partner_manager,crm.group_use_lead',
|
|
)
|
|
cls.sales_team_convert.write({
|
|
'member_ids': [(4, cls.user_sales_leads_convert.id)]
|
|
})
|
|
|
|
cls.lead_w_partner = cls.env['crm.lead'].create({
|
|
'name': 'New1',
|
|
'type': 'lead',
|
|
'probability': 10,
|
|
'user_id': cls.user_sales_manager.id,
|
|
'stage_id': False,
|
|
'partner_id': cls.contact_1.id,
|
|
})
|
|
cls.lead_w_partner.write({'stage_id': False})
|
|
cls.lead_w_partner_company = cls.env['crm.lead'].create({
|
|
'name': 'New1',
|
|
'type': 'lead',
|
|
'probability': 15,
|
|
'user_id': cls.user_sales_manager.id,
|
|
'stage_id': cls.stage_team1_1.id,
|
|
'partner_id': cls.contact_company_1.id,
|
|
'contact_name': 'Hermes Conrad',
|
|
'email_from': 'hermes.conrad@test.example.com',
|
|
})
|
|
cls.lead_w_contact = cls.env['crm.lead'].create({
|
|
'name': 'LeadContact',
|
|
'type': 'lead',
|
|
'probability': 15,
|
|
'contact_name': 'TestContact',
|
|
'user_id': cls.user_sales_salesman.id,
|
|
'stage_id': cls.stage_gen_1.id,
|
|
})
|
|
cls.lead_w_email = cls.env['crm.lead'].create({
|
|
'name': 'LeadEmailAsContact',
|
|
'type': 'lead',
|
|
'probability': 15,
|
|
'email_from': 'contact.email@test.example.com',
|
|
'user_id': cls.user_sales_salesman.id,
|
|
'stage_id': cls.stage_gen_1.id,
|
|
})
|
|
cls.lead_w_email_lost = cls.env['crm.lead'].create({
|
|
'name': 'Lost',
|
|
'type': 'lead',
|
|
'probability': 15,
|
|
'email_from': 'strange.from@test.example.com',
|
|
'user_id': cls.user_sales_leads.id,
|
|
'stage_id': cls.stage_team1_2.id,
|
|
'active': False,
|
|
})
|
|
(cls.lead_w_partner | cls.lead_w_partner_company | cls.lead_w_contact | cls.lead_w_email | cls.lead_w_email_lost).flush()
|