mirror of
https://github.com/bringout/oca-ocb-test.git
synced 2026-04-18 03:42:08 +02:00
Initial commit: Test packages
This commit is contained in:
commit
080accd21c
338 changed files with 32413 additions and 0 deletions
17
README.md
Normal file
17
README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Test
|
||||||
|
|
||||||
|
This repository contains OCA OCB packages for test.
|
||||||
|
|
||||||
|
## Packages Included
|
||||||
|
|
||||||
|
- odoo-bringout-oca-ocb-test_base_automation
|
||||||
|
- odoo-bringout-oca-ocb-test_crm_full
|
||||||
|
- odoo-bringout-oca-ocb-test_discuss_full
|
||||||
|
- odoo-bringout-oca-ocb-test_event_full
|
||||||
|
- odoo-bringout-oca-ocb-test_mail
|
||||||
|
- odoo-bringout-oca-ocb-test_mail_full
|
||||||
|
- odoo-bringout-oca-ocb-test_resource
|
||||||
|
- odoo-bringout-oca-ocb-test_website
|
||||||
|
- odoo-bringout-oca-ocb-test_website_modules
|
||||||
|
- odoo-bringout-oca-ocb-test_website_slides_full
|
||||||
|
- odoo-bringout-oca-ocb-test_xlsx_export
|
||||||
48
odoo-bringout-oca-ocb-test_base_automation/README.md
Normal file
48
odoo-bringout-oca-ocb-test_base_automation/README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Test - Base Automation
|
||||||
|
|
||||||
|
This module contains tests related to base automation. Those are
|
||||||
|
present in a separate module as it contains models used only to perform
|
||||||
|
tests independently to functional aspects of other models.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_base_automation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
- base_automation
|
||||||
|
|
||||||
|
## Manifest Information
|
||||||
|
|
||||||
|
- **Name**: Test - Base Automation
|
||||||
|
- **Version**: 1.0
|
||||||
|
- **Category**: Hidden
|
||||||
|
- **License**: LGPL-3
|
||||||
|
- **Installable**: True
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `test_base_automation`.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||||
|
V --> C[Controllers]
|
||||||
|
V --> W[Wizards – Transient Models]
|
||||||
|
C --> M[Models and ORM]
|
||||||
|
W --> M
|
||||||
|
M --> R[Reports]
|
||||||
|
DX[Data XML] --> M
|
||||||
|
S[Security – ACLs and Groups] -. enforces .-> M
|
||||||
|
|
||||||
|
subgraph Test_base_automation Module - test_base_automation
|
||||||
|
direction LR
|
||||||
|
M:::layer
|
||||||
|
W:::layer
|
||||||
|
C:::layer
|
||||||
|
V:::layer
|
||||||
|
R:::layer
|
||||||
|
S:::layer
|
||||||
|
DX:::layer
|
||||||
|
end
|
||||||
|
|
||||||
|
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Views include tree/form/kanban templates and report templates.
|
||||||
|
- Controllers provide website/portal routes when present.
|
||||||
|
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||||
|
- Data XML loads data/demo records; Security defines groups and access.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Refer to Odoo settings for test_base_automation. Configure related models, access rights, and options as needed.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Controllers
|
||||||
|
|
||||||
|
This module does not define custom HTTP controllers.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
|
||||||
|
- [base_automation](../../odoo-bringout-oca-ocb-base_automation)
|
||||||
4
odoo-bringout-oca-ocb-test_base_automation/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-test_base_automation/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||||
|
- Q: How to enable? A: Start server with --addon test_base_automation or install in UI.
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_base_automation"
|
||||||
|
# or
|
||||||
|
uv pip install odoo-bringout-oca-ocb-test_base_automation"
|
||||||
|
```
|
||||||
15
odoo-bringout-oca-ocb-test_base_automation/doc/MODELS.md
Normal file
15
odoo-bringout-oca-ocb-test_base_automation/doc/MODELS.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Models
|
||||||
|
|
||||||
|
Detected core models and extensions in test_base_automation.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class base_automation_lead_test
|
||||||
|
class base_automation_line_test
|
||||||
|
class base_automation_linked_test
|
||||||
|
class base_automation_link_test
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Classes show model technical names; fields omitted for brevity.
|
||||||
|
- Items listed under _inherit are extensions of existing models.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Packaged Odoo addon: test_base_automation. Provides features documented in upstream Odoo 16 under this addon.
|
||||||
|
|
||||||
|
- Source: OCA/OCB 16.0, addon test_base_automation
|
||||||
|
- License: LGPL-3
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Reports
|
||||||
|
|
||||||
|
This module does not define custom reports.
|
||||||
34
odoo-bringout-oca-ocb-test_base_automation/doc/SECURITY.md
Normal file
34
odoo-bringout-oca-ocb-test_base_automation/doc/SECURITY.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Security
|
||||||
|
|
||||||
|
Access control and security definitions in test_base_automation.
|
||||||
|
|
||||||
|
## Access Control Lists (ACLs)
|
||||||
|
|
||||||
|
Model access permissions defined in:
|
||||||
|
- **[ir.model.access.csv](../test_base_automation/security/ir.model.access.csv)**
|
||||||
|
- 6 model access rules
|
||||||
|
|
||||||
|
## Record Rules
|
||||||
|
|
||||||
|
Row-level security rules defined in:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Security Layers"
|
||||||
|
A[Users] --> B[Groups]
|
||||||
|
B --> C[Access Control Lists]
|
||||||
|
C --> D[Models]
|
||||||
|
B --> E[Record Rules]
|
||||||
|
E --> F[Individual Records]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Security files overview:
|
||||||
|
- **[ir.model.access.csv](../test_base_automation/security/ir.model.access.csv)**
|
||||||
|
- Model access permissions (CRUD rights)
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Access Control Lists define which groups can access which models
|
||||||
|
- Record Rules provide row-level security (filter records by user/group)
|
||||||
|
- Security groups organize users and define permission sets
|
||||||
|
- All security is enforced at the ORM level by Odoo
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
- Ensure Python and Odoo environment matches repo guidance.
|
||||||
|
- Check database connectivity and logs if startup fails.
|
||||||
|
- Validate that dependent addons listed in DEPENDENCIES.md are installed.
|
||||||
7
odoo-bringout-oca-ocb-test_base_automation/doc/USAGE.md
Normal file
7
odoo-bringout-oca-ocb-test_base_automation/doc/USAGE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
Start Odoo including this addon (from repo root):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon test_base_automation
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Wizards
|
||||||
|
|
||||||
|
This module does not include UI wizards.
|
||||||
42
odoo-bringout-oca-ocb-test_base_automation/pyproject.toml
Normal file
42
odoo-bringout-oca-ocb-test_base_automation/pyproject.toml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
[project]
|
||||||
|
name = "odoo-bringout-oca-ocb-test_base_automation"
|
||||||
|
version = "16.0.0"
|
||||||
|
description = "Test - Base Automation - Base Automation Tests: Ensure Flow Robustness"
|
||||||
|
authors = [
|
||||||
|
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"odoo-bringout-oca-ocb-base_automation>=16.0.0",
|
||||||
|
"requests>=2.25.1"
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">= 3.11"
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
"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.12",
|
||||||
|
"Topic :: Office/Business",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
homepage = "https://github.com/bringout/0"
|
||||||
|
repository = "https://github.com/bringout/0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.metadata]
|
||||||
|
allow-direct-references = true
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["test_base_automation"]
|
||||||
|
|
||||||
|
[tool.rye]
|
||||||
|
managed = true
|
||||||
|
dev-dependencies = [
|
||||||
|
"pytest>=8.4.1",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Test - Base Automation',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Hidden',
|
||||||
|
'sequence': 9877,
|
||||||
|
'summary': 'Base Automation Tests: Ensure Flow Robustness',
|
||||||
|
'description': """This module contains tests related to base automation. Those are
|
||||||
|
present in a separate module as it contains models used only to perform
|
||||||
|
tests independently to functional aspects of other models.""",
|
||||||
|
'depends': ['base_automation'],
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import test_base_automation
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from dateutil import relativedelta
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class LeadTest(models.Model):
|
||||||
|
_name = "base.automation.lead.test"
|
||||||
|
_description = "Automated Rule Test"
|
||||||
|
|
||||||
|
name = fields.Char(string='Subject', required=True)
|
||||||
|
user_id = fields.Many2one('res.users', string='Responsible')
|
||||||
|
state = fields.Selection([('draft', 'New'), ('cancel', 'Cancelled'), ('open', 'In Progress'),
|
||||||
|
('pending', 'Pending'), ('done', 'Closed')],
|
||||||
|
string="Status", readonly=True, default='draft')
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
partner_id = fields.Many2one('res.partner', string='Partner')
|
||||||
|
date_action_last = fields.Datetime(string='Last Action', readonly=True)
|
||||||
|
employee = fields.Boolean(compute='_compute_employee_deadline', store=True)
|
||||||
|
line_ids = fields.One2many('base.automation.line.test', 'lead_id')
|
||||||
|
|
||||||
|
priority = fields.Boolean()
|
||||||
|
deadline = fields.Boolean(compute='_compute_employee_deadline', store=True)
|
||||||
|
is_assigned_to_admin = fields.Boolean(string='Assigned to admin user')
|
||||||
|
|
||||||
|
@api.depends('partner_id.employee', 'priority')
|
||||||
|
def _compute_employee_deadline(self):
|
||||||
|
# this method computes two fields on purpose; don't split it
|
||||||
|
for record in self:
|
||||||
|
record.employee = record.partner_id.employee
|
||||||
|
if not record.priority:
|
||||||
|
record.deadline = False
|
||||||
|
else:
|
||||||
|
record.deadline = record.create_date + relativedelta.relativedelta(days=3)
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
result = super().write(vals)
|
||||||
|
# force recomputation of field 'deadline' via 'employee': the action
|
||||||
|
# based on 'deadline' must be triggered
|
||||||
|
self.mapped('employee')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class LineTest(models.Model):
|
||||||
|
_name = "base.automation.line.test"
|
||||||
|
_description = "Automated Rule Line Test"
|
||||||
|
|
||||||
|
name = fields.Char()
|
||||||
|
lead_id = fields.Many2one('base.automation.lead.test', ondelete='cascade')
|
||||||
|
user_id = fields.Many2one('res.users')
|
||||||
|
|
||||||
|
|
||||||
|
class ModelWithAccess(models.Model):
|
||||||
|
_name = "base.automation.link.test"
|
||||||
|
_description = "Automated Rule Link Test"
|
||||||
|
|
||||||
|
name = fields.Char()
|
||||||
|
linked_id = fields.Many2one('base.automation.linked.test', ondelete='cascade')
|
||||||
|
|
||||||
|
|
||||||
|
class ModelWithoutAccess(models.Model):
|
||||||
|
_name = "base.automation.linked.test"
|
||||||
|
_description = "Automated Rule Linked Test"
|
||||||
|
|
||||||
|
name = fields.Char()
|
||||||
|
another_field = fields.Char()
|
||||||
|
|
||||||
|
|
||||||
|
class Project(models.Model):
|
||||||
|
_name = _description = 'test_base_automation.project'
|
||||||
|
|
||||||
|
name = fields.Char()
|
||||||
|
task_ids = fields.One2many('test_base_automation.task', 'project_id')
|
||||||
|
|
||||||
|
|
||||||
|
class Task(models.Model):
|
||||||
|
_name = _description = 'test_base_automation.task'
|
||||||
|
|
||||||
|
name = fields.Char()
|
||||||
|
parent_id = fields.Many2one('test_base_automation.task')
|
||||||
|
project_id = fields.Many2one(
|
||||||
|
'test_base_automation.project',
|
||||||
|
compute='_compute_project_id', recursive=True, store=True, readonly=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends('parent_id.project_id')
|
||||||
|
def _compute_project_id(self):
|
||||||
|
for task in self:
|
||||||
|
if not task.project_id:
|
||||||
|
task.project_id = task.parent_id.project_id
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_base_automation_lead_test,access_base_automation_lead_test,model_base_automation_lead_test,base.group_system,1,1,1,1
|
||||||
|
access_base_automation_line_test,access_base_automation_line_test,model_base_automation_line_test,base.group_system,1,1,1,1
|
||||||
|
access_base_automation_link_test,access_base_automation_link_test,model_base_automation_link_test,,1,1,1,1
|
||||||
|
access_base_automation_linked_test,access_base_automation_linked_test,model_base_automation_linked_test,,1,1,1,1
|
||||||
|
access_test_base_automation_project,access_test_base_automation_project,model_test_base_automation_project,,1,1,1,1
|
||||||
|
access_test_base_automation_task,access_test_base_automation_task,model_test_base_automation_task,,1,1,1,1
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import test_flow
|
||||||
|
|
@ -0,0 +1,462 @@
|
||||||
|
# # -*- coding: utf-8 -*-
|
||||||
|
# # Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
|
||||||
|
from odoo.tests import common, tagged
|
||||||
|
from odoo.exceptions import AccessError
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('post_install', '-at_install')
|
||||||
|
class BaseAutomationTest(TransactionCaseWithUserDemo):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(BaseAutomationTest, self).setUp()
|
||||||
|
self.user_root = self.env.ref('base.user_root')
|
||||||
|
self.user_admin = self.env.ref('base.user_admin')
|
||||||
|
|
||||||
|
self.test_mail_template_automation = self.env['mail.template'].create({
|
||||||
|
'name': 'Template Automation',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'body_html': """<div>Email automation</div>""",
|
||||||
|
})
|
||||||
|
|
||||||
|
self.res_partner_1 = self.env['res.partner'].create({'name': 'My Partner'})
|
||||||
|
self.env['base.automation'].create([
|
||||||
|
{
|
||||||
|
'name': 'Base Automation: test rule on create',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'state': 'code',
|
||||||
|
'code': "records.write({'user_id': %s})" % (self.user_demo.id),
|
||||||
|
'trigger': 'on_create',
|
||||||
|
'active': True,
|
||||||
|
'filter_domain': "[('state', '=', 'draft')]",
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test rule on write',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'state': 'code',
|
||||||
|
'code': "records.write({'user_id': %s})" % (self.user_demo.id),
|
||||||
|
'trigger': 'on_write',
|
||||||
|
'active': True,
|
||||||
|
'filter_domain': "[('state', '=', 'done')]",
|
||||||
|
'filter_pre_domain': "[('state', '=', 'open')]",
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test rule on recompute',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'state': 'code',
|
||||||
|
'code': "records.write({'user_id': %s})" % (self.user_demo.id),
|
||||||
|
'trigger': 'on_write',
|
||||||
|
'active': True,
|
||||||
|
'filter_domain': "[('employee', '=', True)]",
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test recursive rule',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'state': 'code',
|
||||||
|
'code': """
|
||||||
|
record = model.browse(env.context['active_id'])
|
||||||
|
if 'partner_id' in env.context['old_values'][record.id]:
|
||||||
|
record.write({'state': 'draft'})""",
|
||||||
|
'trigger': 'on_write',
|
||||||
|
'active': True,
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test rule on secondary model',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_line_test').id,
|
||||||
|
'state': 'code',
|
||||||
|
'code': "records.write({'user_id': %s})" % (self.user_demo.id),
|
||||||
|
'trigger': 'on_create',
|
||||||
|
'active': True,
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test rule on write check context',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'state': 'code',
|
||||||
|
'code': """
|
||||||
|
record = model.browse(env.context['active_id'])
|
||||||
|
if 'user_id' in env.context['old_values'][record.id]:
|
||||||
|
record.write({'is_assigned_to_admin': (record.user_id.id == 1)})""",
|
||||||
|
'trigger': 'on_write',
|
||||||
|
'active': True,
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test rule with trigger',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'trigger_field_ids': [(4, self.env.ref('test_base_automation.field_base_automation_lead_test__state').id)],
|
||||||
|
'state': 'code',
|
||||||
|
'code': """
|
||||||
|
record = model.browse(env.context['active_id'])
|
||||||
|
record['name'] = record.name + 'X'""",
|
||||||
|
'trigger': 'on_write',
|
||||||
|
'active': True,
|
||||||
|
}, {
|
||||||
|
'name': 'Base Automation: test send an email',
|
||||||
|
'mail_post_method': 'email',
|
||||||
|
'model_id': self.env.ref('test_base_automation.model_base_automation_lead_test').id,
|
||||||
|
'template_id': self.test_mail_template_automation.id,
|
||||||
|
'trigger_field_ids': [(4, self.env.ref('test_base_automation.field_base_automation_lead_test__deadline').id)],
|
||||||
|
'state': 'mail_post',
|
||||||
|
'code': """
|
||||||
|
record = model.browse(env.context['active_id'])
|
||||||
|
record['name'] = record.name + 'X'""",
|
||||||
|
'trigger': 'on_write',
|
||||||
|
'active': True,
|
||||||
|
'filter_domain': "[('deadline', '!=', False)]",
|
||||||
|
'filter_pre_domain': "[('deadline', '=', False)]",
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
self.env['base.automation']._unregister_hook()
|
||||||
|
|
||||||
|
def create_lead(self, **kwargs):
|
||||||
|
vals = {
|
||||||
|
'name': "Lead Test",
|
||||||
|
'user_id': self.user_root.id,
|
||||||
|
}
|
||||||
|
vals.update(kwargs)
|
||||||
|
return self.env['base.automation.lead.test'].create(vals)
|
||||||
|
|
||||||
|
def test_00_check_to_state_open_pre(self):
|
||||||
|
"""
|
||||||
|
Check that a new record (with state = open) doesn't change its responsible
|
||||||
|
when there is a precondition filter which check that the state is open.
|
||||||
|
"""
|
||||||
|
lead = self.create_lead(state='open')
|
||||||
|
self.assertEqual(lead.state, 'open')
|
||||||
|
self.assertEqual(lead.user_id, self.user_root, "Responsible should not change on creation of Lead with state 'open'.")
|
||||||
|
|
||||||
|
def test_01_check_to_state_draft_post(self):
|
||||||
|
"""
|
||||||
|
Check that a new record changes its responsible when there is a postcondition
|
||||||
|
filter which check that the state is draft.
|
||||||
|
"""
|
||||||
|
lead = self.create_lead()
|
||||||
|
self.assertEqual(lead.state, 'draft', "Lead state should be 'draft'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_demo, "Responsible should be change on creation of Lead with state 'draft'.")
|
||||||
|
|
||||||
|
def test_02_check_from_draft_to_done_with_steps(self):
|
||||||
|
"""
|
||||||
|
A new record is created and goes from states 'open' to 'done' via the
|
||||||
|
other states (open, pending and cancel). We have a rule with:
|
||||||
|
- precondition: the record is in "open"
|
||||||
|
- postcondition: that the record is "done".
|
||||||
|
If the state goes from 'open' to 'done' the responsible is changed.
|
||||||
|
If those two conditions aren't verified, the responsible remains the same.
|
||||||
|
"""
|
||||||
|
lead = self.create_lead(state='open')
|
||||||
|
self.assertEqual(lead.state, 'open', "Lead state should be 'open'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_root, "Responsible should not change on creation of Lead with state 'open'.")
|
||||||
|
# change state to pending and check that responsible has not changed
|
||||||
|
lead.write({'state': 'pending'})
|
||||||
|
self.assertEqual(lead.state, 'pending', "Lead state should be 'pending'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_root, "Responsible should not change on creation of Lead with state from 'draft' to 'open'.")
|
||||||
|
# change state to done and check that responsible has not changed
|
||||||
|
lead.write({'state': 'done'})
|
||||||
|
self.assertEqual(lead.state, 'done', "Lead state should be 'done'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_root, "Responsible should not chang on creation of Lead with state from 'pending' to 'done'.")
|
||||||
|
|
||||||
|
def test_03_check_from_draft_to_done_without_steps(self):
|
||||||
|
"""
|
||||||
|
A new record is created and goes from states 'open' to 'done' via the
|
||||||
|
other states (open, pending and cancel). We have a rule with:
|
||||||
|
- precondition: the record is in "open"
|
||||||
|
- postcondition: that the record is "done".
|
||||||
|
If the state goes from 'open' to 'done' the responsible is changed.
|
||||||
|
If those two conditions aren't verified, the responsible remains the same.
|
||||||
|
"""
|
||||||
|
lead = self.create_lead(state='open')
|
||||||
|
self.assertEqual(lead.state, 'open', "Lead state should be 'open'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_root, "Responsible should not change on creation of Lead with state 'open'.")
|
||||||
|
# change state to done and check that responsible has changed
|
||||||
|
lead.write({'state': 'done'})
|
||||||
|
self.assertEqual(lead.state, 'done', "Lead state should be 'done'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_demo, "Responsible should be change on write of Lead with state from 'open' to 'done'.")
|
||||||
|
|
||||||
|
def test_10_recomputed_field(self):
|
||||||
|
"""
|
||||||
|
Check that a rule is executed whenever a field is recomputed after a
|
||||||
|
change on another model.
|
||||||
|
"""
|
||||||
|
partner = self.res_partner_1
|
||||||
|
partner.write({'employee': False})
|
||||||
|
lead = self.create_lead(state='open', partner_id=partner.id)
|
||||||
|
self.assertFalse(lead.employee, "Customer field should updated to False")
|
||||||
|
self.assertEqual(lead.user_id, self.user_root, "Responsible should not change on creation of Lead with state from 'draft' to 'open'.")
|
||||||
|
# change partner, recompute on lead should trigger the rule
|
||||||
|
partner.write({'employee': True})
|
||||||
|
self.env.flush_all()
|
||||||
|
self.assertTrue(lead.employee, "Customer field should updated to True")
|
||||||
|
self.assertEqual(lead.user_id, self.user_demo, "Responsible should be change on write of Lead when Customer becomes True.")
|
||||||
|
|
||||||
|
def test_11_recomputed_field(self):
|
||||||
|
"""
|
||||||
|
Check that a rule is executed whenever a field is recomputed and the
|
||||||
|
context contains the target field
|
||||||
|
"""
|
||||||
|
partner = self.res_partner_1
|
||||||
|
lead = self.create_lead(state='draft', partner_id=partner.id)
|
||||||
|
self.assertFalse(lead.deadline, 'There should not be a deadline defined')
|
||||||
|
# change priority and user; this triggers deadline recomputation, and
|
||||||
|
# the server action should set the boolean field to True
|
||||||
|
lead.write({'priority': True, 'user_id': self.user_root.id})
|
||||||
|
self.assertTrue(lead.deadline, 'Deadline should be defined')
|
||||||
|
self.assertTrue(lead.is_assigned_to_admin, 'Lead should be assigned to admin')
|
||||||
|
|
||||||
|
def test_11b_recomputed_field(self):
|
||||||
|
mail_automation = self.env['base.automation'].search([('name', '=', 'Base Automation: test send an email')])
|
||||||
|
send_mail_count = 0
|
||||||
|
|
||||||
|
def _patched_get_actions(*args, **kwargs):
|
||||||
|
obj = args[0]
|
||||||
|
if '__action_done' not in obj._context:
|
||||||
|
obj = obj.with_context(__action_done={})
|
||||||
|
return mail_automation.with_env(obj.env)
|
||||||
|
|
||||||
|
def _patched_send_mail(*args, **kwargs):
|
||||||
|
nonlocal send_mail_count
|
||||||
|
send_mail_count += 1
|
||||||
|
|
||||||
|
patchers = [
|
||||||
|
patch('odoo.addons.base_automation.models.base_automation.BaseAutomation._get_actions', _patched_get_actions),
|
||||||
|
patch('odoo.addons.mail.models.mail_template.MailTemplate.send_mail', _patched_send_mail),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.startPatcher(patchers[0])
|
||||||
|
|
||||||
|
lead = self.create_lead()
|
||||||
|
self.assertFalse(lead.priority)
|
||||||
|
self.assertFalse(lead.deadline)
|
||||||
|
|
||||||
|
self.startPatcher(patchers[1])
|
||||||
|
|
||||||
|
lead.write({'priority': True})
|
||||||
|
|
||||||
|
self.assertTrue(lead.priority)
|
||||||
|
self.assertTrue(lead.deadline)
|
||||||
|
|
||||||
|
|
||||||
|
self.assertEqual(send_mail_count, 1)
|
||||||
|
|
||||||
|
def test_12_recursive(self):
|
||||||
|
""" Check that a rule is executed recursively by a secondary change. """
|
||||||
|
lead = self.create_lead(state='open')
|
||||||
|
self.assertEqual(lead.state, 'open')
|
||||||
|
self.assertEqual(lead.user_id, self.user_root)
|
||||||
|
# change partner; this should trigger the rule that modifies the state
|
||||||
|
partner = self.res_partner_1
|
||||||
|
lead.write({'partner_id': partner.id})
|
||||||
|
self.assertEqual(lead.state, 'draft')
|
||||||
|
|
||||||
|
def test_20_direct_line(self):
|
||||||
|
"""
|
||||||
|
Check that a rule is executed after creating a line record.
|
||||||
|
"""
|
||||||
|
line = self.env['base.automation.line.test'].create({'name': "Line"})
|
||||||
|
self.assertEqual(line.user_id, self.user_demo)
|
||||||
|
|
||||||
|
def test_20_indirect_line(self):
|
||||||
|
"""
|
||||||
|
Check that creating a lead with a line executes rules on both records.
|
||||||
|
"""
|
||||||
|
lead = self.create_lead(line_ids=[(0, 0, {'name': "Line"})])
|
||||||
|
self.assertEqual(lead.state, 'draft', "Lead state should be 'draft'")
|
||||||
|
self.assertEqual(lead.user_id, self.user_demo, "Responsible should change on creation of Lead test line.")
|
||||||
|
self.assertEqual(len(lead.line_ids), 1, "New test line is not created")
|
||||||
|
self.assertEqual(lead.line_ids.user_id, self.user_demo, "Responsible should be change on creation of Lead test line.")
|
||||||
|
|
||||||
|
def test_21_trigger_fields(self):
|
||||||
|
"""
|
||||||
|
Check that the rule with trigger is executed only once per pertinent update.
|
||||||
|
"""
|
||||||
|
lead = self.create_lead(name="X")
|
||||||
|
lead.priority = True
|
||||||
|
partner1 = self.res_partner_1
|
||||||
|
lead.partner_id = partner1.id
|
||||||
|
self.assertEqual(lead.name, 'X', "No update until now.")
|
||||||
|
|
||||||
|
lead.state = 'open'
|
||||||
|
self.assertEqual(lead.name, 'XX', "One update should have happened.")
|
||||||
|
lead.state = 'done'
|
||||||
|
self.assertEqual(lead.name, 'XXX', "One update should have happened.")
|
||||||
|
lead.state = 'done'
|
||||||
|
self.assertEqual(lead.name, 'XXX', "No update should have happened.")
|
||||||
|
lead.state = 'cancel'
|
||||||
|
self.assertEqual(lead.name, 'XXXX', "One update should have happened.")
|
||||||
|
|
||||||
|
# change the rule to trigger on partner_id
|
||||||
|
rule = self.env['base.automation'].search([('name', '=', 'Base Automation: test rule with trigger')])
|
||||||
|
rule.write({'trigger_field_ids': [(6, 0, [self.env.ref('test_base_automation.field_base_automation_lead_test__partner_id').id])]})
|
||||||
|
|
||||||
|
partner2 = self.env['res.partner'].create({'name': 'A new partner'})
|
||||||
|
lead.name = 'X'
|
||||||
|
lead.state = 'open'
|
||||||
|
self.assertEqual(lead.name, 'X', "No update should have happened.")
|
||||||
|
lead.partner_id = partner2
|
||||||
|
self.assertEqual(lead.name, 'XX', "One update should have happened.")
|
||||||
|
lead.partner_id = partner2
|
||||||
|
self.assertEqual(lead.name, 'XX', "No update should have happened.")
|
||||||
|
lead.partner_id = partner1
|
||||||
|
self.assertEqual(lead.name, 'XXX', "One update should have happened.")
|
||||||
|
|
||||||
|
def test_30_modelwithoutaccess(self):
|
||||||
|
"""
|
||||||
|
Ensure a domain on a M2O without user access doesn't fail.
|
||||||
|
We create a base automation with a filter on a model the user haven't access to
|
||||||
|
- create a group
|
||||||
|
- restrict acl to this group and set only admin in it
|
||||||
|
- create base.automation with a filter
|
||||||
|
- create a record in the restricted model in admin
|
||||||
|
- create a record in the non restricted model in demo
|
||||||
|
"""
|
||||||
|
Model = self.env['base.automation.link.test']
|
||||||
|
Comodel = self.env['base.automation.linked.test']
|
||||||
|
|
||||||
|
access = self.env.ref("test_base_automation.access_base_automation_linked_test")
|
||||||
|
access.group_id = self.env['res.groups'].create({
|
||||||
|
'name': "Access to base.automation.linked.test",
|
||||||
|
"users": [(6, 0, [self.user_admin.id,])]
|
||||||
|
})
|
||||||
|
|
||||||
|
# sanity check: user demo has no access to the comodel of 'linked_id'
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
Comodel.with_user(self.user_demo).check_access_rights('read')
|
||||||
|
|
||||||
|
# check base automation with filter that performs Comodel.search()
|
||||||
|
self.env['base.automation'].create({
|
||||||
|
'name': 'test no access',
|
||||||
|
'model_id': self.env['ir.model']._get_id("base.automation.link.test"),
|
||||||
|
'trigger': 'on_create_or_write',
|
||||||
|
'filter_pre_domain': "[('linked_id.another_field', '=', 'something')]",
|
||||||
|
'state': 'code',
|
||||||
|
'active': True,
|
||||||
|
'code': "action = [rec.name for rec in records]"
|
||||||
|
})
|
||||||
|
Comodel.create([
|
||||||
|
{'name': 'a first record', 'another_field': 'something'},
|
||||||
|
{'name': 'another record', 'another_field': 'something different'},
|
||||||
|
])
|
||||||
|
rec1 = Model.create({'name': 'a record'})
|
||||||
|
rec1.write({'name': 'a first record'})
|
||||||
|
rec2 = Model.with_user(self.user_demo).create({'name': 'another record'})
|
||||||
|
rec2.write({'name': 'another value'})
|
||||||
|
|
||||||
|
# check base automation with filter that performs Comodel.name_search()
|
||||||
|
self.env['base.automation'].create({
|
||||||
|
'name': 'test no name access',
|
||||||
|
'model_id': self.env['ir.model']._get_id("base.automation.link.test"),
|
||||||
|
'trigger': 'on_create_or_write',
|
||||||
|
'filter_pre_domain': "[('linked_id', '=', 'whatever')]",
|
||||||
|
'state': 'code',
|
||||||
|
'active': True,
|
||||||
|
'code': "action = [rec.name for rec in records]"
|
||||||
|
})
|
||||||
|
rec3 = Model.create({'name': 'a random record'})
|
||||||
|
rec3.write({'name': 'a first record'})
|
||||||
|
rec4 = Model.with_user(self.user_demo).create({'name': 'again another record'})
|
||||||
|
rec4.write({'name': 'another value'})
|
||||||
|
|
||||||
|
|
||||||
|
@common.tagged('post_install', '-at_install')
|
||||||
|
class TestCompute(common.TransactionCase):
|
||||||
|
def test_inversion(self):
|
||||||
|
""" If a stored field B depends on A, an update to the trigger for A
|
||||||
|
should trigger the recomputaton of A, then B.
|
||||||
|
|
||||||
|
However if a search() is performed during the computation of A
|
||||||
|
??? and _order is affected ??? a flush will be triggered, forcing the
|
||||||
|
computation of B, based on the previous A.
|
||||||
|
|
||||||
|
This happens if a rule has has a non-empty filter_pre_domain, even if
|
||||||
|
it's an empty list (``'[]'`` as opposed to ``False``).
|
||||||
|
"""
|
||||||
|
company1 = self.env['res.partner'].create({
|
||||||
|
'name': "Gorofy",
|
||||||
|
'is_company': True,
|
||||||
|
})
|
||||||
|
company2 = self.env['res.partner'].create({
|
||||||
|
'name': "Awiclo",
|
||||||
|
'is_company': True
|
||||||
|
})
|
||||||
|
r = self.env['res.partner'].create({
|
||||||
|
'name': 'Bob',
|
||||||
|
'is_company': False,
|
||||||
|
'parent_id': company1.id
|
||||||
|
})
|
||||||
|
self.assertEqual(r.display_name, 'Gorofy, Bob')
|
||||||
|
r.parent_id = company2
|
||||||
|
self.assertEqual(r.display_name, 'Awiclo, Bob')
|
||||||
|
|
||||||
|
self.env['base.automation'].create({
|
||||||
|
'name': "test rule",
|
||||||
|
'filter_pre_domain': False,
|
||||||
|
'trigger': 'on_create_or_write',
|
||||||
|
'state': 'code', # no-op action
|
||||||
|
'model_id': self.env.ref('base.model_res_partner').id,
|
||||||
|
})
|
||||||
|
r.parent_id = company1
|
||||||
|
self.assertEqual(r.display_name, 'Gorofy, Bob')
|
||||||
|
|
||||||
|
self.env['base.automation'].create({
|
||||||
|
'name': "test rule",
|
||||||
|
'filter_pre_domain': '[]',
|
||||||
|
'trigger': 'on_create_or_write',
|
||||||
|
'state': 'code', # no-op action
|
||||||
|
'model_id': self.env.ref('base.model_res_partner').id,
|
||||||
|
})
|
||||||
|
r.parent_id = company2
|
||||||
|
self.assertEqual(r.display_name, 'Awiclo, Bob')
|
||||||
|
|
||||||
|
def test_recursion(self):
|
||||||
|
project = self.env['test_base_automation.project'].create({})
|
||||||
|
|
||||||
|
# this action is executed every time a task is assigned to project
|
||||||
|
self.env['base.automation'].create({
|
||||||
|
'name': 'dummy',
|
||||||
|
'model_id': self.env['ir.model']._get_id('test_base_automation.task'),
|
||||||
|
'state': 'code',
|
||||||
|
'trigger': 'on_create_or_write',
|
||||||
|
'filter_domain': repr([('project_id', '=', project.id)]),
|
||||||
|
})
|
||||||
|
|
||||||
|
# create one task in project with 10 subtasks; all the subtasks are
|
||||||
|
# automatically assigned to project, too
|
||||||
|
task = self.env['test_base_automation.task'].create({'project_id': project.id})
|
||||||
|
subtasks = task.create([{'parent_id': task.id} for _ in range(10)])
|
||||||
|
subtasks.flush_model()
|
||||||
|
|
||||||
|
# This test checks what happens when a stored recursive computed field
|
||||||
|
# is marked to compute on many records, and automated actions are
|
||||||
|
# triggered depending on that field. In this case, we trigger the
|
||||||
|
# recomputation of 'project_id' on 'subtasks' by deleting their parent
|
||||||
|
# task.
|
||||||
|
#
|
||||||
|
# An issue occurs when the domain of automated actions is evaluated by
|
||||||
|
# method search(), because the latter flushes the fields to search on,
|
||||||
|
# which are also the ones being recomputed. Combined with the fact
|
||||||
|
# that recursive fields are not computed in batch, this leads to a huge
|
||||||
|
# amount of recursive calls between the automated action and flush().
|
||||||
|
#
|
||||||
|
# The execution of task.unlink() looks like this:
|
||||||
|
# - mark 'project_id' to compute on subtasks
|
||||||
|
# - delete task
|
||||||
|
# - flush()
|
||||||
|
# - recompute 'project_id' on subtask1
|
||||||
|
# - call compute on subtask1
|
||||||
|
# - in action, search([('id', 'in', subtask1.ids), ('project_id', '=', pid)])
|
||||||
|
# - flush(['id', 'project_id'])
|
||||||
|
# - recompute 'project_id' on subtask2
|
||||||
|
# - call compute on subtask2
|
||||||
|
# - in action search([('id', 'in', subtask2.ids), ('project_id', '=', pid)])
|
||||||
|
# - flush(['id', 'project_id'])
|
||||||
|
# - recompute 'project_id' on subtask3
|
||||||
|
# - call compute on subtask3
|
||||||
|
# - in action, search([('id', 'in', subtask3.ids), ('project_id', '=', pid)])
|
||||||
|
# - flush(['id', 'project_id'])
|
||||||
|
# - recompute 'project_id' on subtask4
|
||||||
|
# ...
|
||||||
|
limit = sys.getrecursionlimit()
|
||||||
|
try:
|
||||||
|
sys.setrecursionlimit(100)
|
||||||
|
task.unlink()
|
||||||
|
finally:
|
||||||
|
sys.setrecursionlimit(limit)
|
||||||
57
odoo-bringout-oca-ocb-test_crm_full/README.md
Normal file
57
odoo-bringout-oca-ocb-test_crm_full/README.md
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Test Full Crm Flow
|
||||||
|
|
||||||
|
|
||||||
|
This module is intended to test the main crm flows of Odoo, both frontend and
|
||||||
|
backend. It notably includes IAP bridges modules to test their impact.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_crm_full
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
- crm
|
||||||
|
- crm_iap_enrich
|
||||||
|
- crm_iap_mine
|
||||||
|
- crm_sms
|
||||||
|
- event_crm
|
||||||
|
- sale_crm
|
||||||
|
- website_crm
|
||||||
|
- website_crm_iap_reveal
|
||||||
|
- website_crm_partner_assign
|
||||||
|
- website_crm_livechat
|
||||||
|
|
||||||
|
## Manifest Information
|
||||||
|
|
||||||
|
- **Name**: Test Full Crm Flow
|
||||||
|
- **Version**: 1.0
|
||||||
|
- **Category**: Hidden/Tests
|
||||||
|
- **License**: LGPL-3
|
||||||
|
- **Installable**: False
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `test_crm_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
|
||||||
32
odoo-bringout-oca-ocb-test_crm_full/doc/ARCHITECTURE.md
Normal file
32
odoo-bringout-oca-ocb-test_crm_full/doc/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||||
|
V --> C[Controllers]
|
||||||
|
V --> W[Wizards – Transient Models]
|
||||||
|
C --> M[Models and ORM]
|
||||||
|
W --> M
|
||||||
|
M --> R[Reports]
|
||||||
|
DX[Data XML] --> M
|
||||||
|
S[Security – ACLs and Groups] -. enforces .-> M
|
||||||
|
|
||||||
|
subgraph Test_crm_full Module - test_crm_full
|
||||||
|
direction LR
|
||||||
|
M:::layer
|
||||||
|
W:::layer
|
||||||
|
C:::layer
|
||||||
|
V:::layer
|
||||||
|
R:::layer
|
||||||
|
S:::layer
|
||||||
|
DX:::layer
|
||||||
|
end
|
||||||
|
|
||||||
|
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Views include tree/form/kanban templates and report templates.
|
||||||
|
- Controllers provide website/portal routes when present.
|
||||||
|
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||||
|
- Data XML loads data/demo records; Security defines groups and access.
|
||||||
3
odoo-bringout-oca-ocb-test_crm_full/doc/CONFIGURATION.md
Normal file
3
odoo-bringout-oca-ocb-test_crm_full/doc/CONFIGURATION.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Refer to Odoo settings for test_crm_full. Configure related models, access rights, and options as needed.
|
||||||
3
odoo-bringout-oca-ocb-test_crm_full/doc/CONTROLLERS.md
Normal file
3
odoo-bringout-oca-ocb-test_crm_full/doc/CONTROLLERS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Controllers
|
||||||
|
|
||||||
|
This module does not define custom HTTP controllers.
|
||||||
14
odoo-bringout-oca-ocb-test_crm_full/doc/DEPENDENCIES.md
Normal file
14
odoo-bringout-oca-ocb-test_crm_full/doc/DEPENDENCIES.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
|
||||||
|
- [crm](../../odoo-bringout-oca-ocb-crm)
|
||||||
|
- [crm_iap_enrich](../../odoo-bringout-oca-ocb-crm_iap_enrich)
|
||||||
|
- [crm_iap_mine](../../odoo-bringout-oca-ocb-crm_iap_mine)
|
||||||
|
- [crm_sms](../../odoo-bringout-oca-ocb-crm_sms)
|
||||||
|
- [event_crm](../../odoo-bringout-oca-ocb-event_crm)
|
||||||
|
- [sale_crm](../../odoo-bringout-oca-ocb-sale_crm)
|
||||||
|
- [website_crm](../../odoo-bringout-oca-ocb-website_crm)
|
||||||
|
- [website_crm_iap_reveal](../../odoo-bringout-oca-ocb-website_crm_iap_reveal)
|
||||||
|
- [website_crm_partner_assign](../../odoo-bringout-oca-ocb-website_crm_partner_assign)
|
||||||
|
- [website_crm_livechat](../../odoo-bringout-oca-ocb-website_crm_livechat)
|
||||||
4
odoo-bringout-oca-ocb-test_crm_full/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-test_crm_full/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||||
|
- Q: How to enable? A: Start server with --addon test_crm_full or install in UI.
|
||||||
7
odoo-bringout-oca-ocb-test_crm_full/doc/INSTALL.md
Normal file
7
odoo-bringout-oca-ocb-test_crm_full/doc/INSTALL.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_crm_full"
|
||||||
|
# or
|
||||||
|
uv pip install odoo-bringout-oca-ocb-test_crm_full"
|
||||||
|
```
|
||||||
11
odoo-bringout-oca-ocb-test_crm_full/doc/MODELS.md
Normal file
11
odoo-bringout-oca-ocb-test_crm_full/doc/MODELS.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Models
|
||||||
|
|
||||||
|
Detected core models and extensions in test_crm_full.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Classes show model technical names; fields omitted for brevity.
|
||||||
|
- Items listed under _inherit are extensions of existing models.
|
||||||
6
odoo-bringout-oca-ocb-test_crm_full/doc/OVERVIEW.md
Normal file
6
odoo-bringout-oca-ocb-test_crm_full/doc/OVERVIEW.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Packaged Odoo addon: test_crm_full. Provides features documented in upstream Odoo 16 under this addon.
|
||||||
|
|
||||||
|
- Source: OCA/OCB 16.0, addon test_crm_full
|
||||||
|
- License: LGPL-3
|
||||||
3
odoo-bringout-oca-ocb-test_crm_full/doc/REPORTS.md
Normal file
3
odoo-bringout-oca-ocb-test_crm_full/doc/REPORTS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Reports
|
||||||
|
|
||||||
|
This module does not define custom reports.
|
||||||
8
odoo-bringout-oca-ocb-test_crm_full/doc/SECURITY.md
Normal file
8
odoo-bringout-oca-ocb-test_crm_full/doc/SECURITY.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Security
|
||||||
|
|
||||||
|
This module does not define custom security rules or access controls beyond Odoo defaults.
|
||||||
|
|
||||||
|
Default Odoo security applies:
|
||||||
|
- Base user access through standard groups
|
||||||
|
- Model access inherited from dependencies
|
||||||
|
- No custom row-level security rules
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
- Ensure Python and Odoo environment matches repo guidance.
|
||||||
|
- Check database connectivity and logs if startup fails.
|
||||||
|
- Validate that dependent addons listed in DEPENDENCIES.md are installed.
|
||||||
7
odoo-bringout-oca-ocb-test_crm_full/doc/USAGE.md
Normal file
7
odoo-bringout-oca-ocb-test_crm_full/doc/USAGE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
Start Odoo including this addon (from repo root):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon test_crm_full
|
||||||
|
```
|
||||||
3
odoo-bringout-oca-ocb-test_crm_full/doc/WIZARDS.md
Normal file
3
odoo-bringout-oca-ocb-test_crm_full/doc/WIZARDS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Wizards
|
||||||
|
|
||||||
|
This module does not include UI wizards.
|
||||||
51
odoo-bringout-oca-ocb-test_crm_full/pyproject.toml
Normal file
51
odoo-bringout-oca-ocb-test_crm_full/pyproject.toml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
[project]
|
||||||
|
name = "odoo-bringout-oca-ocb-test_crm_full"
|
||||||
|
version = "16.0.0"
|
||||||
|
description = "Test Full Crm Flow - Odoo addon"
|
||||||
|
authors = [
|
||||||
|
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"odoo-bringout-oca-ocb-crm>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-crm_iap_enrich>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-crm_iap_mine>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-crm_sms>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-event_crm>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-sale_crm>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_crm>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_crm_iap_reveal>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_crm_partner_assign>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_crm_livechat>=16.0.0",
|
||||||
|
"requests>=2.25.1"
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">= 3.11"
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
"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.12",
|
||||||
|
"Topic :: Office/Business",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
homepage = "https://github.com/bringout/0"
|
||||||
|
repository = "https://github.com/bringout/0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.metadata]
|
||||||
|
allow-direct-references = true
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["test_crm_full"]
|
||||||
|
|
||||||
|
[tool.rye]
|
||||||
|
managed = true
|
||||||
|
dev-dependencies = [
|
||||||
|
"pytest>=8.4.1",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Test Full Crm Flow',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Hidden/Tests',
|
||||||
|
'description': """
|
||||||
|
This module is intended to test the main crm flows of Odoo, both frontend and
|
||||||
|
backend. It notably includes IAP bridges modules to test their impact. """,
|
||||||
|
'depends': [
|
||||||
|
'crm',
|
||||||
|
'crm_iap_enrich',
|
||||||
|
'crm_iap_mine',
|
||||||
|
'crm_sms',
|
||||||
|
'event_crm',
|
||||||
|
'sale_crm',
|
||||||
|
'website_crm',
|
||||||
|
'website_crm_iap_reveal',
|
||||||
|
'website_crm_partner_assign',
|
||||||
|
'website_crm_livechat',
|
||||||
|
],
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import test_performance
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from odoo.addons.crm.tests.common import TestCrmCommon
|
||||||
|
from odoo.addons.crm_iap_mine.tests.common import MockIAPReveal # MockIAPEnrich
|
||||||
|
from odoo.addons.website.tests.test_website_visitor import MockVisitor
|
||||||
|
|
||||||
|
|
||||||
|
class TestCrmFullCommon(TestCrmCommon, MockIAPReveal, MockVisitor):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestCrmFullCommon, cls).setUpClass()
|
||||||
|
cls._init_mail_gateway()
|
||||||
|
cls._activate_multi_company()
|
||||||
|
|
||||||
|
# Context data: dates
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Mock dates to have reproducible computed fields based on time
|
||||||
|
cls.reference_now = datetime(2022, 1, 1, 10, 0, 0)
|
||||||
|
cls.reference_today = datetime(2022, 1, 1)
|
||||||
|
|
||||||
|
# Customers
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
country_be = cls.env.ref('base.be')
|
||||||
|
cls.env['res.lang']._activate_lang('fr_BE')
|
||||||
|
|
||||||
|
cls.partners = cls.env['res.partner'].create([
|
||||||
|
{'country_id': country_be.id,
|
||||||
|
'email': 'partner.email.%02d@test.example.com' % idx,
|
||||||
|
'function': 'Noisy Customer',
|
||||||
|
'lang': 'fr_BE',
|
||||||
|
'mobile': '04569999%02d' % idx,
|
||||||
|
'name': 'PartnerCustomer',
|
||||||
|
'phone': '04560000%02d' % idx,
|
||||||
|
'street': 'Super Street, %092d' % idx,
|
||||||
|
'zip': '1400',
|
||||||
|
} for idx in range(0, 10)
|
||||||
|
])
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from odoo.addons.test_crm_full.tests.common import TestCrmFullCommon
|
||||||
|
from odoo.tests.common import users, warmup, Form
|
||||||
|
from odoo.tests import tagged
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('crm_performance', 'post_install', '-at_install', '-standard')
|
||||||
|
class CrmPerformanceCase(TestCrmFullCommon):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(CrmPerformanceCase, self).setUp()
|
||||||
|
# patch registry to simulate a ready environment
|
||||||
|
self.patch(self.env.registry, 'ready', True)
|
||||||
|
self._flush_tracking()
|
||||||
|
|
||||||
|
self.user_sales_leads.write({
|
||||||
|
'groups_id': [
|
||||||
|
(4, self.env.ref('event.group_event_user').id),
|
||||||
|
(4, self.env.ref('im_livechat.im_livechat_group_user').id),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
def _flush_tracking(self):
|
||||||
|
""" Force the creation of tracking values notably, and ensure tests are
|
||||||
|
reproducible. """
|
||||||
|
self.env.flush_all()
|
||||||
|
self.cr.flush()
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('crm_performance', 'post_install', '-at_install', '-standard')
|
||||||
|
class TestCrmPerformance(CrmPerformanceCase):
|
||||||
|
|
||||||
|
@users('user_sales_leads')
|
||||||
|
@warmup
|
||||||
|
def test_lead_create_batch_mixed(self):
|
||||||
|
""" Test multiple lead creation (import) """
|
||||||
|
batch_size = 10
|
||||||
|
country_be = self.env.ref('base.be')
|
||||||
|
lang_be_id = self.env['res.lang']._lang_get_id('fr_BE')
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(user_sales_leads=194): # tcf 193 / com 194
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
crm_values = [
|
||||||
|
{'country_id': country_be.id,
|
||||||
|
'email_from': 'address.email.%02d@test.example.com' % idx,
|
||||||
|
'function': 'Noisy Customer',
|
||||||
|
'lang_id': lang_be_id,
|
||||||
|
'mobile': '04551111%02d' % idx,
|
||||||
|
'name': 'Test Lead %02d' % idx,
|
||||||
|
'phone': '04550000%02d' % idx,
|
||||||
|
'street': 'Super Street, %092d' % idx,
|
||||||
|
'zip': '1400',
|
||||||
|
} for idx in range(batch_size)
|
||||||
|
]
|
||||||
|
crm_values += [
|
||||||
|
{'partner_id': self.partners[idx].id,
|
||||||
|
'name': 'Test Lead %02d' % idx,
|
||||||
|
} for idx in range(batch_size)
|
||||||
|
]
|
||||||
|
_leads = self.env['crm.lead'].create(crm_values)
|
||||||
|
|
||||||
|
@users('user_sales_leads')
|
||||||
|
@warmup
|
||||||
|
def test_lead_create_form_address(self):
|
||||||
|
""" Test a single lead creation using Form """
|
||||||
|
country_be = self.env.ref('base.be')
|
||||||
|
lang_be = self.env['res.lang']._lang_get('fr_BE')
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(user_sales_leads=189): # tcf only: 173 - com runbot: 174/175
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
with Form(self.env['crm.lead']) as lead_form:
|
||||||
|
lead_form.country_id = country_be
|
||||||
|
lead_form.email_from = 'address.email@test.example.com'
|
||||||
|
lead_form.function = 'Noisy Customer'
|
||||||
|
lead_form.lang_id = lang_be
|
||||||
|
lead_form.mobile = '0455111100'
|
||||||
|
lead_form.name = 'Test Lead'
|
||||||
|
lead_form.phone = '0455000011'
|
||||||
|
lead_form.street = 'Super Street, 00'
|
||||||
|
lead_form.zip = '1400'
|
||||||
|
|
||||||
|
_lead = lead_form.save()
|
||||||
|
|
||||||
|
@users('user_sales_leads')
|
||||||
|
@warmup
|
||||||
|
def test_lead_create_form_partner(self):
|
||||||
|
""" Test a single lead creation using Form with a partner """
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(user_sales_leads=199): # tcf 186 / com 188
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
with self.debug_mode():
|
||||||
|
# {'invisible': ['|', ('type', '=', 'opportunity'), ('is_partner_visible', '=', False)]}
|
||||||
|
# lead.is_partner_visible = bool(lead.type == 'opportunity' or lead.partner_id or is_debug_mode)
|
||||||
|
with Form(self.env['crm.lead']) as lead_form:
|
||||||
|
lead_form.partner_id = self.partners[0]
|
||||||
|
lead_form.name = 'Test Lead'
|
||||||
|
|
||||||
|
_lead = lead_form.save()
|
||||||
|
|
||||||
|
@users('user_sales_leads')
|
||||||
|
@warmup
|
||||||
|
def test_lead_create_single_address(self):
|
||||||
|
""" Test multiple lead creation (import) """
|
||||||
|
country_be = self.env.ref('base.be')
|
||||||
|
lang_be_id = self.env['res.lang']._lang_get_id('fr_BE')
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(user_sales_leads=43): # tcf only: 41 - com runbot: 42
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
crm_values = [
|
||||||
|
{'country_id': country_be.id,
|
||||||
|
'email_from': 'address.email.00@test.example.com',
|
||||||
|
'function': 'Noisy Customer',
|
||||||
|
'lang_id': lang_be_id,
|
||||||
|
'mobile': '0455111100',
|
||||||
|
'name': 'Test Lead',
|
||||||
|
'phone': '0455000000',
|
||||||
|
'street': 'Super Street, 00',
|
||||||
|
'zip': '1400',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
_lead = self.env['crm.lead'].create(crm_values)
|
||||||
|
|
||||||
|
@users('user_sales_leads')
|
||||||
|
@warmup
|
||||||
|
def test_lead_create_single_partner(self):
|
||||||
|
""" Test multiple lead creation (import) """
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(user_sales_leads=49): # tcf only: 47 - com runbot: 48
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
crm_values = [
|
||||||
|
{'partner_id': self.partners[0].id,
|
||||||
|
'name': 'Test Lead',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
_lead = self.env['crm.lead'].create(crm_values)
|
||||||
54
odoo-bringout-oca-ocb-test_discuss_full/README.md
Normal file
54
odoo-bringout-oca-ocb-test_discuss_full/README.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Test Discuss (full)
|
||||||
|
|
||||||
|
Test of Discuss with all possible overrides installed, including feature and performance tests.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_discuss_full
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
- calendar
|
||||||
|
- crm
|
||||||
|
- crm_livechat
|
||||||
|
- hr_holidays
|
||||||
|
- im_livechat
|
||||||
|
- mail
|
||||||
|
- mail_bot
|
||||||
|
- note
|
||||||
|
- website_livechat
|
||||||
|
|
||||||
|
## Manifest Information
|
||||||
|
|
||||||
|
- **Name**: Test Discuss (full)
|
||||||
|
- **Version**: 1.0
|
||||||
|
- **Category**: Hidden
|
||||||
|
- **License**: LGPL-3
|
||||||
|
- **Installable**: True
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `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
|
||||||
32
odoo-bringout-oca-ocb-test_discuss_full/doc/ARCHITECTURE.md
Normal file
32
odoo-bringout-oca-ocb-test_discuss_full/doc/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||||
|
V --> C[Controllers]
|
||||||
|
V --> W[Wizards – Transient Models]
|
||||||
|
C --> M[Models and ORM]
|
||||||
|
W --> M
|
||||||
|
M --> R[Reports]
|
||||||
|
DX[Data XML] --> M
|
||||||
|
S[Security – ACLs and Groups] -. enforces .-> M
|
||||||
|
|
||||||
|
subgraph Test_discuss_full Module - test_discuss_full
|
||||||
|
direction LR
|
||||||
|
M:::layer
|
||||||
|
W:::layer
|
||||||
|
C:::layer
|
||||||
|
V:::layer
|
||||||
|
R:::layer
|
||||||
|
S:::layer
|
||||||
|
DX:::layer
|
||||||
|
end
|
||||||
|
|
||||||
|
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Views include tree/form/kanban templates and report templates.
|
||||||
|
- Controllers provide website/portal routes when present.
|
||||||
|
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||||
|
- Data XML loads data/demo records; Security defines groups and access.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Refer to Odoo settings for test_discuss_full. Configure related models, access rights, and options as needed.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Controllers
|
||||||
|
|
||||||
|
This module does not define custom HTTP controllers.
|
||||||
13
odoo-bringout-oca-ocb-test_discuss_full/doc/DEPENDENCIES.md
Normal file
13
odoo-bringout-oca-ocb-test_discuss_full/doc/DEPENDENCIES.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
|
||||||
|
- [calendar](../../odoo-bringout-oca-ocb-calendar)
|
||||||
|
- [crm](../../odoo-bringout-oca-ocb-crm)
|
||||||
|
- [crm_livechat](../../odoo-bringout-oca-ocb-crm_livechat)
|
||||||
|
- [hr_holidays](../../odoo-bringout-oca-ocb-hr_holidays)
|
||||||
|
- [im_livechat](../../odoo-bringout-oca-ocb-im_livechat)
|
||||||
|
- [mail](../../odoo-bringout-oca-ocb-mail)
|
||||||
|
- [mail_bot](../../odoo-bringout-oca-ocb-mail_bot)
|
||||||
|
- [note](../../odoo-bringout-oca-ocb-note)
|
||||||
|
- [website_livechat](../../odoo-bringout-oca-ocb-website_livechat)
|
||||||
4
odoo-bringout-oca-ocb-test_discuss_full/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-test_discuss_full/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||||
|
- Q: How to enable? A: Start server with --addon test_discuss_full or install in UI.
|
||||||
7
odoo-bringout-oca-ocb-test_discuss_full/doc/INSTALL.md
Normal file
7
odoo-bringout-oca-ocb-test_discuss_full/doc/INSTALL.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_discuss_full"
|
||||||
|
# or
|
||||||
|
uv pip install odoo-bringout-oca-ocb-test_discuss_full"
|
||||||
|
```
|
||||||
11
odoo-bringout-oca-ocb-test_discuss_full/doc/MODELS.md
Normal file
11
odoo-bringout-oca-ocb-test_discuss_full/doc/MODELS.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Models
|
||||||
|
|
||||||
|
Detected core models and extensions in test_discuss_full.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Classes show model technical names; fields omitted for brevity.
|
||||||
|
- Items listed under _inherit are extensions of existing models.
|
||||||
6
odoo-bringout-oca-ocb-test_discuss_full/doc/OVERVIEW.md
Normal file
6
odoo-bringout-oca-ocb-test_discuss_full/doc/OVERVIEW.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Packaged Odoo addon: test_discuss_full. Provides features documented in upstream Odoo 16 under this addon.
|
||||||
|
|
||||||
|
- Source: OCA/OCB 16.0, addon test_discuss_full
|
||||||
|
- License: LGPL-3
|
||||||
3
odoo-bringout-oca-ocb-test_discuss_full/doc/REPORTS.md
Normal file
3
odoo-bringout-oca-ocb-test_discuss_full/doc/REPORTS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Reports
|
||||||
|
|
||||||
|
This module does not define custom reports.
|
||||||
8
odoo-bringout-oca-ocb-test_discuss_full/doc/SECURITY.md
Normal file
8
odoo-bringout-oca-ocb-test_discuss_full/doc/SECURITY.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Security
|
||||||
|
|
||||||
|
This module does not define custom security rules or access controls beyond Odoo defaults.
|
||||||
|
|
||||||
|
Default Odoo security applies:
|
||||||
|
- Base user access through standard groups
|
||||||
|
- Model access inherited from dependencies
|
||||||
|
- No custom row-level security rules
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
- Ensure Python and Odoo environment matches repo guidance.
|
||||||
|
- Check database connectivity and logs if startup fails.
|
||||||
|
- Validate that dependent addons listed in DEPENDENCIES.md are installed.
|
||||||
7
odoo-bringout-oca-ocb-test_discuss_full/doc/USAGE.md
Normal file
7
odoo-bringout-oca-ocb-test_discuss_full/doc/USAGE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
Start Odoo including this addon (from repo root):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon test_discuss_full
|
||||||
|
```
|
||||||
3
odoo-bringout-oca-ocb-test_discuss_full/doc/WIZARDS.md
Normal file
3
odoo-bringout-oca-ocb-test_discuss_full/doc/WIZARDS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Wizards
|
||||||
|
|
||||||
|
This module does not include UI wizards.
|
||||||
50
odoo-bringout-oca-ocb-test_discuss_full/pyproject.toml
Normal file
50
odoo-bringout-oca-ocb-test_discuss_full/pyproject.toml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
[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."
|
||||||
|
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",
|
||||||
|
"requests>=2.25.1"
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">= 3.11"
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
"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.12",
|
||||||
|
"Topic :: Office/Business",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
homepage = "https://github.com/bringout/0"
|
||||||
|
repository = "https://github.com/bringout/0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.metadata]
|
||||||
|
allow-direct-references = true
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["test_discuss_full"]
|
||||||
|
|
||||||
|
[tool.rye]
|
||||||
|
managed = true
|
||||||
|
dev-dependencies = [
|
||||||
|
"pytest>=8.4.1",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import test_performance
|
||||||
File diff suppressed because it is too large
Load diff
65
odoo-bringout-oca-ocb-test_event_full/README.md
Normal file
65
odoo-bringout-oca-ocb-test_event_full/README.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Test Full Event Flow
|
||||||
|
|
||||||
|
|
||||||
|
This module will test the main event flows of Odoo, both frontend and backend.
|
||||||
|
It installs sale capabilities, front-end flow, eCommerce, questions and
|
||||||
|
automatic lead generation, full Online support, ...
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_event_full
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
- event
|
||||||
|
- event_booth
|
||||||
|
- event_crm
|
||||||
|
- event_crm_sale
|
||||||
|
- event_sale
|
||||||
|
- event_sms
|
||||||
|
- payment_demo
|
||||||
|
- website_event_booth_sale_exhibitor
|
||||||
|
- website_event_crm_questions
|
||||||
|
- website_event_exhibitor
|
||||||
|
- website_event_questions
|
||||||
|
- website_event_meet
|
||||||
|
- website_event_sale
|
||||||
|
- website_event_track
|
||||||
|
- website_event_track_live
|
||||||
|
- website_event_track_quiz
|
||||||
|
|
||||||
|
## Manifest Information
|
||||||
|
|
||||||
|
- **Name**: Test Full Event Flow
|
||||||
|
- **Version**: 1.0
|
||||||
|
- **Category**: Hidden/Tests
|
||||||
|
- **License**: LGPL-3
|
||||||
|
- **Installable**: False
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `test_event_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
|
||||||
32
odoo-bringout-oca-ocb-test_event_full/doc/ARCHITECTURE.md
Normal file
32
odoo-bringout-oca-ocb-test_event_full/doc/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||||
|
V --> C[Controllers]
|
||||||
|
V --> W[Wizards – Transient Models]
|
||||||
|
C --> M[Models and ORM]
|
||||||
|
W --> M
|
||||||
|
M --> R[Reports]
|
||||||
|
DX[Data XML] --> M
|
||||||
|
S[Security – ACLs and Groups] -. enforces .-> M
|
||||||
|
|
||||||
|
subgraph Test_event_full Module - test_event_full
|
||||||
|
direction LR
|
||||||
|
M:::layer
|
||||||
|
W:::layer
|
||||||
|
C:::layer
|
||||||
|
V:::layer
|
||||||
|
R:::layer
|
||||||
|
S:::layer
|
||||||
|
DX:::layer
|
||||||
|
end
|
||||||
|
|
||||||
|
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Views include tree/form/kanban templates and report templates.
|
||||||
|
- Controllers provide website/portal routes when present.
|
||||||
|
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||||
|
- Data XML loads data/demo records; Security defines groups and access.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Refer to Odoo settings for test_event_full. Configure related models, access rights, and options as needed.
|
||||||
3
odoo-bringout-oca-ocb-test_event_full/doc/CONTROLLERS.md
Normal file
3
odoo-bringout-oca-ocb-test_event_full/doc/CONTROLLERS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Controllers
|
||||||
|
|
||||||
|
This module does not define custom HTTP controllers.
|
||||||
20
odoo-bringout-oca-ocb-test_event_full/doc/DEPENDENCIES.md
Normal file
20
odoo-bringout-oca-ocb-test_event_full/doc/DEPENDENCIES.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
|
||||||
|
- [event](../../odoo-bringout-oca-ocb-event)
|
||||||
|
- [event_booth](../../odoo-bringout-oca-ocb-event_booth)
|
||||||
|
- [event_crm](../../odoo-bringout-oca-ocb-event_crm)
|
||||||
|
- [event_crm_sale](../../odoo-bringout-oca-ocb-event_crm_sale)
|
||||||
|
- [event_sale](../../odoo-bringout-oca-ocb-event_sale)
|
||||||
|
- [event_sms](../../odoo-bringout-oca-ocb-event_sms)
|
||||||
|
- [payment_demo](../../odoo-bringout-oca-ocb-payment_demo)
|
||||||
|
- [website_event_booth_sale_exhibitor](../../odoo-bringout-oca-ocb-website_event_booth_sale_exhibitor)
|
||||||
|
- [website_event_crm_questions](../../odoo-bringout-oca-ocb-website_event_crm_questions)
|
||||||
|
- [website_event_exhibitor](../../odoo-bringout-oca-ocb-website_event_exhibitor)
|
||||||
|
- [website_event_questions](../../odoo-bringout-oca-ocb-website_event_questions)
|
||||||
|
- [website_event_meet](../../odoo-bringout-oca-ocb-website_event_meet)
|
||||||
|
- [website_event_sale](../../odoo-bringout-oca-ocb-website_event_sale)
|
||||||
|
- [website_event_track](../../odoo-bringout-oca-ocb-website_event_track)
|
||||||
|
- [website_event_track_live](../../odoo-bringout-oca-ocb-website_event_track_live)
|
||||||
|
- [website_event_track_quiz](../../odoo-bringout-oca-ocb-website_event_track_quiz)
|
||||||
4
odoo-bringout-oca-ocb-test_event_full/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-test_event_full/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||||
|
- Q: How to enable? A: Start server with --addon test_event_full or install in UI.
|
||||||
7
odoo-bringout-oca-ocb-test_event_full/doc/INSTALL.md
Normal file
7
odoo-bringout-oca-ocb-test_event_full/doc/INSTALL.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_event_full"
|
||||||
|
# or
|
||||||
|
uv pip install odoo-bringout-oca-ocb-test_event_full"
|
||||||
|
```
|
||||||
11
odoo-bringout-oca-ocb-test_event_full/doc/MODELS.md
Normal file
11
odoo-bringout-oca-ocb-test_event_full/doc/MODELS.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Models
|
||||||
|
|
||||||
|
Detected core models and extensions in test_event_full.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Classes show model technical names; fields omitted for brevity.
|
||||||
|
- Items listed under _inherit are extensions of existing models.
|
||||||
6
odoo-bringout-oca-ocb-test_event_full/doc/OVERVIEW.md
Normal file
6
odoo-bringout-oca-ocb-test_event_full/doc/OVERVIEW.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Overview
|
||||||
|
|
||||||
|
Packaged Odoo addon: test_event_full. Provides features documented in upstream Odoo 16 under this addon.
|
||||||
|
|
||||||
|
- Source: OCA/OCB 16.0, addon test_event_full
|
||||||
|
- License: LGPL-3
|
||||||
3
odoo-bringout-oca-ocb-test_event_full/doc/REPORTS.md
Normal file
3
odoo-bringout-oca-ocb-test_event_full/doc/REPORTS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Reports
|
||||||
|
|
||||||
|
This module does not define custom reports.
|
||||||
8
odoo-bringout-oca-ocb-test_event_full/doc/SECURITY.md
Normal file
8
odoo-bringout-oca-ocb-test_event_full/doc/SECURITY.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Security
|
||||||
|
|
||||||
|
This module does not define custom security rules or access controls beyond Odoo defaults.
|
||||||
|
|
||||||
|
Default Odoo security applies:
|
||||||
|
- Base user access through standard groups
|
||||||
|
- Model access inherited from dependencies
|
||||||
|
- No custom row-level security rules
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
- Ensure Python and Odoo environment matches repo guidance.
|
||||||
|
- Check database connectivity and logs if startup fails.
|
||||||
|
- Validate that dependent addons listed in DEPENDENCIES.md are installed.
|
||||||
7
odoo-bringout-oca-ocb-test_event_full/doc/USAGE.md
Normal file
7
odoo-bringout-oca-ocb-test_event_full/doc/USAGE.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
Start Odoo including this addon (from repo root):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon test_event_full
|
||||||
|
```
|
||||||
3
odoo-bringout-oca-ocb-test_event_full/doc/WIZARDS.md
Normal file
3
odoo-bringout-oca-ocb-test_event_full/doc/WIZARDS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Wizards
|
||||||
|
|
||||||
|
This module does not include UI wizards.
|
||||||
57
odoo-bringout-oca-ocb-test_event_full/pyproject.toml
Normal file
57
odoo-bringout-oca-ocb-test_event_full/pyproject.toml
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
[project]
|
||||||
|
name = "odoo-bringout-oca-ocb-test_event_full"
|
||||||
|
version = "16.0.0"
|
||||||
|
description = "Test Full Event Flow - Odoo addon"
|
||||||
|
authors = [
|
||||||
|
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"odoo-bringout-oca-ocb-event>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-event_booth>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-event_crm>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-event_crm_sale>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-event_sale>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-event_sms>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-payment_demo>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_booth_sale_exhibitor>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_crm_questions>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_exhibitor>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_questions>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_meet>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_sale>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_track>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_track_live>=16.0.0",
|
||||||
|
"odoo-bringout-oca-ocb-website_event_track_quiz>=16.0.0",
|
||||||
|
"requests>=2.25.1"
|
||||||
|
]
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">= 3.11"
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
"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.12",
|
||||||
|
"Topic :: Office/Business",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
homepage = "https://github.com/bringout/0"
|
||||||
|
repository = "https://github.com/bringout/0"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.metadata]
|
||||||
|
allow-direct-references = true
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["test_event_full"]
|
||||||
|
|
||||||
|
[tool.rye]
|
||||||
|
managed = true
|
||||||
|
dev-dependencies = [
|
||||||
|
"pytest>=8.4.1",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Test Full Event Flow',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Hidden/Tests',
|
||||||
|
'description': """
|
||||||
|
This module will test the main event flows of Odoo, both frontend and backend.
|
||||||
|
It installs sale capabilities, front-end flow, eCommerce, questions and
|
||||||
|
automatic lead generation, full Online support, ...
|
||||||
|
""",
|
||||||
|
'depends': [
|
||||||
|
'event',
|
||||||
|
'event_booth',
|
||||||
|
'event_crm',
|
||||||
|
'event_crm_sale',
|
||||||
|
'event_sale',
|
||||||
|
'event_sms',
|
||||||
|
'payment_demo',
|
||||||
|
'website_event_booth_sale_exhibitor',
|
||||||
|
'website_event_crm_questions',
|
||||||
|
'website_event_exhibitor',
|
||||||
|
'website_event_questions',
|
||||||
|
'website_event_meet',
|
||||||
|
'website_event_sale',
|
||||||
|
'website_event_track',
|
||||||
|
'website_event_track_live',
|
||||||
|
'website_event_track_quiz',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
# 'data/event_type_data.xml', # uncomment to reproduce test tour
|
||||||
|
'data/ir_actions_report_data.xml',
|
||||||
|
'views/event_registration_templates_reports.xml',
|
||||||
|
],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_tests': [
|
||||||
|
'test_event_full/static/**/*',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo><data noupdate="0">
|
||||||
|
|
||||||
|
<record id="event_booth_category_data_1" model="event.booth.category">
|
||||||
|
<field name="description" type="html"><p>Standard</p></field>
|
||||||
|
<field name="name">Standard</field>
|
||||||
|
<field name="product_id" ref="event_booth_sale.product_product_event_booth"/>
|
||||||
|
</record>
|
||||||
|
<record id="event_booth_category_data_2" model="event.booth.category">
|
||||||
|
<field name="description" type="html"><p>Premium</p></field>
|
||||||
|
<field name="name">Premium</field>
|
||||||
|
<field name="product_id" ref="event_booth_sale.product_product_event_booth"/>
|
||||||
|
<field name="price">90</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="event_type_data_full" model="event.type">
|
||||||
|
<field name="auto_confirm" eval="True"/>
|
||||||
|
<field name="default_timezone">Europe/Paris</field>
|
||||||
|
<field name="event_type_booth_ids" eval="[
|
||||||
|
(5, 0),
|
||||||
|
(0, 0, {'booth_category_id': ref('test_event_full.event_booth_category_data_1'),
|
||||||
|
'name': 'Standard Booth',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'booth_category_id': ref('test_event_full.event_booth_category_data_1'),
|
||||||
|
'name': 'Standard Booth 2',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'booth_category_id': ref('test_event_full.event_booth_category_data_2'),
|
||||||
|
'name': 'Premium Booth',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'booth_category_id': ref('test_event_full.event_booth_category_data_2'),
|
||||||
|
'name': 'Premium Booth 2',
|
||||||
|
}
|
||||||
|
)]"/>
|
||||||
|
<field name="event_type_mail_ids" eval="[
|
||||||
|
(5, 0),
|
||||||
|
(0, 0, {'interval_unit': 'now',
|
||||||
|
'interval_type': 'after_sub',
|
||||||
|
'notification_type': 'mail',
|
||||||
|
'template_ref': 'mail.template,%i' % ref('event.event_subscription'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'interval_nbr': 1,
|
||||||
|
'interval_unit': 'days',
|
||||||
|
'interval_type': 'before_event',
|
||||||
|
'notification_type': 'mail',
|
||||||
|
'template_ref': 'mail.template,%i' % ref('event.event_reminder'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'interval_nbr': 1,
|
||||||
|
'interval_unit': 'days',
|
||||||
|
'interval_type': 'after_event',
|
||||||
|
'notification_type': 'sms',
|
||||||
|
'template_ref': 'sms.template,%i' % ref('event_sms.sms_template_data_event_reminder'),
|
||||||
|
}
|
||||||
|
)]"/>
|
||||||
|
<field name="event_type_ticket_ids" eval="[
|
||||||
|
(5, 0),
|
||||||
|
(0, 0, {'description': 'Ticket1 Description',
|
||||||
|
'name': 'Ticket1',
|
||||||
|
'product_id': ref('event_sale.product_product_event'),
|
||||||
|
'seats_max': 10,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'description': 'Ticket2 Description',
|
||||||
|
'name': 'Ticket2',
|
||||||
|
'product_id': ref('event_sale.product_product_event'),
|
||||||
|
'price': 45,
|
||||||
|
}
|
||||||
|
)]"/>
|
||||||
|
<field name="has_seats_limitation" eval="True"/>
|
||||||
|
<field name="name">Test Type</field>
|
||||||
|
<field name="note" type="html"><p>Template note</p></field>
|
||||||
|
<field name="question_ids" eval="[(5, 0)]"/>
|
||||||
|
<field name="seats_max">30</field>
|
||||||
|
<field name="tag_ids" eval="[(5, 0)]"/>
|
||||||
|
<field name="ticket_instructions" type="html"><p>Ticket Instructions</p></field>
|
||||||
|
<field name="website_menu" eval="True"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="event_question_type_full_1" model="event.question">
|
||||||
|
<field name="question_type">simple_choice</field>
|
||||||
|
<field name="once_per_order" eval="False"/>
|
||||||
|
<field name="event_type_id" ref="test_event_full.event_type_data_full"/>
|
||||||
|
<field name="title">Question1</field>
|
||||||
|
</record>
|
||||||
|
<record id="event_question_type_full_1_answer_1" model="event.question.answer">
|
||||||
|
<field name="name">Q1-Answer1</field>
|
||||||
|
<field name="sequence">1</field>
|
||||||
|
<field name="question_id" ref="test_event_full.event_question_type_full_1"/>
|
||||||
|
</record>
|
||||||
|
<record id="event_question_type_full_1_answer_2" model="event.question.answer">
|
||||||
|
<field name="name">Q1-Answer2</field>
|
||||||
|
<field name="sequence">2</field>
|
||||||
|
<field name="question_id" ref="test_event_full.event_question_type_full_1"/>
|
||||||
|
</record>
|
||||||
|
<record id="event_question_type_full_2" model="event.question">
|
||||||
|
<field name="question_type">simple_choice</field>
|
||||||
|
<field name="once_per_order" eval="False"/>
|
||||||
|
<field name="event_type_id" ref="test_event_full.event_type_data_full"/>
|
||||||
|
<field name="title">Question2</field>
|
||||||
|
</record>
|
||||||
|
<record id="event_question_type_full_2_answer_1" model="event.question.answer">
|
||||||
|
<field name="name">Q2-Answer1</field>
|
||||||
|
<field name="sequence">1</field>
|
||||||
|
<field name="question_id" ref="test_event_full.event_question_type_full_2"/>
|
||||||
|
</record>
|
||||||
|
<record id="event_question_type_full_2_answer_2" model="event.question.answer">
|
||||||
|
<field name="name">Q2-Answer2</field>
|
||||||
|
<field name="sequence">2</field>
|
||||||
|
<field name="question_id" ref="test_event_full.event_question_type_full_2"/>
|
||||||
|
</record>
|
||||||
|
<record id="event_question_type_full_3" model="event.question">
|
||||||
|
<field name="question_type">text_box</field>
|
||||||
|
<field name="once_per_order" eval="True"/>
|
||||||
|
<field name="event_type_id" ref="test_event_full.event_type_data_full"/>
|
||||||
|
<field name="title">Question3</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data></odoo>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="event_registration_report_test" model="ir.actions.report">
|
||||||
|
<field name="name">Test Report</field>
|
||||||
|
<field name="model">event.registration</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">test_event_full.event_registration_template_report</field>
|
||||||
|
<field name="report_file">test_event_full.event_registration_template_report</field>
|
||||||
|
<field name="print_report_name">'Badge - %s - %s' % ((object.event_id.name or 'Event').replace('/',''), (object.name or '').replace('/',''))</field>
|
||||||
|
<field name="binding_model_id" ref="event.model_event_registration"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
odoo.define('test_event_full.tour.performance', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var tour = require('web_tour.tour');
|
||||||
|
|
||||||
|
var registerSteps = [{
|
||||||
|
content: "Select 2 units of 'Ticket1' ticket type",
|
||||||
|
trigger: '#o_wevent_tickets_collapse .row.o_wevent_ticket_selector[name="Ticket1"] select',
|
||||||
|
run: 'text 2',
|
||||||
|
}, {
|
||||||
|
content: "Select 1 unit of 'Ticket2' ticket type",
|
||||||
|
trigger: '#o_wevent_tickets_collapse .row.o_wevent_ticket_selector[name="Ticket2"] select',
|
||||||
|
run: 'text 1',
|
||||||
|
}, {
|
||||||
|
content: "Click on 'Register' button",
|
||||||
|
trigger: '#o_wevent_tickets .btn-primary:contains("Register"):not(:disabled)',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: "Fill attendees details",
|
||||||
|
trigger: 'form[id="attendee_registration"] .btn:contains("Continue")',
|
||||||
|
run: function () {
|
||||||
|
$("input[name='1-name']").val("Raoulette Poiluchette");
|
||||||
|
$("input[name='1-phone']").val("0456112233");
|
||||||
|
$("input[name='1-email']").val("raoulette@example.com");
|
||||||
|
$("div[name*='Question1'] select[name*='question_answer-1']").val($("select[name*='question_answer-1'] option:contains('Q1-Answer2')").val());
|
||||||
|
$("div[name*='Question2'] select[name*='question_answer-1']").val($("select[name*='question_answer-1'] option:contains('Q2-Answer1')").val());
|
||||||
|
$("input[name='2-name']").val("Michel Tractopelle");
|
||||||
|
$("input[name='2-phone']").val("0456332211");
|
||||||
|
$("input[name='2-email']").val("michel@example.com");
|
||||||
|
$("div[name*='Question1'] select[name*='question_answer-2']").val($("select[name*='question_answer-2'] option:contains('Q1-Answer1')").val());
|
||||||
|
$("div[name*='Question2'] select[name*='question_answer-2']").val($("select[name*='question_answer-2'] option:contains('Q2-Answer2')").val());
|
||||||
|
$("input[name='3-name']").val("Hubert Boitaclous");
|
||||||
|
$("input[name='3-phone']").val("0456995511");
|
||||||
|
$("input[name='3-email']").val("hubert@example.com");
|
||||||
|
$("div[name*='Question1'] select[name*='question_answer-3']").val($("select[name*='question_answer-3'] option:contains('Q1-Answer2')").val());
|
||||||
|
$("div[name*='Question2'] select[name*='question_answer-3']").val($("select[name*='question_answer-3'] option:contains('Q2-Answer2')").val());
|
||||||
|
$("textarea[name*='question_answer']").text("Random answer from random guy");
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
content: "Validate attendees details",
|
||||||
|
extra_trigger: "input[name='1-name'], input[name='2-name'], input[name='3-name']",
|
||||||
|
trigger: 'button:contains("Continue")',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: "Address filling",
|
||||||
|
trigger: 'select[name="country_id"]',
|
||||||
|
run: function () {
|
||||||
|
$('input[name="name"]').val('Raoulette Poiluchette');
|
||||||
|
$('input[name="phone"]').val('0456112233');
|
||||||
|
$('input[name="email"]').val('raoulette@example.com');
|
||||||
|
$('input[name="street"]').val('Cheesy Crust Street, 42');
|
||||||
|
$('input[name="city"]').val('CheeseCity');
|
||||||
|
$('input[name="zip"]').val('8888');
|
||||||
|
$('#country_id option:eq(1)').attr('selected', true);
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
content: "Next",
|
||||||
|
trigger: '.oe_cart .btn:contains("Next")',
|
||||||
|
}, {
|
||||||
|
content: 'Select Test payment provider',
|
||||||
|
trigger: '.o_payment_option_card:contains("Demo")'
|
||||||
|
}, {
|
||||||
|
content: 'Add card number',
|
||||||
|
trigger: 'input[name="customer_input"]',
|
||||||
|
run: 'text 4242424242424242'
|
||||||
|
}, {
|
||||||
|
content: "Pay now",
|
||||||
|
extra_trigger: "#cart_products:contains(Ticket1):contains(Ticket2)",
|
||||||
|
trigger: 'button:contains(Pay Now)',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: 'Payment is successful',
|
||||||
|
trigger: '.oe_website_sale_tx_status:contains("Your payment has been successfully processed.")',
|
||||||
|
run: function () {}
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
tour.register('wevent_performance_register', {
|
||||||
|
test: true
|
||||||
|
}, [].concat(
|
||||||
|
registerSteps,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
odoo.define('test_event_full.tour.register', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var tour = require('web_tour.tour');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TALKS STEPS
|
||||||
|
*/
|
||||||
|
|
||||||
|
var discoverTalkSteps = function (talkName, fromList, reminderOn, toggleReminder) {
|
||||||
|
var steps;
|
||||||
|
if (fromList) {
|
||||||
|
steps = [{
|
||||||
|
content: 'Go on "' + talkName + '" talk in List',
|
||||||
|
trigger: 'a:contains("' + talkName + '")',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
steps = [{
|
||||||
|
content: 'Click on Live Track',
|
||||||
|
trigger: 'article span:contains("' + talkName + '")',
|
||||||
|
run: 'click',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
steps = steps.concat([{
|
||||||
|
content: `Check we are on the "${talkName}" talk page`,
|
||||||
|
trigger: 'div.o_wesession_track_main',
|
||||||
|
run: function () {}, // it's a check
|
||||||
|
}]);
|
||||||
|
|
||||||
|
if (reminderOn) {
|
||||||
|
steps = steps.concat([{
|
||||||
|
content: `Check Favorite for ${talkName} was already on`,
|
||||||
|
trigger: 'div.o_wetrack_js_reminder i.fa-bell',
|
||||||
|
extra_trigger: 'span.o_wetrack_js_reminder_text:contains("Favorite On")',
|
||||||
|
run: function () {}, // it's a check
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
steps = steps.concat([{
|
||||||
|
content: `Check Favorite for ${talkName} was off`,
|
||||||
|
trigger: 'span.o_wetrack_js_reminder_text:contains("Set Favorite")',
|
||||||
|
run: function () {}, // it's a check
|
||||||
|
}]);
|
||||||
|
if (toggleReminder) {
|
||||||
|
steps = steps.concat([{
|
||||||
|
content: "Set Favorite",
|
||||||
|
trigger: 'span.o_wetrack_js_reminder_text',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: `Check Favorite for ${talkName} is now on`,
|
||||||
|
trigger: 'div.o_wetrack_js_reminder i.fa-bell',
|
||||||
|
extra_trigger: 'span.o_wetrack_js_reminder_text:contains("Favorite On")',
|
||||||
|
run: function () {}, // it's a check
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return steps;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ROOMS STEPS
|
||||||
|
*/
|
||||||
|
|
||||||
|
var discoverRoomSteps = function (roomName) {
|
||||||
|
var steps = [{
|
||||||
|
content: 'Go on "' + roomName + '" room in List',
|
||||||
|
trigger: 'a.o_wevent_meeting_room_card h4:contains("' + roomName + '")',
|
||||||
|
run: function() {
|
||||||
|
// can't click on it, it will try to launch Jitsi and fail on chrome headless
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
return steps;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REGISTER STEPS
|
||||||
|
*/
|
||||||
|
|
||||||
|
var registerSteps = [{
|
||||||
|
content: 'Go on Register',
|
||||||
|
trigger: 'a.btn-primary:contains("Register")',
|
||||||
|
}, {
|
||||||
|
content: "Select 2 units of 'Standard' ticket type",
|
||||||
|
trigger: '#o_wevent_tickets_collapse .row:has(.o_wevent_registration_multi_select:contains("Free")) select',
|
||||||
|
run: 'text 2',
|
||||||
|
}, {
|
||||||
|
content: "Click on 'Register' button",
|
||||||
|
trigger: '#o_wevent_tickets .btn-primary:contains("Register"):not(:disabled)',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: "Fill attendees details",
|
||||||
|
trigger: 'form[id="attendee_registration"] .btn:contains("Continue")',
|
||||||
|
run: function () {
|
||||||
|
$("input[name='1-name']").val("Raoulette Poiluchette");
|
||||||
|
$("input[name='1-phone']").val("0456112233");
|
||||||
|
$("input[name='1-email']").val("raoulette@example.com");
|
||||||
|
$("select[name*='question_answer-1']").val($("select[name*='question_answer-1'] option:contains('Consumers')").val());
|
||||||
|
$("input[name='2-name']").val("Michel Tractopelle");
|
||||||
|
$("input[name='2-phone']").val("0456332211");
|
||||||
|
$("input[name='2-email']").val("michel@example.com");
|
||||||
|
$("select[name*='question_answer-2']").val($("select[name*='question_answer-1'] option:contains('Research')").val());
|
||||||
|
$("textarea[name*='question_answer']").text("An unicorn told me about you. I ate it afterwards.");
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
content: "Validate attendees details",
|
||||||
|
extra_trigger: "input[name='1-name'], input[name='2-name'], input[name='3-name']",
|
||||||
|
trigger: 'button:contains("Continue")',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
trigger: 'div.o_wereg_confirmed_attendees span:contains("Raoulette Poiluchette")',
|
||||||
|
run: function () {} // check
|
||||||
|
}, {
|
||||||
|
trigger: 'div.o_wereg_confirmed_attendees span:contains("Michel Tractopelle")',
|
||||||
|
run: function () {} // check
|
||||||
|
}, {
|
||||||
|
content: "Click on 'register favorites talks' button",
|
||||||
|
trigger: 'a:contains("register to your favorites talks now")',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
trigger: 'h1:contains("Book your talks")',
|
||||||
|
run: function() {},
|
||||||
|
}];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MAIN STEPS
|
||||||
|
*/
|
||||||
|
|
||||||
|
var initTourSteps = function (eventName) {
|
||||||
|
return [{
|
||||||
|
content: 'Go on "' + eventName + '" page',
|
||||||
|
trigger: 'a[href*="/event"]:contains("' + eventName + '"):first',
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
var browseTalksSteps = [{
|
||||||
|
content: 'Browse Talks',
|
||||||
|
trigger: 'a:contains("Talks")',
|
||||||
|
}, {
|
||||||
|
content: 'Check we are on the talk list page',
|
||||||
|
trigger: 'h1:contains("Book your talks")',
|
||||||
|
run: function () {} // check
|
||||||
|
}];
|
||||||
|
|
||||||
|
var browseMeetSteps = [{
|
||||||
|
content: 'Browse Meet',
|
||||||
|
trigger: 'a:contains("Community")',
|
||||||
|
}, {
|
||||||
|
content: 'Check we are on the community page',
|
||||||
|
trigger: 'span:contains("Join a room")',
|
||||||
|
run: function () {} // check
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
tour.register('wevent_register', {
|
||||||
|
url: '/event',
|
||||||
|
test: true
|
||||||
|
}, [].concat(
|
||||||
|
initTourSteps('Online Reveal'),
|
||||||
|
browseTalksSteps,
|
||||||
|
discoverTalkSteps('What This Event Is All About', true, true),
|
||||||
|
browseTalksSteps,
|
||||||
|
discoverTalkSteps('Live Testimonial', false, false, false),
|
||||||
|
browseTalksSteps,
|
||||||
|
discoverTalkSteps('Our Last Day Together !', true, false, true),
|
||||||
|
browseMeetSteps,
|
||||||
|
discoverRoomSteps('Best wood for furniture'),
|
||||||
|
registerSteps,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import common
|
||||||
|
from . import test_event_crm
|
||||||
|
from . import test_event_event
|
||||||
|
from . import test_event_mail
|
||||||
|
from . import test_event_security
|
||||||
|
from . import test_performance
|
||||||
|
from . import test_wevent_register
|
||||||
|
from . import test_event_discount
|
||||||
|
|
@ -0,0 +1,462 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, time
|
||||||
|
|
||||||
|
from odoo.addons.base.tests.common import HttpCaseWithUserDemo, HttpCaseWithUserPortal
|
||||||
|
from odoo.addons.event_crm.tests.common import EventCrmCase
|
||||||
|
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||||
|
from odoo.addons.sales_team.tests.common import TestSalesCommon
|
||||||
|
from odoo.addons.website.tests.test_website_visitor import MockVisitor
|
||||||
|
|
||||||
|
|
||||||
|
class TestEventFullCommon(EventCrmCase, TestSalesCommon, MockVisitor):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestEventFullCommon, cls).setUpClass()
|
||||||
|
cls._init_mail_gateway()
|
||||||
|
|
||||||
|
# Context data: dates
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Mock dates to have reproducible computed fields based on time
|
||||||
|
cls.reference_now = datetime(2021, 12, 1, 10, 0, 0)
|
||||||
|
cls.reference_today = datetime(2021, 12, 1)
|
||||||
|
|
||||||
|
# Users and contacts
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
cls.admin_user = cls.env.ref('base.user_admin')
|
||||||
|
cls.admin_user.write({
|
||||||
|
'country_id': cls.env.ref('base.be').id,
|
||||||
|
'login': 'admin',
|
||||||
|
'notification_type': 'inbox',
|
||||||
|
})
|
||||||
|
cls.company_admin = cls.admin_user.company_id
|
||||||
|
# set country in order to format Belgian numbers
|
||||||
|
cls.company_admin.write({
|
||||||
|
'country_id': cls.env.ref('base.be').id,
|
||||||
|
})
|
||||||
|
cls.event_user = mail_new_test_user(
|
||||||
|
cls.env,
|
||||||
|
company_id=cls.company_admin.id,
|
||||||
|
company_ids=[(4, cls.company_admin.id)],
|
||||||
|
country_id=cls.env.ref('base.be').id,
|
||||||
|
groups='base.group_user,base.group_partner_manager,event.group_event_user',
|
||||||
|
email='e.e@example.com',
|
||||||
|
login='event_user',
|
||||||
|
name='Ernest Employee',
|
||||||
|
notification_type='inbox',
|
||||||
|
signature='--\nErnest',
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.customer = cls.env['res.partner'].create({
|
||||||
|
'country_id': cls.env.ref('base.be').id,
|
||||||
|
'email': 'customer.test@example.com',
|
||||||
|
'name': 'Test Customer',
|
||||||
|
'mobile': '0456123456',
|
||||||
|
'phone': '0456123456',
|
||||||
|
})
|
||||||
|
# make a SO for a customer, selling some tickets
|
||||||
|
cls.customer_so = cls.env['sale.order'].with_user(cls.user_sales_salesman).create({
|
||||||
|
'partner_id': cls.event_customer.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Side records for event main records
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
cls.ticket_product = cls.env['product.product'].create({
|
||||||
|
'description_sale': 'Ticket Product Description',
|
||||||
|
'detailed_type': 'event',
|
||||||
|
'list_price': 10,
|
||||||
|
'name': 'Test Registration Product',
|
||||||
|
'standard_price': 30.0,
|
||||||
|
})
|
||||||
|
cls.booth_product = cls.env['product.product'].create({
|
||||||
|
'description_sale': 'Booth Product Description',
|
||||||
|
'detailed_type': 'event_booth',
|
||||||
|
'list_price': 20,
|
||||||
|
'name': 'Test Booth Product',
|
||||||
|
'standard_price': 60.0,
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.tag_categories = cls.env['event.tag.category'].create([
|
||||||
|
{'is_published': True, 'name': 'Published Category'},
|
||||||
|
{'is_published': False, 'name': 'Unpublished Category'},
|
||||||
|
])
|
||||||
|
cls.tags = cls.env['event.tag'].create([
|
||||||
|
{'category_id': cls.tag_categories[0].id, 'name': 'PubTag1'},
|
||||||
|
{'category_id': cls.tag_categories[0].id, 'color': 0, 'name': 'PubTag2'},
|
||||||
|
{'category_id': cls.tag_categories[1].id, 'name': 'UnpubTag1'},
|
||||||
|
])
|
||||||
|
|
||||||
|
cls.event_booth_categories = cls.env['event.booth.category'].create([
|
||||||
|
{'description': '<p>Standard</p>',
|
||||||
|
'name': 'Standard',
|
||||||
|
'product_id': cls.booth_product.id,
|
||||||
|
},
|
||||||
|
{'description': '<p>Premium</p>',
|
||||||
|
'name': 'Premium',
|
||||||
|
'product_id': cls.booth_product.id,
|
||||||
|
'price': 90,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
cls.sponsor_types = cls.env['event.sponsor.type'].create([
|
||||||
|
{'name': 'GigaTop',
|
||||||
|
'sequence': 1,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
cls.sponsor_partners = cls.env['res.partner'].create([
|
||||||
|
{'country_id': cls.env.ref('base.be').id,
|
||||||
|
'email': 'event.sponsor@example.com',
|
||||||
|
'name': 'EventSponsor',
|
||||||
|
'phone': '04856112233',
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
# Event type
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
test_registration_report = cls.env.ref('test_event_full.event_registration_report_test')
|
||||||
|
subscription_template = cls.env.ref('event.event_subscription')
|
||||||
|
subscription_template.write({'report_template': test_registration_report.id})
|
||||||
|
cls.test_event_type = cls.env['event.type'].create({
|
||||||
|
'auto_confirm': True,
|
||||||
|
'default_timezone': 'Europe/Paris',
|
||||||
|
'event_type_booth_ids': [
|
||||||
|
(0, 0, {'booth_category_id': cls.event_booth_categories[0].id,
|
||||||
|
'name': 'Standard Booth',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'booth_category_id': cls.event_booth_categories[0].id,
|
||||||
|
'name': 'Standard Booth 2',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'booth_category_id': cls.event_booth_categories[1].id,
|
||||||
|
'name': 'Premium Booth',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'booth_category_id': cls.event_booth_categories[1].id,
|
||||||
|
'name': 'Premium Booth 2',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'event_type_mail_ids': [
|
||||||
|
(0, 0, {'interval_unit': 'now', # right at subscription
|
||||||
|
'interval_type': 'after_sub',
|
||||||
|
'notification_type': 'mail',
|
||||||
|
'template_ref': 'mail.template,%i' % subscription_template.id,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'interval_nbr': 1, # 1 days before event
|
||||||
|
'interval_unit': 'days',
|
||||||
|
'interval_type': 'before_event',
|
||||||
|
'notification_type': 'mail',
|
||||||
|
'template_ref': 'mail.template,%i' % cls.env['ir.model.data']._xmlid_to_res_id('event.event_reminder'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'interval_nbr': 1, # 1 days after event
|
||||||
|
'interval_unit': 'days',
|
||||||
|
'interval_type': 'after_event',
|
||||||
|
'notification_type': 'sms',
|
||||||
|
'template_ref': 'sms.template,%i' % cls.env['ir.model.data']._xmlid_to_res_id('event_sms.sms_template_data_event_reminder'),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'event_type_ticket_ids': [
|
||||||
|
(0, 0, {'description': 'Ticket1 Description',
|
||||||
|
'name': 'Ticket1',
|
||||||
|
'product_id': cls.ticket_product.id,
|
||||||
|
'seats_max': 10,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'description': 'Ticket2 Description',
|
||||||
|
'name': 'Ticket2',
|
||||||
|
'product_id': cls.ticket_product.id,
|
||||||
|
'price': 45,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
'has_seats_limitation': True,
|
||||||
|
'name': 'Test Type',
|
||||||
|
'note': '<p>Template note</p>',
|
||||||
|
'question_ids': [
|
||||||
|
(0, 0, {'answer_ids':
|
||||||
|
[(0, 0, {'name': 'Q1-Answer1'}),
|
||||||
|
(0, 0, {'name': 'Q1-Answer2'}),
|
||||||
|
],
|
||||||
|
'question_type': 'simple_choice',
|
||||||
|
'once_per_order': False,
|
||||||
|
'title': 'Question1',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'answer_ids':
|
||||||
|
[(0, 0, {'name': 'Q2-Answer1'}),
|
||||||
|
(0, 0, {'name': 'Q2-Answer2'}),
|
||||||
|
],
|
||||||
|
'question_type': 'simple_choice',
|
||||||
|
'once_per_order': False,
|
||||||
|
'title': 'Question2',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(0, 0, {'question_type': 'text_box',
|
||||||
|
'once_per_order': True,
|
||||||
|
'title': 'Question3',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
],
|
||||||
|
'seats_max': 30,
|
||||||
|
'tag_ids': [(4, tag.id) for tag in cls.tags],
|
||||||
|
'ticket_instructions': '<p>Ticket Instructions</p>',
|
||||||
|
'website_menu': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Stages
|
||||||
|
cls.stage_def = cls.env['event.stage'].create({
|
||||||
|
'name': 'First Stage',
|
||||||
|
'sequence': 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Event data
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
cls.event_base_vals = {
|
||||||
|
'name': 'Test Event',
|
||||||
|
'date_begin': cls.reference_now + timedelta(days=1),
|
||||||
|
'date_end': cls.reference_now + timedelta(days=5),
|
||||||
|
'is_published': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.test_event = cls.env['event.event'].create({
|
||||||
|
'name': 'Test Event',
|
||||||
|
'auto_confirm': True,
|
||||||
|
'date_begin': datetime.now() + timedelta(days=1),
|
||||||
|
'date_end': datetime.now() + timedelta(days=5),
|
||||||
|
'date_tz': 'Europe/Brussels',
|
||||||
|
'event_type_id': cls.test_event_type.id,
|
||||||
|
'is_published': True,
|
||||||
|
})
|
||||||
|
# update post-synchronize data
|
||||||
|
ticket_1 = cls.test_event.event_ticket_ids.filtered(lambda t: t.name == 'Ticket1')
|
||||||
|
ticket_2 = cls.test_event.event_ticket_ids.filtered(lambda t: t.name == 'Ticket2')
|
||||||
|
ticket_1.start_sale_datetime = cls.reference_now + timedelta(hours=1)
|
||||||
|
ticket_2.start_sale_datetime = cls.reference_now + timedelta(hours=2)
|
||||||
|
|
||||||
|
# Website data
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
cls.website = cls.env['website'].search([
|
||||||
|
('company_id', '=', cls.company_admin.id)
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
cls.customer_data = [
|
||||||
|
{'email': 'customer.email.%02d@test.example.com' % x,
|
||||||
|
'name': 'My Customer %02d' % x,
|
||||||
|
'mobile': '04569999%02d' % x,
|
||||||
|
'partner_id': False,
|
||||||
|
'phone': '04560000%02d' % x,
|
||||||
|
} for x in range(0, 10)
|
||||||
|
]
|
||||||
|
cls.website_customer_data = [
|
||||||
|
{'email': 'website.email.%02d@test.example.com' % x,
|
||||||
|
'name': 'My Customer %02d' % x,
|
||||||
|
'mobile': '04569999%02d' % x,
|
||||||
|
'partner_id': cls.env.ref('base.public_partner').id,
|
||||||
|
'phone': '04560000%02d' % x,
|
||||||
|
'registration_answer_ids': [
|
||||||
|
(0, 0, {
|
||||||
|
'question_id': cls.test_event.question_ids[0].id,
|
||||||
|
'value_answer_id': cls.test_event.question_ids[0].answer_ids[(x % 2)].id,
|
||||||
|
}), (0, 0, {
|
||||||
|
'question_id': cls.test_event.question_ids[1].id,
|
||||||
|
'value_answer_id': cls.test_event.question_ids[1].answer_ids[(x % 2)].id,
|
||||||
|
}), (0, 0, {
|
||||||
|
'question_id': cls.test_event.question_ids[2].id,
|
||||||
|
'value_text_box': 'CustomerAnswer%s' % x,
|
||||||
|
})
|
||||||
|
],
|
||||||
|
} for x in range(0, 10)
|
||||||
|
]
|
||||||
|
cls.partners = cls.env['res.partner'].create([
|
||||||
|
{'email': 'partner.email.%02d@test.example.com' % x,
|
||||||
|
'name': 'PartnerCustomer',
|
||||||
|
'mobile': '04569999%02d' % x,
|
||||||
|
'phone': '04560000%02d' % x,
|
||||||
|
} for x in range(0, 10)
|
||||||
|
])
|
||||||
|
|
||||||
|
def assertLeadConvertion(self, rule, registrations, partner=None, **expected):
|
||||||
|
super(TestEventFullCommon, self).assertLeadConvertion(rule, registrations, partner=partner, **expected)
|
||||||
|
lead = self.env['crm.lead'].sudo().search([
|
||||||
|
('registration_ids', 'in', registrations.ids),
|
||||||
|
('event_lead_rule_id', '=', rule.id)
|
||||||
|
])
|
||||||
|
|
||||||
|
for registration in registrations:
|
||||||
|
if not registration.registration_answer_ids:
|
||||||
|
continue
|
||||||
|
for answer in registration.registration_answer_ids:
|
||||||
|
self.assertIn(answer.question_id.title, lead.description)
|
||||||
|
if answer.question_type == 'simple_choice':
|
||||||
|
self.assertIn(answer.value_answer_id.name, lead.description)
|
||||||
|
else:
|
||||||
|
self.assertIn(answer.value_text_box, lead.description) # better: check multi line
|
||||||
|
|
||||||
|
|
||||||
|
class TestWEventCommon(HttpCaseWithUserDemo, HttpCaseWithUserPortal, MockVisitor):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestWEventCommon, self).setUp()
|
||||||
|
|
||||||
|
# Context data: dates
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Mock dates to have reproducible computed fields based on time
|
||||||
|
self.reference_now = datetime(2021, 12, 1, 10, 0, 0)
|
||||||
|
self.reference_today = datetime(2021, 12, 1)
|
||||||
|
|
||||||
|
self.event_product = self.env['product.product'].create({
|
||||||
|
'name': 'Test Event Registration',
|
||||||
|
'default_code': 'EVENT_REG',
|
||||||
|
'description_sale': 'Mighty Description',
|
||||||
|
'list_price': 10,
|
||||||
|
'standard_price': 30.0,
|
||||||
|
'detailed_type': 'event',
|
||||||
|
})
|
||||||
|
|
||||||
|
self.event_tag_category_1 = self.env['event.tag.category'].create({
|
||||||
|
'name': 'Type',
|
||||||
|
'sequence': 2,
|
||||||
|
})
|
||||||
|
self.event_tag_category_1_tag_1 = self.env['event.tag'].create({
|
||||||
|
'name': 'Online',
|
||||||
|
'sequence': 10,
|
||||||
|
'category_id': self.event_tag_category_1.id,
|
||||||
|
'color': 8,
|
||||||
|
})
|
||||||
|
self.env['event.event'].search(
|
||||||
|
[('name', 'like', '%Online Reveal%')]
|
||||||
|
).write(
|
||||||
|
{'name': 'Do not click on me'}
|
||||||
|
)
|
||||||
|
self.event = self.env['event.event'].create({
|
||||||
|
'name': 'Online Reveal TestEvent',
|
||||||
|
'auto_confirm': True,
|
||||||
|
'stage_id': self.env.ref('event.event_stage_booked').id,
|
||||||
|
'address_id': False,
|
||||||
|
'user_id': self.user_demo.id,
|
||||||
|
'tag_ids': [(4, self.event_tag_category_1_tag_1.id)],
|
||||||
|
# event if 8-18 in Europe/Brussels (DST) (first day: begins at 7, last day: ends at 17)
|
||||||
|
'date_tz': 'Europe/Brussels',
|
||||||
|
'date_begin': datetime.combine(self.reference_now, time(5, 0)) - timedelta(days=1),
|
||||||
|
'date_end': datetime.combine(self.reference_now, time(15, 0)) + timedelta(days=1),
|
||||||
|
# ticket informations
|
||||||
|
'event_ticket_ids': [
|
||||||
|
(0, 0, {
|
||||||
|
'name': 'Standard',
|
||||||
|
'product_id': self.event_product.id,
|
||||||
|
'price': 0,
|
||||||
|
}), (0, 0, {
|
||||||
|
'name': 'VIP',
|
||||||
|
'product_id': self.event_product.id,
|
||||||
|
'seats_max': 10,
|
||||||
|
})
|
||||||
|
],
|
||||||
|
# activate menus
|
||||||
|
'is_published': True,
|
||||||
|
'website_menu': True,
|
||||||
|
'website_track': True,
|
||||||
|
'website_track_proposal': True,
|
||||||
|
'exhibitor_menu': True,
|
||||||
|
'community_menu': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.event_customer = self.env['res.partner'].create({
|
||||||
|
'name': 'Constantin Customer',
|
||||||
|
'email': 'constantin@test.example.com',
|
||||||
|
'country_id': self.env.ref('base.be').id,
|
||||||
|
'phone': '0485112233',
|
||||||
|
'mobile': False,
|
||||||
|
})
|
||||||
|
self.event_speaker = self.env['res.partner'].create({
|
||||||
|
'name': 'Brandon Freeman',
|
||||||
|
'email': 'brandon.freeman55@example.com',
|
||||||
|
'phone': '(355)-687-3262',
|
||||||
|
})
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# QUESTIONS
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
self.event_question_1 = self.env['event.question'].create({
|
||||||
|
'title': 'Which field are you working in',
|
||||||
|
'question_type': 'simple_choice',
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'once_per_order': False,
|
||||||
|
'answer_ids': [
|
||||||
|
(0, 0, {'name': 'Consumers'}),
|
||||||
|
(0, 0, {'name': 'Sales'}),
|
||||||
|
(0, 0, {'name': 'Research'}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
self.event_question_2 = self.env['event.question'].create({
|
||||||
|
'title': 'How did you hear about us ?',
|
||||||
|
'question_type': 'text_box',
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'once_per_order': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# TRACKS
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
self.track_0 = self.env['event.track'].create({
|
||||||
|
'name': 'What This Event Is All About',
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'stage_id': self.env.ref('website_event_track.event_track_stage3').id,
|
||||||
|
'date': self.reference_now + timedelta(hours=1),
|
||||||
|
'duration': 2,
|
||||||
|
'is_published': True,
|
||||||
|
'wishlisted_by_default': True,
|
||||||
|
'user_id': self.user_admin.id,
|
||||||
|
'partner_id': self.event_speaker.id,
|
||||||
|
})
|
||||||
|
self.track_1 = self.env['event.track'].create({
|
||||||
|
'name': 'Live Testimonial',
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'stage_id': self.env.ref('website_event_track.event_track_stage3').id,
|
||||||
|
'date': self.reference_now - timedelta(minutes=30),
|
||||||
|
'duration': 0.75,
|
||||||
|
'is_published': True,
|
||||||
|
'user_id': self.user_admin.id,
|
||||||
|
'partner_id': self.event_speaker.id,
|
||||||
|
})
|
||||||
|
self.track_2 = self.env['event.track'].create({
|
||||||
|
'name': 'Our Last Day Together !',
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'stage_id': self.env.ref('website_event_track.event_track_stage3').id,
|
||||||
|
'date': self.reference_now + timedelta(days=1),
|
||||||
|
'duration': 0.75,
|
||||||
|
'is_published': True,
|
||||||
|
'user_id': self.user_admin.id,
|
||||||
|
'partner_id': self.event_speaker.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# MEETING ROOMS
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
self.env['event.meeting.room'].create({
|
||||||
|
'name': 'Best wood for furniture',
|
||||||
|
'summary': 'Let\'s talk about wood types for furniture',
|
||||||
|
'target_audience': 'wood expert(s)',
|
||||||
|
'is_pinned': True,
|
||||||
|
'website_published': True,
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'room_lang_id': self.env.ref('base.lang_en').id,
|
||||||
|
'room_max_capacity': '12',
|
||||||
|
'room_participant_count': 9,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.env.flush_all()
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestEventFullCommon
|
||||||
|
from odoo.tests import users
|
||||||
|
|
||||||
|
|
||||||
|
class TestEventCrm(TestEventFullCommon):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestEventCrm, cls).setUpClass()
|
||||||
|
|
||||||
|
cls.TICKET1_COUNT, cls.TICKET2_COUNT = 3, 1
|
||||||
|
ticket1 = cls.test_event.event_ticket_ids[0]
|
||||||
|
ticket2 = cls.test_event.event_ticket_ids[1]
|
||||||
|
|
||||||
|
(cls.test_rule_attendee + cls.test_rule_order).write({'event_id': cls.test_event.id})
|
||||||
|
|
||||||
|
# PREPARE SO DATA
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
# adding some tickets to SO
|
||||||
|
cls.customer_so.write({
|
||||||
|
'order_line': [
|
||||||
|
(0, 0, {
|
||||||
|
'event_id': cls.test_event.id,
|
||||||
|
'event_ticket_id': ticket1.id,
|
||||||
|
'product_id': ticket1.product_id.id,
|
||||||
|
'product_uom_qty': cls.TICKET1_COUNT,
|
||||||
|
}), (0, 0, {
|
||||||
|
'event_id': cls.test_event.id,
|
||||||
|
'event_ticket_id': ticket2.id,
|
||||||
|
'product_id': ticket2.product_id.id,
|
||||||
|
'product_uom_qty': cls.TICKET2_COUNT,
|
||||||
|
'price_unit': 50,
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
@users('user_sales_salesman')
|
||||||
|
def test_event_crm_sale_customer(self):
|
||||||
|
""" Test a SO with a real customer set on it, check partner propagation
|
||||||
|
as well as group-based lead update. """
|
||||||
|
customer_so = self.env['sale.order'].browse(self.customer_so.id)
|
||||||
|
|
||||||
|
# adding some tickets to SO
|
||||||
|
t1_reg_vals = [
|
||||||
|
dict(customer_data,
|
||||||
|
partner_id=customer_so.partner_id.id,
|
||||||
|
sale_order_line_id=customer_so.order_line[0].id)
|
||||||
|
for customer_data in self.website_customer_data[:self.TICKET1_COUNT]
|
||||||
|
]
|
||||||
|
t1_registrations = self.env['event.registration'].create(t1_reg_vals)
|
||||||
|
|
||||||
|
# check effect: registrations, leads
|
||||||
|
self.assertEqual(self.test_event.registration_ids, t1_registrations)
|
||||||
|
self.assertEqual(len(self.test_rule_order.lead_ids), 1)
|
||||||
|
self.assertEqual(self.test_rule_order_done.lead_ids, self.env['crm.lead'])
|
||||||
|
# check lead converted based on registrations
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, t1_registrations, partner=customer_so.partner_id)
|
||||||
|
|
||||||
|
# SO is confirmed -> missing registrations should be automatically added
|
||||||
|
# and added to the lead as part of the same group
|
||||||
|
customer_so.action_confirm()
|
||||||
|
self.assertEqual(customer_so.state, 'sale')
|
||||||
|
self.assertEqual(len(self.test_event.registration_ids), self.TICKET1_COUNT + self.TICKET2_COUNT)
|
||||||
|
self.assertEqual(len(self.test_rule_order.lead_ids), 1) # no new lead created
|
||||||
|
self.assertEqual(self.test_rule_order_done.lead_ids, self.env['crm.lead']) # this one still not triggered
|
||||||
|
|
||||||
|
# check existing lead has been updated with new registrations
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, self.test_event.registration_ids, partner=customer_so.partner_id)
|
||||||
|
|
||||||
|
# Confirm registrations -> trigger the "DONE" rule, one new lead linked to all
|
||||||
|
# event registrations created in this test as all belong to the same SO
|
||||||
|
self.test_event.registration_ids.write({'state': 'done'})
|
||||||
|
self.assertLeadConvertion(self.test_rule_order_done, self.test_event.registration_ids, partner=customer_so.partner_id)
|
||||||
|
|
||||||
|
@users('user_sales_salesman')
|
||||||
|
def test_event_crm_sale_mixed_group(self):
|
||||||
|
""" Test a mixed sale order line creation. This should not happen in a customer
|
||||||
|
use case but should be supported by the code. """
|
||||||
|
public_partner = self.env.ref('base.public_partner')
|
||||||
|
public_so = self.env['sale.order'].create({
|
||||||
|
'partner_id': public_partner.id,
|
||||||
|
'order_line': [
|
||||||
|
(0, 0, {
|
||||||
|
'event_id': self.test_event.id,
|
||||||
|
'event_ticket_id': self.test_event.event_ticket_ids[0].id,
|
||||||
|
'product_id': self.test_event.event_ticket_ids[0].product_id.id,
|
||||||
|
'product_uom_qty': 2,
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
customer_so = self.env['sale.order'].browse(self.customer_so.id)
|
||||||
|
|
||||||
|
# make a multi-SO create
|
||||||
|
mixed_reg_vals = [
|
||||||
|
dict(self.website_customer_data[0],
|
||||||
|
partner_id=customer_so.partner_id.id,
|
||||||
|
sale_order_line_id=customer_so.order_line[0].id),
|
||||||
|
dict(self.website_customer_data[1],
|
||||||
|
partner_id=customer_so.partner_id.id,
|
||||||
|
sale_order_line_id=customer_so.order_line[0].id),
|
||||||
|
dict(self.website_customer_data[2],
|
||||||
|
partner_id=public_so.partner_id.id,
|
||||||
|
sale_order_line_id=public_so.order_line[0].id),
|
||||||
|
dict(self.website_customer_data[3],
|
||||||
|
partner_id=public_so.partner_id.id,
|
||||||
|
sale_order_line_id=public_so.order_line[0].id),
|
||||||
|
]
|
||||||
|
self.env['event.registration'].create(mixed_reg_vals)
|
||||||
|
|
||||||
|
public_regs = self.test_event.registration_ids.filtered(lambda reg: reg.sale_order_id == public_so)
|
||||||
|
self.assertEqual(len(public_regs), 2)
|
||||||
|
customer_regs = self.test_event.registration_ids.filtered(lambda reg: reg.sale_order_id == customer_so)
|
||||||
|
self.assertEqual(len(customer_regs), 2)
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, public_regs, partner=None)
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, customer_regs, partner=customer_so.partner_id)
|
||||||
|
|
||||||
|
@users('user_sales_salesman')
|
||||||
|
def test_event_crm_sale_public(self):
|
||||||
|
""" Test a SO with a public partner on it, then updated when SO is confirmed.
|
||||||
|
This somehow simulates a simplified website_event_sale flow. """
|
||||||
|
public_partner = self.env.ref('base.public_partner')
|
||||||
|
customer_so = self.env['sale.order'].browse(self.customer_so.id)
|
||||||
|
customer_so.write({
|
||||||
|
'partner_id': public_partner.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
# adding some tickets to SO
|
||||||
|
t1_reg_vals = [
|
||||||
|
dict(customer_data,
|
||||||
|
partner_id=public_partner.id,
|
||||||
|
sale_order_line_id=customer_so.order_line[0].id)
|
||||||
|
for customer_data in self.website_customer_data[:self.TICKET1_COUNT]
|
||||||
|
]
|
||||||
|
t1_registrations = self.env['event.registration'].create(t1_reg_vals)
|
||||||
|
self.assertEqual(self.test_event.registration_ids, t1_registrations)
|
||||||
|
|
||||||
|
# check lead converted based on registrations
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, t1_registrations, partner=None)
|
||||||
|
|
||||||
|
# SO is confirmed -> missing registrations should be automatically added
|
||||||
|
# BUT as public user -> no email -> not taken into account by rule
|
||||||
|
customer_so.action_confirm()
|
||||||
|
self.assertEqual(customer_so.state, 'sale')
|
||||||
|
self.assertEqual(len(self.test_event.registration_ids), self.TICKET1_COUNT + self.TICKET2_COUNT)
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, t1_registrations, partner=None)
|
||||||
|
|
||||||
|
# SO has a customer set -> main contact of lead is updated accordingly
|
||||||
|
customer_so.write({'partner_id': self.event_customer.id})
|
||||||
|
self.assertLeadConvertion(self.test_rule_order, t1_registrations, partner=self.event_customer)
|
||||||
|
|
||||||
|
def test_event_update_lead(self):
|
||||||
|
"""Make sure that we update leads without issues when question's answer is added to an event attendee."""
|
||||||
|
self.env['event.lead.rule'].search([]).write({'active': False})
|
||||||
|
self.env['event.lead.rule'].create({
|
||||||
|
'name': 'test_event_lead_rule',
|
||||||
|
'lead_creation_basis': 'attendee',
|
||||||
|
'lead_creation_trigger': 'create',
|
||||||
|
'event_registration_filter': [['partner_id', '!=', False]],
|
||||||
|
'lead_type': 'lead',
|
||||||
|
})
|
||||||
|
event_registration = self.env['event.registration'].create({
|
||||||
|
'name': 'Event Registration without answers added at first',
|
||||||
|
'event_id': self.test_event.id,
|
||||||
|
'partner_id': self.event_customer.id,
|
||||||
|
})
|
||||||
|
event_registration.write({
|
||||||
|
'registration_answer_ids': [(0, 0, {
|
||||||
|
'question_id': self.test_event.question_ids[1].id,
|
||||||
|
'value_answer_id': self.test_event.question_ids[1].answer_ids[0].id,
|
||||||
|
})]
|
||||||
|
})
|
||||||
|
self.assertIn(self.test_event.question_ids[1].answer_ids[0].name, event_registration.lead_ids[0].description,
|
||||||
|
"lead description not updated with the answer to the question")
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from odoo.tests import tagged
|
||||||
|
from odoo.fields import Command
|
||||||
|
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestEventFullCommon
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('post_install', '-at_install')
|
||||||
|
class TestEventTicketPriceRounding(TestEventFullCommon):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
cls.ticket_product.write({
|
||||||
|
'lst_price': 1.0
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.currency_jpy = cls.env['res.currency'].create({
|
||||||
|
'name': 'JPX',
|
||||||
|
'symbol': '¥',
|
||||||
|
'rounding': 1.0,
|
||||||
|
'rate_ids': [Command.create({'rate': 133.6200, 'name': time.strftime('%Y-%m-%d')})],
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.currency_cad = cls.env['res.currency'].create({
|
||||||
|
'name': 'CXD',
|
||||||
|
'symbol': '$',
|
||||||
|
'rounding': 0.01,
|
||||||
|
'rate_ids': [Command.create({'rate': 1.338800, 'name': time.strftime('%Y-%m-%d')})],
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.pricelist_usd = cls.env['product.pricelist'].create({
|
||||||
|
'name': 'Pricelist USD',
|
||||||
|
'currency_id': cls.env.ref('base.USD').id,
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.pricelist_jpy = cls.env['product.pricelist'].create({
|
||||||
|
'name': 'Pricelist JPY',
|
||||||
|
'currency_id': cls.currency_jpy.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.pricelist_cad = cls.env['product.pricelist'].create({
|
||||||
|
'name': 'Pricelist CAD',
|
||||||
|
'currency_id': cls.currency_cad.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.event_type = cls.env['event.type'].create({
|
||||||
|
'name': 'Test Event Type',
|
||||||
|
'auto_confirm': True,
|
||||||
|
'event_type_ticket_ids': [
|
||||||
|
(0, 0, {
|
||||||
|
'name': 'Test Event Ticket',
|
||||||
|
'product_id': cls.ticket_product.id,
|
||||||
|
'price': 30.0,
|
||||||
|
})
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
cls.event_ticket = cls.event_type.event_type_ticket_ids[0]
|
||||||
|
|
||||||
|
def test_no_discount_usd(self):
|
||||||
|
ticket = self.event_ticket.with_context(pricelist=self.pricelist_usd.id)
|
||||||
|
ticket._compute_price_reduce()
|
||||||
|
self.assertAlmostEqual(ticket.price_reduce, 30.0, places=6, msg="No discount should be applied for the USD pricelist.")
|
||||||
|
|
||||||
|
def test_no_discount_jpy(self):
|
||||||
|
ticket = self.event_ticket.with_context(pricelist=self.pricelist_jpy.id)
|
||||||
|
ticket._compute_price_reduce()
|
||||||
|
self.assertAlmostEqual(ticket.price_reduce, 30.0, places=6, msg="No discount should be applied for the JPY pricelist.")
|
||||||
|
|
||||||
|
def test_no_discount_cad(self):
|
||||||
|
ticket = self.event_ticket.with_context(pricelist=self.pricelist_cad.id)
|
||||||
|
ticket._compute_price_reduce()
|
||||||
|
self.assertAlmostEqual(ticket.price_reduce, 30.0, places=6, msg="No discount should be applied for the CAD pricelist.")
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from odoo import Command, exceptions
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestEventFullCommon
|
||||||
|
from odoo.tests.common import users
|
||||||
|
|
||||||
|
|
||||||
|
class TestEventEvent(TestEventFullCommon):
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
def test_event_create_wtype(self):
|
||||||
|
""" Test a single event creation with a type defining all sub records. """
|
||||||
|
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||||
|
|
||||||
|
event_values = dict(
|
||||||
|
self.event_base_vals,
|
||||||
|
event_type_id=event_type.id,
|
||||||
|
)
|
||||||
|
event = self.env['event.event'].create([event_values])
|
||||||
|
event.write({
|
||||||
|
'event_ticket_ids': [
|
||||||
|
Command.update(
|
||||||
|
event.event_ticket_ids[0].id,
|
||||||
|
{'start_sale_datetime': self.reference_now + timedelta(hours=1)},
|
||||||
|
),
|
||||||
|
Command.update(
|
||||||
|
event.event_ticket_ids[1].id,
|
||||||
|
{'start_sale_datetime': self.reference_now + timedelta(hours=2)},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
# check result
|
||||||
|
self.assertEqual(event.address_id, self.env.user.company_id.partner_id)
|
||||||
|
self.assertTrue(event.auto_confirm)
|
||||||
|
self.assertEqual(event.country_id, self.env.user.company_id.country_id)
|
||||||
|
self.assertEqual(event.date_tz, 'Europe/Paris')
|
||||||
|
self.assertEqual(event.event_booth_count, 4)
|
||||||
|
self.assertEqual(len(event.event_mail_ids), 3)
|
||||||
|
self.assertEqual(len(event.event_ticket_ids), 2)
|
||||||
|
self.assertTrue(event.introduction_menu)
|
||||||
|
self.assertTrue(event.location_menu)
|
||||||
|
self.assertTrue(event.menu_register_cta)
|
||||||
|
self.assertEqual(event.message_partner_ids, self.env.user.partner_id + self.env.user.company_id.partner_id)
|
||||||
|
self.assertEqual(event.note, '<p>Template note</p>')
|
||||||
|
self.assertTrue(event.register_menu)
|
||||||
|
self.assertEqual(len(event.question_ids), 3)
|
||||||
|
self.assertTrue(event.seats_limited)
|
||||||
|
self.assertEqual(event.seats_max, 30)
|
||||||
|
self.assertEqual(event.stage_id, self.stage_def)
|
||||||
|
self.assertEqual(event.tag_ids, self.tags)
|
||||||
|
self.assertTrue(event.website_menu)
|
||||||
|
|
||||||
|
# check time dependent computation: before event
|
||||||
|
with freeze_time(self.reference_now):
|
||||||
|
self.assertFalse(event.is_finished)
|
||||||
|
self.assertFalse(event.is_ongoing)
|
||||||
|
self.assertFalse(event.event_registrations_started)
|
||||||
|
|
||||||
|
# check time dependent computation: registrations started
|
||||||
|
with freeze_time(self.reference_now + timedelta(hours=1)):
|
||||||
|
event.invalidate_model(['is_finished', 'is_ongoing', 'event_registrations_started'])
|
||||||
|
self.assertFalse(event.is_finished)
|
||||||
|
self.assertFalse(event.is_ongoing)
|
||||||
|
self.assertTrue(event.event_registrations_started)
|
||||||
|
|
||||||
|
# check time dependent computation: during event
|
||||||
|
with freeze_time(self.reference_now + timedelta(days=1, hours=1)):
|
||||||
|
event.invalidate_model(['is_finished', 'is_ongoing', 'event_registrations_started'])
|
||||||
|
self.assertFalse(event.is_finished)
|
||||||
|
self.assertTrue(event.is_ongoing)
|
||||||
|
self.assertTrue(event.event_registrations_started)
|
||||||
|
|
||||||
|
@freeze_time('2021-12-01 11:00:00')
|
||||||
|
@users('event_user')
|
||||||
|
def test_event_seats_and_schedulers(self):
|
||||||
|
now = datetime.now() # used to force create_date, as sql is not wrapped by freeze gun
|
||||||
|
self.env.cr._now = now
|
||||||
|
|
||||||
|
test_event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
ticket_1 = test_event.event_ticket_ids.filtered(lambda ticket: ticket.name == 'Ticket1')
|
||||||
|
ticket_2 = test_event.event_ticket_ids.filtered(lambda ticket: ticket.name == 'Ticket2')
|
||||||
|
|
||||||
|
# check initial data
|
||||||
|
self.assertTrue(test_event.event_registrations_started)
|
||||||
|
self.assertEqual(test_event.seats_available, 30)
|
||||||
|
self.assertEqual(ticket_1.seats_available, 10)
|
||||||
|
self.assertTrue(ticket_1.sale_available)
|
||||||
|
self.assertEqual(ticket_2.seats_available, 0)
|
||||||
|
self.assertFalse(ticket_2.sale_available)
|
||||||
|
|
||||||
|
# make 9 registrations (let 1 on ticket)
|
||||||
|
with self.mock_mail_gateway():
|
||||||
|
self.env['event.registration'].create([
|
||||||
|
{'create_date': now,
|
||||||
|
'email': 'test.customer.%02d@test.example.com' % x,
|
||||||
|
'phone': '04560011%02d' % x,
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'event_ticket_id': ticket_1.id,
|
||||||
|
'name': 'Customer %d' % x,
|
||||||
|
}
|
||||||
|
for x in range(0, 9)
|
||||||
|
])
|
||||||
|
# generated emails from scheduler
|
||||||
|
self.assertEqual(len(self._new_mails), 9)
|
||||||
|
# event and ticket seats update
|
||||||
|
self.assertEqual(len(test_event.registration_ids), 9)
|
||||||
|
self.assertEqual(test_event.seats_available, 21)
|
||||||
|
self.assertEqual(ticket_1.seats_available, 1)
|
||||||
|
self.assertEqual(ticket_2.seats_available, 0)
|
||||||
|
|
||||||
|
# prevent registration due to ticket limit
|
||||||
|
with self.assertRaises(exceptions.ValidationError):
|
||||||
|
self.env['event.registration'].create([
|
||||||
|
{'create_date': now,
|
||||||
|
'email': 'additional.customer.%02d@test.example.com' % x,
|
||||||
|
'phone': '04560011%02d' % x,
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'event_ticket_id': ticket_1.id,
|
||||||
|
'name': 'Additional Customer %d' % x,
|
||||||
|
}
|
||||||
|
for x in range(0, 2)
|
||||||
|
])
|
||||||
|
|
||||||
|
# make 20 registrations (on free ticket)
|
||||||
|
with self.mock_mail_gateway():
|
||||||
|
self.env['event.registration'].create([
|
||||||
|
{'create_date': now,
|
||||||
|
'email': 'other.customer.%02d@test.example.com' % x,
|
||||||
|
'phone': '04560011%02d' % x,
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'event_ticket_id': ticket_2.id,
|
||||||
|
'name': 'Other Customer %d' % x,
|
||||||
|
}
|
||||||
|
for x in range(0, 20)
|
||||||
|
])
|
||||||
|
# event and ticket seats update
|
||||||
|
self.assertEqual(len(test_event.registration_ids), 29)
|
||||||
|
self.assertEqual(test_event.seats_available, 1)
|
||||||
|
self.assertEqual(ticket_1.seats_available, 1)
|
||||||
|
self.assertEqual(ticket_2.seats_available, 0)
|
||||||
|
|
||||||
|
# prevent registration due to event limit
|
||||||
|
with self.assertRaises(exceptions.ValidationError):
|
||||||
|
self.env['event.registration'].create([
|
||||||
|
{'create_date': now,
|
||||||
|
'email': 'additional.customer.%02d@test.example.com' % x,
|
||||||
|
'phone': '04560011%02d' % x,
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'event_ticket_id': ticket_2.id,
|
||||||
|
'name': 'Additional Customer %d' % x,
|
||||||
|
}
|
||||||
|
for x in range(0, 2)
|
||||||
|
])
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from odoo.addons.mail.tests.common import MockEmail
|
||||||
|
from odoo.addons.sms.tests.common import MockSMS
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestWEventCommon
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tools import mute_logger
|
||||||
|
|
||||||
|
class TestTemplateRefModel(TestWEventCommon):
|
||||||
|
|
||||||
|
def test_template_ref_delete_lines(self):
|
||||||
|
""" When deleting a template, related lines should be deleted too """
|
||||||
|
event_type = self.env['event.type'].create({
|
||||||
|
'name': 'Event Type',
|
||||||
|
'default_timezone': 'Europe/Brussels',
|
||||||
|
'event_type_mail_ids': [
|
||||||
|
(0, 0, {
|
||||||
|
'interval_unit': 'now',
|
||||||
|
'interval_type': 'after_sub',
|
||||||
|
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')}),
|
||||||
|
(0, 0, {
|
||||||
|
'interval_unit': 'now',
|
||||||
|
'interval_type': 'after_sub',
|
||||||
|
'notification_type': 'sms',
|
||||||
|
'template_ref': 'sms.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event_sms.sms_template_data_event_registration')}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
template_mail = event_type.event_type_mail_ids[0].template_ref
|
||||||
|
template_sms = event_type.event_type_mail_ids[1].template_ref
|
||||||
|
|
||||||
|
event = self.env['event.event'].create({
|
||||||
|
'name': 'event mail template removed',
|
||||||
|
'event_type_id': event_type.id,
|
||||||
|
'date_begin': datetime(2020, 2, 1, 8, 30, 0),
|
||||||
|
'date_end': datetime(2020, 2, 4, 18, 45, 0),
|
||||||
|
'date_tz': 'Europe/Brussels',
|
||||||
|
})
|
||||||
|
self.assertEqual(len(event_type.event_type_mail_ids), 2)
|
||||||
|
self.assertEqual(len(event.event_mail_ids), 2)
|
||||||
|
|
||||||
|
template_mail.unlink()
|
||||||
|
self.assertEqual(len(event_type.event_type_mail_ids.exists()), 1)
|
||||||
|
self.assertEqual(len(event.event_mail_ids.exists()), 1)
|
||||||
|
|
||||||
|
template_sms.unlink()
|
||||||
|
self.assertEqual(len(event_type.event_type_mail_ids.exists()), 0)
|
||||||
|
self.assertEqual(len(event.event_mail_ids.exists()), 0)
|
||||||
|
|
||||||
|
def test_template_ref_model_constraint(self):
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
('mail', 'mail.template', True),
|
||||||
|
('mail', 'sms.template', False),
|
||||||
|
('sms', 'sms.template', True),
|
||||||
|
('sms', 'mail.template', False),
|
||||||
|
]
|
||||||
|
|
||||||
|
for notification_type, template_type, valid in test_cases:
|
||||||
|
with self.subTest(notification_type=notification_type, template_type=template_type):
|
||||||
|
if template_type == 'mail.template':
|
||||||
|
template = self.env[template_type].create({
|
||||||
|
'name': 'test template',
|
||||||
|
'model_id': self.env['ir.model']._get_id('event.registration'),
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
template = self.env[template_type].create({
|
||||||
|
'name': 'test template',
|
||||||
|
'body': 'Body Test',
|
||||||
|
'model_id': self.env['ir.model']._get_id('event.registration'),
|
||||||
|
})
|
||||||
|
if not valid:
|
||||||
|
with self.assertRaises(ValidationError) as cm:
|
||||||
|
self.env['event.mail'].create({
|
||||||
|
'event_id': self.event.id,
|
||||||
|
'notification_type': notification_type,
|
||||||
|
'interval_unit': 'now',
|
||||||
|
'interval_type': 'before_event',
|
||||||
|
'template_ref': template,
|
||||||
|
})
|
||||||
|
if notification_type == 'mail':
|
||||||
|
self.assertEqual(str(cm.exception), 'The template which is referenced should be coming from mail.template model.')
|
||||||
|
else:
|
||||||
|
self.assertEqual(str(cm.exception), 'The template which is referenced should be coming from sms.template model.')
|
||||||
|
|
||||||
|
class TestEventSmsMailSchedule(TestWEventCommon, MockEmail, MockSMS):
|
||||||
|
|
||||||
|
@freeze_time('2020-07-06 12:00:00')
|
||||||
|
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||||
|
def test_event_mail_before_trigger_sent_count(self):
|
||||||
|
""" Emails are sent to both confirmed and unconfirmed attendees.
|
||||||
|
This test checks that the count of sent emails includes the emails sent to unconfirmed ones
|
||||||
|
|
||||||
|
Time in the test is frozen to simulate the following state:
|
||||||
|
|
||||||
|
NOW Event Start Event End
|
||||||
|
12:00 13:00 14:00
|
||||||
|
| | |
|
||||||
|
──────────────────────────────────────►
|
||||||
|
| | time
|
||||||
|
◄─────────────────►
|
||||||
|
3 hours
|
||||||
|
Trigger before event
|
||||||
|
"""
|
||||||
|
self.sms_template_rem = self.env['sms.template'].create({
|
||||||
|
'name': 'Test reminder',
|
||||||
|
'model_id': self.env.ref('event.model_event_registration').id,
|
||||||
|
'body': '{{ object.event_id.organizer_id.name }} reminder',
|
||||||
|
'lang': '{{ object.partner_id.lang }}'
|
||||||
|
})
|
||||||
|
test_event = self.env['event.event'].create({
|
||||||
|
'name': 'TestEventMail',
|
||||||
|
# 'user_id': self.env.ref('base.user_admin').id,
|
||||||
|
'auto_confirm': False,
|
||||||
|
'date_begin': datetime.now() + timedelta(hours=1),
|
||||||
|
'date_end': datetime.now() + timedelta(hours=2),
|
||||||
|
'event_mail_ids': [
|
||||||
|
(0, 0, { # email 3 hours before event
|
||||||
|
'interval_nbr': 3,
|
||||||
|
'interval_unit': 'hours',
|
||||||
|
'interval_type': 'before_event',
|
||||||
|
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')}),
|
||||||
|
(0, 0, { # sms 3 hours before event
|
||||||
|
'interval_nbr': 3,
|
||||||
|
'interval_unit': 'hours',
|
||||||
|
'interval_type': 'before_event',
|
||||||
|
'notification_type': 'sms',
|
||||||
|
'template_ref': 'sms.template,%i' % self.sms_template_rem.id}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
mail_scheduler = test_event.event_mail_ids
|
||||||
|
self.assertEqual(len(mail_scheduler), 2, 'There should be two mail schedulers. One for mail one for sms. Cannot perform test')
|
||||||
|
|
||||||
|
# Add registrations
|
||||||
|
self.env['event.registration'].create([{
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'name': 'RegistrationUnconfirmed',
|
||||||
|
'email': 'Registration@Unconfirmed.com',
|
||||||
|
'state': 'draft',
|
||||||
|
}, {
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'name': 'RegistrationCanceled',
|
||||||
|
'email': 'Registration@Canceled.com',
|
||||||
|
'state': 'cancel',
|
||||||
|
}, {
|
||||||
|
'event_id': test_event.id,
|
||||||
|
'name': 'RegistrationConfirmed',
|
||||||
|
'email': 'Registration@Confirmed.com',
|
||||||
|
'state': 'open',
|
||||||
|
}])
|
||||||
|
|
||||||
|
with self.mock_mail_gateway(), self.mockSMSGateway():
|
||||||
|
mail_scheduler.execute()
|
||||||
|
|
||||||
|
self.assertEqual(len(self._new_mails), 2, 'Mails were not created')
|
||||||
|
self.assertEqual(len(self._new_sms), 2, 'SMS were not created')
|
||||||
|
|
||||||
|
self.assertEqual(test_event.seats_expected, 2, 'Wrong number of expected seats (attendees)')
|
||||||
|
|
||||||
|
self.assertEqual(mail_scheduler.filtered(lambda r: r.notification_type == 'mail').mail_count_done, 2,
|
||||||
|
'Wrong Emails Sent Count! Probably emails sent to unconfirmed attendees were not included into the Sent Count')
|
||||||
|
self.assertEqual(mail_scheduler.filtered(lambda r: r.notification_type == 'sms').mail_count_done, 2,
|
||||||
|
'Wrong SMS Sent Count! Probably SMS sent to unconfirmed attendees were not included into the Sent Count')
|
||||||
|
|
@ -0,0 +1,213 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestEventFullCommon
|
||||||
|
from odoo.exceptions import AccessError
|
||||||
|
from odoo.tests import tagged
|
||||||
|
from odoo.tests.common import users
|
||||||
|
from odoo.tools import mute_logger
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('security')
|
||||||
|
class TestEventSecurity(TestEventFullCommon):
|
||||||
|
|
||||||
|
@users('user_employee')
|
||||||
|
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||||
|
def test_event_access_employee(self):
|
||||||
|
# Event: read ok
|
||||||
|
event = self.test_event.with_user(self.env.user)
|
||||||
|
event.read(['name'])
|
||||||
|
|
||||||
|
# Event: read only
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.env['event.event'].create({
|
||||||
|
'name': 'TestEvent',
|
||||||
|
'date_begin': datetime.now() + timedelta(days=-1),
|
||||||
|
'date_end': datetime.now() + timedelta(days=1),
|
||||||
|
'seats_limited': True,
|
||||||
|
'seats_max': 10,
|
||||||
|
})
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
event.write({
|
||||||
|
'name': 'TestEvent Modified',
|
||||||
|
})
|
||||||
|
|
||||||
|
# Event Type
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.test_event_type.with_user(self.env.user).read(['name'])
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.test_event_type.with_user(self.env.user).write({'name': 'Test Write'})
|
||||||
|
|
||||||
|
# Event Stage
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.env['event.stage'].create({
|
||||||
|
'name': 'TestStage',
|
||||||
|
})
|
||||||
|
|
||||||
|
# Event Registration
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.env['event.registration'].search([])
|
||||||
|
|
||||||
|
@users('user_eventregistrationdesk')
|
||||||
|
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||||
|
def test_event_access_event_registration(self):
|
||||||
|
# Event: read ok
|
||||||
|
event = self.test_event.with_user(self.env.user)
|
||||||
|
event.read(['name', 'user_id', 'kanban_state_label'])
|
||||||
|
|
||||||
|
# Event: read only
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
event.name = 'Test'
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
event.unlink()
|
||||||
|
|
||||||
|
# Event Registration
|
||||||
|
registration = self.env['event.registration'].create({
|
||||||
|
'event_id': event.id,
|
||||||
|
})
|
||||||
|
self.assertEqual(registration.event_id.name, event.name, 'Registration users should be able to read')
|
||||||
|
registration.name = 'Test write'
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
registration.unlink()
|
||||||
|
|
||||||
|
@users('user_eventuser')
|
||||||
|
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||||
|
def test_event_access_event_user(self):
|
||||||
|
# Event
|
||||||
|
event = self.test_event.with_user(self.env.user)
|
||||||
|
event.read(['name', 'user_id', 'kanban_state_label'])
|
||||||
|
event.write({'name': 'New name'})
|
||||||
|
self.env['event.event'].create({
|
||||||
|
'name': 'Event',
|
||||||
|
'date_begin': datetime.now() + timedelta(days=-1),
|
||||||
|
'date_end': datetime.now() + timedelta(days=1),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Event: cannot unlink
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
event.unlink()
|
||||||
|
|
||||||
|
# Event Type
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.env['event.type'].create({
|
||||||
|
'name': 'ManagerEventType',
|
||||||
|
'event_type_mail_ids': [(5, 0), (0, 0, {
|
||||||
|
'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event',
|
||||||
|
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')})]
|
||||||
|
})
|
||||||
|
|
||||||
|
@users('user_eventmanager')
|
||||||
|
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||||
|
def test_event_access_event_manager(self):
|
||||||
|
# Event Type
|
||||||
|
event_type = self.env['event.type'].create({
|
||||||
|
'name': 'ManagerEventType',
|
||||||
|
'event_type_mail_ids': [(5, 0), (0, 0, {
|
||||||
|
'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event',
|
||||||
|
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')})]
|
||||||
|
})
|
||||||
|
event_type.write({'name': 'New Name'})
|
||||||
|
|
||||||
|
# Event
|
||||||
|
event = self.env['event.event'].create({
|
||||||
|
'name': 'ManagerEvent',
|
||||||
|
'date_begin': datetime.now() + timedelta(days=-1),
|
||||||
|
'date_end': datetime.now() + timedelta(days=1),
|
||||||
|
})
|
||||||
|
event.write({'name': 'New Event Name'})
|
||||||
|
|
||||||
|
# Event Stage
|
||||||
|
stage = self.env['event.stage'].create({'name': 'test'})
|
||||||
|
stage.write({'name': 'ManagerTest'})
|
||||||
|
event.write({'stage_id': stage.id})
|
||||||
|
|
||||||
|
# Event Registration
|
||||||
|
registration = self.env['event.registration'].create({'event_id': event.id, 'name': 'Myself'})
|
||||||
|
registration.write({'name': 'Myself2'})
|
||||||
|
registration.unlink()
|
||||||
|
|
||||||
|
event.unlink()
|
||||||
|
stage.unlink()
|
||||||
|
event_type.unlink()
|
||||||
|
|
||||||
|
# Settings access rights required to enable some features
|
||||||
|
self.user_eventmanager.write({'groups_id': [
|
||||||
|
(3, self.env.ref('base.group_system').id),
|
||||||
|
(4, self.env.ref('base.group_erp_manager').id)
|
||||||
|
]})
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
event_config = self.env['res.config.settings'].with_user(self.user_eventmanager).create({
|
||||||
|
})
|
||||||
|
event_config.execute()
|
||||||
|
|
||||||
|
def test_implied_groups(self):
|
||||||
|
"""Test that the implied groups are correctly set.
|
||||||
|
|
||||||
|
- Event Manager imply Event User
|
||||||
|
- Event User imply Registration user
|
||||||
|
"""
|
||||||
|
# Event Manager
|
||||||
|
self.assertTrue(
|
||||||
|
self.user_eventmanager.has_group('event.group_event_user'),
|
||||||
|
'The event manager group must imply the event user group')
|
||||||
|
self.assertTrue(
|
||||||
|
self.user_eventmanager.has_group('event.group_event_registration_desk'),
|
||||||
|
'The event manager group must imply the registration user group')
|
||||||
|
|
||||||
|
# Event User
|
||||||
|
self.assertTrue(
|
||||||
|
self.user_eventuser.has_group('event.group_event_registration_desk'),
|
||||||
|
'The event user group must imply the event user group')
|
||||||
|
self.assertFalse(
|
||||||
|
self.user_eventuser.has_group('event.group_event_manager'),
|
||||||
|
'The event user group must not imply the event user group')
|
||||||
|
|
||||||
|
# Registration User
|
||||||
|
self.assertFalse(
|
||||||
|
self.user_eventregistrationdesk.has_group('event.group_event_manager'),
|
||||||
|
'The event registration group must not imply the event user manager')
|
||||||
|
self.assertFalse(
|
||||||
|
self.user_eventregistrationdesk.has_group('event.group_event_user'),
|
||||||
|
'The event registration group must not imply the event user group')
|
||||||
|
|
||||||
|
def test_multi_companies(self):
|
||||||
|
"""Test ACLs with multi company. """
|
||||||
|
company_1 = self.env.ref("base.main_company")
|
||||||
|
company_2 = self.env['res.company'].create({'name': 'Company 2'})
|
||||||
|
user_company_1 = self.user_eventuser
|
||||||
|
|
||||||
|
event_company_1, event_company_2 = self.env['event.event'].create([
|
||||||
|
{
|
||||||
|
'name': 'Event Company 1',
|
||||||
|
'date_begin': datetime.now() + timedelta(days=-1),
|
||||||
|
'date_end': datetime.now() + timedelta(days=1),
|
||||||
|
'company_id': company_1.id,
|
||||||
|
}, {
|
||||||
|
'name': 'Event Company 2',
|
||||||
|
'date_begin': datetime.now() + timedelta(days=-1),
|
||||||
|
'date_end': datetime.now() + timedelta(days=1),
|
||||||
|
'company_id': company_2.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
registration_company_1, registration_company_2 = self.env['event.registration'].create([
|
||||||
|
{
|
||||||
|
'name': 'Registration Company 1',
|
||||||
|
'event_id': event_company_1.id,
|
||||||
|
'company_id': company_1.id,
|
||||||
|
}, {
|
||||||
|
'name': 'Registration Company 2',
|
||||||
|
'event_id': event_company_2.id,
|
||||||
|
'company_id': company_2.id,
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
result = self.env['event.event'].with_user(user_company_1).search([])
|
||||||
|
self.assertIn(event_company_1, result, 'You must be able to read the events in your company')
|
||||||
|
self.assertNotIn(event_company_2, result, 'You must not be able to read events outside of your company')
|
||||||
|
|
||||||
|
result = self.env['event.registration'].with_user(user_company_1).search([])
|
||||||
|
self.assertIn(registration_company_1, result, 'You must be able to read the registrations in your company')
|
||||||
|
self.assertNotIn(registration_company_2, result, 'You must not be able to read registrations outside of your company')
|
||||||
|
|
@ -0,0 +1,483 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestEventFullCommon
|
||||||
|
from odoo.addons.website.tests.test_performance import UtilPerf
|
||||||
|
from odoo.tests.common import users, warmup, Form
|
||||||
|
from odoo.tests import tagged
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('event_performance', 'post_install', '-at_install', '-standard')
|
||||||
|
class EventPerformanceCase(TestEventFullCommon):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(EventPerformanceCase, self).setUp()
|
||||||
|
# patch registry to simulate a ready environment
|
||||||
|
self.patch(self.env.registry, 'ready', True)
|
||||||
|
self._flush_tracking()
|
||||||
|
|
||||||
|
def _flush_tracking(self):
|
||||||
|
""" Force the creation of tracking values notably, and ensure tests are
|
||||||
|
reproducible. """
|
||||||
|
self.env.flush_all()
|
||||||
|
self.cr.flush()
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('event_performance', 'post_install', '-at_install', '-standard')
|
||||||
|
class TestEventPerformance(EventPerformanceCase):
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_batch_notype(self):
|
||||||
|
""" Test multiple event creation (import) """
|
||||||
|
batch_size = 20
|
||||||
|
|
||||||
|
# simple without type involved
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=335): # tef 335
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = [
|
||||||
|
dict(self.event_base_vals,
|
||||||
|
website_menu=False)
|
||||||
|
for x in range(batch_size)
|
||||||
|
]
|
||||||
|
self.env['event.event'].create(event_values)
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_batch_notype_website(self):
|
||||||
|
""" Test multiple event creation (import) """
|
||||||
|
batch_size = 20
|
||||||
|
|
||||||
|
# simple without type involved + website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=5368): # tef 4944 / com 4943
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = [
|
||||||
|
dict(self.event_base_vals,
|
||||||
|
website_menu=True
|
||||||
|
)
|
||||||
|
for x in range(batch_size)
|
||||||
|
]
|
||||||
|
self.env['event.event'].create(event_values)
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_batch_wtype(self):
|
||||||
|
""" Test multiple event creation (import) """
|
||||||
|
batch_size = 20
|
||||||
|
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||||
|
|
||||||
|
# complex with type
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=439): # 439
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = [
|
||||||
|
dict(self.event_base_vals,
|
||||||
|
event_type_id=event_type.id,
|
||||||
|
website_menu=False,
|
||||||
|
)
|
||||||
|
for x in range(batch_size)
|
||||||
|
]
|
||||||
|
self.env['event.event'].create(event_values)
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_batch_wtype_website(self):
|
||||||
|
""" Test multiple event creation (import) """
|
||||||
|
batch_size = 20
|
||||||
|
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||||
|
|
||||||
|
# complex with type + website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=5480): # tef 5056 / com 5055
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = [
|
||||||
|
dict(self.event_base_vals,
|
||||||
|
event_type_id=event_type.id,
|
||||||
|
)
|
||||||
|
for x in range(batch_size)
|
||||||
|
]
|
||||||
|
self.env['event.event'].create(event_values)
|
||||||
|
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_form_notype(self):
|
||||||
|
""" Test a single event creation using Form """
|
||||||
|
has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise
|
||||||
|
|
||||||
|
# no type, no website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=206): # tef 160 / com 160
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
# Require for `website_menu` to be visible
|
||||||
|
# <div name="event_menu_configuration" groups="base.group_no_one">
|
||||||
|
with self.debug_mode():
|
||||||
|
with Form(self.env['event.event']) as event_form:
|
||||||
|
event_form.name = 'Test Event'
|
||||||
|
event_form.date_begin = self.reference_now + timedelta(days=1)
|
||||||
|
event_form.date_end = self.reference_now + timedelta(days=5)
|
||||||
|
event_form.website_menu = False
|
||||||
|
if has_social:
|
||||||
|
event_form.social_menu = False
|
||||||
|
_event = event_form.save()
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_form_notype_website(self):
|
||||||
|
""" Test a single event creation using Form """
|
||||||
|
has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise
|
||||||
|
|
||||||
|
# no type, website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=666): # tef 565 / com 566
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
# Require for `website_menu` to be visible
|
||||||
|
# <div name="event_menu_configuration" groups="base.group_no_one">
|
||||||
|
with self.debug_mode():
|
||||||
|
with Form(self.env['event.event']) as event_form:
|
||||||
|
event_form.name = 'Test Event'
|
||||||
|
event_form.date_begin = self.reference_now + timedelta(days=1)
|
||||||
|
event_form.date_end = self.reference_now + timedelta(days=5)
|
||||||
|
event_form.website_menu = True
|
||||||
|
if has_social:
|
||||||
|
event_form.social_menu = False
|
||||||
|
_event = event_form.save()
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_form_type_website(self):
|
||||||
|
""" Test a single event creation using Form """
|
||||||
|
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||||
|
has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise
|
||||||
|
|
||||||
|
# type and website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=692): # tef 593 / com 596
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
# Require for `website_menu` to be visible
|
||||||
|
# <div name="event_menu_configuration" groups="base.group_no_one">
|
||||||
|
with self.debug_mode():
|
||||||
|
with Form(self.env['event.event']) as event_form:
|
||||||
|
event_form.name = 'Test Event'
|
||||||
|
event_form.date_begin = self.reference_now + timedelta(days=1)
|
||||||
|
event_form.date_end = self.reference_now + timedelta(days=5)
|
||||||
|
event_form.event_type_id = event_type
|
||||||
|
if has_social:
|
||||||
|
event_form.social_menu = False
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_single_notype(self):
|
||||||
|
""" Test a single event creation """
|
||||||
|
# simple without type involved
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=31): # 31
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = dict(
|
||||||
|
self.event_base_vals,
|
||||||
|
website_menu=False
|
||||||
|
)
|
||||||
|
self.env['event.event'].create([event_values])
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_single_notype_website(self):
|
||||||
|
""" Test a single event creation """
|
||||||
|
# simple without type involved + website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=352): # tef 327 / com 326
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = dict(
|
||||||
|
self.event_base_vals,
|
||||||
|
website_menu=True
|
||||||
|
)
|
||||||
|
self.env['event.event'].create([event_values])
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_single_wtype(self):
|
||||||
|
""" Test a single event creation """
|
||||||
|
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||||
|
|
||||||
|
# complex with type
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=58): # 58
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = dict(
|
||||||
|
self.event_base_vals,
|
||||||
|
event_type_id=event_type.id,
|
||||||
|
website_menu=False
|
||||||
|
)
|
||||||
|
self.env['event.event'].create([event_values])
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_event_create_single_wtype_website(self):
|
||||||
|
""" Test a single event creation """
|
||||||
|
event_type = self.env['event.type'].browse(self.test_event_type.ids)
|
||||||
|
|
||||||
|
# complex with type + website
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=387): # tef 362 / com 361
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
event_values = dict(
|
||||||
|
self.event_base_vals,
|
||||||
|
event_type_id=event_type.id,
|
||||||
|
)
|
||||||
|
self.env['event.event'].create([event_values])
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('event_performance', 'registration_performance', 'post_install', '-at_install', '-standard')
|
||||||
|
class TestRegistrationPerformance(EventPerformanceCase):
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_batch(self):
|
||||||
|
""" Test multiple registrations creation (batch of 10 without partner
|
||||||
|
and batch of 10 with partner)
|
||||||
|
|
||||||
|
# TODO: with self.profile(collectors=['sql']) as _profile:
|
||||||
|
"""
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=720): # tef only: 674? - com runbot 716 - ent runbot 719
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = [
|
||||||
|
dict(reg_data,
|
||||||
|
event_id=event.id)
|
||||||
|
for reg_data in self.customer_data
|
||||||
|
]
|
||||||
|
registration_values += [
|
||||||
|
{'event_id': event.id,
|
||||||
|
'partner_id': partner.id,
|
||||||
|
} for partner in self.partners
|
||||||
|
]
|
||||||
|
_registrations = self.env['event.registration'].create(registration_values)
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_batch_nolead(self):
|
||||||
|
""" Test multiple registrations creation (batch of 10 without partner
|
||||||
|
and batch of 10 with partner)
|
||||||
|
|
||||||
|
# TODO: with self.profile(collectors=['sql']) as _profile:
|
||||||
|
"""
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=210): # tef 167 / com runbot 206
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = [
|
||||||
|
dict(reg_data,
|
||||||
|
event_id=event.id)
|
||||||
|
for reg_data in self.customer_data
|
||||||
|
]
|
||||||
|
registration_values += [
|
||||||
|
{'event_id': event.id,
|
||||||
|
'partner_id': partner.id,
|
||||||
|
} for partner in self.partners
|
||||||
|
]
|
||||||
|
_registrations = self.env['event.registration'].with_context(event_lead_rule_skip=True).create(registration_values)
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_batch_website(self):
|
||||||
|
""" Test multiple registrations creation (batch of 10 without partner
|
||||||
|
and batch of 10 with partner) with some additional informations (register
|
||||||
|
form like) """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=731): # tef only: 685? - com runbot 727
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = [
|
||||||
|
dict(reg_data,
|
||||||
|
event_id=event.id)
|
||||||
|
for reg_data in self.website_customer_data
|
||||||
|
]
|
||||||
|
registration_values += [
|
||||||
|
{'event_id': event.id,
|
||||||
|
'partner_id': partner.id,
|
||||||
|
'registration_answer_ids': self.website_customer_data[0]['registration_answer_ids'],
|
||||||
|
} for partner in self.partners
|
||||||
|
]
|
||||||
|
_registrations = self.env['event.registration'].create(registration_values)
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_form_customer(self):
|
||||||
|
""" Test a single registration creation using Form """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=231): # tef only: 210? - com runbot 216
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
with Form(self.env['event.registration']) as reg_form:
|
||||||
|
reg_form.event_id = event
|
||||||
|
reg_form.email = 'email.00@test.example.com'
|
||||||
|
reg_form.mobile = '0456999999'
|
||||||
|
reg_form.name = 'My Customer'
|
||||||
|
reg_form.phone = '0456000000'
|
||||||
|
_registration = reg_form.save()
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_form_partner(self):
|
||||||
|
""" Test a single registration creation using Form """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=233): # tef only: 213? - com runbot 217
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
with Form(self.env['event.registration']) as reg_form:
|
||||||
|
reg_form.event_id = event
|
||||||
|
reg_form.partner_id = self.partners[0]
|
||||||
|
_registration = reg_form.save()
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_form_partner_nolead(self):
|
||||||
|
""" Test a single registration creation using Form """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=124): # tef 107 / com 109
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
with Form(self.env['event.registration'].with_context(event_lead_rule_skip=True)) as reg_form:
|
||||||
|
reg_form.event_id = event
|
||||||
|
reg_form.partner_id = self.partners[0]
|
||||||
|
_registration = reg_form.save()
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_single_customer(self):
|
||||||
|
""" Test a single registration creation """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
# simple customer data
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=143): # tef only: 135? - com runbot 140
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = dict(
|
||||||
|
self.customer_data[0],
|
||||||
|
event_id=event.id)
|
||||||
|
_registration = self.env['event.registration'].create([registration_values])
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_single_partner(self):
|
||||||
|
""" Test a single registration creation """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
# partner-based customer
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=149): # tef only: 142? - com runbot 146
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = {
|
||||||
|
'event_id': event.id,
|
||||||
|
'partner_id': self.partners[0].id,
|
||||||
|
}
|
||||||
|
_registration = self.env['event.registration'].create([registration_values])
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_single_partner_nolead(self):
|
||||||
|
""" Test a single registration creation """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
# partner-based customer
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=46): # tef 41 / com 43
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = {
|
||||||
|
'event_id': event.id,
|
||||||
|
'partner_id': self.partners[0].id,
|
||||||
|
}
|
||||||
|
_registration = self.env['event.registration'].with_context(event_lead_rule_skip=True).create([registration_values])
|
||||||
|
|
||||||
|
@users('event_user')
|
||||||
|
@warmup
|
||||||
|
def test_registration_create_single_website(self):
|
||||||
|
""" Test a single registration creation """
|
||||||
|
event = self.env['event.event'].browse(self.test_event.ids)
|
||||||
|
|
||||||
|
# website customer data
|
||||||
|
with freeze_time(self.reference_now), self.assertQueryCount(event_user=151): # tef only: 142? - com runbot 146
|
||||||
|
self.env.cr._now = self.reference_now # force create_date to check schedulers
|
||||||
|
registration_values = dict(
|
||||||
|
self.website_customer_data[0],
|
||||||
|
event_id=event.id)
|
||||||
|
_registration = self.env['event.registration'].create([registration_values])
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('event_performance', 'event_online', 'post_install', '-at_install', '-standard')
|
||||||
|
class TestOnlineEventPerformance(EventPerformanceCase, UtilPerf):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestOnlineEventPerformance, cls).setUpClass()
|
||||||
|
# if website_livechat is installed, disable it
|
||||||
|
if 'channel_id' in cls.env['website']:
|
||||||
|
cls.env['website'].search([]).channel_id = False
|
||||||
|
|
||||||
|
cash_journal = cls.env['account.journal'].create({
|
||||||
|
'name': 'Cash - Test',
|
||||||
|
'type': 'cash',
|
||||||
|
'code': 'CASH - Test'
|
||||||
|
})
|
||||||
|
cls.env['payment.provider'].search([('code', '=', 'test')]).write({
|
||||||
|
'journal_id': cash_journal.id,
|
||||||
|
'state': 'test'
|
||||||
|
})
|
||||||
|
|
||||||
|
# clean even page to make it reproducible
|
||||||
|
cls.env['event.event'].search([('name', '!=', 'Test Event')]).write({'active': False})
|
||||||
|
# create noise for events
|
||||||
|
cls.noise_events = cls.env['event.event'].create([
|
||||||
|
{'name': 'Event %02d' % idx,
|
||||||
|
'date_begin': cls.reference_now + timedelta(days=(-2 + int(idx/10))),
|
||||||
|
'date_end': cls.reference_now + timedelta(days=5),
|
||||||
|
'is_published': True,
|
||||||
|
}
|
||||||
|
for idx in range(0, 50)
|
||||||
|
])
|
||||||
|
|
||||||
|
def _test_url_open(self, url):
|
||||||
|
url += ('?' not in url and '?' or '') + '&debug=disable-t-cache'
|
||||||
|
return self.url_open(url)
|
||||||
|
|
||||||
|
@warmup
|
||||||
|
def test_event_page_event_manager(self):
|
||||||
|
# website customer data
|
||||||
|
with freeze_time(self.reference_now):
|
||||||
|
self.authenticate('user_eventmanager', 'user_eventmanager')
|
||||||
|
with self.assertQueryCount(default=36): # tef 35
|
||||||
|
self._test_url_open('/event/%i' % self.test_event.id)
|
||||||
|
|
||||||
|
@warmup
|
||||||
|
def test_event_page_public(self):
|
||||||
|
# website customer data
|
||||||
|
with freeze_time(self.reference_now):
|
||||||
|
self.authenticate(None, None)
|
||||||
|
with self.assertQueryCount(default=27):
|
||||||
|
self._test_url_open('/event/%i' % self.test_event.id)
|
||||||
|
|
||||||
|
@warmup
|
||||||
|
def test_events_browse_event_manager(self):
|
||||||
|
# website customer data
|
||||||
|
with freeze_time(self.reference_now):
|
||||||
|
self.authenticate('user_eventmanager', 'user_eventmanager')
|
||||||
|
with self.assertQueryCount(default=39): # tef 38
|
||||||
|
self._test_url_open('/event')
|
||||||
|
|
||||||
|
@warmup
|
||||||
|
def test_events_browse_public(self):
|
||||||
|
# website customer data
|
||||||
|
with freeze_time(self.reference_now):
|
||||||
|
self.authenticate(None, None)
|
||||||
|
with self.assertQueryCount(default=28):
|
||||||
|
self._test_url_open('/event')
|
||||||
|
|
||||||
|
# @warmup
|
||||||
|
# def test_register_public(self):
|
||||||
|
# with freeze_time(self.reference_now + timedelta(hours=3)): # be sure sales has started
|
||||||
|
# self.assertTrue(self.test_event.event_registrations_started)
|
||||||
|
# self.authenticate(None, None)
|
||||||
|
# with self.assertQueryCount(default=99999): # tef only: 1110
|
||||||
|
# self.browser_js(
|
||||||
|
# '/event/%i/register' % self.test_event.id,
|
||||||
|
# 'odoo.__DEBUG__.services["web_tour.tour"].run("wevent_performance_register")',
|
||||||
|
# 'odoo.__DEBUG__.services["web_tour.tour"].tours.wevent_performance_register.ready',
|
||||||
|
# login=None,
|
||||||
|
# timeout=200,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# # minimal checkup, to be improved in future tests independently from performance
|
||||||
|
# self.assertEqual(len(self.test_event.registration_ids), 3)
|
||||||
|
# self.assertEqual(len(self.test_event.registration_ids.visitor_id), 1)
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from odoo import tests
|
||||||
|
from odoo.addons.test_event_full.tests.common import TestWEventCommon
|
||||||
|
|
||||||
|
|
||||||
|
@tests.common.tagged('event_online', 'post_install', '-at_install')
|
||||||
|
class TestWEventRegister(TestWEventCommon):
|
||||||
|
|
||||||
|
def test_register(self):
|
||||||
|
with freeze_time(self.reference_now, tick=True):
|
||||||
|
self.browser_js(
|
||||||
|
'/event',
|
||||||
|
'odoo.__DEBUG__.services["web_tour.tour"].run("wevent_register")',
|
||||||
|
'odoo.__DEBUG__.services["web_tour.tour"].tours.wevent_register.ready',
|
||||||
|
login=None
|
||||||
|
)
|
||||||
|
new_registrations = self.event.registration_ids
|
||||||
|
visitor = new_registrations.visitor_id
|
||||||
|
|
||||||
|
# check registration content
|
||||||
|
self.assertEqual(len(new_registrations), 2)
|
||||||
|
self.assertEqual(
|
||||||
|
set(new_registrations.mapped("name")),
|
||||||
|
set(["Raoulette Poiluchette", "Michel Tractopelle"])
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
set(new_registrations.mapped("phone")),
|
||||||
|
set(["0456112233", "0456332211"])
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
set(new_registrations.mapped("email")),
|
||||||
|
set(["raoulette@example.com", "michel@example.com"])
|
||||||
|
)
|
||||||
|
|
||||||
|
# check visitor stored information
|
||||||
|
self.assertEqual(visitor.display_name, "Raoulette Poiluchette")
|
||||||
|
self.assertEqual(visitor.event_registration_ids, new_registrations)
|
||||||
|
self.assertEqual(visitor.partner_id, self.env['res.partner'])
|
||||||
|
self.assertEqual(visitor.mobile, "0456112233")
|
||||||
|
self.assertEqual(visitor.email, "raoulette@example.com")
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="event_registration_template_report">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="registration">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<div class="page">
|
||||||
|
<p>This is a sample of an external report.</p>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
49
odoo-bringout-oca-ocb-test_mail/README.md
Normal file
49
odoo-bringout-oca-ocb-test_mail/README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Mail Tests
|
||||||
|
|
||||||
|
This module contains tests related to mail. Those are
|
||||||
|
present in a separate module as it contains models used only to perform
|
||||||
|
tests independently to functional aspects of other models.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_mail
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
- mail
|
||||||
|
- test_performance
|
||||||
|
|
||||||
|
## Manifest Information
|
||||||
|
|
||||||
|
- **Name**: Mail Tests
|
||||||
|
- **Version**: 1.0
|
||||||
|
- **Category**: Hidden
|
||||||
|
- **License**: LGPL-3
|
||||||
|
- **Installable**: True
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Based on [OCA/OCB](https://github.com/OCA/OCB) branch 16.0, addon `test_mail`.
|
||||||
|
|
||||||
|
## 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
|
||||||
32
odoo-bringout-oca-ocb-test_mail/doc/ARCHITECTURE.md
Normal file
32
odoo-bringout-oca-ocb-test_mail/doc/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
U[Users] -->|HTTP| V[Views and QWeb Templates]
|
||||||
|
V --> C[Controllers]
|
||||||
|
V --> W[Wizards – Transient Models]
|
||||||
|
C --> M[Models and ORM]
|
||||||
|
W --> M
|
||||||
|
M --> R[Reports]
|
||||||
|
DX[Data XML] --> M
|
||||||
|
S[Security – ACLs and Groups] -. enforces .-> M
|
||||||
|
|
||||||
|
subgraph Test_mail Module - test_mail
|
||||||
|
direction LR
|
||||||
|
M:::layer
|
||||||
|
W:::layer
|
||||||
|
C:::layer
|
||||||
|
V:::layer
|
||||||
|
R:::layer
|
||||||
|
S:::layer
|
||||||
|
DX:::layer
|
||||||
|
end
|
||||||
|
|
||||||
|
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- Views include tree/form/kanban templates and report templates.
|
||||||
|
- Controllers provide website/portal routes when present.
|
||||||
|
- Wizards are UI flows implemented with `models.TransientModel`.
|
||||||
|
- Data XML loads data/demo records; Security defines groups and access.
|
||||||
3
odoo-bringout-oca-ocb-test_mail/doc/CONFIGURATION.md
Normal file
3
odoo-bringout-oca-ocb-test_mail/doc/CONFIGURATION.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Refer to Odoo settings for test_mail. Configure related models, access rights, and options as needed.
|
||||||
3
odoo-bringout-oca-ocb-test_mail/doc/CONTROLLERS.md
Normal file
3
odoo-bringout-oca-ocb-test_mail/doc/CONTROLLERS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Controllers
|
||||||
|
|
||||||
|
This module does not define custom HTTP controllers.
|
||||||
6
odoo-bringout-oca-ocb-test_mail/doc/DEPENDENCIES.md
Normal file
6
odoo-bringout-oca-ocb-test_mail/doc/DEPENDENCIES.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
This addon depends on:
|
||||||
|
|
||||||
|
- [mail](../../odoo-bringout-oca-ocb-mail)
|
||||||
|
- test_performance
|
||||||
4
odoo-bringout-oca-ocb-test_mail/doc/FAQ.md
Normal file
4
odoo-bringout-oca-ocb-test_mail/doc/FAQ.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
|
||||||
|
- Q: How to enable? A: Start server with --addon test_mail or install in UI.
|
||||||
7
odoo-bringout-oca-ocb-test_mail/doc/INSTALL.md
Normal file
7
odoo-bringout-oca-ocb-test_mail/doc/INSTALL.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install odoo-bringout-oca-ocb-test_mail"
|
||||||
|
# or
|
||||||
|
uv pip install odoo-bringout-oca-ocb-test_mail"
|
||||||
|
```
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue