Merge missing commits from bemade_fsm into bemade-addons
This commit is contained in:
commit
cc2d9a6aba
15 changed files with 366 additions and 152 deletions
144
.gitignore
vendored
144
.gitignore
vendored
|
|
@ -1,36 +1,28 @@
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
/.venv
|
||||||
|
/.pytest_cache
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
|
env/
|
||||||
|
bin/
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
downloads/
|
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
var/
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
*.egg
|
*.egg
|
||||||
MANIFEST
|
*.eggs
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
# Installer logs
|
||||||
pip-log.txt
|
pip-log.txt
|
||||||
|
|
@ -39,122 +31,44 @@ pip-delete-this-directory.txt
|
||||||
# Unit test / coverage reports
|
# Unit test / coverage reports
|
||||||
htmlcov/
|
htmlcov/
|
||||||
.tox/
|
.tox/
|
||||||
.nox/
|
|
||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
|
||||||
.cache
|
.cache
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
*.pot
|
|
||||||
|
# Pycharm
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.settings
|
||||||
|
|
||||||
|
# Visual Studio cache/options directory
|
||||||
|
.vs/
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# OSX Files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Django stuff:
|
# Django stuff:
|
||||||
*.log
|
*.log
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
# Mr Developer
|
||||||
instance/
|
.mr.developer.cfg
|
||||||
.webassets-cache
|
.project
|
||||||
|
.pydevproject
|
||||||
|
|
||||||
# Scrapy stuff:
|
# Rope
|
||||||
.scrapy
|
.ropeproject
|
||||||
|
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
# PyBuilder
|
# Backup files
|
||||||
.pybuilder/
|
*~
|
||||||
target/
|
*.swp
|
||||||
|
|
||||||
# Jupyter Notebook
|
# OCA rules
|
||||||
.ipynb_checkpoints
|
!static/lib/
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# poetry
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
||||||
#poetry.lock
|
|
||||||
|
|
||||||
# pdm
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
||||||
#pdm.lock
|
|
||||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
||||||
# in version control.
|
|
||||||
# https://pdm.fming.dev/#use-with-ide
|
|
||||||
.pdm.toml
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
'industry_fsm',
|
'industry_fsm',
|
||||||
'industry_fsm_sale',
|
'industry_fsm_sale',
|
||||||
'bemade_partner_root_ancestor',
|
'bemade_partner_root_ancestor',
|
||||||
|
'mail',
|
||||||
],
|
],
|
||||||
'data': ['views/task_template_views.xml',
|
'data': ['views/task_template_views.xml',
|
||||||
'views/equipment.xml',
|
'views/equipment.xml',
|
||||||
|
|
@ -43,11 +44,12 @@
|
||||||
'views/res_partner.xml',
|
'views/res_partner.xml',
|
||||||
'views/menus.xml',
|
'views/menus.xml',
|
||||||
'views/task_views.xml',
|
'views/task_views.xml',
|
||||||
|
'views/sale_order_views.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_tests': [
|
'web.assets_tests': [
|
||||||
'bemade_fsm/static/tests/tours/task_template_tour.js',
|
'bemade_fsm/static/tests/tours/task_template_tour.js',
|
||||||
'bemade_fsm/static/tests/tours/task_equipment_tour.js',
|
'bemade_fsm/static/tests/tours/equipment_tour.js',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class EquipmentType(models.Model):
|
||||||
|
|
||||||
name = fields.Char(string='Intervention Name', required=True, translate=True)
|
name = fields.Char(string='Intervention Name', required=True, translate=True)
|
||||||
|
|
||||||
|
|
||||||
class Equipment(models.Model):
|
class Equipment(models.Model):
|
||||||
_name = 'bemade_fsm.equipment'
|
_name = 'bemade_fsm.equipment'
|
||||||
_rec_name = 'complete_name'
|
_rec_name = 'complete_name'
|
||||||
|
|
@ -39,13 +40,16 @@ class Equipment(models.Model):
|
||||||
"Waste water, Pure water")
|
"Waste water, Pure water")
|
||||||
partner_id = fields.Many2one('res.partner',
|
partner_id = fields.Many2one('res.partner',
|
||||||
string="Owner",
|
string="Owner",
|
||||||
compute="_compute_partner")
|
compute="_compute_partner",
|
||||||
|
search="_search_partner",)
|
||||||
|
|
||||||
description = fields.Text(string="Description",
|
description = fields.Text(string="Description",
|
||||||
tracking=True)
|
tracking=True)
|
||||||
|
|
||||||
partner_location_id = fields.Many2one('res.partner',
|
partner_location_id = fields.Many2one('res.partner',
|
||||||
string="Physical Address",
|
string="Physical Address",
|
||||||
tracking=True,)
|
tracking=True,
|
||||||
|
ondelete='cascade')
|
||||||
|
|
||||||
location_notes = fields.Text(string="Physical Location Notes",
|
location_notes = fields.Text(string="Physical Location Notes",
|
||||||
tracking=True)
|
tracking=True)
|
||||||
|
|
@ -58,6 +62,10 @@ class Equipment(models.Model):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
rec.partner_id = rec.partner_location_id and rec.partner_location_id.root_ancestor
|
rec.partner_id = rec.partner_location_id and rec.partner_location_id.root_ancestor
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _search_partner(self, operator, value):
|
||||||
|
return [('partner_location_id.root_ancestor', operator, value)]
|
||||||
|
|
||||||
@api.depends('pid_tag', 'name')
|
@api.depends('pid_tag', 'name')
|
||||||
def _compute_complete_name(self):
|
def _compute_complete_name(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
|
|
@ -87,4 +95,4 @@ class Equipment(models.Model):
|
||||||
'context': self.env.context,
|
'context': self.env.context,
|
||||||
'res_model': 'bemade_fsm.equipment',
|
'res_model': 'bemade_fsm.equipment',
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ class Partner(models.Model):
|
||||||
string='Site Equipment')
|
string='Site Equipment')
|
||||||
|
|
||||||
is_site_contact = fields.Boolean(string='Is a site contact',
|
is_site_contact = fields.Boolean(string='Is a site contact',
|
||||||
compute="_compute_is_site_contact")
|
compute="_compute_is_site_contact",
|
||||||
|
search="_search_is_site_contact",)
|
||||||
|
|
||||||
site_ids = fields.Many2many(string='Work Sites',
|
site_ids = fields.Many2many(string='Work Sites',
|
||||||
comodel_name='res.partner',
|
comodel_name='res.partner',
|
||||||
|
|
@ -33,6 +34,14 @@ class Partner(models.Model):
|
||||||
domain=[('is_company', '=', False)],
|
domain=[('is_company', '=', False)],
|
||||||
tracking=True)
|
tracking=True)
|
||||||
|
|
||||||
|
work_order_contacts = fields.Many2many(string='Work Order Recipients',
|
||||||
|
comodel_name='res.partner',
|
||||||
|
relation='res_partner_work_order_contacts_rel',
|
||||||
|
column1='res_partner_id',
|
||||||
|
column2='work_order_contact_id',
|
||||||
|
domain=[('is_company', '=', False)],
|
||||||
|
tracking=True)
|
||||||
|
|
||||||
@api.depends('site_ids')
|
@api.depends('site_ids')
|
||||||
def _compute_is_site_contact(self):
|
def _compute_is_site_contact(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
|
|
@ -41,5 +50,9 @@ class Partner(models.Model):
|
||||||
@api.depends('equipment_ids')
|
@api.depends('equipment_ids')
|
||||||
def _compute_equipment_count(self):
|
def _compute_equipment_count(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
all_equipmemt_ids = self.env['bemade_fsm.equipment'].search([('partner_id', '=', rec.id)])
|
all_equipment_ids = self.env['bemade_fsm.equipment'].search([('partner_id', '=', rec.id)])
|
||||||
rec.equipment_count = len(all_equipmemt_ids)
|
rec.equipment_count = len(all_equipment_ids)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _search_is_site_contact(self, operator, value):
|
||||||
|
return [('site_contacts', '!=', False)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,49 @@
|
||||||
from odoo import fields, models, api, _, Command
|
from odoo import fields, models, api, _, Command
|
||||||
|
|
||||||
|
|
||||||
|
class SaleOrder(models.Model):
|
||||||
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
site_contacts = fields.Many2many(comodel_name='res.partner',
|
||||||
|
relation="sale_order_site_contacts_rel",
|
||||||
|
compute="_compute_default_contacts",
|
||||||
|
inverse="_inverse_default_contacts",
|
||||||
|
string='Site Contacts',
|
||||||
|
store=True)
|
||||||
|
|
||||||
|
work_order_contacts = fields.Many2many(comodel_name='res.partner',
|
||||||
|
relation='sale_order_work_order_contacts_rel',
|
||||||
|
compute='_compute_default_contacts',
|
||||||
|
inverse='_inverse_default_contacts',
|
||||||
|
string='Work Order Recipients',
|
||||||
|
store=True)
|
||||||
|
|
||||||
|
equipment_ids = fields.Many2many(comodel_name='bemade_fsm.equipment',
|
||||||
|
compute='_compute_equipment',
|
||||||
|
inverse='_inverse_equipment',
|
||||||
|
string='Equipment to Service',
|
||||||
|
store=True,
|
||||||
|
ondelete='restrict')
|
||||||
|
|
||||||
|
@api.depends('partner_id')
|
||||||
|
def _compute_default_contacts(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.site_contacts = rec.partner_shipping_id.site_contacts
|
||||||
|
rec.work_order_contacts = rec.partner_shipping_id.work_order_contacts
|
||||||
|
|
||||||
|
def _inverse_default_contacts(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@api.depends('partner_id')
|
||||||
|
def _compute_equipment(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.equipment_ids = self.partner_shipping_id.equipment_ids if len(
|
||||||
|
self.partner_shipping_id.equipment_ids) <= 1 else False
|
||||||
|
|
||||||
|
def _inverse_equipment(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderLine(models.Model):
|
class SaleOrderLine(models.Model):
|
||||||
_inherit = 'sale.order.line'
|
_inherit = 'sale.order.line'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from odoo import models, fields, api, _
|
||||||
|
|
||||||
class TaskTemplate(models.Model):
|
class TaskTemplate(models.Model):
|
||||||
_name = 'project.task.template'
|
_name = 'project.task.template'
|
||||||
|
_description = "Template for new project tasks"
|
||||||
@api.model
|
@api.model
|
||||||
def _current_company(self):
|
def _current_company(self):
|
||||||
return self.env.company
|
return self.env.company
|
||||||
|
|
|
||||||
126
bemade_fsm/static/tests/tours/equipment_tour.js
Normal file
126
bemade_fsm/static/tests/tours/equipment_tour.js
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import tour from 'web_tour.tour';
|
||||||
|
|
||||||
|
const TEST_COMPANY = "Test Partner Company";
|
||||||
|
const TEST_EQPT1 = "Test Equipment 1";
|
||||||
|
const TEST_EQPT2 = "Test Equipment 2";
|
||||||
|
tour.register('equipment_base_tour', {
|
||||||
|
test: true,
|
||||||
|
url: '/web',
|
||||||
|
}, /* Create a new "Test Equipment" and link it to "Test Partner Company" */
|
||||||
|
[tour.stepUtils.showAppsMenuItem(), {
|
||||||
|
content: 'Navigate to the Service menu',
|
||||||
|
trigger: '.o_app[data-menu-xmlid="industry_fsm.fsm_menu_root"]',
|
||||||
|
}, {
|
||||||
|
content: 'Navigate to the Clients submenu',
|
||||||
|
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
||||||
|
}, {
|
||||||
|
content: 'Navigate to the Equipment menu',
|
||||||
|
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_equipment"]',
|
||||||
|
}, {
|
||||||
|
content: 'Click the create button',
|
||||||
|
trigger: '.o_list_button_add',
|
||||||
|
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Equipment))',
|
||||||
|
}, {
|
||||||
|
content: 'Add a tag',
|
||||||
|
trigger: 'input[name="pid_tag"]',
|
||||||
|
run: 'text TestPIDTag',
|
||||||
|
}, {
|
||||||
|
content: 'Set the name',
|
||||||
|
trigger: 'input[name="name"]',
|
||||||
|
run: `text ${TEST_EQPT2}`,
|
||||||
|
}, {
|
||||||
|
content: 'Set the partner',
|
||||||
|
trigger: 'div[name="partner_location_id"] div div input',
|
||||||
|
run: 'text Test Partner Company',
|
||||||
|
}, {
|
||||||
|
content: 'Click the partner in the dropdown',
|
||||||
|
trigger: `li a.dropdown-item:contains(${TEST_COMPANY})`,
|
||||||
|
}, {
|
||||||
|
content: 'Save equipment',
|
||||||
|
trigger: 'button.o_form_button_save',
|
||||||
|
}, {
|
||||||
|
/* Navigate to the client and make sure that there are two equipments saved (one from the Python test case) */
|
||||||
|
content: 'Navigate to the Clients submenu',
|
||||||
|
trigger: 'button.dropdown-toggle[data-menu-xmlid="bemade_fsm.menu_service_client"]',
|
||||||
|
}, {
|
||||||
|
content: 'Click on the clients submenu',
|
||||||
|
trigger: '.dropdown-item[data-menu-xmlid="bemade_fsm.menu_service_client_clients"]',
|
||||||
|
}, {
|
||||||
|
content: 'Search for the Test Partner Company',
|
||||||
|
trigger: 'input.o_searchview_input',
|
||||||
|
extra_trigger: 'li.breadcrumb-item.active:has(span:contains(Customers))',
|
||||||
|
run: `text ${TEST_COMPANY}`
|
||||||
|
}, {
|
||||||
|
content: 'Validate Search',
|
||||||
|
trigger: '.o_menu_item.o_selection_focus',
|
||||||
|
run: 'click',
|
||||||
|
}, {
|
||||||
|
content: 'Open the test client.',
|
||||||
|
trigger: `div.o_kanban_record:has(span:contains(${TEST_COMPANY}))`,
|
||||||
|
}, {
|
||||||
|
content: 'Click the Field Service tab.',
|
||||||
|
trigger: 'a.nav-link:contains(Field Service)',
|
||||||
|
extra_trigger: `h1 span.o_field_partner_autocomplete[name="name"]:contains(${TEST_COMPANY})`,
|
||||||
|
}, {
|
||||||
|
content: 'Make sure we have a first test equipment',
|
||||||
|
/*trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT1}))`,*/
|
||||||
|
trigger: `td:contains(${TEST_EQPT1})`,
|
||||||
|
run: function () {
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
content: 'Make sure we have a second test equipment',
|
||||||
|
trigger: `div[name="equipment_ids"]:has(td:contains(${TEST_EQPT2}))`,
|
||||||
|
run: function () {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
tour.register('equipment_sale_order_tour', {
|
||||||
|
test: true,
|
||||||
|
url: '/web',
|
||||||
|
}, [tour.stepUtils.showAppsMenuItem(), {
|
||||||
|
content: 'Navigate to the Sales menu',
|
||||||
|
trigger: '.o_app[data-menu-xmlid="sale.sale_menu_root"]',
|
||||||
|
}, {
|
||||||
|
content: 'Create an order',
|
||||||
|
trigger: 'button.o_list_button_add',
|
||||||
|
}, {
|
||||||
|
content: 'Select the test partner',
|
||||||
|
trigger: 'div[name="partner_id"] input',
|
||||||
|
run: `text ${TEST_COMPANY}`,
|
||||||
|
}, {
|
||||||
|
content: 'Click the partner in the dropdown',
|
||||||
|
trigger: `li a.dropdown-item:contains(${TEST_COMPANY})`,
|
||||||
|
}, {
|
||||||
|
content: 'Save',
|
||||||
|
trigger: 'button.o_form_button_save',
|
||||||
|
}, {
|
||||||
|
content: 'Navigate to the Field Service tab.',
|
||||||
|
trigger: 'a.nav-link[role="tab"]:contains(Field Service)'
|
||||||
|
}, {
|
||||||
|
content: 'Check that default equipment shows up.',
|
||||||
|
trigger: `.o_field_cell:contains(${TEST_EQPT1})`,
|
||||||
|
}, {
|
||||||
|
content: 'Add test equipment 2',
|
||||||
|
trigger: `div[name="equipment_ids"] a:contains(Add a line)`,
|
||||||
|
}, {
|
||||||
|
content: 'Click create',
|
||||||
|
trigger: '.modal-footer > button:has(span:contains(Create))',
|
||||||
|
}, {
|
||||||
|
content: 'Name the new equipment',
|
||||||
|
trigger: 'input.o_field_widget[name="name"]',
|
||||||
|
run: `text ${TEST_EQPT2}`,
|
||||||
|
}, {
|
||||||
|
content: 'Save & Close',
|
||||||
|
trigger: 'button:has(span:contains(Save & Close))',
|
||||||
|
}, {
|
||||||
|
content: 'Save',
|
||||||
|
trigger: 'button.o_form_button_save',
|
||||||
|
}, {
|
||||||
|
content: 'Check that the new equipment was added',
|
||||||
|
trigger: `.o_field_cell:contains(${TEST_EQPT2})`,
|
||||||
|
run: function () {},
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
@ -2,3 +2,4 @@ from . import test_bemade_fsm_common
|
||||||
from . import test_task_template
|
from . import test_task_template
|
||||||
from . import test_task_template_sale_order
|
from . import test_task_template_sale_order
|
||||||
from . import test_equipment
|
from . import test_equipment
|
||||||
|
from . import test_fsm_contact_setting
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
from odoo.tests.common import TransactionCase
|
from odoo.tests.common import TransactionCase, tagged
|
||||||
from odoo import Command
|
from odoo import Command
|
||||||
|
|
||||||
|
|
||||||
|
@tagged("-at_install", "post_install")
|
||||||
class FSMManagerUserTransactionCase(TransactionCase):
|
class FSMManagerUserTransactionCase(TransactionCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
from odoo.tests.common import HttpCase, tagged
|
from odoo.tests.common import HttpCase, tagged
|
||||||
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||||
|
from odoo import Command
|
||||||
|
from odoo.exceptions import MissingError
|
||||||
|
|
||||||
|
|
||||||
|
@tagged("-at_install", "post_install")
|
||||||
class TestEquipmentCommon(FSMManagerUserTransactionCase):
|
class TestEquipmentCommon(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -13,8 +17,8 @@ class TestEquipmentCommon(FSMManagerUserTransactionCase):
|
||||||
'company_type': 'company',
|
'company_type': 'company',
|
||||||
'street': '123 Street St.',
|
'street': '123 Street St.',
|
||||||
'city': 'Montreal',
|
'city': 'Montreal',
|
||||||
'state_id': cls.env['res.country.state'].search([('name','ilike','Quebec%')]).id,
|
'state_id': cls.env['res.country.state'].search([('name', 'ilike', 'Quebec%')]).id,
|
||||||
'country_id': cls.env['res.country'].search([('name','=','Canada')]).id
|
'country_id': cls.env['res.country'].search([('name', '=', 'Canada')]).id
|
||||||
})
|
})
|
||||||
|
|
||||||
cls.partner_contact = cls.env['res.partner'].create({
|
cls.partner_contact = cls.env['res.partner'].create({
|
||||||
|
|
@ -30,8 +34,26 @@ class TestEquipmentCommon(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
|
|
||||||
@tagged('-at_install', 'post_install')
|
@tagged('-at_install', 'post_install')
|
||||||
class TestEquipmentTour(HttpCase, TestEquipmentCommon):
|
class TestEquipmentBase(TestEquipmentCommon):
|
||||||
|
|
||||||
def test_equipment_tour(self):
|
def test_crd(self):
|
||||||
self.start_tour('/web', 'task_equipment_tour',
|
# Just make sure the basic ORM stuff is OK
|
||||||
|
self.assertTrue(self.equipment in self.partner_company.equipment_ids)
|
||||||
|
self.assertTrue(len(self.partner_company.equipment_ids) == 1)
|
||||||
|
self.partner_company.write({'equipment_ids': [Command.set([])]})
|
||||||
|
# Delete should cascade
|
||||||
|
with self.assertRaises(MissingError):
|
||||||
|
self.equipment.name
|
||||||
|
|
||||||
|
|
||||||
|
@tagged('-at_install', 'post_install')
|
||||||
|
class TestEquipmentTours(HttpCase, TestEquipmentCommon):
|
||||||
|
|
||||||
|
def test_equipment_base_tour(self):
|
||||||
|
self.start_tour('/web', 'equipment_base_tour',
|
||||||
login=self.user.login, )
|
login=self.user.login, )
|
||||||
|
|
||||||
|
def test_equipment_sale_order_tour(self):
|
||||||
|
self.start_tour('/web', 'equipment_sale_order_tour', login=self.user.login)
|
||||||
|
# Make sure the equipment added to the SO in the test tour was added to the partner
|
||||||
|
self.assertEqual(len(self.partner_company.equipment_ids), 2)
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,56 @@
|
||||||
from odoo.tests import TransactionCase, HttpCase, tagged
|
from odoo.tests import TransactionCase, HttpCase, tagged
|
||||||
from odoo import Command
|
from odoo import Command
|
||||||
from test_bemade_fsm_common import FSMManagerUserTransactionCase
|
from .test_bemade_fsm_common import FSMManagerUserTransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
@tagged("-at_install", "post_install")
|
||||||
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
class SaleOrderFSMContactsCase(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
def test_default_site_contacts(self):
|
@classmethod
|
||||||
# Make sure the SO pulls the defaults from the partner correctly
|
def setUpClass(cls):
|
||||||
pass
|
super().setUpClass()
|
||||||
|
Partner = cls.env['res.partner']
|
||||||
|
cls.parent_co = Partner.create({
|
||||||
|
'name': 'Parent Co',
|
||||||
|
'company_type': 'company',
|
||||||
|
})
|
||||||
|
cls.contact_1 = Partner.create({
|
||||||
|
'name': 'Contact 1',
|
||||||
|
'company_type': 'person',
|
||||||
|
'parent_id': cls.parent_co.id,
|
||||||
|
})
|
||||||
|
cls.contact_2 = Partner.create({
|
||||||
|
'name': 'Contact 2',
|
||||||
|
'company_type': 'person',
|
||||||
|
'parent_id': cls.parent_co.id,
|
||||||
|
})
|
||||||
|
|
||||||
def test_default_billing_contacts(self):
|
def test_site_contacts(self):
|
||||||
# Make sure the SO pulls the defaults from the partner correctly
|
# Make sure the SO pulls the defaults from the partner correctly
|
||||||
pass
|
self.parent_co.write({'site_contacts': [Command.set([self.contact_1.id, self.contact_2.id])]})
|
||||||
|
so = self.env['sale.order'].create({
|
||||||
|
'partner_id': self.parent_co.id,
|
||||||
|
})
|
||||||
|
self.assertTrue(so.site_contacts == self.parent_co.site_contacts)
|
||||||
|
# Make sure updating the site contacts on the SO doesn't feed back to the partner
|
||||||
|
so.write({'site_contacts': [Command.set([self.contact_1.id])]})
|
||||||
|
self.assertTrue(self.contact_1 in so.site_contacts)
|
||||||
|
self.assertTrue(self.contact_2 not in so.site_contacts)
|
||||||
|
self.assertTrue(so.site_contacts != so.partner_id.site_contacts and len(so.partner_id.site_contacts) == 2)
|
||||||
|
|
||||||
def test_default_workorder_contacts(self):
|
def test_default_workorder_contacts(self):
|
||||||
# Make sure the SO pulls the defaults from the partner correctly
|
# Make sure the SO pulls the defaults from the partner correctly
|
||||||
pass
|
self.parent_co.write({'work_order_contacts': [Command.set([self.contact_1.id, self.contact_2.id])]})
|
||||||
|
so = self.env['sale.order'].create({
|
||||||
def test_change_site_contacts(self):
|
'partner_id': self.parent_co.id,
|
||||||
# Make sure the SO contacts can be updated without feeding back to the partner
|
})
|
||||||
pass
|
self.assertTrue(self.contact_1 in self.parent_co.work_order_contacts)
|
||||||
|
self.assertTrue(self.contact_2 in self.parent_co.work_order_contacts)
|
||||||
def test_change_workorder_contacts(self):
|
self.assertTrue(self.contact_1 in so.work_order_contacts)
|
||||||
# Make sure the SO contacts can be updated without feeding back to the partner
|
self.assertTrue(self.contact_2 in so.work_order_contacts)
|
||||||
pass
|
# Make sure setting the work order contacts on the SO doesn't feed back to the partner
|
||||||
|
so.write({'work_order_contacts': [Command.set([self.contact_1.id])]})
|
||||||
def test_change_billing_contacts(self):
|
self.assertTrue(self.contact_1 in so.work_order_contacts)
|
||||||
# Make sure the SO contacts can be updated without feedin back to the partner
|
self.assertTrue(self.contact_2 not in so.work_order_contacts)
|
||||||
pass
|
self.assertTrue(
|
||||||
|
so.work_order_contacts != so.partner_id.work_order_contacts and len(so.partner_id.work_order_contacts) == 2)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from odoo import Command
|
||||||
from psycopg2.errors import ForeignKeyViolation
|
from psycopg2.errors import ForeignKeyViolation
|
||||||
|
|
||||||
|
|
||||||
|
@tagged("-at_install", "post_install")
|
||||||
class TestTaskTemplateCommon(FSMManagerUserTransactionCase):
|
class TestTaskTemplateCommon(FSMManagerUserTransactionCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from .test_task_template import TestTaskTemplateCommon
|
||||||
from odoo.tests.common import tagged
|
from odoo.tests.common import tagged
|
||||||
|
|
||||||
|
|
||||||
|
@tagged("-at_install", "post_install")
|
||||||
class TestTaskTemplateSalesOrder(TestTaskTemplateCommon):
|
class TestTaskTemplateSalesOrder(TestTaskTemplateCommon):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,18 @@
|
||||||
<field name="mobile" widget="phone"/>
|
<field name="mobile" widget="phone"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="work_order_contacts" attrs="{'invisible': [('company_type','=','person')]}">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="name" widget="res_partner_many2one" />
|
||||||
|
<field name="email" widget="email"/>
|
||||||
|
<field name="phone" widget="phone"/>
|
||||||
|
<field name="mobile" widget="phone"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
<field name="equipment_ids" attrs="{'invisible': [('company_type','=','person')]}">
|
<field name="equipment_ids" attrs="{'invisible': [('company_type','=','person')]}">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="pid_tag"/>
|
<field name="pid_tag"/>
|
||||||
<field name="name" />
|
<field name="name"/>
|
||||||
<field name="location_notes"/>
|
<field name="location_notes"/>
|
||||||
<button name="action_view_equipment"
|
<button name="action_view_equipment"
|
||||||
type="object"
|
type="object"
|
||||||
|
|
@ -40,7 +48,7 @@
|
||||||
name="owned_equipment_ids">
|
name="owned_equipment_ids">
|
||||||
<tree editable="False">
|
<tree editable="False">
|
||||||
<field name="pid_tag"/>
|
<field name="pid_tag"/>
|
||||||
<field name="name" />
|
<field name="name"/>
|
||||||
<field name="partner_location_id" widget="res_partner_many2one"/>
|
<field name="partner_location_id" widget="res_partner_many2one"/>
|
||||||
<field name="location_notes"/>
|
<field name="location_notes"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
@ -48,7 +56,7 @@
|
||||||
<field name="site_ids"
|
<field name="site_ids"
|
||||||
attrs="{'invisible': [('company_type','=','company')]}">
|
attrs="{'invisible': [('company_type','=','company')]}">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="name" widget="res_partner_many2one" />
|
<field name="name" widget="res_partner_many2one"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</group>
|
</group>
|
||||||
|
|
|
||||||
47
bemade_fsm/views/sale_order_views.xml
Normal file
47
bemade_fsm/views/sale_order_views.xml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="sale_order_form_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">bemade_fsm.sale_order.form</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//page[@name='other_information']" position="before">
|
||||||
|
<page name="field_service" string="Field Service">
|
||||||
|
<group name="contacts">
|
||||||
|
<field name="site_contacts">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="name" widget="res_partner_many2one"/>
|
||||||
|
<field name="email" widget="email"/>
|
||||||
|
<field name="phone" widget="phone"/>
|
||||||
|
<field name="mobile" widget="phone"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<field name="work_order_contacts">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="name" widget="res_partner_many2one"/>
|
||||||
|
<field name="email" widget="email"/>
|
||||||
|
<field name="phone" widget="phone"/>
|
||||||
|
<field name="mobile" widget="phone"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<field name="equipment_ids"
|
||||||
|
domain="[('partner_location_id', '=', partner_shipping_id)]"
|
||||||
|
context="{'default_partner_location_id': partner_shipping_id}">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="pid_tag"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="location_notes"/>
|
||||||
|
<button name="action_view_equipment"
|
||||||
|
type="object"
|
||||||
|
string="Details"
|
||||||
|
icon="fa-external-link"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in a new issue