19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:39 +01:00
parent 38c6088dcc
commit d9452d2060
243 changed files with 30797 additions and 10815 deletions

View file

@ -10,45 +10,27 @@ pip install odoo-bringout-oca-ocb-test_discuss_full
## Dependencies
This addon depends on:
- calendar
- crm
- crm_livechat
- hr_attendance
- hr_fleet
- hr_holidays
- hr_homeworking
- im_livechat
- mail
- mail_bot
- note
- project_todo
- website_livechat
## Manifest Information
- **Name**: Test Discuss (full)
- **Version**: 1.0
- **Category**: Hidden
- **License**: LGPL-3
- **Installable**: True
- website_sale
- website_slides
## Source
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `test_discuss_full`.
- Repository: https://github.com/OCA/OCB
- Branch: 19.0
- Path: addons/test_discuss_full
## License
This package maintains the original LGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md
This package preserves the original LGPL-3 license.

View file

@ -1,20 +1,27 @@
[project]
name = "odoo-bringout-oca-ocb-test_discuss_full"
version = "16.0.0"
description = "Test Discuss (full) - Test of Discuss with all possible overrides installed."
description = "Test Discuss (full) -
Test of Discuss with all possible overrides installed.
"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-calendar>=16.0.0",
"odoo-bringout-oca-ocb-crm>=16.0.0",
"odoo-bringout-oca-ocb-crm_livechat>=16.0.0",
"odoo-bringout-oca-ocb-hr_holidays>=16.0.0",
"odoo-bringout-oca-ocb-im_livechat>=16.0.0",
"odoo-bringout-oca-ocb-mail>=16.0.0",
"odoo-bringout-oca-ocb-mail_bot>=16.0.0",
"odoo-bringout-oca-ocb-note>=16.0.0",
"odoo-bringout-oca-ocb-website_livechat>=16.0.0",
"odoo-bringout-oca-ocb-calendar>=19.0.0",
"odoo-bringout-oca-ocb-crm>=19.0.0",
"odoo-bringout-oca-ocb-crm_livechat>=19.0.0",
"odoo-bringout-oca-ocb-hr_attendance>=19.0.0",
"odoo-bringout-oca-ocb-hr_fleet>=19.0.0",
"odoo-bringout-oca-ocb-hr_holidays>=19.0.0",
"TODO_MAP-hr_homeworking>=19.0.0",
"odoo-bringout-oca-ocb-im_livechat>=19.0.0",
"odoo-bringout-oca-ocb-mail>=19.0.0",
"odoo-bringout-oca-ocb-mail_bot>=19.0.0",
"TODO_MAP-project_todo>=19.0.0",
"odoo-bringout-oca-ocb-website_livechat>=19.0.0",
"odoo-bringout-oca-ocb-website_sale>=19.0.0",
"odoo-bringout-oca-ocb-website_slides>=19.0.0",
"requests>=2.25.1"
]
readme = "README.md"
@ -24,7 +31,7 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]

View file

@ -2,23 +2,34 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Test Discuss (full)',
'version': '1.0',
'category': 'Hidden',
'sequence': 9877,
'summary': 'Test of Discuss with all possible overrides installed.',
'description': """Test of Discuss with all possible overrides installed, including feature and performance tests.""",
'depends': [
'calendar',
'crm',
'crm_livechat',
'hr_holidays',
'im_livechat',
'mail',
'mail_bot',
'note',
'website_livechat',
"name": "Test Discuss (full)",
"version": "1.0",
"category": "Productivity/Discuss",
"sequence": 9877,
"summary": "Test of Discuss with all possible overrides installed.",
"description": """Test of Discuss with all possible overrides installed, including feature and performance tests.""",
"depends": [
"calendar",
"crm",
"crm_livechat",
"hr_attendance",
"hr_fleet",
"hr_holidays",
"hr_homeworking",
"im_livechat",
"mail",
"mail_bot",
"project_todo",
"website_livechat",
"website_sale",
"website_slides",
],
'installable': True,
'license': 'LGPL-3',
"installable": True,
"assets": {
"web.assets_tests": [
"test_discuss_full/static/tests/tours/**/*",
],
},
"author": "Odoo S.A.",
"license": "LGPL-3",
}

View file

@ -0,0 +1,60 @@
import { registry } from "@web/core/registry";
const steps = [
{
content: "Open the avatar card popover",
trigger: ".o-mail-Message-avatar",
run: "click",
},
{
content: "Check that the employee's work email is displayed",
trigger: ".o_avatar_card:contains(test_employee@test.com)",
},
{
content: "Check that the employee's department is displayed",
trigger: ".o_avatar_card:contains(Test Department)",
},
{
content: "Check that the employee's work phone is displayed",
trigger: ".o_avatar_card:contains(123456789)",
},
{
content: "Check that the employee's holiday status is displayed",
trigger: ".o_avatar_card:contains(Back on)",
},
];
registry.category("web_tour.tours").add("avatar_card_tour", {
steps: () => [
...steps,
{
content: "Check that the employee's job title is displayed",
trigger: ".o_avatar_card:contains(Test Job Title)",
},
{
trigger: ".o-mail-ActivityMenu-counter:text('2')",
},
{
trigger: ".o_switch_company_menu button",
run: "click",
},
{
trigger: `[role=button][title='Switch to Company 2']`,
run: "click",
expectUnloadPage: true,
},
{
trigger: ".o-mail-ActivityMenu-counter:text('1')",
},
],
});
registry.category("web_tour.tours").add("avatar_card_tour_no_hr_access", {
steps: () => [
...steps,
{
content: "Check that the employee's job title is displayed",
trigger: ":not(.o_avatar_card:contains(Test Job Title))",
},
],
});

View file

@ -0,0 +1,28 @@
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("chatbot_redirect_to_portal", {
url: "/contactus",
steps: () => [
{
trigger: ".o-livechat-root:shadow .o-livechat-LivechatButton",
run: "click",
},
{
trigger:
".o-livechat-root:shadow .o-mail-Message:contains(Hello, were do you want to go?)",
run: "click",
},
{
trigger: ".o-livechat-root:shadow li button:contains(Go to the portal page)",
run: "click",
expectUnloadPage: true,
},
{
trigger: ".o-livechat-root:shadow .o-mail-Message:contains('Go to the portal page')",
},
{ trigger: "#chatterRoot:shadow .o-mail-Chatter" },
{
trigger: ".o-livechat-root:shadow .o-mail-Message:last:contains('Tadam')",
},
],
});

View file

@ -0,0 +1,17 @@
import { registry } from "@web/core/registry";
registry.category("web_tour.tours").add("im_livechat_session_open", {
steps: () => [
{
trigger: "button.o_switch_view.o_list",
run: "click",
},
{
trigger: ".o_data_cell:contains(Visitor)",
run: "click",
},
{
trigger: ".o-mail-Thread:contains('The conversation is empty.')",
},
],
});

View file

@ -1,4 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_avatar_card_tour
from . import test_livechat_hr_holidays
from . import test_im_livechat_portal
from . import test_performance
from . import test_performance_inbox
from . import test_livechat_session_open
from . import test_res_partner

View file

@ -0,0 +1,138 @@
from datetime import date, timedelta
from odoo import Command
from odoo.tests import tagged, users
from odoo.tests.common import HttpCase, new_test_user
from odoo.addons.mail.tests.common import MailCommon
@tagged("post_install", "-at_install")
class TestAvatarCardTour(MailCommon, HttpCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
new_test_user(
cls.env,
login="hr_user",
company_ids=[Command.link(cls.env.company.id), Command.link(cls.company_2.id)],
groups="hr.group_hr_user",
)
# hr setup for multi-company
department = (
cls.env["hr.department"]
.with_company(cls.company_2)
.create({"name": "Test Department", "company_id": cls.company_2.id})
)
job = (
cls.env["hr.job"]
.with_company(cls.company_2)
.create({"name": "Test Job Title", "company_id": cls.company_2.id})
)
other_partner = (
cls.env["res.partner"]
.with_company(cls.company_2)
.create({
"name": "Test Other Partner",
"company_id": cls.company_2.id,
"phone": "987654321",
})
)
test_employee = (
cls.env["hr.employee"]
.with_company(cls.company_2)
.create({
"name": "Test Employee",
"user_id": cls.user_employee_c2.id,
"company_id": cls.company_2.id,
"department_id": department.id,
"job_id": job.id,
"address_id": other_partner.id,
"work_email": "test_employee@test.com",
"work_phone": "123456789",
})
)
cls.test_employee = test_employee
cls.user_employee_c2.write({"employee_ids": [Command.link(test_employee.id)]})
new_test_user(
cls.env,
login="base_user",
company_ids=[Command.link(cls.env.company.id), Command.link(cls.company_2.id)],
)
# hr_holidays setup for multi-company
leave_type = (
cls.env["hr.leave.type"]
.with_company(cls.company_2)
.create(
{
"name": "Time Off multi company",
"company_id": cls.company_2.id,
"time_type": "leave",
"requires_allocation": False,
}
)
)
cls.env["hr.leave"].with_company(cls.company_2).with_context(
leave_skip_state_check=True
).create(
{
"name": "Test Leave",
"company_id": cls.company_2.id,
"holiday_status_id": leave_type.id,
"employee_id": cls.test_employee.id,
"request_date_from": (date.today() - timedelta(days=1)),
"request_date_to": (date.today() + timedelta(days=1)),
"state": "validate",
}
)
cls.test_record = cls.env["hr.department"].create(
[
{"name": "Test", "company_id": cls.env.company.id},
{"name": "Test 2", "company_id": cls.env.company.id},
{"name": "Test 3", "company_id": cls.company_2.id},
]
)
def _setup_channel(self, user):
self.user_employee_c2.partner_id.sudo().with_user(self.user_employee_c2).message_post(
body="Test message in chatter",
message_type="comment",
subtype_xmlid="mail.mt_comment",
)
activity_type_todo = "mail.mail_activity_data_todo"
self.test_record[0].activity_schedule(
activity_type_todo,
summary="Test Activity for Company 2",
user_id=user.id,
)
self.test_record[1].activity_schedule(
activity_type_todo,
summary="Another Test Activity for Company 2",
user_id=user.id,
)
self.test_record[2].activity_schedule(
activity_type_todo,
summary="Test Activity for Company 3",
user_id=user.id,
)
@users("admin", "hr_user")
def test_avatar_card_tour_multi_company(self):
# Clear existing activities to avoid interference with the test
self.env["mail.activity"].with_user(self.env.user).search([]).unlink()
self._setup_channel(self.env.user)
self.start_tour(
f"/odoo/res.partner/{self.user_employee_c2.partner_id.id}",
"avatar_card_tour",
login=self.env.user.login,
)
@users("base_user")
def test_avatar_card_tour_multi_company_no_hr_access(self):
self._setup_channel(self.env.user)
self.start_tour(
f"/odoo/res.partner/{self.user_employee_c2.partner_id.id}",
"avatar_card_tour_no_hr_access",
login=self.env.user.login,
)

View file

@ -0,0 +1,52 @@
from odoo import Command, tests
from odoo.addons.website_livechat.tests.test_chatbot_ui import TestLivechatChatbotUI
@tests.common.tagged("post_install", "-at_install")
class TestImLivechatPortal(TestLivechatChatbotUI):
def test_chatbot_redirect_to_portal(self):
project = self.env["project.project"].create({"name": "Portal Project"})
task = self.env["project.task"].create(
{"name": "Test Task Name Match", "project_id": project.id}
)
chatbot_redirect_script = self.env["chatbot.script"].create({"title": "Redirection Bot"})
question_step = self.env["chatbot.script.step"].create(
[
{
"chatbot_script_id": chatbot_redirect_script.id,
"message": "Hello, were do you want to go?",
"step_type": "question_selection",
},
{
"chatbot_script_id": chatbot_redirect_script.id,
"message": "Tadam, we are on the page you asked for!",
"step_type": "text",
},
]
)[0]
self.env["chatbot.script.answer"].create(
[
{
"name": "Go to the portal page",
"redirect_link": f"/my/tasks/{task.id}?access_token={task.access_token}",
"script_step_id": question_step.id,
},
]
)
livechat_channel = self.env["im_livechat.channel"].create(
{
"name": "Redirection Channel",
"rule_ids": [
Command.create(
{
"regex_url": "/",
"chatbot_script_id": chatbot_redirect_script.id,
}
)
],
}
)
default_website = self.env.ref("website.default_website")
default_website.channel_id = livechat_channel.id
self.env.ref("website.default_website").channel_id = livechat_channel.id
self.start_tour("/contactus", "chatbot_redirect_to_portal")

View file

@ -0,0 +1,52 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from odoo import Command, fields
from odoo.tests.common import HttpCase, tagged
from odoo.addons.mail.tests.common import MailCommon
@tagged("post_install", "-at_install")
class TestLivechatHrHolidays(HttpCase, MailCommon):
"""Tests for bridge between im_livechat and hr_holidays modules."""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env["mail.presence"]._update_presence(cls.user_employee)
leave_type = cls.env["hr.leave.type"].create(
{"name": "Legal Leaves", "requires_allocation": False, "time_type": "leave"}
)
employee = cls.env["hr.employee"].create({"user_id": cls.user_employee.id})
cls.env["hr.leave"].with_context(leave_skip_state_check=True).create(
{
"employee_id": employee.id,
"holiday_status_id": leave_type.id,
"request_date_from": fields.Datetime.today() + relativedelta(days=-2),
"request_date_to": fields.Datetime.today() + relativedelta(days=2),
"state": "validate",
}
)
def test_operator_available_on_leave(self):
"""Test operator is available on leave when they are online."""
livechat_channel = self.env["im_livechat.channel"].create(
{"name": "support", "user_ids": [Command.link(self.user_employee.id)]}
)
self.assertEqual(self.user_employee.im_status, "leave_online")
self.assertEqual(livechat_channel.available_operator_ids, self.user_employee)
def test_operator_limit_on_leave(self):
"""Test livechat limit is correctly applied when operator is on leave and online."""
livechat_channel = self.env["im_livechat.channel"].create(
{
"max_sessions_mode": "limited",
"max_sessions": 1,
"name": "support",
"user_ids": [Command.link(self.user_employee.id)],
}
)
self.make_jsonrpc_request("/im_livechat/get_session", {"channel_id": livechat_channel.id})
self.assertEqual(self.user_employee.im_status, "leave_online")
self.assertFalse(livechat_channel.available_operator_ids)

View file

@ -0,0 +1,21 @@
import odoo
from odoo.addons.im_livechat.tests.common import TestImLivechatCommon
from odoo.tests import new_test_user
@odoo.tests.tagged("-at_install", "post_install")
class TestImLivechatSessions(TestImLivechatCommon):
def test_livechat_session_open(self):
new_test_user(
self.env,
login="operator",
groups="base.group_user,im_livechat.im_livechat_group_manager",
)
self.make_jsonrpc_request(
"/im_livechat/get_session", {"channel_id": self.livechat_channel.id}
)
action = self.env.ref("im_livechat.discuss_channel_action_from_livechat_channel")
self.start_tour(
f"/odoo/livechat/{self.livechat_channel.id}/action-{action.id}", "im_livechat_session_open",
login="operator"
)

View file

@ -0,0 +1,82 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from itertools import chain
from odoo.addons.mail.tests.common import MailCommon
from odoo.tests.common import HttpCase, tagged, warmup
@tagged("post_install", "-at_install", "is_query_count")
class TestInboxPerformance(HttpCase, MailCommon):
@warmup
def test_fetch_with_rating_stats_enabled(self):
"""
Computation of rating_stats should run a single query per model with rating_stats enabled.
"""
# Queries (in order):
# - search website (get_current_website by domain)
# - search website (get_current_website default)
# - search website_rewrite (_get_rewrites) sometimes occurs depending on the routing cache
# - insert res_device_log
# - _xmlid_lookup (_get_public_users)
# - fetch website (_get_cached_values)
# - get_param ir_config_parameter (_pre_dispatch website_sale)
# 4 _message_fetch:
# 2 _search_needaction:
# - fetch res_users (current user)
# - search ir_rule (_get_rules for mail.notification)
# - search ir_rule (_get_rules)
# - search mail_message
# 30 message _to_store:
# - search mail_message_schedule
# - fetch mail_message
# - search mail_followers
# 2 thread _to_store:
# - fetch slide_channel
# - fetch product_template
# - search mail_message_res_partner_starred_rel (_compute_starred)
# - search message_attachment_rel
# - search mail_link_preview
# - search mail_message_reaction
# - search mail_message_res_partner_rel
# - fetch mail_message_subtype
# - search mail_notification
# 7 _filtered_for_web_client:
# - fetch mail_notification
# 4 _compute_domain:
# - search ir_rule (_get_rules for res.partner)
# - search res_groups_users_rel
# - search rule_group_rel
# - fetch ir_rule
# - fetch res_company
# - fetch res_partner
# 2 _compute_rating_id:
# - search rating_rating
# - fetch rating_rating
# - search mail_tracking_value
# 3 _author_to_store:
# - fetch res_partner
# - search res_users
# - fetch res_users
# - search ir_rule (_get_rules for rating.rating)
# - read group rating_rating (_rating_get_stats_per_record for slide.channel)
# - read group rating_rating (_compute_rating_stats for slide.channel)
# - read group rating_rating (_rating_get_stats_per_record for product.template)
# - read group rating_rating (_compute_rating_stats for product.template)
# - get_param ir_config_parameter (_save_session)
first_model_records = self.env["product.template"].create(
[{"name": "Product A1"}, {"name": "Product A2"}]
)
second_model_records = self.env["slide.channel"].create(
[{"name": "Course B1"}, {"name": "Course B2"}]
)
for record in chain(first_model_records, second_model_records):
record.message_post(
body=f"<p>Test message for {record.name}</p>",
message_type="comment",
partner_ids=[self.user_employee.partner_id.id],
rating_value="4",
)
self.authenticate(self.user_employee.login, self.user_employee.password)
with self.assertQueryCount(43):
self.make_jsonrpc_request("/mail/inbox/messages")

View file

@ -0,0 +1,12 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.addons.mail.tests.common import MailCommon
from odoo.addons.mail.tools.discuss import Store
class TestResPartner(MailCommon):
def test_portal_user_store_data_access(self):
portal_user = mail_new_test_user(self.env, login="portal-user", groups="base.group_portal")
Store().add(portal_user.partner_id.with_user(self.user_employee_c2))