[REF] website_event: move website.visitor's parent_id to website

The concept of 'parent_id' on website.visitors was introduced in the saas-13.3
stable while implementing the "event online" feature:
However, it should have been part of the website module from the start since
it's a 'global' concept that does not depend on events at all.
See #53540 for more details.

This commit aims to clean the code by moving the field to the website module,
which allows a nice cleaning of associated overridden methods as well.

Along with that, we move the website.visitor demo data from the event module to
the website module, allowing a fresh install of website to showcase some of our
visitors feature.

We also took this opportunity to do some minor improvements in the visitors
kanban view in order to make relevant information more visible.

Task-2429652

Part-of: odoo/odoo#65113
This commit is contained in:
Aurélien Warnon
2022-02-17 17:37:40 +00:00
parent d6e774f8db
commit 0641cb20e8
17 changed files with 114 additions and 113 deletions
+1
View File
@@ -113,6 +113,7 @@
],
'demo': [
'data/website_demo.xml',
'data/website_visitor_demo.xml',
],
'application': True,
'post_init_hook': 'post_init_hook',
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="website_visitor_0" model="website.visitor">
<field name="name">Edwin Hansen</field>
<field name="partner_id" ref="base.res_partner_address_5"/>
<field name="country_id" ref="base.nl"/>
<field name="page_ids" eval="[(4, ref('website.homepage_page'))]"/>
<field name="website_track_ids" eval="[(0, 0, {'page_id': ref('website.homepage_page'), 'url': '/'})]"/>
</record>
<record id="website_visitor_1" model="website.visitor">
<field name="name">Soham Palmer</field>
<field name="partner_id" ref="base.res_partner_address_11"/>
<field name="country_id" ref="base.us"/>
<field name="page_ids" eval="[(4, ref('website.contactus_page'))]"/>
<field name="website_track_ids" eval="[(0, 0, {'page_id': ref('website.contactus_page'), 'url': '/contactus'})]"/>
</record>
<record id="website_visitor_2" model="website.visitor">
<field name="name">Philipe J. Fry</field>
<field name="partner_id" eval="False"/>
<field name="country_id" ref="base.be"/>
<field name="page_ids" eval="[(4, ref('website.homepage_page'))]"/>
<field name="website_track_ids" eval="[(0, 0, {'page_id': ref('website.homepage_page'), 'url': '/'})]"/>
</record>
<record id="website_visitor_2_1" model="website.visitor">
<field name="name">Philipe J. Fry (old)</field>
<field name="active" eval="False"></field>
<field name="parent_id" ref="website.website_visitor_2"/>
<field name="partner_id" eval="False"/>
<field name="country_id" ref="base.be"/>
<field name="page_ids" eval="[(4, ref('website.contactus_page'))]"/>
<field name="website_track_ids" eval="[(0, 0, {'page_id': ref('website.contactus_page'), 'url': '/contactus'})]"/>
</record>
</odoo>
+1 -1
View File
@@ -87,7 +87,7 @@ class ResUsers(models.Model):
if other_user_visitor_sudo:
visitor_main = other_user_visitor_sudo[0]
other_visitors = other_user_visitor_sudo[1:] # normally void
(visitor_sudo + other_visitors)._link_to_visitor(visitor_main, keep_unique=True)
(visitor_sudo + other_visitors)._link_to_visitor(visitor_main)
visitor_main.name = user_partner.name
visitor_main.active = True
visitor_main._update_visitor_last_visit()
+29 -9
View File
@@ -35,6 +35,7 @@ class WebsiteVisitor(models.Model):
active = fields.Boolean('Active', default=True)
website_id = fields.Many2one('website', "Website", readonly=True)
partner_id = fields.Many2one('res.partner', string="Contact", help="Partner of the last logged in user.", index='btree_not_null')
parent_id = fields.Many2one('website.visitor', string="Parent", ondelete='set null', index='btree_not_null', help="Main identity")
partner_image = fields.Binary(related='partner_id.image_1920')
# localisation and info
@@ -161,10 +162,17 @@ class WebsiteVisitor(models.Model):
}
def _get_visitor_from_request(self, force_create=False):
""" Return the visitor as sudo from the request if there is a visitor_uuid cookie.
It is possible that the partner has changed or has disconnected.
In that case the cookie is still referencing the old visitor and need to be replaced
with the one of the visitor returned !!!. """
""" Return the visitor as sudo from the request if there is a
visitor_uuid cookie.
When fetching visitor, now that duplicates are linked to a main visitor
instead of unlinked, you may have more collisions issues with cookie
being set (after a de-connection for example).
The visitor associated to a partner in case of public user is not taken
into account, it is considered as desynchronized cookie.
In addition, we also discard if the visitor has a main visitor whose
partner is set (aka wrong after logout partner). """
# This function can be called in json with mobile app.
# In case of mobile app, no uid is set on the jsonRequest env.
@@ -189,10 +197,21 @@ class WebsiteVisitor(models.Model):
# Cookie associated to a Partner
visitor = Visitor
# also check that visitor parent partner is not different from user's one
# (indicates duplicate due to invalid or wrong cookie)
if visitor and visitor.parent_id.partner_id:
if self.env.user._is_public():
visitor = self.env['website.visitor'].sudo()
elif not visitor.partner_id:
visitor = self.env['website.visitor'].sudo().with_context(active_test=False).search(
[('partner_id', '=', self.env.user.partner_id.id)]
)
if visitor and not visitor.timezone:
tz = self._get_visitor_timezone()
if tz:
visitor._update_visitor_timezone(tz)
if not visitor and force_create:
visitor = self._create_visitor()
@@ -265,15 +284,14 @@ class WebsiteVisitor(models.Model):
vals.update(update_values)
self.write(vals)
def _link_to_visitor(self, target, keep_unique=True):
def _link_to_visitor(self, target):
""" Link visitors to target visitors, because they are linked to the
same identity. Purpose is mainly to propagate partner identity to sub
records to ease database update and decide what to do with "duplicated".
THis method is meant to be overridden in order to implement some specific
This method is meant to be overridden in order to implement some specific
behavior linked to sub records of duplicate management.
:param target: main visitor, target of link process;
:param keep_unique: if True, find a way to make target unique;
"""
# Link sub records of self to target partner
if target.partner_id:
@@ -281,8 +299,10 @@ class WebsiteVisitor(models.Model):
# Link sub records of self to target visitor
self.website_track_ids.write({'visitor_id': target.id})
if keep_unique:
self.unlink()
# Archive current record and set its parent visitor
self.partner_id = False
self.parent_id = target.id
self.active = False
return target
+6 -14
View File
@@ -120,21 +120,13 @@ class WebsiteVisitorTests(MockVisitor, HttpCaseWithUserDemo):
)
def assertVisitorDeactivated(self, visitor, main_visitor):
""" Temporary method to check that a visitor has been de-activated / merged
""" Method that checks that a visitor has been de-activated / merged
with other visitor, notably in case of login (see User.authenticate() as
well as Visitor._link_to_visitor() ).
As final result depends on installed modules (see overrides) due to stable
improvements linked to EventOnline, this method contains a hack to avoid
doing too much overrides just for that behavior. """
if 'parent_id' in self.env['website.visitor']:
self.assertTrue(bool(visitor))
self.assertFalse(visitor.active)
self.assertTrue(main_visitor.active)
self.assertEqual(visitor.parent_id, main_visitor)
else:
self.assertFalse(visitor)
self.assertTrue(bool(main_visitor))
well as Visitor._link_to_visitor() ). """
self.assertTrue(bool(visitor))
self.assertFalse(visitor.active)
self.assertTrue(main_visitor.active)
self.assertEqual(visitor.parent_id, main_visitor)
def test_visitor_creation_on_tracked_page(self):
""" Test various flows involving visitor creation and update. """
+10 -6
View File
@@ -128,12 +128,16 @@
<div>Visits</div>
</div>
<div class="col-lg col-sm-4 col-6 py-0 my-2">
<b><field name="last_visited_page_id"/></b>
<div>Last Page</div>
<span t-att-class="record.page_count.raw_value ? 'font-weight-bold' : 'text-muted'">
<field name="last_visited_page_id"/>
</span>
<div t-att-class="record.page_count.raw_value ? '' : 'text-muted'">Last Page</div>
</div>
<div id="wvisitor_visited_page" class="col-lg col-sm-4 col-6 py-0 my-2">
<b><field name="page_count"/></b>
<div>Visited Pages</div>
<span t-att-class="record.page_count.raw_value ? 'font-weight-bold' : 'text-muted'">
<field name="page_count"/>
</span>
<div t-att-class="record.page_count.raw_value ? '' : 'text-muted'">Visited Pages</div>
</div>
<div class="w_visitor_kanban_actions_ungrouped col-lg-3 col-sm-12 py-0 my-2 text-lg-right">
<button name="action_send_mail" type="object"
@@ -155,9 +159,9 @@
<field name="display_name"/>
</strong>
<div class="mb-2">Active <field name="time_since_last_action"/></div>
<div>Last Page<span class="float-right font-weight-bold"><field name="last_visited_page_id"/></span></div>
<div t-if="record.page_count.raw_value">Last Page<span class="float-right font-weight-bold"><field name="last_visited_page_id"/></span></div>
<div>Visits<span class="float-right font-weight-bold"><field name="visit_count"/></span></div>
<div id="o_page_count">Visited Pages<span class="float-right font-weight-bold"><field name="page_count"/></span></div>
<div t-if="record.page_count.raw_value" id="o_page_count">Visited Pages<span class="float-right font-weight-bold"><field name="page_count"/></span></div>
<div class="w_visitor_kanban_actions">
<button name="action_send_mail" type="object"
class="btn btn-secondary" attrs="{'invisible': [('email', '=', False)]}">
@@ -48,12 +48,19 @@
<field name="lead_count"/>
</field>
<xpath expr="//div[@id='o_page_count']" position="after">
<div>Leads / Opportunities<span class="float-right font-weight-bold"><field name="lead_count"/></span></div>
<div t-if="record.lead_count.raw_value">
Leads / Opportunities
<span class="float-right font-weight-bold">
<field name="lead_count"/>
</span>
</div>
</xpath>
<xpath expr="//div[@id='wvisitor_visited_page']" position="after">
<div class="col-lg col-sm-4 col-6 py-0 my-2">
<b><field name="lead_count"/></b>
<div>Leads / Opportunities</div>
<span t-att-class="record.lead_count.raw_value ? 'font-weight-bold' : 'text-muted'">
<field name="lead_count"/>
</span>
<div t-att-class="record.lead_count.raw_value ? '' : 'text-muted'">Leads / Opportunities</div>
</div>
</xpath>
</field>
-1
View File
@@ -40,7 +40,6 @@
],
'demo': [
'data/res_partner_demo.xml',
'data/website_visitor_demo.xml',
'data/event_demo.xml',
'data/event_registration_demo.xml',
],
@@ -3,16 +3,16 @@
<!-- OpenWood Collection Online Reveal: Gemini (all) -->
<record id="event.event_registration_7_0" model="event.registration">
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
<field name="visitor_id" ref="website.website_visitor_0"/>
</record>
<record id="event.event_registration_7_1" model="event.registration">
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
<field name="visitor_id" ref="website.website_visitor_0"/>
</record>
<record id="event.event_registration_7_2" model="event.registration">
<field name="visitor_id" ref="website_event.website_visitor_event_1"/>
<field name="visitor_id" ref="website.website_visitor_1"/>
</record>
<record id="event.event_registration_7_3" model="event.registration">
<field name="visitor_id" ref="website_event.website_visitor_event_1"/>
<field name="visitor_id" ref="website.website_visitor_1"/>
</record>
</data></odoo>
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="website_visitor_event_0" model="website.visitor">
<field name="name">Edwin Hansen</field>
<field name="partner_id" ref="base.res_partner_address_5"/>
<field name="country_id" ref="base.us"/>
</record>
<record id="website_visitor_event_1" model="website.visitor">
<field name="name">Soham Palmer</field>
<field name="partner_id" ref="base.res_partner_address_11"/>
<field name="country_id" ref="base.us"/>
</record>
<record id="website_visitor_event_2" model="website.visitor">
<field name="name">Philipe J. Fry</field>
<field name="partner_id" eval="False"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="website_visitor_event_2_1" model="website.visitor">
<field name="name">Philipe J. Fry (old)</field>
<field name="parent_id" ref="website_event.website_visitor_event_2"/>
<field name="partner_id" eval="False"/>
<field name="country_id" ref="base.be"/>
</record>
</odoo>
+2 -37
View File
@@ -8,9 +8,6 @@ class WebsiteVisitor(models.Model):
_name = 'website.visitor'
_inherit = ['website.visitor']
parent_id = fields.Many2one(
'website.visitor', string="Parent", ondelete='set null', index='btree_not_null',
help="Main identity")
event_registration_ids = fields.One2many(
'event.registration', 'visitor_id', string='Event Registrations',
groups="event.group_event_registration_desk")
@@ -87,40 +84,8 @@ class WebsiteVisitor(models.Model):
registration_wo_partner.partner_id = partner
super(WebsiteVisitor, self)._link_to_partner(partner, update_values=update_values)
def _link_to_visitor(self, target, keep_unique=True):
def _link_to_visitor(self, target):
""" Override linking process to link registrations to the final visitor. """
self.event_registration_ids.write({'visitor_id': target.id})
res = super(WebsiteVisitor, self)._link_to_visitor(target, keep_unique=False)
if keep_unique:
self.partner_id = False
self.parent_id = target.id
self.active = False
return res
def _get_visitor_from_request(self, force_create=False):
""" When fetching visitor, now that duplicates are linked to a main visitor
instead of unlinked, you may have more collisions issues with cookie being
set after a de-connection for example.
In base method, visitor associated to a partner in case of public user is
not taken into account. It is considered as desynchronized cookie. Here
we also discard if the visitor has a main visitor whose partner is set
(aka wrong after logout partner). """
visitor = super(WebsiteVisitor, self)._get_visitor_from_request(force_create=force_create)
# also check that visitor parent partner is not different from user's one (indicates duplicate due to invalid or wrong cookie)
if visitor and visitor.parent_id.partner_id:
if self.env.user._is_public():
visitor = self.env['website.visitor'].sudo()
elif not visitor.partner_id:
visitor = self.env['website.visitor'].sudo().with_context(active_test=False).search(
[('partner_id', '=', self.env.user.partner_id.id)]
)
if not visitor and force_create:
visitor = self._create_visitor()
return visitor
return super(WebsiteVisitor, self)._link_to_visitor(target)
@@ -3,12 +3,12 @@
<record id="event_7_track_2_visitor_0" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_2"/>
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
<field name="visitor_id" ref="website.website_visitor_0"/>
<field name="is_wishlisted" eval="True"/>
</record>
<record id="event_7_track_2_visitor_2" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_2"/>
<field name="visitor_id" ref="website_event.website_visitor_event_2"/>
<field name="visitor_id" ref="website.website_visitor_2"/>
<field name="is_wishlisted" eval="True"/>
</record>
@@ -69,7 +69,7 @@ class WebsiteVisitor(models.Model):
track_visitor_wo_partner.partner_id = partner
super(WebsiteVisitor, self)._link_to_partner(partner, update_values=update_values)
def _link_to_visitor(self, target, keep_unique=True):
def _link_to_visitor(self, target):
""" Override linking process to link wishlist to the final visitor. """
self.event_track_visitor_ids.visitor_id = target.id
return super(WebsiteVisitor, self)._link_to_visitor(target, keep_unique=keep_unique)
return super(WebsiteVisitor, self)._link_to_visitor(target)
@@ -133,21 +133,21 @@
<record id="event_track_visitor_admin_event_7_track_1" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_1"/>
<field name="visitor_id" ref="website_event.website_visitor_event_0"/>
<field name="visitor_id" ref="website.website_visitor_0"/>
<field name="quiz_completed" eval="True"/>
<field name="quiz_points">3</field>
</record>
<record id="event_track_visitor_demo_event_7_track_1" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_1"/>
<field name="visitor_id" ref="website_event.website_visitor_event_1"/>
<field name="visitor_id" ref="website.website_visitor_1"/>
<field name="quiz_completed" eval="True"/>
<field name="quiz_points">2</field>
</record>
<record id="event_track_visitor_portal_event_7_track_1" model="event.track.visitor">
<field name="track_id" ref="website_event_track.event_7_track_1"/>
<field name="visitor_id" ref="website_event.website_visitor_event_2"/>
<field name="visitor_id" ref="website.website_visitor_2"/>
<field name="quiz_completed" eval="True"/>
<field name="quiz_points">1</field>
</record>
@@ -92,11 +92,11 @@ class WebsiteVisitor(models.Model):
notifications.append([operator.partner_id, 'website_livechat.send_chat_request', mail_channel_info])
self.env['bus.bus']._sendmany(notifications)
def _link_to_visitor(self, target, keep_unique=True):
def _link_to_visitor(self, target):
""" Copy sessions of the secondary visitors to the main partner visitor. """
if target.partner_id:
target.mail_channel_ids |= self.mail_channel_ids
super(WebsiteVisitor, self)._link_to_visitor(target, keep_unique=keep_unique)
super(WebsiteVisitor, self)._link_to_visitor(target)
def _link_to_partner(self, partner, update_values=None):
""" Adapt partner in members of related livechats """
@@ -4,3 +4,4 @@ from . import common
from . import test_ui
from . import test_livechat_basic_flow
from . import test_livechat_request
from . import test_website_visitor
@@ -34,9 +34,10 @@
<field name="arch" type="xml">
<field name="page_ids" position="after">
<field name="livechat_operator_id"/>
<field name="session_count"/>
</field>
<xpath expr="//div[hasclass('w_visitor_kanban_actions')]" position="before">
<div>Chats<span class="float-right font-weight-bold"><field name="session_count"/></span></div>
<div t-if="record.session_count.raw_value">Chats<span class="float-right font-weight-bold"><field name="session_count"/></span></div>
<div t-if="record.livechat_operator_id.raw_value">
Speaking With
<div name="livechat_operator_id"
@@ -53,8 +54,10 @@
</xpath>
<xpath expr="//div[hasclass('w_visitor_kanban_actions_ungrouped')]" position="before">
<div class="col-lg col-sm-4 col-6 py-0 my-2">
<b><field name="session_count"/></b>
<div>Chats</div>
<span t-att-class="record.session_count.raw_value ? 'font-weight-bold' : 'text-muted'">
<field name="session_count"/>
</span>
<div t-att-class="record.session_count.raw_value ? '' : 'text-muted'">Chats</div>
</div>
<div t-if="record.livechat_operator_id.raw_value" class="col-lg col-sm-4 col-6 py-0 my-2">
<div>