bemade_fsm: code style cleanup
This commit is contained in:
parent
94ec52ff99
commit
3d8d648fb7
38 changed files with 2474 additions and 1622 deletions
188
.eslintrc.yml
Normal file
188
.eslintrc.yml
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
env:
|
||||
browser: true
|
||||
es6: true
|
||||
|
||||
# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449
|
||||
parserOptions:
|
||||
ecmaVersion: 2019
|
||||
|
||||
overrides:
|
||||
- files:
|
||||
- "**/*.esm.js"
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
|
||||
# Globals available in Odoo that shouldn't produce errorings
|
||||
globals:
|
||||
_: readonly
|
||||
$: readonly
|
||||
fuzzy: readonly
|
||||
jQuery: readonly
|
||||
moment: readonly
|
||||
odoo: readonly
|
||||
openerp: readonly
|
||||
owl: readonly
|
||||
luxon: readonly
|
||||
|
||||
# Styling is handled by Prettier, so we only need to enable AST rules;
|
||||
# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890
|
||||
rules:
|
||||
accessor-pairs: warn
|
||||
array-callback-return: warn
|
||||
callback-return: warn
|
||||
capitalized-comments:
|
||||
- warn
|
||||
- always
|
||||
- ignoreConsecutiveComments: true
|
||||
ignoreInlineComments: true
|
||||
complexity:
|
||||
- warn
|
||||
- 15
|
||||
constructor-super: warn
|
||||
dot-notation: warn
|
||||
eqeqeq: warn
|
||||
global-require: warn
|
||||
handle-callback-err: warn
|
||||
id-blacklist: warn
|
||||
id-match: warn
|
||||
init-declarations: error
|
||||
max-depth: warn
|
||||
max-nested-callbacks: warn
|
||||
max-statements-per-line: warn
|
||||
no-alert: warn
|
||||
no-array-constructor: warn
|
||||
no-caller: warn
|
||||
no-case-declarations: warn
|
||||
no-class-assign: warn
|
||||
no-cond-assign: error
|
||||
no-const-assign: error
|
||||
no-constant-condition: warn
|
||||
no-control-regex: warn
|
||||
no-debugger: error
|
||||
no-delete-var: warn
|
||||
no-div-regex: warn
|
||||
no-dupe-args: error
|
||||
no-dupe-class-members: error
|
||||
no-dupe-keys: error
|
||||
no-duplicate-case: error
|
||||
no-duplicate-imports: error
|
||||
no-else-return: warn
|
||||
no-empty-character-class: warn
|
||||
no-empty-function: error
|
||||
no-empty-pattern: error
|
||||
no-empty: warn
|
||||
no-eq-null: error
|
||||
no-eval: error
|
||||
no-ex-assign: error
|
||||
no-extend-native: warn
|
||||
no-extra-bind: warn
|
||||
no-extra-boolean-cast: warn
|
||||
no-extra-label: warn
|
||||
no-fallthrough: warn
|
||||
no-func-assign: error
|
||||
no-global-assign: error
|
||||
no-implicit-coercion:
|
||||
- warn
|
||||
- allow: ["~"]
|
||||
no-implicit-globals: warn
|
||||
no-implied-eval: warn
|
||||
no-inline-comments: warn
|
||||
no-inner-declarations: warn
|
||||
no-invalid-regexp: warn
|
||||
no-irregular-whitespace: warn
|
||||
no-iterator: warn
|
||||
no-label-var: warn
|
||||
no-labels: warn
|
||||
no-lone-blocks: warn
|
||||
no-lonely-if: error
|
||||
no-mixed-requires: error
|
||||
no-multi-str: warn
|
||||
no-native-reassign: error
|
||||
no-negated-condition: warn
|
||||
no-negated-in-lhs: error
|
||||
no-new-func: warn
|
||||
no-new-object: warn
|
||||
no-new-require: warn
|
||||
no-new-symbol: warn
|
||||
no-new-wrappers: warn
|
||||
no-new: warn
|
||||
no-obj-calls: warn
|
||||
no-octal-escape: warn
|
||||
no-octal: warn
|
||||
no-param-reassign: warn
|
||||
no-path-concat: warn
|
||||
no-process-env: warn
|
||||
no-process-exit: warn
|
||||
no-proto: warn
|
||||
no-prototype-builtins: warn
|
||||
no-redeclare: warn
|
||||
no-regex-spaces: warn
|
||||
no-restricted-globals: warn
|
||||
no-restricted-imports: warn
|
||||
no-restricted-modules: warn
|
||||
no-restricted-syntax: warn
|
||||
no-return-assign: error
|
||||
no-script-url: warn
|
||||
no-self-assign: warn
|
||||
no-self-compare: warn
|
||||
no-sequences: warn
|
||||
no-shadow-restricted-names: warn
|
||||
no-shadow: warn
|
||||
no-sparse-arrays: warn
|
||||
no-sync: warn
|
||||
no-this-before-super: warn
|
||||
no-throw-literal: warn
|
||||
no-undef-init: warn
|
||||
no-undef: error
|
||||
no-unmodified-loop-condition: warn
|
||||
no-unneeded-ternary: error
|
||||
no-unreachable: error
|
||||
no-unsafe-finally: error
|
||||
no-unused-expressions: error
|
||||
no-unused-labels: error
|
||||
no-unused-vars: error
|
||||
no-use-before-define: error
|
||||
no-useless-call: warn
|
||||
no-useless-computed-key: warn
|
||||
no-useless-concat: warn
|
||||
no-useless-constructor: warn
|
||||
no-useless-escape: warn
|
||||
no-useless-rename: warn
|
||||
no-void: warn
|
||||
no-with: warn
|
||||
operator-assignment: [error, always]
|
||||
prefer-const: warn
|
||||
radix: warn
|
||||
require-yield: warn
|
||||
sort-imports: warn
|
||||
spaced-comment: [error, always]
|
||||
strict: [error, function]
|
||||
use-isnan: error
|
||||
valid-jsdoc:
|
||||
- warn
|
||||
- prefer:
|
||||
arg: param
|
||||
argument: param
|
||||
augments: extends
|
||||
constructor: class
|
||||
exception: throws
|
||||
func: function
|
||||
method: function
|
||||
prop: property
|
||||
return: returns
|
||||
virtual: abstract
|
||||
yield: yields
|
||||
preferType:
|
||||
array: Array
|
||||
bool: Boolean
|
||||
boolean: Boolean
|
||||
number: Number
|
||||
object: Object
|
||||
str: String
|
||||
string: String
|
||||
requireParamDescription: false
|
||||
requireReturn: false
|
||||
requireReturnDescription: false
|
||||
requireReturnType: false
|
||||
valid-typeof: warn
|
||||
yoda: warn
|
||||
|
|
@ -89,7 +89,7 @@ repos:
|
|||
args: [--fix, --exit-non-zero-on-fix]
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/OCA/pylint-odoo
|
||||
rev: v8.0.19
|
||||
rev: v9.1.2
|
||||
hooks:
|
||||
- id: pylint_odoo
|
||||
name: pylint with optional checks
|
||||
|
|
|
|||
110
.pylintrc
Normal file
110
.pylintrc
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
[MASTER]
|
||||
load-plugins=pylint_odoo
|
||||
score=n
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=all
|
||||
|
||||
# This .pylintrc contains optional AND mandatory checks and is meant to be
|
||||
# loaded in an IDE to have it check everything, in the hope this will make
|
||||
# optional checks more visible to contributors who otherwise never look at a
|
||||
# green travis to see optional checks that failed.
|
||||
# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
|
||||
# config as a blocking check.
|
||||
|
||||
enable=anomalous-backslash-in-string,
|
||||
api-one-deprecated,
|
||||
api-one-multi-together,
|
||||
assignment-from-none,
|
||||
attribute-deprecated,
|
||||
class-camelcase,
|
||||
dangerous-default-value,
|
||||
dangerous-view-replace-wo-priority,
|
||||
development-status-allowed,
|
||||
duplicate-id-csv,
|
||||
duplicate-key,
|
||||
duplicate-xml-fields,
|
||||
duplicate-xml-record-id,
|
||||
eval-referenced,
|
||||
eval-used,
|
||||
incoherent-interpreter-exec-perm,
|
||||
license-allowed,
|
||||
manifest-author-string,
|
||||
manifest-deprecated-key,
|
||||
manifest-required-key,
|
||||
manifest-version-format,
|
||||
method-compute,
|
||||
method-inverse,
|
||||
method-required-super,
|
||||
method-search,
|
||||
openerp-exception-warning,
|
||||
pointless-statement,
|
||||
pointless-string-statement,
|
||||
print-used,
|
||||
redundant-keyword-arg,
|
||||
redundant-modulename-xml,
|
||||
reimported,
|
||||
relative-import,
|
||||
return-in-init,
|
||||
rst-syntax-error,
|
||||
sql-injection,
|
||||
too-few-format-args,
|
||||
translation-field,
|
||||
translation-required,
|
||||
unreachable,
|
||||
use-vim-comment,
|
||||
wrong-tabs-instead-of-spaces,
|
||||
xml-syntax-error,
|
||||
attribute-string-redundant,
|
||||
character-not-valid-in-resource-link,
|
||||
consider-merging-classes-inherited,
|
||||
context-overridden,
|
||||
create-user-wo-reset-password,
|
||||
dangerous-filter-wo-user,
|
||||
dangerous-qweb-replace-wo-priority,
|
||||
deprecated-data-xml-node,
|
||||
deprecated-openerp-xml-node,
|
||||
duplicate-po-message-definition,
|
||||
except-pass,
|
||||
file-not-used,
|
||||
invalid-commit,
|
||||
manifest-maintainers-list,
|
||||
missing-newline-extrafiles,
|
||||
missing-return,
|
||||
odoo-addons-relative-import,
|
||||
old-api7-method-defined,
|
||||
po-msgstr-variables,
|
||||
po-syntax-error,
|
||||
renamed-field-parameter,
|
||||
resource-not-exist,
|
||||
str-format-used,
|
||||
test-folder-imported,
|
||||
translation-contains-variable,
|
||||
translation-positional-used,
|
||||
unnecessary-utf8-coding-comment,
|
||||
website-manifest-key-not-valid-uri,
|
||||
xml-attribute-translatable,
|
||||
xml-deprecated-qweb-directive,
|
||||
xml-deprecated-tree-attribute,
|
||||
external-request-timeout,
|
||||
# messages that do not cause the lint step to fail
|
||||
consider-merging-classes-inherited,
|
||||
create-user-wo-reset-password,
|
||||
dangerous-filter-wo-user,
|
||||
deprecated-module,
|
||||
file-not-used,
|
||||
invalid-commit,
|
||||
missing-manifest-dependency,
|
||||
missing-newline-extrafiles,
|
||||
no-utf8-coding-comment,
|
||||
odoo-addons-relative-import,
|
||||
old-api7-method-defined,
|
||||
redefined-builtin,
|
||||
too-complex,
|
||||
unnecessary-utf8-coding-comment
|
||||
|
||||
|
||||
[REPORTS]
|
||||
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
|
||||
output-format=colorized
|
||||
reports=no
|
||||
110
.pylintrc-mandatory
Normal file
110
.pylintrc-mandatory
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
[MASTER]
|
||||
load-plugins=pylint_odoo
|
||||
score=n
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=all
|
||||
|
||||
# This .pylintrc contains optional AND mandatory checks and is meant to be
|
||||
# loaded in an IDE to have it check everything, in the hope this will make
|
||||
# optional checks more visible to contributors who otherwise never look at a
|
||||
# green travis to see optional checks that failed.
|
||||
# .pylintrc-mandatory containing only mandatory checks is used the pre-commit
|
||||
# config as a blocking check.
|
||||
|
||||
enable=anomalous-backslash-in-string,
|
||||
api-one-deprecated,
|
||||
api-one-multi-together,
|
||||
assignment-from-none,
|
||||
attribute-deprecated,
|
||||
class-camelcase,
|
||||
dangerous-default-value,
|
||||
dangerous-view-replace-wo-priority,
|
||||
development-status-allowed,
|
||||
duplicate-id-csv,
|
||||
duplicate-key,
|
||||
duplicate-xml-fields,
|
||||
duplicate-xml-record-id,
|
||||
eval-referenced,
|
||||
eval-used,
|
||||
incoherent-interpreter-exec-perm,
|
||||
license-allowed,
|
||||
manifest-author-string,
|
||||
manifest-deprecated-key,
|
||||
manifest-required-key,
|
||||
manifest-version-format,
|
||||
method-compute,
|
||||
method-inverse,
|
||||
method-required-super,
|
||||
method-search,
|
||||
openerp-exception-warning,
|
||||
pointless-statement,
|
||||
pointless-string-statement,
|
||||
print-used,
|
||||
redundant-keyword-arg,
|
||||
redundant-modulename-xml,
|
||||
reimported,
|
||||
relative-import,
|
||||
return-in-init,
|
||||
rst-syntax-error,
|
||||
sql-injection,
|
||||
too-few-format-args,
|
||||
translation-field,
|
||||
translation-required,
|
||||
unreachable,
|
||||
use-vim-comment,
|
||||
wrong-tabs-instead-of-spaces,
|
||||
xml-syntax-error,
|
||||
attribute-string-redundant,
|
||||
character-not-valid-in-resource-link,
|
||||
consider-merging-classes-inherited,
|
||||
context-overridden,
|
||||
create-user-wo-reset-password,
|
||||
dangerous-filter-wo-user,
|
||||
dangerous-qweb-replace-wo-priority,
|
||||
deprecated-data-xml-node,
|
||||
deprecated-openerp-xml-node,
|
||||
duplicate-po-message-definition,
|
||||
except-pass,
|
||||
file-not-used,
|
||||
invalid-commit,
|
||||
manifest-maintainers-list,
|
||||
missing-newline-extrafiles,
|
||||
missing-return,
|
||||
odoo-addons-relative-import,
|
||||
old-api7-method-defined,
|
||||
po-msgstr-variables,
|
||||
po-syntax-error,
|
||||
renamed-field-parameter,
|
||||
resource-not-exist,
|
||||
str-format-used,
|
||||
test-folder-imported,
|
||||
translation-contains-variable,
|
||||
translation-positional-used,
|
||||
unnecessary-utf8-coding-comment,
|
||||
website-manifest-key-not-valid-uri,
|
||||
xml-attribute-translatable,
|
||||
xml-deprecated-qweb-directive,
|
||||
xml-deprecated-tree-attribute,
|
||||
external-request-timeout,
|
||||
# messages that do not cause the lint step to fail
|
||||
consider-merging-classes-inherited,
|
||||
create-user-wo-reset-password,
|
||||
dangerous-filter-wo-user,
|
||||
deprecated-module,
|
||||
file-not-used,
|
||||
invalid-commit,
|
||||
missing-manifest-dependency,
|
||||
missing-newline-extrafiles,
|
||||
no-utf8-coding-comment,
|
||||
odoo-addons-relative-import,
|
||||
old-api7-method-defined,
|
||||
redefined-builtin,
|
||||
too-complex,
|
||||
unnecessary-utf8-coding-comment
|
||||
|
||||
|
||||
[REPORTS]
|
||||
msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
|
||||
output-format=colorized
|
||||
reports=no
|
||||
|
|
@ -19,53 +19,52 @@
|
|||
#
|
||||
########################################################################################
|
||||
{
|
||||
'name': 'Improved Field Service Management',
|
||||
'version': '17.0.0.3.0',
|
||||
'summary': 'Adds functionality necessary for managing field service operations at Durpro.',
|
||||
'description': 'Adds functionality necessary for managing field service operations at Durpro.',
|
||||
'category': 'Services/Field Service',
|
||||
'author': 'Bemade Inc.',
|
||||
'website': 'http://www.bemade.org',
|
||||
'license': 'OPL-1',
|
||||
'depends': [
|
||||
'sale_stock',
|
||||
'sale_project',
|
||||
'account',
|
||||
'project_enterprise',
|
||||
'industry_fsm_stock',
|
||||
'industry_fsm_report',
|
||||
'industry_fsm_sale_report',
|
||||
'bemade_partner_root_ancestor',
|
||||
'mail',
|
||||
"name": "Improved Field Service Management",
|
||||
"version": "17.0.0.3.0",
|
||||
"summary": (
|
||||
"Adds functionality necessary for managing field service operations at Durpro."
|
||||
),
|
||||
"category": "Services/Field Service",
|
||||
"author": "Bemade Inc.",
|
||||
"website": "http://www.bemade.org",
|
||||
"license": "LGPL-3",
|
||||
"depends": [
|
||||
"sale_stock",
|
||||
"sale_project",
|
||||
"account",
|
||||
"project_enterprise",
|
||||
"industry_fsm_stock",
|
||||
"industry_fsm_report",
|
||||
"industry_fsm_sale_report",
|
||||
"bemade_partner_root_ancestor",
|
||||
"mail",
|
||||
],
|
||||
'data': [
|
||||
'data/fsm_data.xml',
|
||||
'views/task_template_views.xml',
|
||||
'views/equipment.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/product_views.xml',
|
||||
'views/res_partner.xml',
|
||||
'views/menus.xml',
|
||||
'views/task_views.xml',
|
||||
'views/sale_order_views.xml',
|
||||
'reports/worksheet_custom_report_templates.xml',
|
||||
'reports/worksheet_custom_reports.xml',
|
||||
'wizard/new_task_from_template.xml',
|
||||
'wizard/res_config_settings.xml',
|
||||
"data": [
|
||||
"data/fsm_data.xml",
|
||||
"views/task_template_views.xml",
|
||||
"views/equipment.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"views/product_views.xml",
|
||||
"views/res_partner.xml",
|
||||
"views/menus.xml",
|
||||
"views/task_views.xml",
|
||||
"views/sale_order_views.xml",
|
||||
"reports/worksheet_custom_report_templates.xml",
|
||||
"reports/worksheet_custom_reports.xml",
|
||||
"wizard/new_task_from_template.xml",
|
||||
"wizard/res_config_settings.xml",
|
||||
],
|
||||
'assets': {
|
||||
'web.report_assets_common': [
|
||||
'bemade_fsm/static/src/scss/bemade_fsm.scss'
|
||||
],
|
||||
'web.assets_backend': [
|
||||
"assets": {
|
||||
"web.report_assets_common": ["bemade_fsm/static/src/scss/bemade_fsm.scss"],
|
||||
"web.assets_backend": [
|
||||
# BV: need to readd these files
|
||||
# 'bemade_fsm/static/src/js/kanban_view.js',
|
||||
# 'bemade_fsm/static/src/js/list_view.js',
|
||||
],
|
||||
'web.assets_qweb': [
|
||||
'bemade_fsm/static/src/xml/project_view_buttons.xml',
|
||||
]
|
||||
"web.assets_qweb": [
|
||||
"bemade_fsm/static/src/xml/project_view_buttons.xml",
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'auto_install': False
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="planning_project_stage_waiting_parts" model="project.task.type">
|
||||
<field name="sequence">2</field>
|
||||
<field name="name">Waiting on Parts</field>
|
||||
<!-- BV: legend_blocked n'existe plus -->
|
||||
<!-- BV: <field name="legend_blocked">Blocked</field>-->
|
||||
<field name="fold" eval="False"/>
|
||||
<field name="fold" eval="False" />
|
||||
<!-- <field name="is_closed" eval="False"/>-->
|
||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]" />
|
||||
</record>
|
||||
<record id="planning_project_stage_work_completed" model="project.task.type">
|
||||
<field name="sequence">15</field>
|
||||
<field name="name">Work Executed</field>
|
||||
<!-- BV: <field name="legend_blocked">Blocked</field>-->
|
||||
<field name="fold" eval="False"/>
|
||||
<field name="fold" eval="False" />
|
||||
<!-- <field name="is_closed" eval="False"/>-->
|
||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]" />
|
||||
</record>
|
||||
<record id="planning_project_stage_exception" model="project.task.type">
|
||||
<field name="sequence">19</field>
|
||||
<field name="name">Exception</field>
|
||||
<!-- BV: <field name="legend_blocked">Blocked</field>-->
|
||||
<field name="fold" eval="False"/>
|
||||
<field name="fold" eval="False" />
|
||||
<!-- <field name="is_closed" eval="False"/>-->
|
||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]"/>
|
||||
<field name="project_ids" eval="[(4,ref('industry_fsm.fsm_project'))]" />
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -2,75 +2,86 @@ from odoo import api, fields, models
|
|||
|
||||
|
||||
class Equipment(models.Model):
|
||||
_name = 'bemade_fsm.equipment'
|
||||
_rec_name = 'complete_name'
|
||||
_description = 'Field service equipment'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_name = "bemade_fsm.equipment"
|
||||
_rec_name = "complete_name"
|
||||
_description = "Field service equipment"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
|
||||
pid_tag = fields.Char(string="P&ID Tag", tracking=True)
|
||||
|
||||
name = fields.Char(string="Name", tracking=True, required=True)
|
||||
name = fields.Char(tracking=True, required=True)
|
||||
|
||||
complete_name = fields.Char(string="Equipment Name", compute="_compute_complete_name", store=True)
|
||||
|
||||
tag_ids = fields.Many2many(
|
||||
comodel_name='bemade_fsm.equipment.tag',
|
||||
string='Application',
|
||||
help="Classify and analyze your equipment categories like: Boiler, Laboratory, Waste water, Pure water"
|
||||
complete_name = fields.Char(
|
||||
string="Equipment Name", compute="_compute_complete_name", store=True
|
||||
)
|
||||
|
||||
description = fields.Text(string="Description", tracking=True)
|
||||
tag_ids = fields.Many2many(
|
||||
comodel_name="bemade_fsm.equipment.tag",
|
||||
string="Application",
|
||||
help=(
|
||||
"Classify and analyze your equipment categories like: Boiler, Laboratory,"
|
||||
" Waste water, Pure water"
|
||||
),
|
||||
)
|
||||
|
||||
description = fields.Text(tracking=True)
|
||||
|
||||
partner_location_id = fields.Many2one(
|
||||
comodel_name='res.partner',
|
||||
comodel_name="res.partner",
|
||||
string="Physical Address",
|
||||
tracking=True,
|
||||
ondelete='cascade'
|
||||
ondelete="cascade",
|
||||
)
|
||||
|
||||
location_notes = fields.Text(string="Physical Location Notes", tracking=True)
|
||||
|
||||
task_ids = fields.Many2many(
|
||||
comodel_name='project.task',
|
||||
comodel_name="project.task",
|
||||
relation="bemade_fsm_task_equipment_rel",
|
||||
column1="equipment_id",
|
||||
column2="task_id",
|
||||
string='Interventions'
|
||||
string="Interventions",
|
||||
)
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
@api.depends('partner_location_id')
|
||||
@api.depends("partner_location_id")
|
||||
def _compute_partner(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 name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
def name_search(self, name="", args=None, operator="ilike", limit=100):
|
||||
args = args or []
|
||||
if name:
|
||||
equipments = self.search([
|
||||
'|', '|',
|
||||
('pid_tag', operator, name),
|
||||
('name', operator, name),
|
||||
('partner_location_id.name', operator, name)],
|
||||
limit=limit)
|
||||
equipments = self.search(
|
||||
[
|
||||
"|",
|
||||
"|",
|
||||
("pid_tag", operator, name),
|
||||
("name", operator, name),
|
||||
("partner_location_id.name", operator, name),
|
||||
],
|
||||
limit=limit,
|
||||
)
|
||||
else:
|
||||
equipments = self.search(args, limit=limit)
|
||||
return [(equipment.id, equipment.display_name) for equipment in equipments]
|
||||
|
||||
def action_view_equipment(self):
|
||||
return {
|
||||
'name': 'Equipment',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_id': self.id,
|
||||
'context': self.env.context,
|
||||
'res_model': 'bemade_fsm.equipment',
|
||||
'type': 'ir.actions.act_window',
|
||||
"name": "Equipment",
|
||||
"view_type": "form",
|
||||
"view_mode": "form",
|
||||
"res_id": self.id,
|
||||
"context": self.env.context,
|
||||
"res_model": "bemade_fsm.equipment",
|
||||
"type": "ir.actions.act_window",
|
||||
}
|
||||
|
||||
@api.depends('pid_tag', 'name')
|
||||
@api.depends("pid_tag", "name")
|
||||
def _compute_complete_name(self):
|
||||
for rec in self:
|
||||
tag_part = "[%s] " % rec.pid_tag if rec.pid_tag else ""
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from odoo import api, fields, models
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class EquipmentTag(models.Model):
|
||||
_name = "bemade_fsm.equipment.tag"
|
||||
_description = 'Field service equipment category'
|
||||
_description = "Field service equipment category"
|
||||
|
||||
name = fields.Char('Name', required=True, translate=True)
|
||||
color = fields.Integer('Color Index', default=10)
|
||||
name = fields.Char(required=True, translate=True)
|
||||
color = fields.Integer("Color Index", default=10)
|
||||
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique (name)', "Tag name already exists !"),
|
||||
("name_uniq", "unique (name)", "Tag name already exists !"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from odoo import api, fields, models
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class EquipmentType(models.Model):
|
||||
_name = 'bemade_fsm.equipment.type'
|
||||
_description = 'Field service equipment type'
|
||||
_order = 'id'
|
||||
_name = "bemade_fsm.equipment.type"
|
||||
_description = "Field service equipment type"
|
||||
_order = "id"
|
||||
|
||||
name = fields.Char(string='Intervention Name', required=True, translate=True)
|
||||
name = fields.Char(string="Intervention Name", required=True, translate=True)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class FSMVisit(models.Model):
|
||||
_name = "bemade_fsm.visit"
|
||||
_description = 'Represents a single visit by assigned service personnel.'
|
||||
_description = "Represents a single visit by assigned service personnel."
|
||||
|
||||
label = fields.Text(string="Label", required=True, related='so_section_id.name', readonly=False, copy=True)
|
||||
label = fields.Text(
|
||||
string="Label",
|
||||
required=True,
|
||||
related="so_section_id.name",
|
||||
readonly=False,
|
||||
copy=True,
|
||||
)
|
||||
|
||||
approx_date = fields.Date(string='Approximate Date', copy=False)
|
||||
approx_date = fields.Date(string="Approximate Date", copy=False)
|
||||
|
||||
so_section_id = fields.Many2one(
|
||||
comodel_name="sale.order.line",
|
||||
string="Sale Order Section",
|
||||
help="The section on the sale order that represents the labour and parts for this visit",
|
||||
ondelete="cascade"
|
||||
help=(
|
||||
"The section on the sale order that represents the labour and parts for"
|
||||
" this visit"
|
||||
),
|
||||
ondelete="cascade",
|
||||
)
|
||||
|
||||
sale_order_id = fields.Many2one(
|
||||
|
|
@ -22,14 +31,18 @@ class FSMVisit(models.Model):
|
|||
readonly=True,
|
||||
)
|
||||
|
||||
is_completed = fields.Boolean(string="Completed", related="so_section_id.is_fully_delivered")
|
||||
is_completed = fields.Boolean(
|
||||
string="Completed", related="so_section_id.is_fully_delivered"
|
||||
)
|
||||
|
||||
is_invoiced = fields.Boolean(string="Invoiced", related="so_section_id.is_fully_delivered_and_invoiced")
|
||||
is_invoiced = fields.Boolean(
|
||||
string="Invoiced", related="so_section_id.is_fully_delivered_and_invoiced"
|
||||
)
|
||||
|
||||
summarized_equipment_ids = fields.Many2many(
|
||||
comodel_name="bemade_fsm.equipment",
|
||||
string="Equipment to Service",
|
||||
compute="_compute_summarized_equipment_ids"
|
||||
compute="_compute_summarized_equipment_ids",
|
||||
)
|
||||
|
||||
task_id = fields.Many2one(
|
||||
|
|
@ -38,21 +51,18 @@ class FSMVisit(models.Model):
|
|||
string="Service Visit",
|
||||
)
|
||||
|
||||
task_ids = fields.One2many(
|
||||
comodel_name="project.task",
|
||||
inverse_name="visit_id"
|
||||
)
|
||||
task_ids = fields.One2many(comodel_name="project.task", inverse_name="visit_id")
|
||||
|
||||
visit_no = fields.Integer(
|
||||
compute="_compute_visit_no",
|
||||
)
|
||||
|
||||
@api.depends('task_ids')
|
||||
@api.depends("task_ids")
|
||||
def _compute_task_id(self):
|
||||
for rec in self:
|
||||
rec.task_id = rec.task_ids and rec.task_ids[0]
|
||||
|
||||
@api.depends('so_section_id', 'sale_order_id.summary_equipment_ids')
|
||||
@api.depends("so_section_id", "sale_order_id.summary_equipment_ids")
|
||||
def _compute_summarized_equipment_ids(self):
|
||||
for rec in self:
|
||||
lines = rec.so_section_id.get_section_line_ids()
|
||||
|
|
@ -66,23 +76,27 @@ class FSMVisit(models.Model):
|
|||
def create(self, vals_list):
|
||||
recs = super().create(vals_list)
|
||||
for i, rec in enumerate(recs.filtered(lambda visit: not visit.so_section_id)):
|
||||
rec.so_section_id = rec.env['sale.order.line'].create({
|
||||
'order_id': rec.sale_order_id.id,
|
||||
'display_type': 'line_section',
|
||||
'name': vals_list[i].get('label', False),
|
||||
})
|
||||
rec.so_section_id = rec.env["sale.order.line"].create(
|
||||
{
|
||||
"order_id": rec.sale_order_id.id,
|
||||
"display_type": "line_section",
|
||||
"name": vals_list[i].get("label", False),
|
||||
}
|
||||
)
|
||||
return recs
|
||||
|
||||
@api.depends(
|
||||
'so_section_id',
|
||||
'sale_order_id',
|
||||
'sale_order_id.visit_ids',
|
||||
'sale_order_id.visit_ids.so_section_id',
|
||||
'sale_order_id.visit_ids.so_section_id.sequence'
|
||||
"so_section_id",
|
||||
"sale_order_id",
|
||||
"sale_order_id.visit_ids",
|
||||
"sale_order_id.visit_ids.so_section_id",
|
||||
"sale_order_id.visit_ids.so_section_id.sequence",
|
||||
)
|
||||
def _compute_visit_no(self):
|
||||
for rec in self:
|
||||
ordered_visit_lines = self.sale_order_id.visit_ids.so_section_id.sorted("sequence")
|
||||
ordered_visit_lines = self.sale_order_id.visit_ids.so_section_id.sorted(
|
||||
"sequence"
|
||||
)
|
||||
# Just a straight O(n) search here since n will always be relatively small
|
||||
for index, line in enumerate(ordered_visit_lines):
|
||||
if line == rec.so_section_id:
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
from odoo import fields, models, api
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = 'product.template'
|
||||
_inherit = "product.template"
|
||||
|
||||
task_template_id = fields.Many2one(
|
||||
comodel_name="project.task.template",
|
||||
string="Task Template",
|
||||
ondelete='restrict'
|
||||
ondelete="restrict",
|
||||
)
|
||||
|
||||
is_field_service = fields.Boolean(
|
||||
string="Plan as field service",
|
||||
help="Products planned as field service will have travel time considered in planning."
|
||||
help=(
|
||||
"Products planned as field service will have travel time considered in"
|
||||
" planning."
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ from odoo import models
|
|||
class Project(models.Model):
|
||||
_inherit = "project.project"
|
||||
|
||||
def _fetch_sale_order_item_ids(self, domain_per_model=None, limit=None, offset=None):
|
||||
def _fetch_sale_order_item_ids(
|
||||
self, domain_per_model=None, limit=None, offset=None
|
||||
):
|
||||
# Override to flush the ORM cache to the database prior to running the query
|
||||
# Temporary fix until Odoo fixes this method (PR #160067 submitted for this)
|
||||
self.env.flush_all()
|
||||
|
|
|
|||
|
|
@ -1,86 +1,83 @@
|
|||
from odoo import api, fields, models, Command
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
_inherit = "res.partner"
|
||||
|
||||
equipment_count = fields.Integer(compute='_compute_equipment_count', string='Equipment Count')
|
||||
equipment_count = fields.Integer(compute="_compute_equipment_count")
|
||||
|
||||
owned_equipment_ids = fields.One2many(
|
||||
comodel_name="bemade_fsm.equipment",
|
||||
compute="_compute_owned_equipment_ids",
|
||||
string="Owned Equipments"
|
||||
string="Owned Equipments",
|
||||
)
|
||||
|
||||
equipment_ids = fields.One2many(
|
||||
comodel_name='bemade_fsm.equipment',
|
||||
inverse_name='partner_location_id',
|
||||
string='Site Equipment'
|
||||
comodel_name="bemade_fsm.equipment",
|
||||
inverse_name="partner_location_id",
|
||||
string="Site Equipment",
|
||||
)
|
||||
|
||||
is_site_contact = fields.Boolean(
|
||||
string='Is a site contact',
|
||||
string="Is a site contact",
|
||||
compute="_compute_is_site_contact",
|
||||
search="_search_is_site_contact",
|
||||
)
|
||||
|
||||
site_ids = fields.Many2many(
|
||||
string='Work Sites',
|
||||
comodel_name='res.partner',
|
||||
relation='res_partner_site_contact_rel',
|
||||
column1='site_contact_id',
|
||||
column2='site_id',
|
||||
tracking=True
|
||||
string="Work Sites",
|
||||
comodel_name="res.partner",
|
||||
relation="res_partner_site_contact_rel",
|
||||
column1="site_contact_id",
|
||||
column2="site_id",
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
site_contacts = fields.Many2many(
|
||||
string='Site Contacts',
|
||||
comodel_name='res.partner',
|
||||
relation='res_partner_site_contact_rel',
|
||||
column1='site_id',
|
||||
column2='site_contact_id',
|
||||
domain=[('is_company', '=', False)],
|
||||
tracking=True
|
||||
comodel_name="res.partner",
|
||||
relation="res_partner_site_contact_rel",
|
||||
column1="site_id",
|
||||
column2="site_contact_id",
|
||||
domain=[("is_company", "=", False)],
|
||||
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
|
||||
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,
|
||||
)
|
||||
|
||||
is_service_site = fields.Boolean(
|
||||
compute="_compute_is_service_site",
|
||||
)
|
||||
@api.depends(
|
||||
'equipment_ids',
|
||||
'child_ids.company_type',
|
||||
'child_ids.equipment_ids'
|
||||
)
|
||||
|
||||
@api.depends("equipment_ids", "child_ids.company_type", "child_ids.equipment_ids")
|
||||
def _compute_owned_equipment_ids(self):
|
||||
for rec in self:
|
||||
ids = rec.equipment_ids | rec.child_ids.mapped('equipment_ids')
|
||||
ids = rec.equipment_ids | rec.child_ids.mapped("equipment_ids")
|
||||
rec.owned_equipment_ids = ids or False
|
||||
|
||||
@api.depends('site_ids')
|
||||
@api.depends("site_ids")
|
||||
def _compute_is_site_contact(self):
|
||||
for rec in self:
|
||||
rec.is_site_contact = rec.site_ids is not False
|
||||
|
||||
@api.depends('equipment_ids')
|
||||
@api.depends("equipment_ids")
|
||||
def _compute_equipment_count(self):
|
||||
for rec in self:
|
||||
all_equipment_ids = self.env['bemade_fsm.equipment'].search(
|
||||
[('partner_location_id', '=', rec.id)])
|
||||
all_equipment_ids = self.env["bemade_fsm.equipment"].search(
|
||||
[("partner_location_id", "=", rec.id)]
|
||||
)
|
||||
rec.equipment_count = len(all_equipment_ids)
|
||||
|
||||
@api.model
|
||||
def _search_is_site_contact(self, operator, value):
|
||||
return [('site_contacts', '!=', False)]
|
||||
return [("site_contacts", "!=", False)]
|
||||
|
||||
def _compute_is_service_site(self):
|
||||
for rec in self:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
from odoo import fields, models, api, _, Command
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_round
|
||||
import re
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
_inherit = "sale.order"
|
||||
|
||||
valid_equipment_ids = fields.One2many(
|
||||
comodel_name="bemade_fsm.equipment",
|
||||
related="partner_id.owned_equipment_ids"
|
||||
comodel_name="bemade_fsm.equipment", related="partner_id.owned_equipment_ids"
|
||||
)
|
||||
|
||||
default_equipment_ids = fields.Many2many(
|
||||
|
|
@ -18,66 +14,67 @@ class SaleOrder(models.Model):
|
|||
help="The default equipment to service for new sale order lines.",
|
||||
compute="_compute_default_equipment",
|
||||
inverse="_inverse_default_equipment",
|
||||
store=True
|
||||
store=True,
|
||||
)
|
||||
|
||||
summary_equipment_ids = fields.Many2many(
|
||||
comodel_name="bemade_fsm.equipment",
|
||||
string="Equipment Being Serviced",
|
||||
compute="_compute_summary_equipment_ids")
|
||||
compute="_compute_summary_equipment_ids",
|
||||
)
|
||||
|
||||
site_contacts = fields.Many2many(
|
||||
comodel_name='res.partner',
|
||||
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
|
||||
)
|
||||
|
||||
visit_ids = fields.One2many(
|
||||
comodel_name='bemade_fsm.visit',
|
||||
inverse_name="sale_order_id",
|
||||
readonly=False
|
||||
)
|
||||
|
||||
is_fsm = fields.Boolean(
|
||||
compute='_compute_is_fsm',
|
||||
string='Is FSM',
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends('order_line.task_id')
|
||||
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,
|
||||
)
|
||||
|
||||
visit_ids = fields.One2many(
|
||||
comodel_name="bemade_fsm.visit", inverse_name="sale_order_id", readonly=False
|
||||
)
|
||||
|
||||
is_fsm = fields.Boolean(
|
||||
compute="_compute_is_fsm",
|
||||
string="Is FSM",
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends("order_line.task_id")
|
||||
def get_relevant_order_lines(self, task_id):
|
||||
self.ensure_one()
|
||||
linked_lines = self.order_line.filtered(lambda l: l.task_id == task_id
|
||||
or l == task_id.visit_id.so_section_id)
|
||||
visit_lines = linked_lines.filtered(lambda l: l.visit_id)
|
||||
linked_lines = self.order_line.filtered(
|
||||
lambda line: line.task_id == task_id
|
||||
or line == task_id.visit_id.so_section_id
|
||||
)
|
||||
visit_lines = linked_lines.filtered(lambda line: line.visit_id)
|
||||
for line in visit_lines:
|
||||
linked_lines |= line.get_section_line_ids()
|
||||
return linked_lines
|
||||
|
||||
@api.depends('order_line.equipment_ids')
|
||||
@api.depends("order_line.equipment_ids")
|
||||
def _compute_summary_equipment_ids(self):
|
||||
for rec in self:
|
||||
rec.summary_equipment_ids = rec.order_line.mapped('equipment_ids')
|
||||
rec.summary_equipment_ids = rec.order_line.mapped("equipment_ids")
|
||||
|
||||
@api.onchange('partner_shipping_id')
|
||||
@api.onchange("partner_shipping_id")
|
||||
def _onchange_partner_shipping_id(self):
|
||||
super()._onchange_partner_shipping_id()
|
||||
res = super()._onchange_partner_shipping_id()
|
||||
self._compute_default_equipment()
|
||||
self._compute_default_contacts()
|
||||
return res
|
||||
|
||||
@api.depends('partner_shipping_id')
|
||||
@api.depends("partner_shipping_id")
|
||||
def _compute_default_contacts(self):
|
||||
for rec in self:
|
||||
rec.site_contacts = rec.partner_shipping_id.site_contacts
|
||||
|
|
@ -87,10 +84,10 @@ class SaleOrder(models.Model):
|
|||
pass
|
||||
|
||||
@api.depends(
|
||||
'partner_id',
|
||||
'partner_shipping_id',
|
||||
'partner_shipping_id.equipment_ids',
|
||||
'partner_id.owned_equipment_ids'
|
||||
"partner_id",
|
||||
"partner_shipping_id",
|
||||
"partner_shipping_id.equipment_ids",
|
||||
"partner_id.owned_equipment_ids",
|
||||
)
|
||||
def _compute_default_equipment(self):
|
||||
for rec in self:
|
||||
|
|
@ -109,36 +106,46 @@ class SaleOrder(models.Model):
|
|||
return rec
|
||||
|
||||
def _create_default_visit(self):
|
||||
""" Called when an order is confirmed with lines that will create an FSM task, in order to make sure there is
|
||||
"""Called when an order is confirmed with lines that will create an FSM task, in order to make sure there is
|
||||
a visit line grouping all the service being done."""
|
||||
self.ensure_one()
|
||||
visit = self.env['bemade_fsm.visit'].create({
|
||||
'label': _('Service Visit'),
|
||||
'sale_order_id': self.id,
|
||||
})
|
||||
visit = self.env["bemade_fsm.visit"].create(
|
||||
{
|
||||
"label": _("Service Visit"),
|
||||
"sale_order_id": self.id,
|
||||
}
|
||||
)
|
||||
# Make sure it goes to the top of the list
|
||||
visit.so_section_id.sequence = 0
|
||||
|
||||
def _create_or_organize_visits_if_needed(self):
|
||||
""" Adds a visit line to the top of the order if there are not already visit lines for an order with lines that
|
||||
"""Adds a visit line to the top of the order if there are not already visit lines for an order with lines that
|
||||
will create an FSM task."""
|
||||
for order in self.filtered("company_id.create_default_fsm_visit"):
|
||||
if not order.visit_ids and order.is_fsm:
|
||||
order._create_default_visit()
|
||||
if order.is_fsm:
|
||||
# Make sure that all the lines producing FSM tasks are under a visit
|
||||
visit_line_ids = order.mapped('visit_ids').mapped('so_section_id').mapped('section_line_ids')
|
||||
if any([
|
||||
True for line in
|
||||
order.order_line.filtered(lambda line: not line.display_type)
|
||||
if line not in visit_line_ids
|
||||
]):
|
||||
visit_line_ids = (
|
||||
order.mapped("visit_ids")
|
||||
.mapped("so_section_id")
|
||||
.mapped("section_line_ids")
|
||||
)
|
||||
if any(
|
||||
[
|
||||
True
|
||||
for line in order.order_line.filtered(
|
||||
lambda line: not line.display_type
|
||||
)
|
||||
if line not in visit_line_ids
|
||||
]
|
||||
):
|
||||
# If not, promote the first visit to the top of the order items list
|
||||
for line in order.order_line:
|
||||
line.sequence += 1
|
||||
order.mapped('visit_ids').mapped('so_section_id')[0].sequence = 0
|
||||
order.mapped("visit_ids").mapped("so_section_id")[0].sequence = 0
|
||||
|
||||
@api.depends('order_line.is_fsm')
|
||||
@api.depends("order_line.is_fsm")
|
||||
def _compute_is_fsm(self):
|
||||
for rec in self:
|
||||
rec.is_fsm = any([line.is_fsm for line in rec.order_line])
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
from odoo import fields, models, api, _, Command
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_round
|
||||
import re
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
_inherit = "sale.order.line"
|
||||
valid_equipment_ids = fields.One2many(
|
||||
comodel_name="bemade_fsm.equipment",
|
||||
related="order_id.valid_equipment_ids"
|
||||
comodel_name="bemade_fsm.equipment", related="order_id.valid_equipment_ids"
|
||||
)
|
||||
|
||||
visit_ids = fields.One2many(
|
||||
|
|
@ -22,20 +18,26 @@ class SaleOrderLine(models.Model):
|
|||
comodel_name="bemade_fsm.visit",
|
||||
compute="_compute_visit_id",
|
||||
string="Visit",
|
||||
ondelete='cascade',
|
||||
ondelete="cascade",
|
||||
store=True,
|
||||
)
|
||||
|
||||
is_fully_delivered = fields.Boolean(
|
||||
string="Fully Delivered",
|
||||
compute="_compute_is_fully_delivered",
|
||||
help="Indicates whether a line or all the lines in a section have been entirely delivered."
|
||||
help=(
|
||||
"Indicates whether a line or all the lines in a section have been entirely"
|
||||
" delivered."
|
||||
),
|
||||
)
|
||||
|
||||
is_fully_delivered_and_invoiced = fields.Boolean(
|
||||
string="Fully Invoiced",
|
||||
compute="_compute_is_fully_invoiced",
|
||||
help="Indicates whether a line or all the lines in a section have been entirely delivered and invoiced."
|
||||
help=(
|
||||
"Indicates whether a line or all the lines in a section have been entirely"
|
||||
" delivered and invoiced."
|
||||
),
|
||||
)
|
||||
|
||||
equipment_ids = fields.Many2many(
|
||||
|
|
@ -43,32 +45,28 @@ class SaleOrderLine(models.Model):
|
|||
comodel_name="bemade_fsm.equipment",
|
||||
relation="bemade_fsm_equipment_sale_order_line_rel",
|
||||
column1="sale_order_line_id",
|
||||
column2="equipment_id"
|
||||
column2="equipment_id",
|
||||
)
|
||||
|
||||
is_field_service = fields.Boolean(
|
||||
string="Is Field Service",
|
||||
compute="_compute_is_field_service",
|
||||
store=True
|
||||
)
|
||||
is_field_service = fields.Boolean(compute="_compute_is_field_service", store=True)
|
||||
|
||||
is_fsm = fields.Boolean(
|
||||
string='Is FSM',
|
||||
compute='_compute_is_fsm',
|
||||
string="Is FSM",
|
||||
compute="_compute_is_fsm",
|
||||
store=True,
|
||||
)
|
||||
|
||||
section_line_ids = fields.One2many(
|
||||
comodel_name='sale.order.line',
|
||||
compute='_compute_section_line_ids',
|
||||
comodel_name="sale.order.line",
|
||||
compute="_compute_section_line_ids",
|
||||
)
|
||||
|
||||
@api.depends('visit_ids')
|
||||
@api.depends("visit_ids")
|
||||
def _compute_visit_id(self):
|
||||
for rec in self:
|
||||
rec.visit_id = rec.visit_ids and rec.visit_ids[0]
|
||||
|
||||
@api.depends('product_id')
|
||||
@api.depends("product_id")
|
||||
def _compute_is_field_service(self):
|
||||
for rec in self:
|
||||
rec.is_field_service = rec.product_id.is_field_service
|
||||
|
|
@ -84,38 +82,49 @@ class SaleOrderLine(models.Model):
|
|||
def copy_data(self, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
if 'visit_ids' not in default:
|
||||
default['visit_ids'] = [(0, 0, visit.copy_data()[0]) for visit in self.visit_ids]
|
||||
if "visit_ids" not in default:
|
||||
default["visit_ids"] = [
|
||||
(0, 0, visit.copy_data()[0]) for visit in self.visit_ids
|
||||
]
|
||||
return super().copy_data(default)
|
||||
|
||||
def _timesheet_create_task(self, project):
|
||||
""" Generate task for the given so line, and link it.
|
||||
:param project: record of project.project in which the task should be created
|
||||
:return task: record of the created task
|
||||
"""Generate task for the given so line, and link it.
|
||||
:param project: record of project.project in which the task should be created
|
||||
:return task: record of the created task
|
||||
|
||||
Override to add the logic needed to implement task templates and equipment linkages."""
|
||||
Override to add the logic needed to implement task templates and equipment linkages.
|
||||
"""
|
||||
|
||||
def _create_task_from_template(project, template, parent):
|
||||
""" Recursively generates the task and any subtasks from a project.task.template.
|
||||
"""Recursively generates the task and any subtasks from a project.task.template.
|
||||
|
||||
:param project: project.project record to set on the task's project_id field.
|
||||
:param template: project.task.template to use to create the task.
|
||||
:param parent: project.task to set as the parent to this task.
|
||||
"""
|
||||
values = _timesheet_create_task_prepare_values_from_template(project, template, parent)
|
||||
task = self.env['project.task'].sudo().create(values)
|
||||
values = _timesheet_create_task_prepare_values_from_template(
|
||||
project, template, parent
|
||||
)
|
||||
task = self.env["project.task"].sudo().create(values)
|
||||
subtasks = []
|
||||
for t in template.subtasks:
|
||||
subtask = _create_task_from_template(project, t, task)
|
||||
subtasks.append(subtask)
|
||||
task.write({'child_ids': [Command.set([t.id for t in subtasks])]})
|
||||
task.write({"child_ids": [Command.set([t.id for t in subtasks])]})
|
||||
# We don't want to see the sub-tasks on the SO
|
||||
task.child_ids.write({'sale_order_id': None, 'sale_line_id': None, })
|
||||
task.child_ids.write(
|
||||
{
|
||||
"sale_order_id": None,
|
||||
"sale_line_id": None,
|
||||
}
|
||||
)
|
||||
return task
|
||||
|
||||
def _timesheet_create_task_prepare_values_from_template(project, template,
|
||||
parent):
|
||||
""" Copies the values from a project.task.template over to the set of values used to create a project.task.
|
||||
def _timesheet_create_task_prepare_values_from_template(
|
||||
project, template, parent
|
||||
):
|
||||
"""Copies the values from a project.task.template over to the set of values used to create a project.task.
|
||||
|
||||
:param project: project.project record to set on the task's project_id field.
|
||||
Pass the project.project model or an empty recordset to leave task project_id blank.
|
||||
|
|
@ -124,15 +133,17 @@ class SaleOrderLine(models.Model):
|
|||
:param parent: project.task to set as the parent to this task.
|
||||
"""
|
||||
vals = self._timesheet_create_task_prepare_values(project)
|
||||
vals['name'] = template.name
|
||||
vals['description'] = template.description or '' if parent else vals['description']
|
||||
vals['parent_id'] = parent and parent.id
|
||||
vals['user_ids'] = template.assignees.ids
|
||||
vals['tag_ids'] = template.tags.ids
|
||||
vals['allocated_hours'] = template.planned_hours
|
||||
vals['sequence'] = template.sequence
|
||||
vals["name"] = template.name
|
||||
vals["description"] = (
|
||||
template.description or "" if parent else vals["description"]
|
||||
)
|
||||
vals["parent_id"] = parent and parent.id
|
||||
vals["user_ids"] = template.assignees.ids
|
||||
vals["tag_ids"] = template.tags.ids
|
||||
vals["allocated_hours"] = template.planned_hours
|
||||
vals["sequence"] = template.sequence
|
||||
if template.equipment_ids:
|
||||
vals['equipment_ids'] = template.equipment_ids.ids
|
||||
vals["equipment_ids"] = template.equipment_ids.ids
|
||||
return vals
|
||||
|
||||
tmpl = self.product_id.task_template_id
|
||||
|
|
@ -140,11 +151,16 @@ class SaleOrderLine(models.Model):
|
|||
task = super()._timesheet_create_task(project)
|
||||
else:
|
||||
task = _create_task_from_template(project, tmpl, None)
|
||||
self.write({'task_id': task.id})
|
||||
self.write({"task_id": task.id})
|
||||
# post message on task
|
||||
task_msg = _(
|
||||
"This task has been created from: <a href=# data-oe-model=sale.order data-oe-id=%d>%s</a> (%s)") % (
|
||||
self.order_id.id, self.order_id.name, self.product_id.name)
|
||||
"This task has been created from: <a href=# data-oe-model=sale.order"
|
||||
" data-oe-id=%(so_id)d>%(so_name)s</a> (%(product_name)s)"
|
||||
) % {
|
||||
"so_id": self.order_id.id,
|
||||
"so_name": self.order_id.name,
|
||||
"product_name": self.product_id.name,
|
||||
}
|
||||
task.message_post(body=task_msg)
|
||||
if not task.equipment_ids and self.equipment_ids:
|
||||
task.equipment_ids = self.equipment_ids.ids
|
||||
|
|
@ -152,9 +168,9 @@ class SaleOrderLine(models.Model):
|
|||
|
||||
def _timesheet_service_generation(self):
|
||||
super()._timesheet_service_generation()
|
||||
visit_lines = self.filtered(lambda l: l.visit_id)
|
||||
visit_lines = self.filtered(lambda line: line.visit_id)
|
||||
for index, line in enumerate(visit_lines):
|
||||
task_ids = line.get_section_line_ids().mapped('task_id')
|
||||
task_ids = line.get_section_line_ids().mapped("task_id")
|
||||
if not task_ids:
|
||||
continue
|
||||
if len(set([task.project_id for task in task_ids])) > 1:
|
||||
|
|
@ -162,72 +178,89 @@ class SaleOrderLine(models.Model):
|
|||
return
|
||||
project_id = task_ids[0].project_id
|
||||
line.visit_id.task_id = line._generate_task_for_visit_line(
|
||||
project_id, index + 1,
|
||||
sum(task_ids.mapped("allocated_hours"))
|
||||
project_id, index + 1, sum(task_ids.mapped("allocated_hours"))
|
||||
)
|
||||
task_ids.write({'parent_id': line.visit_id.task_id.id})
|
||||
(self.mapped('task_id') | self.visit_ids.task_id).filtered("is_fsm").synchronize_name_fsm()
|
||||
task_ids.write({"parent_id": line.visit_id.task_id.id})
|
||||
(self.mapped("task_id") | self.visit_ids.task_id).filtered(
|
||||
"is_fsm"
|
||||
).synchronize_name_fsm()
|
||||
|
||||
def _generate_task_for_visit_line(self, project, visit_no: int, allocated_hours: int):
|
||||
def _generate_task_for_visit_line(
|
||||
self, project, visit_no: int, allocated_hours: int
|
||||
):
|
||||
self.ensure_one()
|
||||
|
||||
allocated_hours = sum(self.get_section_line_ids().task_id.mapped('allocated_hours'))
|
||||
task = self.env['project.task'].create({
|
||||
'name': f"{self.order_id.name} - " + _("Visit") + f" {visit_no} - {self.name}",
|
||||
'project_id': project.id,
|
||||
'equipment_ids': self.get_section_line_ids().mapped('equipment_ids').ids,
|
||||
'sale_order_id': self.order_id.id,
|
||||
'partner_id': self.order_id.partner_shipping_id.id,
|
||||
'visit_id': self.visit_id.id,
|
||||
'allocated_hours': allocated_hours,
|
||||
'date_deadline': self.visit_id.approx_date,
|
||||
'user_ids': False, # Force to empty or it uses the current user
|
||||
})
|
||||
allocated_hours = sum(
|
||||
self.get_section_line_ids().task_id.mapped("allocated_hours")
|
||||
)
|
||||
task = self.env["project.task"].create(
|
||||
{
|
||||
"name": (
|
||||
f"{self.order_id.name} - "
|
||||
+ _("Visit")
|
||||
+ f" {visit_no} - {self.name}"
|
||||
),
|
||||
"project_id": project.id,
|
||||
"equipment_ids": (
|
||||
self.get_section_line_ids().mapped("equipment_ids").ids
|
||||
),
|
||||
"sale_order_id": self.order_id.id,
|
||||
"partner_id": self.order_id.partner_shipping_id.id,
|
||||
"visit_id": self.visit_id.id,
|
||||
"allocated_hours": allocated_hours,
|
||||
"date_deadline": self.visit_id.approx_date,
|
||||
"user_ids": False, # Force to empty or it uses the current user
|
||||
}
|
||||
)
|
||||
return task
|
||||
|
||||
@api.depends(
|
||||
'order_id.order_line',
|
||||
'display_type',
|
||||
'qty_to_deliver',
|
||||
'order_id.order_line.qty_to_deliver',
|
||||
'order_id.order_line.display_type'
|
||||
"order_id.order_line",
|
||||
"display_type",
|
||||
"qty_to_deliver",
|
||||
"order_id.order_line.qty_to_deliver",
|
||||
"order_id.order_line.display_type",
|
||||
)
|
||||
def _compute_is_fully_delivered(self):
|
||||
for rec in self:
|
||||
rec.is_fully_delivered = rec._iterate_items_compute_bool(
|
||||
lambda l: l.qty_to_deliver == 0)
|
||||
lambda line: line.qty_to_deliver == 0
|
||||
)
|
||||
|
||||
@api.depends('is_fully_delivered')
|
||||
@api.depends("is_fully_delivered")
|
||||
def _compute_is_fully_invoiced(self):
|
||||
for rec in self:
|
||||
if not rec.is_fully_delivered:
|
||||
rec.is_fully_delivered_and_invoiced = False
|
||||
return
|
||||
rec.is_fully_delivered_and_invoiced = rec._iterate_items_compute_bool(
|
||||
lambda l: l.qty_to_invoice == 0)
|
||||
lambda line: line.qty_to_invoice == 0
|
||||
)
|
||||
|
||||
def get_section_line_ids(self):
|
||||
""" Return a recordset of sale.order.line records that are in this sale order section. """
|
||||
"""Return a recordset of sale.order.line records that are in this sale order section."""
|
||||
self.ensure_one()
|
||||
assert self.display_type == 'line_section', "Cannot get section lines for a non-section."
|
||||
assert (
|
||||
self.display_type == "line_section"
|
||||
), "Cannot get section lines for a non-section."
|
||||
found = False
|
||||
lines = []
|
||||
for line in self.order_id.order_line.sorted(lambda l: l.sequence):
|
||||
for line in self.order_id.order_line.sorted(lambda line: line.sequence):
|
||||
if line == self:
|
||||
found = True
|
||||
continue
|
||||
if not found:
|
||||
continue
|
||||
if line.display_type == 'line_section': # Stop when we hit the next section
|
||||
if line.display_type == "line_section": # Stop when we hit the next section
|
||||
break
|
||||
else:
|
||||
lines.append(line)
|
||||
return self.env['sale.order.line'].union(*lines)
|
||||
return self.env["sale.order.line"].union(*lines)
|
||||
|
||||
@api.depends('display_type', 'order_id.order_line')
|
||||
@api.depends("display_type", "order_id.order_line")
|
||||
def _compute_section_line_ids(self):
|
||||
for rec in self:
|
||||
if rec.display_type == 'line_section':
|
||||
if rec.display_type == "line_section":
|
||||
rec.section_line_ids = [Command.set(rec.get_section_line_ids().ids)]
|
||||
else:
|
||||
rec.section_line_ids = False
|
||||
|
|
@ -235,7 +268,7 @@ class SaleOrderLine(models.Model):
|
|||
def _iterate_items_compute_bool(self, single_line_func):
|
||||
if not self.display_type:
|
||||
return single_line_func(self)
|
||||
elif self.display_type == 'line_note':
|
||||
elif self.display_type == "line_note":
|
||||
return True
|
||||
else:
|
||||
for line in self.order_id.order_line:
|
||||
|
|
@ -244,15 +277,17 @@ class SaleOrderLine(models.Model):
|
|||
found = True
|
||||
if not found:
|
||||
continue
|
||||
if found and line.display_type == 'line_section':
|
||||
if found and line.display_type == "line_section":
|
||||
return True
|
||||
val = single_line_func(self)
|
||||
if not val:
|
||||
return val
|
||||
return True
|
||||
|
||||
@api.depends('product_id.detailed_type', 'product_id.service_tracking')
|
||||
@api.depends("product_id.detailed_type", "product_id.service_tracking")
|
||||
def _compute_is_fsm(self):
|
||||
for rec in self:
|
||||
rec.is_fsm = (rec.product_id.detailed_type == 'service'
|
||||
and rec.product_id.service_tracking == 'task_global_project')
|
||||
rec.is_fsm = (
|
||||
rec.product_id.detailed_type == "service"
|
||||
and rec.product_id.service_tracking == "task_global_project"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from odoo import fields, models, api, Command, _
|
||||
from odoo import fields, models, api, Command
|
||||
from odoo.addons.project.models.project_task import CLOSED_STATES
|
||||
import re
|
||||
|
||||
|
|
@ -34,25 +34,24 @@ class Task(models.Model):
|
|||
string="Can be billed",
|
||||
related=False,
|
||||
compute="_compute_allow_billable",
|
||||
store=True
|
||||
store=True,
|
||||
)
|
||||
|
||||
visit_id = fields.Many2one(
|
||||
comodel_name='bemade_fsm.visit',
|
||||
comodel_name="bemade_fsm.visit",
|
||||
string="Visit",
|
||||
)
|
||||
|
||||
relevant_order_lines = fields.Many2many(
|
||||
comodel_name='sale.order.line',
|
||||
comodel_name="sale.order.line",
|
||||
store=False,
|
||||
compute='_compute_relevant_order_lines',
|
||||
compute="_compute_relevant_order_lines",
|
||||
)
|
||||
|
||||
work_order_number = fields.Char(readonly=True)
|
||||
|
||||
propagate_assignment = fields.Boolean(
|
||||
string='Propagate Assignment',
|
||||
help='Propagate assignment of this task to all subtasks.',
|
||||
help="Propagate assignment of this task to all subtasks.",
|
||||
default=False,
|
||||
)
|
||||
|
||||
|
|
@ -76,18 +75,29 @@ class Task(models.Model):
|
|||
rec.site_contacts = rec.parent_id.site_contacts
|
||||
if rec.sale_order_id:
|
||||
seq = 1
|
||||
prev_seqs = self.sale_order_id.tasks_ids and \
|
||||
self.sale_order_id.tasks_ids.mapped('work_order_number')
|
||||
prev_seqs = (
|
||||
self.sale_order_id.tasks_ids
|
||||
and self.sale_order_id.tasks_ids.mapped("work_order_number")
|
||||
)
|
||||
if prev_seqs:
|
||||
pattern = re.compile(r"(\d+)$")
|
||||
matches = map(lambda n: pattern.search(n), prev_seqs)
|
||||
seq += max(map(lambda n: int(n.group(1)) if n else 0, matches))
|
||||
rec.work_order_number = rec.sale_order_id.name.replace('SO', 'SVR', 1) \
|
||||
+ f"-{seq}"
|
||||
rec.work_order_number = (
|
||||
rec.sale_order_id.name.replace("SO", "SVR", 1) + f"-{seq}"
|
||||
)
|
||||
# If the task is linked to a sales order and has no parent, it should inherit SO work order contacts
|
||||
if not rec.parent_id and not rec.work_order_contacts and rec.sale_order_id.work_order_contacts:
|
||||
if (
|
||||
not rec.parent_id
|
||||
and not rec.work_order_contacts
|
||||
and rec.sale_order_id.work_order_contacts
|
||||
):
|
||||
rec.work_order_contacts = rec.sale_order_id.work_order_contacts
|
||||
if not rec.parent_id and not rec.site_contacts and rec.sale_order_id.site_contacts:
|
||||
if (
|
||||
not rec.parent_id
|
||||
and not rec.site_contacts
|
||||
and rec.sale_order_id.site_contacts
|
||||
):
|
||||
rec.site_contacts = rec.sale_order_id.site_contacts
|
||||
return res
|
||||
|
||||
|
|
@ -95,45 +105,57 @@ class Task(models.Model):
|
|||
res = super().write(vals)
|
||||
if not self: # End recursion on empty RecordSet
|
||||
return res
|
||||
if 'propagate_assignment' in vals:
|
||||
if "propagate_assignment" in vals:
|
||||
# When a user sets propagate assignment, it should propagate that setting all the way down the chain
|
||||
self.child_ids.write({'propagate_assignment': vals['propagate_assignment']})
|
||||
if 'user_ids' in vals:
|
||||
self.child_ids.write({"propagate_assignment": vals["propagate_assignment"]})
|
||||
if "user_ids" in vals:
|
||||
to_propagate = self.filtered(lambda task: task.propagate_assignment)
|
||||
# Here we use child_ids instead of _get_all_subtasks() so as to allow for setting propagate_assignment
|
||||
# to false on a child task.
|
||||
to_propagate.child_ids.write({'user_ids': vals['user_ids']})
|
||||
if 'site_contacts' in vals and self.child_ids:
|
||||
self._get_all_subtasks().write({'site_contacts': [Command.set(self.site_contacts.ids)]})
|
||||
if 'work_order_contacts' in vals and self.child_ids:
|
||||
self._get_all_subtasks().write({'work_order_contacts': [Command.set(self.work_order_contacts.ids)]})
|
||||
to_propagate.child_ids.write({"user_ids": vals["user_ids"]})
|
||||
if "site_contacts" in vals and self.child_ids:
|
||||
self._get_all_subtasks().write(
|
||||
{"site_contacts": [Command.set(self.site_contacts.ids)]}
|
||||
)
|
||||
if "work_order_contacts" in vals and self.child_ids:
|
||||
self._get_all_subtasks().write(
|
||||
{"work_order_contacts": [Command.set(self.work_order_contacts.ids)]}
|
||||
)
|
||||
return res
|
||||
|
||||
@api.depends('sale_order_id')
|
||||
@api.depends("sale_order_id")
|
||||
def _compute_relevant_order_lines(self):
|
||||
for rec in self:
|
||||
rec.relevant_order_lines = (
|
||||
rec.sale_order_id and rec.sale_order_id.get_relevant_order_lines(
|
||||
rec) or False)
|
||||
rec.sale_order_id
|
||||
and rec.sale_order_id.get_relevant_order_lines(rec)
|
||||
or False
|
||||
)
|
||||
|
||||
def _get_closed_stage_by_project(self):
|
||||
""" Gets the stage representing completed tasks for each project in
|
||||
"""Gets the stage representing completed tasks for each project in
|
||||
self.project_id. Copied from industry_fsm/.../project.py:217-221
|
||||
for consistency.
|
||||
|
||||
:returns: Dict of project.project -> project.task.type"""
|
||||
return {
|
||||
project:
|
||||
project: (
|
||||
project.type_ids.filtered(lambda stage: stage.is_closed)[:1]
|
||||
or project.type_ids[-1:]
|
||||
)
|
||||
for project in self.project_id
|
||||
}
|
||||
|
||||
@api.depends('parent_id.visit_id', 'project_id.is_fsm', 'project_id.allow_billable')
|
||||
@api.depends("parent_id.visit_id", "project_id.is_fsm", "project_id.allow_billable")
|
||||
def _compute_allow_billable(self):
|
||||
for rec in self:
|
||||
# If an FSM task has a parent that is linked to an SO line, then the parent is the billable one
|
||||
if rec.parent_id and not rec.parent_id.visit_id and rec.project_id and rec.project_id.is_fsm:
|
||||
if (
|
||||
rec.parent_id
|
||||
and not rec.parent_id.visit_id
|
||||
and rec.project_id
|
||||
and rec.project_id.is_fsm
|
||||
):
|
||||
rec.allow_billable = False
|
||||
else:
|
||||
rec.allow_billable = rec.project_id.allow_billable
|
||||
|
|
@ -157,7 +179,7 @@ class Task(models.Model):
|
|||
return self
|
||||
|
||||
def synchronize_name_fsm(self):
|
||||
""" Applies naming to the entire task tree for tasks that are part of this
|
||||
"""Applies naming to the entire task tree for tasks that are part of this
|
||||
recordset. Root tasks are named:
|
||||
|
||||
Partner Shipping Name - Sale Line Name (Template Name)
|
||||
|
|
@ -176,7 +198,7 @@ class Task(models.Model):
|
|||
if template:
|
||||
title = template.name
|
||||
elif rec.sale_line_id:
|
||||
name_parts = rec.sale_line_id.name.split('\n')
|
||||
name_parts = rec.sale_line_id.name.split("\n")
|
||||
title = name_parts and name_parts[0] or rec.sale_line_id.product_id.name
|
||||
elif rec.visit_id:
|
||||
title = rec.visit_id.label
|
||||
|
|
@ -187,7 +209,7 @@ class Task(models.Model):
|
|||
client_name = rec.sale_order_id.partner_shipping_id.name
|
||||
|
||||
if not rec.parent_id:
|
||||
rec.name = f"{rec.work_order_number} - {client_name} - " f"{title}"
|
||||
rec.name = f"{rec.work_order_number} - {client_name} - {title}"
|
||||
else:
|
||||
rec.name = title
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from odoo import models, fields, api, _, Command
|
||||
from odoo import models, fields, api, Command
|
||||
|
||||
|
||||
class TaskTemplate(models.Model):
|
||||
_name = 'project.task.template'
|
||||
_name = "project.task.template"
|
||||
_description = "Template for new project tasks"
|
||||
|
||||
@api.model
|
||||
|
|
@ -11,50 +11,47 @@ class TaskTemplate(models.Model):
|
|||
|
||||
name = fields.Char(string="Task Title", required=True)
|
||||
|
||||
description = fields.Html(string="Description")
|
||||
description = fields.Html()
|
||||
|
||||
assignees = fields.Many2many(
|
||||
comodel_name="res.users",
|
||||
string="Default Assignees",
|
||||
help="Employees assigned to tasks created from this template."
|
||||
help="Employees assigned to tasks created from this template.",
|
||||
)
|
||||
|
||||
customer = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Default Customer",
|
||||
help="Default customer for tasks created from this template."
|
||||
help="Default customer for tasks created from this template.",
|
||||
)
|
||||
|
||||
project = fields.Many2one(
|
||||
comodel_name="project.project",
|
||||
string="Default Project",
|
||||
help="Default project for tasks created from this template."
|
||||
help="Default project for tasks created from this template.",
|
||||
)
|
||||
|
||||
tags = fields.Many2many(
|
||||
comodel_name="project.tags",
|
||||
string="Default Tags",
|
||||
help="Default tags for tasks created from this template."
|
||||
help="Default tags for tasks created from this template.",
|
||||
)
|
||||
|
||||
parent = fields.Many2one(
|
||||
comodel_name="project.task.template",
|
||||
string="Parent Task Template",
|
||||
ondelete='cascade'
|
||||
ondelete="cascade",
|
||||
)
|
||||
|
||||
subtasks = fields.One2many(
|
||||
comodel_name="project.task.template",
|
||||
inverse_name="parent",
|
||||
string="Subtask Templates"
|
||||
string="Subtask Templates",
|
||||
)
|
||||
sequence = fields.Integer(string="Sequence")
|
||||
sequence = fields.Integer()
|
||||
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
index=1,
|
||||
default=_current_company
|
||||
comodel_name="res.company", string="Company", index=1, default=_current_company
|
||||
)
|
||||
|
||||
planned_hours = fields.Float("Initially Planned Hours")
|
||||
|
|
@ -69,50 +66,53 @@ class TaskTemplate(models.Model):
|
|||
|
||||
def action_open_task(self):
|
||||
return {
|
||||
'view_mode': 'form',
|
||||
'res_model': 'project.task.template',
|
||||
'res_id': self.id,
|
||||
'type': 'ir.actions.act_window',
|
||||
'context': self._context
|
||||
"view_mode": "form",
|
||||
"res_model": "project.task.template",
|
||||
"res_id": self.id,
|
||||
"type": "ir.actions.act_window",
|
||||
"context": self._context,
|
||||
}
|
||||
|
||||
@api.onchange('customer')
|
||||
@api.onchange("customer")
|
||||
def _onchange_customer(self):
|
||||
for rec in self:
|
||||
new_equipment_ids = [eq.id for eq in rec.equipment_ids if eq.partner_location_id == rec.customer]
|
||||
rec.write({'equipment_ids': [Command.set(new_equipment_ids)]})
|
||||
new_equipment_ids = [
|
||||
eq.id
|
||||
for eq in rec.equipment_ids
|
||||
if eq.partner_location_id == rec.customer
|
||||
]
|
||||
rec.write({"equipment_ids": [Command.set(new_equipment_ids)]})
|
||||
|
||||
def _prepare_new_task_values_from_self(self, project, name=False, parent_id=False):
|
||||
vals = {
|
||||
'project_id': project.id,
|
||||
'name': name or self.name,
|
||||
'description': self.description,
|
||||
'parent_id': parent_id,
|
||||
'user_ids': self.assignees.ids,
|
||||
'tag_ids': self.tags.ids,
|
||||
'allocated_hours': self.planned_hours,
|
||||
'sequence': self.sequence,
|
||||
'equipment_ids': [Command.set(self.equipment_ids.ids)] if self.equipment_ids else False,
|
||||
'partner_id': project.partner_id and project.partner_id.id,
|
||||
'company_id': self.company_id.id,
|
||||
"project_id": project.id,
|
||||
"name": name or self.name,
|
||||
"description": self.description,
|
||||
"parent_id": parent_id,
|
||||
"user_ids": self.assignees.ids,
|
||||
"tag_ids": self.tags.ids,
|
||||
"allocated_hours": self.planned_hours,
|
||||
"sequence": self.sequence,
|
||||
"equipment_ids": (
|
||||
[Command.set(self.equipment_ids.ids)] if self.equipment_ids else False
|
||||
),
|
||||
"partner_id": project.partner_id and project.partner_id.id,
|
||||
"company_id": self.company_id.id,
|
||||
}
|
||||
return vals
|
||||
|
||||
def create_task_from_self(self, project, name=False, parent_id=False):
|
||||
""" Create a project.task from this template and return it. Can be called on a RecordSet of multiple templates.
|
||||
"""Create a project.task from this template and return it. Can be called on a RecordSet of multiple templates.
|
||||
|
||||
:param project: project.project record the task should be added to
|
||||
:param name: name for the new task (defaults to template name)
|
||||
:param parent_id: parent task for the new task (none by default)
|
||||
:return: project.task record created from this template
|
||||
"""
|
||||
tasks = self.env['project.task']
|
||||
tasks = self.env["project.task"]
|
||||
for rec in self:
|
||||
vals = rec._prepare_new_task_values_from_self(project, name, parent_id)
|
||||
task = rec.env['project.task'].create(vals)
|
||||
task = rec.env["project.task"].create(vals)
|
||||
rec.subtasks.create_task_from_self(project, name=False, parent_id=task.id)
|
||||
tasks |= task
|
||||
return tasks
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6,6 +6,8 @@ class TaskCustomReport(models.AbstractModel):
|
|||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
vals = super()._get_report_values(docids, data)
|
||||
split_time_materials = self.env.company.split_time_from_materials_on_service_work_orders
|
||||
split_time_materials = (
|
||||
self.env.company.split_time_from_materials_on_service_work_orders
|
||||
)
|
||||
vals.update({"split_time_materials": split_time_materials})
|
||||
return vals
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="industry_fsm_report.task_custom_report" model="ir.actions.report">
|
||||
<field name="print_report_name">'%s %s' % (
|
||||
object.planned_date_begin.strftime('%Y-%m-%d') if object.planned_date_begin else time.strftime('%Y-%m-%d'),
|
||||
object.name
|
||||
)
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
<record id="industry_fsm_report.task_custom_report" model="ir.actions.report">
|
||||
<field name="print_report_name">'%s %s' % (
|
||||
object.planned_date_begin.strftime(
|
||||
'%Y-%m-%d') if object.planned_date_begin else time.strftime('%Y-%m-%d'),
|
||||
object.name
|
||||
)
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
@classmethod
|
||||
def _generate_project_manager_user(cls, name, login):
|
||||
group_ids = cls.__get_user_groups()
|
||||
user_group_project_manager = cls.env.ref('project.group_project_manager')
|
||||
user_group_fsm_manager = cls.env.ref('industry_fsm.group_fsm_manager')
|
||||
user_group_project_manager = cls.env.ref("project.group_project_manager")
|
||||
user_group_fsm_manager = cls.env.ref("industry_fsm.group_fsm_manager")
|
||||
group_ids.append(user_group_fsm_manager.id)
|
||||
group_ids.append(user_group_project_manager.id)
|
||||
|
||||
|
|
@ -21,118 +21,170 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
|
||||
@classmethod
|
||||
def __generate_user(cls, name, login, group_ids):
|
||||
return cls.env['res.users'].with_context({'no_reset_password': True}).create({
|
||||
'name': name,
|
||||
'login': login,
|
||||
'password': login,
|
||||
'email': f"{login}@test.co",
|
||||
'groups_id': [Command.set(group_ids)]
|
||||
})
|
||||
return (
|
||||
cls.env["res.users"]
|
||||
.with_context(no_reset_password=True)
|
||||
.create(
|
||||
{
|
||||
"name": name,
|
||||
"login": login,
|
||||
"password": login,
|
||||
"email": f"{login}@test.co",
|
||||
"groups_id": [Command.set(group_ids)],
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def __get_user_groups(cls):
|
||||
user_group_employee = cls.env.ref('base.group_user')
|
||||
user_group_project_user = cls.env.ref('project.group_project_user')
|
||||
user_group_fsm_user = cls.env.ref('industry_fsm.group_fsm_user')
|
||||
user_group_sales_user = cls.env.ref('sales_team.group_sale_salesman')
|
||||
user_group_sales_manager = cls.env.ref('sales_team.group_sale_manager')
|
||||
user_group_employee = cls.env.ref("base.group_user")
|
||||
user_group_project_user = cls.env.ref("project.group_project_user")
|
||||
user_group_fsm_user = cls.env.ref("industry_fsm.group_fsm_user")
|
||||
user_group_sales_user = cls.env.ref("sales_team.group_sale_salesman")
|
||||
user_group_sales_manager = cls.env.ref("sales_team.group_sale_manager")
|
||||
user_product_customer = cls.env.ref(
|
||||
'customer_product_code.group_product_customer_code_user',
|
||||
raise_if_not_found=False
|
||||
"customer_product_code.group_product_customer_code_user",
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
|
||||
group_ids = [user_group_employee.id,
|
||||
user_group_project_user.id,
|
||||
user_group_fsm_user.id,
|
||||
user_group_sales_manager.id,
|
||||
user_group_sales_user.id, ]
|
||||
group_ids = [
|
||||
user_group_employee.id,
|
||||
user_group_project_user.id,
|
||||
user_group_fsm_user.id,
|
||||
user_group_sales_manager.id,
|
||||
user_group_sales_user.id,
|
||||
]
|
||||
if user_product_customer:
|
||||
group_ids.append(user_product_customer.id)
|
||||
return group_ids
|
||||
|
||||
@classmethod
|
||||
def _generate_partner(cls, name: str = 'Test Company', company_type: str = 'company', parent=None,
|
||||
location_type='other'):
|
||||
""" Generates a partner with basic address filled in.
|
||||
def _generate_partner(
|
||||
cls,
|
||||
name: str = "Test Company",
|
||||
company_type: str = "company",
|
||||
parent=None,
|
||||
location_type="other",
|
||||
):
|
||||
"""Generates a partner with basic address filled in.
|
||||
|
||||
:param name: The partner's name.
|
||||
:param company_type: The type of partner, either 'company' or 'person' are accepted."""
|
||||
return cls.env['res.partner'].create({
|
||||
'name': name,
|
||||
'company_type': company_type,
|
||||
'street': '123 Street St.',
|
||||
'city': 'Montreal',
|
||||
'state_id': cls.env.ref('base.state_ca_qc').id,
|
||||
'country_id': cls.env.ref('base.ca').id,
|
||||
'parent_id': parent and parent.id or False,
|
||||
'type': location_type,
|
||||
})
|
||||
:param company_type: The type of partner, either 'company' or 'person' are accepted.
|
||||
"""
|
||||
return cls.env["res.partner"].create(
|
||||
{
|
||||
"name": name,
|
||||
"company_type": company_type,
|
||||
"street": "123 Street St.",
|
||||
"city": "Montreal",
|
||||
"state_id": cls.env.ref("base.state_ca_qc").id,
|
||||
"country_id": cls.env.ref("base.ca").id,
|
||||
"parent_id": parent and parent.id or False,
|
||||
"type": location_type,
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _generate_sale_order(cls, partner=None, client_order_ref='Test Order', equipment=None, shipping_location=None):
|
||||
def _generate_sale_order(
|
||||
cls,
|
||||
partner=None,
|
||||
client_order_ref="Test Order",
|
||||
equipment=None,
|
||||
shipping_location=None,
|
||||
):
|
||||
partner = partner or cls._generate_partner()
|
||||
vals = {'partner_id': partner.id,
|
||||
'client_order_ref': client_order_ref}
|
||||
vals = {"partner_id": partner.id, "client_order_ref": client_order_ref}
|
||||
if equipment:
|
||||
vals.update({'default_equipment_ids': [Command.set([equipment.id])]})
|
||||
vals.update({"default_equipment_ids": [Command.set([equipment.id])]})
|
||||
if shipping_location:
|
||||
vals.update({'partner_shipping_id': shipping_location.id})
|
||||
return cls.env['sale.order'].create(vals)
|
||||
vals.update({"partner_shipping_id": shipping_location.id})
|
||||
return cls.env["sale.order"].create(vals)
|
||||
|
||||
@classmethod
|
||||
def _generate_sale_order_line(cls, sale_order, product=None, qty=1.0, uom=None, price=100.0, tax_id=False):
|
||||
def _generate_sale_order_line(
|
||||
cls, sale_order, product=None, qty=1.0, uom=None, price=100.0, tax_id=False
|
||||
):
|
||||
if not product:
|
||||
product = cls._generate_product()
|
||||
return cls.env['sale.order.line'].create({
|
||||
'order_id': sale_order.id,
|
||||
'product_id': product.id,
|
||||
'product_uom_qty': qty,
|
||||
'product_uom': uom and uom.id or cls.env.ref("uom.product_uom_hour").id,
|
||||
'price_unit': price,
|
||||
'tax_id': tax_id,
|
||||
})
|
||||
return cls.env["sale.order.line"].create(
|
||||
{
|
||||
"order_id": sale_order.id,
|
||||
"product_id": product.id,
|
||||
"product_uom_qty": qty,
|
||||
"product_uom": uom and uom.id or cls.env.ref("uom.product_uom_hour").id,
|
||||
"price_unit": price,
|
||||
"tax_id": tax_id,
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _generate_equipment(cls, name='test equipment', partner=None):
|
||||
return cls.env['bemade_fsm.equipment'].create({
|
||||
'name': name,
|
||||
'partner_location_id': partner and partner.id or False,
|
||||
})
|
||||
def _generate_equipment(cls, name="test equipment", partner=None):
|
||||
return cls.env["bemade_fsm.equipment"].create(
|
||||
{
|
||||
"name": name,
|
||||
"partner_location_id": partner and partner.id or False,
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _generate_product(cls, name='Test Product', product_type='service', service_tracking='task_global_project',
|
||||
project=None, task_template=None, service_policy='delivered_manual', uom=None):
|
||||
if 'project' in service_tracking and not project:
|
||||
def _generate_product(
|
||||
cls,
|
||||
name="Test Product",
|
||||
product_type="service",
|
||||
service_tracking="task_global_project",
|
||||
project=None,
|
||||
task_template=None,
|
||||
service_policy="delivered_manual",
|
||||
uom=None,
|
||||
):
|
||||
if "project" in service_tracking and not project:
|
||||
project = cls.env.ref("industry_fsm.fsm_project")
|
||||
uom_id = uom and uom.id or cls.env.ref("uom.product_uom_hour").id or False
|
||||
return cls.env['product.product'].create({
|
||||
'name': name,
|
||||
'type': product_type,
|
||||
'service_tracking': service_tracking,
|
||||
'service_type': 'timesheet',
|
||||
'project_id': service_tracking in ('task_global_project', 'project_only') and project.id or False,
|
||||
'project_template_id': service_tracking == 'task_in_project' and project.id or False,
|
||||
'task_template_id': task_template and task_template.id or False,
|
||||
'service_policy': service_policy,
|
||||
'uom_id': uom_id,
|
||||
'uom_po_id': uom_id,
|
||||
})
|
||||
return cls.env["product.product"].create(
|
||||
{
|
||||
"name": name,
|
||||
"type": product_type,
|
||||
"service_tracking": service_tracking,
|
||||
"service_type": "timesheet",
|
||||
"project_id": (
|
||||
service_tracking in ("task_global_project", "project_only")
|
||||
and project.id
|
||||
or False
|
||||
),
|
||||
"project_template_id": (
|
||||
service_tracking == "task_in_project" and project.id or False
|
||||
),
|
||||
"task_template_id": task_template and task_template.id or False,
|
||||
"service_policy": service_policy,
|
||||
"uom_id": uom_id,
|
||||
"uom_po_id": uom_id,
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _generate_fsm_project(cls, name='Test Project'):
|
||||
return cls.env['project.project'].create({
|
||||
'name': name,
|
||||
'allow_material': True,
|
||||
'allow_timesheets': True,
|
||||
'allow_quotations': True,
|
||||
'allow_worksheets': True,
|
||||
'is_fsm': True,
|
||||
})
|
||||
def _generate_fsm_project(cls, name="Test Project"):
|
||||
return cls.env["project.project"].create(
|
||||
{
|
||||
"name": name,
|
||||
"allow_material": True,
|
||||
"allow_timesheets": True,
|
||||
"allow_quotations": True,
|
||||
"allow_worksheets": True,
|
||||
"is_fsm": True,
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _generate_task_template(cls, parent=None, structure=None, names=None, planned_hours=1,
|
||||
equipment=None, customer=None):
|
||||
""" Generates a task template with the specified structure and naming.
|
||||
def _generate_task_template(
|
||||
cls,
|
||||
parent=None,
|
||||
structure=None,
|
||||
names=None,
|
||||
planned_hours=1,
|
||||
equipment=None,
|
||||
customer=None,
|
||||
):
|
||||
"""Generates a task template with the specified structure and naming.
|
||||
|
||||
:param parent: The parent task template for the top-level task template being generated
|
||||
:param structure: A list of integers describing the number of tasks for each level of descendants. An empty
|
||||
|
|
@ -142,48 +194,64 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
by a sequential integer for its level. Child 1, Child 2, Grandchild 1, etc. If no names argument
|
||||
is passed, a default ['Task Template'] argument will be used.
|
||||
:param planned_hours: The number of planned hours for the top-level task template being generated.
|
||||
:param equipment: The equipment to add as linked equipment to the task template."""
|
||||
:param equipment: The equipment to add as linked equipment to the task template.
|
||||
"""
|
||||
if not names:
|
||||
names = ['Task Template']
|
||||
names = ["Task Template"]
|
||||
if not structure:
|
||||
structure = []
|
||||
if len(structure) != len(names) - 1:
|
||||
raise ValueError("The length of the structure argument must contain exactly one element less than the "
|
||||
"names argument.")
|
||||
raise ValueError(
|
||||
"The length of the structure argument must contain exactly one element"
|
||||
" less than the names argument."
|
||||
)
|
||||
name = names.pop(0)
|
||||
template = cls.env['project.task.template'].create({
|
||||
'name': name,
|
||||
'parent': parent and parent.id or False,
|
||||
'planned_hours': planned_hours,
|
||||
'equipment_ids': [Command.set(equipment and [equipment.id] or [])],
|
||||
'customer': customer and customer.id or False,
|
||||
})
|
||||
template = cls.env["project.task.template"].create(
|
||||
{
|
||||
"name": name,
|
||||
"parent": parent and parent.id or False,
|
||||
"planned_hours": planned_hours,
|
||||
"equipment_ids": [Command.set(equipment and [equipment.id] or [])],
|
||||
"customer": customer and customer.id or False,
|
||||
}
|
||||
)
|
||||
parent = template
|
||||
while structure:
|
||||
subtasks = []
|
||||
for i in range(0, structure[0]):
|
||||
subtasks.append(cls.env['project.task.template'].create({
|
||||
'parent': parent.id,
|
||||
'name': names[0] + f" {i}",
|
||||
}))
|
||||
subtasks.append(
|
||||
cls.env["project.task.template"].create(
|
||||
{
|
||||
"parent": parent.id,
|
||||
"name": names[0] + f" {i}",
|
||||
}
|
||||
)
|
||||
)
|
||||
structure.pop(0)
|
||||
names.pop(0)
|
||||
parent = subtasks[0]
|
||||
return template
|
||||
|
||||
def _invoice_sale_order(self, so):
|
||||
wiz = self.env['sale.advance.payment.inv'].with_context(
|
||||
{'active_ids': [so.id]}).create({})
|
||||
wiz = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(active_ids=[so.id])
|
||||
.create({})
|
||||
)
|
||||
wiz.create_invoices()
|
||||
inv = so.invoice_ids[-1]
|
||||
inv.action_post()
|
||||
return inv
|
||||
|
||||
def _generate_visit(self, sale_order, label="Test Label"):
|
||||
return self.env['bemade_fsm.visit'].create([{
|
||||
'sale_order_id': sale_order.id,
|
||||
'label': label,
|
||||
}])
|
||||
return self.env["bemade_fsm.visit"].create(
|
||||
[
|
||||
{
|
||||
"sale_order_id": sale_order.id,
|
||||
"label": label,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
def _generate_so_with_one_visit_two_lines(self):
|
||||
so = self._generate_sale_order()
|
||||
|
|
@ -198,8 +266,10 @@ class BemadeFSMBaseTest(TransactionCase):
|
|||
def _generate_so_with_one_visit_two_lines_and_descendants(self):
|
||||
so = self._generate_sale_order()
|
||||
visit = self._generate_visit(sale_order=so)
|
||||
task_template = self._generate_task_template(structure=[2, 2, 2],
|
||||
names=['Parent', 'Child', 'Grandchild', 'Great-grandchild'])
|
||||
task_template = self._generate_task_template(
|
||||
structure=[2, 2, 2],
|
||||
names=["Parent", "Child", "Grandchild", "Great-grandchild"],
|
||||
)
|
||||
product = self._generate_product(task_template=task_template)
|
||||
sol1 = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
sol2 = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from odoo.tests.common import HttpCase, tagged
|
||||
from odoo.tests.common import tagged
|
||||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
from odoo import Command
|
||||
from odoo.exceptions import MissingError
|
||||
|
|
@ -8,14 +8,14 @@ from odoo.exceptions import MissingError
|
|||
class TestEquipment(BemadeFSMBaseTest):
|
||||
def test_crud(self):
|
||||
partner_company = self._generate_partner()
|
||||
partner_contact = self._generate_partner('Site Contact', 'person', partner_company)
|
||||
equipment = self._generate_equipment('Test Equipment 1', partner_company)
|
||||
self._generate_partner("Site Contact", "person", partner_company)
|
||||
equipment = self._generate_equipment("Test Equipment 1", partner_company)
|
||||
|
||||
# Just make sure the basic ORM stuff is OK
|
||||
self.assertTrue(equipment in partner_company.equipment_ids)
|
||||
self.assertTrue(len(partner_company.equipment_ids) == 1)
|
||||
|
||||
# Delete should cascade
|
||||
partner_company.write({'equipment_ids': [Command.set([])]})
|
||||
partner_company.write({"equipment_ids": [Command.set([])]})
|
||||
with self.assertRaises(MissingError):
|
||||
equipment.name
|
||||
_ = equipment.name
|
||||
|
|
|
|||
|
|
@ -1,34 +1,38 @@
|
|||
from odoo.tests import TransactionCase, HttpCase, tagged, Form
|
||||
from odoo.tests import tagged, Form
|
||||
from odoo import Command
|
||||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class SaleOrderFSMContactsCase(BemadeFSMBaseTest):
|
||||
|
||||
def test_site_contacts(self):
|
||||
parent_co = self._generate_partner('Parent Co')
|
||||
contact_1 = self._generate_partner('Contact 1', 'person', parent_co)
|
||||
contact_2 = self._generate_partner('Contact 2', 'person', parent_co)
|
||||
parent_co = self._generate_partner("Parent Co")
|
||||
contact_1 = self._generate_partner("Contact 1", "person", parent_co)
|
||||
contact_2 = self._generate_partner("Contact 2", "person", parent_co)
|
||||
|
||||
# Make sure the SO pulls the defaults from the partner correctly
|
||||
parent_co.write({'site_contacts': [Command.set([contact_1.id, contact_2.id])]})
|
||||
parent_co.write({"site_contacts": [Command.set([contact_1.id, contact_2.id])]})
|
||||
so = self._generate_sale_order(parent_co)
|
||||
self.assertTrue(so.site_contacts == 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([contact_1.id])]})
|
||||
so.write({"site_contacts": [Command.set([contact_1.id])]})
|
||||
self.assertTrue(contact_1 in so.site_contacts)
|
||||
self.assertTrue(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)
|
||||
self.assertTrue(
|
||||
so.site_contacts != so.partner_id.site_contacts
|
||||
and len(so.partner_id.site_contacts) == 2
|
||||
)
|
||||
|
||||
def test_default_workorder_contacts(self):
|
||||
parent_co = self._generate_partner('Parent Co')
|
||||
contact_1 = self._generate_partner('Contact 1', 'person', parent_co)
|
||||
contact_2 = self._generate_partner('Contact 2', 'person', parent_co)
|
||||
parent_co = self._generate_partner("Parent Co")
|
||||
contact_1 = self._generate_partner("Contact 1", "person", parent_co)
|
||||
contact_2 = self._generate_partner("Contact 2", "person", parent_co)
|
||||
|
||||
# Make sure the SO pulls the defaults from the partner correctly
|
||||
parent_co.write({'work_order_contacts': [Command.set([contact_1.id, contact_2.id])]})
|
||||
parent_co.write(
|
||||
{"work_order_contacts": [Command.set([contact_1.id, contact_2.id])]}
|
||||
)
|
||||
so = self._generate_sale_order(parent_co)
|
||||
self.assertTrue(contact_1 in parent_co.work_order_contacts)
|
||||
self.assertTrue(contact_2 in parent_co.work_order_contacts)
|
||||
|
|
@ -36,42 +40,74 @@ class SaleOrderFSMContactsCase(BemadeFSMBaseTest):
|
|||
self.assertTrue(contact_2 in so.work_order_contacts)
|
||||
|
||||
# Make sure setting the work order contacts on the SO doesn't feed back to the partner
|
||||
so.write({'work_order_contacts': [Command.set([contact_1.id])]})
|
||||
so.write({"work_order_contacts": [Command.set([contact_1.id])]})
|
||||
self.assertTrue(contact_1 in so.work_order_contacts)
|
||||
self.assertTrue(contact_2 not in so.work_order_contacts)
|
||||
self.assertTrue(
|
||||
so.work_order_contacts != so.partner_id.work_order_contacts and len(so.partner_id.work_order_contacts) == 2)
|
||||
so.work_order_contacts != so.partner_id.work_order_contacts
|
||||
and len(so.partner_id.work_order_contacts) == 2
|
||||
)
|
||||
|
||||
def test_multilayer_site_contacts(self):
|
||||
parent_co = self._generate_partner('Parent Co')
|
||||
shipping_location = self._generate_partner('Shipping Location', 'company', parent_co, 'delivery')
|
||||
wo_contact_1 = self._generate_partner('WO Contact 1', 'person', shipping_location)
|
||||
wo_contact_2 = self._generate_partner('WO Contact 2', 'person', shipping_location)
|
||||
site_contact_1 = self._generate_partner('Site Contact 1', 'person', shipping_location)
|
||||
site_contact_2 = self._generate_partner('Site Contact 2', 'person', shipping_location)
|
||||
shipping_location.write({
|
||||
'work_order_contacts': [Command.set([wo_contact_1.id, wo_contact_2.id])],
|
||||
'site_contacts': [Command.set([site_contact_1.id, site_contact_2.id])]
|
||||
})
|
||||
parent_co = self._generate_partner("Parent Co")
|
||||
shipping_location = self._generate_partner(
|
||||
"Shipping Location", "company", parent_co, "delivery"
|
||||
)
|
||||
wo_contact_1 = self._generate_partner(
|
||||
"WO Contact 1", "person", shipping_location
|
||||
)
|
||||
wo_contact_2 = self._generate_partner(
|
||||
"WO Contact 2", "person", shipping_location
|
||||
)
|
||||
site_contact_1 = self._generate_partner(
|
||||
"Site Contact 1", "person", shipping_location
|
||||
)
|
||||
site_contact_2 = self._generate_partner(
|
||||
"Site Contact 2", "person", shipping_location
|
||||
)
|
||||
shipping_location.write(
|
||||
{
|
||||
"work_order_contacts": [
|
||||
Command.set([wo_contact_1.id, wo_contact_2.id])
|
||||
],
|
||||
"site_contacts": [Command.set([site_contact_1.id, site_contact_2.id])],
|
||||
}
|
||||
)
|
||||
|
||||
so = self._generate_sale_order(parent_co)
|
||||
so.write({'partner_shipping_id': shipping_location.id})
|
||||
so.write({"partner_shipping_id": shipping_location.id})
|
||||
|
||||
self.assertEqual(so.site_contacts, shipping_location.site_contacts)
|
||||
self.assertEqual(so.work_order_contacts, shipping_location.work_order_contacts)
|
||||
|
||||
def test_onchange_shipping_address(self):
|
||||
self.env.user.groups_id += self.env.ref('account.group_delivery_invoice_address')
|
||||
parent_co = self._generate_partner('Parent Co')
|
||||
shipping_location = self._generate_partner('Shipping Location', 'company', parent_co, 'delivery')
|
||||
wo_contact_1 = self._generate_partner('WO Contact 1', 'person', shipping_location)
|
||||
wo_contact_2 = self._generate_partner('WO Contact 2', 'person', shipping_location)
|
||||
site_contact_1 = self._generate_partner('Site Contact 1', 'person', shipping_location)
|
||||
site_contact_2 = self._generate_partner('Site Contact 2', 'person', shipping_location)
|
||||
shipping_location.write({
|
||||
'work_order_contacts': [Command.set([wo_contact_1.id, wo_contact_2.id])],
|
||||
'site_contacts': [Command.set([site_contact_1.id, site_contact_2.id])]
|
||||
})
|
||||
self.env.user.groups_id += self.env.ref(
|
||||
"account.group_delivery_invoice_address"
|
||||
)
|
||||
parent_co = self._generate_partner("Parent Co")
|
||||
shipping_location = self._generate_partner(
|
||||
"Shipping Location", "company", parent_co, "delivery"
|
||||
)
|
||||
wo_contact_1 = self._generate_partner(
|
||||
"WO Contact 1", "person", shipping_location
|
||||
)
|
||||
wo_contact_2 = self._generate_partner(
|
||||
"WO Contact 2", "person", shipping_location
|
||||
)
|
||||
site_contact_1 = self._generate_partner(
|
||||
"Site Contact 1", "person", shipping_location
|
||||
)
|
||||
site_contact_2 = self._generate_partner(
|
||||
"Site Contact 2", "person", shipping_location
|
||||
)
|
||||
shipping_location.write(
|
||||
{
|
||||
"work_order_contacts": [
|
||||
Command.set([wo_contact_1.id, wo_contact_2.id])
|
||||
],
|
||||
"site_contacts": [Command.set([site_contact_1.id, site_contact_2.id])],
|
||||
}
|
||||
)
|
||||
|
||||
so = self._generate_sale_order(parent_co)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from odoo.tests import TransactionCase, tagged, Form
|
||||
from odoo.tests import tagged
|
||||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
from datetime import date, timedelta
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
@tagged("-at_install", "post_install")
|
||||
class FSMVisitTest(BemadeFSMBaseTest):
|
||||
|
||||
def test_create_visit_sets_name_on_section(self):
|
||||
so = self._generate_sale_order()
|
||||
self._generate_sale_order_line(sale_order=so)
|
||||
|
|
@ -38,7 +37,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
visit = self._generate_visit(so)
|
||||
self._generate_sale_order_line(so)
|
||||
so.action_confirm()
|
||||
task = so.order_line.filtered(lambda l: l.task_id).task_id
|
||||
task = so.order_line.filtered(lambda line: line.task_id).task_id
|
||||
|
||||
task.action_fsm_validate()
|
||||
|
||||
|
|
@ -49,7 +48,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
visit = self._generate_visit(so)
|
||||
self._generate_sale_order_line(so)
|
||||
so.action_confirm()
|
||||
task = so.order_line.filtered(lambda l: l.task_id).task_id
|
||||
task = so.order_line.filtered(lambda line: line.task_id).task_id
|
||||
task.action_fsm_validate()
|
||||
|
||||
self._invoice_sale_order(so)
|
||||
|
|
@ -65,7 +64,10 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
self.assertTrue(visit_task)
|
||||
visit_subtasks = visit_task.child_ids
|
||||
self.assertTrue(
|
||||
visit_subtasks and sol1.task_id in visit_subtasks and sol2.task_id in visit_subtasks)
|
||||
visit_subtasks
|
||||
and sol1.task_id in visit_subtasks
|
||||
and sol2.task_id in visit_subtasks
|
||||
)
|
||||
|
||||
def test_visit_task_gets_correct_due_date_on_confirmation(self):
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
|
|
@ -94,7 +96,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
self.assertEqual(visit_task.allocated_hours, 8.0)
|
||||
|
||||
def test_adding_visit_creates_one_sale_order_line(self):
|
||||
partner = self._generate_partner()
|
||||
self._generate_partner()
|
||||
so = self._generate_sale_order()
|
||||
self._generate_sale_order_line(sale_order=so)
|
||||
self._generate_sale_order_line(sale_order=so)
|
||||
|
|
@ -104,7 +106,12 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
self.assertEqual(len(so.order_line), 3)
|
||||
|
||||
def test_marking_visit_task_done_completes_descendants(self):
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines_and_descendants()
|
||||
(
|
||||
so,
|
||||
visit,
|
||||
sol1,
|
||||
sol2,
|
||||
) = self._generate_so_with_one_visit_two_lines_and_descendants()
|
||||
so.action_confirm()
|
||||
parent = visit.task_id
|
||||
|
||||
|
|
@ -113,7 +120,7 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
self._assert_is_done(parent)
|
||||
|
||||
def _assert_is_done(self, task):
|
||||
""" Recursively assert all tasks in a hierarchy are complete """
|
||||
"""Recursively assert all tasks in a hierarchy are complete"""
|
||||
self.assertTrue(task.is_closed)
|
||||
for child in task.child_ids:
|
||||
self._assert_is_done(child)
|
||||
|
|
@ -127,11 +134,11 @@ class FSMVisitTest(BemadeFSMBaseTest):
|
|||
self.assertEqual(len(so.order_line), 3)
|
||||
|
||||
def test_confirming_so_names_visit_properly(self):
|
||||
""" Visits should be named <SO NUMBER> - Visit <visit #> - <visit label>"""
|
||||
"""Visits should be named <SO NUMBER> - Visit <visit #> - <visit label>"""
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
so.name = "SO12345"
|
||||
so.action_confirm()
|
||||
task = visit.task_id
|
||||
|
||||
supposed_name = f"SVR12345-1 - Test Company - Test Label"
|
||||
supposed_name = "SVR12345-1 - Test Company - Test Label"
|
||||
self.assertEqual(task.name, supposed_name)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
from .test_task_template import BemadeFSMBaseTest
|
||||
from odoo.tests.common import tagged, HttpCase, Form
|
||||
from odoo.tests.common import tagged, Form
|
||||
from odoo import Command
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestSalesOrder(BemadeFSMBaseTest):
|
||||
@tagged('-at_install', 'post_install')
|
||||
@tagged("-at_install", "post_install")
|
||||
def test_order_confirmation_simple_template(self):
|
||||
""" Confirming the order should create a task in the global project based on the task template. """
|
||||
"""Confirming the order should create a task in the global project based on the
|
||||
task template."""
|
||||
partner = self._generate_partner()
|
||||
so = self._generate_sale_order(partner=partner)
|
||||
task_template = self._generate_task_template(planned_hours=8)
|
||||
|
|
@ -24,10 +25,10 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
def test_task_template_tree_order_confirmation(self):
|
||||
partner = self._generate_partner()
|
||||
so = self._generate_sale_order(partner=partner)
|
||||
parent_template = self._generate_task_template(structure=[2, 1],
|
||||
names=['Parent Template',
|
||||
'Child Template',
|
||||
'Grandchild Template'])
|
||||
parent_template = self._generate_task_template(
|
||||
structure=[2, 1],
|
||||
names=["Parent Template", "Child Template", "Grandchild Template"],
|
||||
)
|
||||
child_template_1 = parent_template.subtasks[0]
|
||||
child_template_2 = parent_template.subtasks[1]
|
||||
grandchild_template = parent_template.subtasks[0].subtasks[0]
|
||||
|
|
@ -47,7 +48,7 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(grandchild_template.name, gc.name)
|
||||
|
||||
def test_order_confirmation_single_equipment(self):
|
||||
""" The equipment selected on the SO should transfer to the task."""
|
||||
"""The equipment selected on the SO should transfer to the task."""
|
||||
partner = self._generate_partner()
|
||||
equipment = self._generate_equipment(partner=partner)
|
||||
so = self._generate_sale_order(partner=partner, equipment=equipment)
|
||||
|
|
@ -65,18 +66,22 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(task2.equipment_ids[0], equipment)
|
||||
|
||||
def test_order_confirmation_multiple_equipment(self):
|
||||
""" All equipment items should flow from the sale order line to the final task """
|
||||
"""All equipment items should flow from the sale order line to the final task"""
|
||||
partner = self._generate_partner()
|
||||
for i in range(5):
|
||||
self._generate_equipment(partner=partner)
|
||||
sale_order = self._generate_sale_order(
|
||||
partner=partner) # No default equipment since more than 3 on partner
|
||||
sol1, sol2, sol3 = [self._generate_sale_order_line(sale_order=sale_order) for i
|
||||
in range(3)]
|
||||
partner=partner
|
||||
) # No default equipment since more than 3 on partner
|
||||
sol1, sol2, sol3 = [
|
||||
self._generate_sale_order_line(sale_order=sale_order) for _ in range(3)
|
||||
]
|
||||
sol1.equipment_ids = [
|
||||
Command.set([partner.equipment_ids[i].id for i in range(2)])]
|
||||
Command.set([partner.equipment_ids[i].id for i in range(2)])
|
||||
]
|
||||
sol3.equipment_ids = [
|
||||
Command.set([partner.equipment_ids[i].id for i in range(2, 5)])]
|
||||
Command.set([partner.equipment_ids[i].id for i in range(2, 5)])
|
||||
]
|
||||
|
||||
sale_order.action_confirm()
|
||||
|
||||
|
|
@ -85,7 +90,8 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(sol3.equipment_ids, sol3.task_id.equipment_ids)
|
||||
|
||||
def test_task_template_with_equipment_flow(self):
|
||||
""" The equipment selected on a task template should flow down to the task created on SO confirmation."""
|
||||
"""The equipment selected on a task template should flow down to the task
|
||||
created on SO confirmation."""
|
||||
partner = self._generate_partner()
|
||||
equipment = self._generate_equipment(partner=partner)
|
||||
so = self._generate_sale_order(partner=partner)
|
||||
|
|
@ -98,7 +104,8 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(sol.task_id.equipment_ids[0], equipment)
|
||||
|
||||
def test_sale_order_line_gets_default_equipment(self):
|
||||
""" Sale order lines created on an SO with default equipment set should inherit that default equipment. """
|
||||
"""Sale order lines created on a SO with default equipment set should inherit
|
||||
that default equipment."""
|
||||
partner = self._generate_partner()
|
||||
self._generate_equipment(partner=partner)
|
||||
sale_order = self._generate_sale_order(partner=partner)
|
||||
|
|
@ -108,7 +115,7 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(sol.equipment_ids, partner.equipment_ids)
|
||||
|
||||
def test_sale_order_gets_correct_default_equipment_from_partner(self):
|
||||
""" Should pick up equipment from the partner."""
|
||||
"""Should pick up equipment from the partner."""
|
||||
partner = self._generate_partner()
|
||||
self._generate_equipment(partner=partner)
|
||||
|
||||
|
|
@ -126,7 +133,9 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
|
||||
self.assertEqual(sale_order.default_equipment_ids, parent.owned_equipment_ids)
|
||||
|
||||
def test_sale_order_no_default_equipment_with_more_than_three_owned_on_partner(self):
|
||||
def test_sale_order_no_default_equipment_with_more_than_three_owned_on_partner(
|
||||
self,
|
||||
):
|
||||
parent = self._generate_partner()
|
||||
child = self._generate_partner(parent=parent)
|
||||
for i in range(4):
|
||||
|
|
@ -150,7 +159,7 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
|
||||
def test_sale_order_prioritize_shipping_location_equipments(self):
|
||||
parent = self._generate_partner()
|
||||
child = self._generate_partner(parent=parent, location_type='delivery')
|
||||
child = self._generate_partner(parent=parent, location_type="delivery")
|
||||
self._generate_equipment(partner=parent)
|
||||
self._generate_equipment(partner=child)
|
||||
|
||||
|
|
@ -171,11 +180,13 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertEqual(line.equipment_ids, partner.equipment_ids)
|
||||
|
||||
def test_task_mark_done(self):
|
||||
""" Marking the task linked to an SO line should mark the line delivered. Marking sub-tasks done should not."""
|
||||
"""Marking the task linked to a SO line should mark the line delivered.
|
||||
Marking sub-tasks done should not."""
|
||||
partner = self._generate_partner()
|
||||
so = self._generate_sale_order(partner=partner)
|
||||
task_template = self._generate_task_template(structure=[2],
|
||||
names=["Parent Task", "Subtask"])
|
||||
task_template = self._generate_task_template(
|
||||
structure=[2], names=["Parent Task", "Subtask"]
|
||||
)
|
||||
product = self._generate_product(task_template=task_template)
|
||||
sol = self._generate_sale_order_line(so, product=product)
|
||||
so.action_confirm()
|
||||
|
|
@ -186,20 +197,24 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
subtasks.action_fsm_validate(True)
|
||||
self.assertEqual(sol.qty_delivered, 0)
|
||||
|
||||
# Marking the top-level tasks done should set the delivered quantity to some non-zero value based on the UOM
|
||||
# Marking the top-level tasks done should set the delivered quantity to some
|
||||
# non-zero value based on the UOM
|
||||
parent_task.action_fsm_validate(True)
|
||||
self.assertTrue(sol.qty_delivered != 0)
|
||||
|
||||
def test_task_contacts_through_sale_order(self):
|
||||
""" Make sure the site contacts and work order contacts transfer correctly from the SO to the task."""
|
||||
"""Make sure the site contacts and work order contacts transfer correctly
|
||||
from the SO to the task."""
|
||||
|
||||
partner = self._generate_partner()
|
||||
contact1 = self._generate_partner('Site contact', 'person', partner)
|
||||
contact2 = self._generate_partner('Work order contact', 'person', partner)
|
||||
partner.write({
|
||||
'site_contacts': [Command.set([contact1.id])],
|
||||
'work_order_contacts': [Command.set([contact2.id])],
|
||||
})
|
||||
contact1 = self._generate_partner("Site contact", "person", partner)
|
||||
contact2 = self._generate_partner("Work order contact", "person", partner)
|
||||
partner.write(
|
||||
{
|
||||
"site_contacts": [Command.set([contact1.id])],
|
||||
"work_order_contacts": [Command.set([contact2.id])],
|
||||
}
|
||||
)
|
||||
so = self._generate_sale_order(partner)
|
||||
product = self._generate_product()
|
||||
sol = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
|
|
@ -213,7 +228,7 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
|
||||
def test_tasks_created_at_order_confirmation_have_no_assignees(self):
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
user = self._generate_project_user(name="User", login='login')
|
||||
user = self._generate_project_user(name="User", login="login")
|
||||
|
||||
# We test as a specific user since testing as root may not produce the error
|
||||
so.with_user(user).action_confirm()
|
||||
|
|
@ -224,13 +239,15 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertFalse(visit_task.user_ids)
|
||||
self.assertFalse(subtask1.user_ids)
|
||||
self.assertFalse(subtask2.user_ids)
|
||||
|
||||
|
||||
def test_long_line_name_overflows_to_task_description(self):
|
||||
so = self._generate_sale_order()
|
||||
product = self._generate_product()
|
||||
product.description_sale = "This is a long product description.\n" \
|
||||
"It even spans multiple lines.\n" \
|
||||
"One could find this annoying in a task name."
|
||||
product.description_sale = (
|
||||
"This is a long product description.\n"
|
||||
"It even spans multiple lines.\n"
|
||||
"One could find this annoying in a task name."
|
||||
)
|
||||
|
||||
sol = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
|
||||
|
|
@ -241,12 +258,15 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
self.assertFalse("It even spans multiple lines." in task.name)
|
||||
self.assertFalse("One could find this annoying in a task name." in task.name)
|
||||
self.assertTrue("It even spans multiple lines." in task.description)
|
||||
self.assertTrue("One could find this annoying in a task name."
|
||||
in task.description)
|
||||
self.assertTrue(
|
||||
"One could find this annoying in a task name." in task.description
|
||||
)
|
||||
|
||||
def test_subtask_templates_no_description_if_blank_on_template(self):
|
||||
so = self._generate_sale_order()
|
||||
template = self._generate_task_template(structure=[5], names=['Parent', 'Child'])
|
||||
template = self._generate_task_template(
|
||||
structure=[5], names=["Parent", "Child"]
|
||||
)
|
||||
template.description = ""
|
||||
template.subtasks[0].description = "Some fixed description"
|
||||
for t in template.subtasks[1:]:
|
||||
|
|
@ -257,13 +277,15 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
so.action_confirm()
|
||||
|
||||
task = sol.task_id
|
||||
self.assertEqual(task.child_ids[0].description, template.subtasks[0].description)
|
||||
self.assertEqual(
|
||||
task.child_ids[0].description, template.subtasks[0].description
|
||||
)
|
||||
for t in task.child_ids[1:]:
|
||||
self.assertFalse(t.description)
|
||||
|
||||
def test_duplicate_sale_order_duplicates_visits(self):
|
||||
""" Duplicated sales orders should have visits tied to their SO lines as in the original. The copied visits
|
||||
should not have approximate dates set, however."""
|
||||
"""Duplicated sales orders should have visits tied to their SO lines as in the
|
||||
original. The copied visits should not have approximate dates set, however."""
|
||||
so, visit, line1, line2 = self._generate_so_with_one_visit_two_lines()
|
||||
|
||||
so2 = so.copy()
|
||||
|
|
@ -278,19 +300,19 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
so = self._generate_sale_order()
|
||||
so.company_id.create_default_fsm_visit = True
|
||||
product = self._generate_product()
|
||||
sol = self._generate_sale_order_line(so, product)
|
||||
self._generate_sale_order_line(so, product)
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
visit_line = so.order_line.sorted('sequence')[0]
|
||||
visit_line = so.order_line.sorted("sequence")[0]
|
||||
self.assertTrue(so.visit_ids)
|
||||
self.assertEqual(visit_line.visit_id, so.visit_ids)
|
||||
|
||||
def test_confirming_sale_order_with_visit_creates_no_new_lines(self):
|
||||
so = self._generate_sale_order()
|
||||
so.company_id.create_default_fsm_visit = True
|
||||
product = self._generate_product()
|
||||
visit = self._generate_visit(so)
|
||||
self._generate_product()
|
||||
self._generate_visit(so)
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
|
|
@ -300,9 +322,8 @@ class TestSalesOrder(BemadeFSMBaseTest):
|
|||
so = self._generate_sale_order()
|
||||
so.company_id.create_default_fsm_visit = False
|
||||
product = self._generate_product()
|
||||
sol = self._generate_sale_order_line(so, product)
|
||||
self._generate_sale_order_line(so, product)
|
||||
|
||||
so.action_confirm()
|
||||
|
||||
visit_line = so.order_line.sorted('sequence')[0]
|
||||
self.assertFalse(so.visit_ids)
|
||||
|
|
|
|||
|
|
@ -1,54 +1,48 @@
|
|||
from odoo.tests import TransactionCase, Form, tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestSettings(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.test_partner_co = cls.env['res.partner'].create({
|
||||
'name': 'Test Co',
|
||||
})
|
||||
cls.test_co = cls.env['res.company'].create({
|
||||
'name': 'Test Co',
|
||||
'country_id': cls.env.ref('base.ca').id,
|
||||
|
||||
})
|
||||
cls.test_partner_co = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Test Co",
|
||||
}
|
||||
)
|
||||
cls.test_co = cls.env["res.company"].create(
|
||||
{
|
||||
"name": "Test Co",
|
||||
"country_id": cls.env.ref("base.ca").id,
|
||||
}
|
||||
)
|
||||
cls.env.user.company_id = cls.test_co
|
||||
|
||||
def test_enabling_separate_time_on_work_orders(self):
|
||||
wizard = self.env['res.config.settings'].create({})
|
||||
self.assertFalse(
|
||||
self.test_co.split_time_from_materials_on_service_work_orders
|
||||
)
|
||||
wizard = self.env["res.config.settings"].create({})
|
||||
self.assertFalse(self.test_co.split_time_from_materials_on_service_work_orders)
|
||||
with Form(wizard) as form:
|
||||
form.separate_time_on_work_orders = True
|
||||
self.assertTrue(
|
||||
self.test_co.split_time_from_materials_on_service_work_orders
|
||||
)
|
||||
self.assertTrue(self.test_co.split_time_from_materials_on_service_work_orders)
|
||||
|
||||
def test_disabling_separate_time_on_work_orders(self):
|
||||
wizard = self.env['res.config.settings'].create({})
|
||||
wizard = self.env["res.config.settings"].create({})
|
||||
self.test_co.split_time_from_materials_on_service_work_orders = True
|
||||
with Form(wizard) as form:
|
||||
form.separate_time_on_work_orders = False
|
||||
self.assertFalse(
|
||||
self.test_co.split_time_from_materials_on_service_work_orders
|
||||
)
|
||||
self.assertFalse(self.test_co.split_time_from_materials_on_service_work_orders)
|
||||
|
||||
def test_enabling_create_default_fsm_visit(self):
|
||||
wizard = self.env['res.config.settings'].create({})
|
||||
wizard = self.env["res.config.settings"].create({})
|
||||
self.test_co.create_default_fsm_visit = False
|
||||
with Form(wizard) as form:
|
||||
form.create_default_fsm_visit = True
|
||||
self.assertTrue(
|
||||
self.test_co.create_default_fsm_visit
|
||||
)
|
||||
self.assertTrue(self.test_co.create_default_fsm_visit)
|
||||
|
||||
def test_disabling_create_default_fsm_visit(self):
|
||||
wizard = self.env['res.config.settings'].create({})
|
||||
wizard = self.env["res.config.settings"].create({})
|
||||
self.test_co.create_default_fsm_visit = True
|
||||
with Form(wizard) as form:
|
||||
form.create_default_fsm_visit = False
|
||||
self.assertFalse(
|
||||
self.test_co.create_default_fsm_visit
|
||||
)
|
||||
self.assertFalse(self.test_co.create_default_fsm_visit)
|
||||
|
|
|
|||
|
|
@ -3,18 +3,19 @@ from odoo.tests.common import tagged, Form
|
|||
from odoo import Command
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
@tagged("post_install", "-at_install")
|
||||
class TaskTest(BemadeFSMBaseTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# Chose to set up all tests the same way since this code was becoming very redundant
|
||||
super().setUpClass()
|
||||
cls.user = cls._generate_project_manager_user('Bob', 'Bob')
|
||||
cls.user = cls._generate_project_manager_user("Bob", "Bob")
|
||||
|
||||
def _generate_so_with_multilevel_task_template(self):
|
||||
so = self._generate_sale_order()
|
||||
template = self._generate_task_template(names=['Parent', 'Child', 'Grandchild'], structure=[2, 1])
|
||||
template = self._generate_task_template(
|
||||
names=["Parent", "Child", "Grandchild"], structure=[2, 1]
|
||||
)
|
||||
product = self._generate_product(task_template=template)
|
||||
sol = self._generate_sale_order_line(sale_order=so, product=product)
|
||||
return so, sol
|
||||
|
|
@ -25,21 +26,27 @@ class TaskTest(BemadeFSMBaseTest):
|
|||
task = sol.task_id
|
||||
|
||||
task.propagate_assignment = True
|
||||
task.write({
|
||||
'user_ids': [Command.set([self.user.id])],
|
||||
'propagate_assignment': True,
|
||||
})
|
||||
task.write(
|
||||
{
|
||||
"user_ids": [Command.set([self.user.id])],
|
||||
"propagate_assignment": True,
|
||||
}
|
||||
)
|
||||
|
||||
self.assertTrue(all([t.user_ids == self.user for t in task | task._get_all_subtasks()]))
|
||||
self.assertTrue(
|
||||
all([t.user_ids == self.user for t in task | task._get_all_subtasks()])
|
||||
)
|
||||
|
||||
def test_reassigning_task_doesnt_propagate_by_default(self):
|
||||
so, sol = self._generate_so_with_multilevel_task_template()
|
||||
so.action_confirm()
|
||||
task = sol.task_id
|
||||
|
||||
task.write({
|
||||
'user_ids': [Command.set([self.user.id])],
|
||||
})
|
||||
task.write(
|
||||
{
|
||||
"user_ids": [Command.set([self.user.id])],
|
||||
}
|
||||
)
|
||||
|
||||
self.assertFalse(any([t.user_ids for t in task.child_ids.child_ids]))
|
||||
|
||||
|
|
@ -49,24 +56,25 @@ class TaskTest(BemadeFSMBaseTest):
|
|||
task = sol.task_id
|
||||
# First, set propagation and assign
|
||||
task.propagate_assignment = True
|
||||
task.write({
|
||||
'user_ids': [Command.set([self.user.id])]
|
||||
})
|
||||
task.write({"user_ids": [Command.set([self.user.id])]})
|
||||
# Then, unset propagation for the children and re-set assignment
|
||||
task.child_ids.write({'propagate_assignment': False})
|
||||
self.assertFalse(any([t.propagate_assignment for t in task._get_all_subtasks()]))
|
||||
task.child_ids.write({"propagate_assignment": False})
|
||||
self.assertFalse(
|
||||
any([t.propagate_assignment for t in task._get_all_subtasks()])
|
||||
)
|
||||
# Then, test that assigning the parent only assigns its children, not its grandchildren
|
||||
task.write({
|
||||
'user_ids': [Command.set([])]
|
||||
})
|
||||
task.write({"user_ids": [Command.set([])]})
|
||||
self.assertTrue(all([not t.user_ids for t in task | task.child_ids]))
|
||||
self.assertTrue(all([t.user_ids == self.user for t in task.child_ids.child_ids]))
|
||||
self.assertTrue(
|
||||
all([t.user_ids == self.user for t in task.child_ids.child_ids])
|
||||
)
|
||||
|
||||
def test_task_gets_work_order_contacts_from_sale_order(self):
|
||||
so, sol = self._generate_so_with_multilevel_task_template()
|
||||
work_order_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(
|
||||
parent=so.partner_id)
|
||||
so.write({'work_order_contacts': [(6, 0, work_order_contacts.ids)]})
|
||||
work_order_contacts = self._generate_partner(
|
||||
parent=so.partner_id
|
||||
) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({"work_order_contacts": [(6, 0, work_order_contacts.ids)]})
|
||||
|
||||
so.action_confirm()
|
||||
task = sol.task_id
|
||||
|
|
@ -80,8 +88,10 @@ class TaskTest(BemadeFSMBaseTest):
|
|||
|
||||
def test_task_gets_site_contacts_from_sale_order(self):
|
||||
so, sol = self._generate_so_with_multilevel_task_template()
|
||||
site_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({'site_contacts': [(6, 0, site_contacts.ids)]})
|
||||
site_contacts = self._generate_partner(
|
||||
parent=so.partner_id
|
||||
) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({"site_contacts": [(6, 0, site_contacts.ids)]})
|
||||
|
||||
so.action_confirm()
|
||||
task = sol.task_id
|
||||
|
|
@ -95,12 +105,20 @@ class TaskTest(BemadeFSMBaseTest):
|
|||
|
||||
def test_task_gets_work_order_contacts_from_parent(self):
|
||||
so, sol = self._generate_so_with_multilevel_task_template()
|
||||
work_order_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({'work_order_contacts': [(6, 0, work_order_contacts.ids)]})
|
||||
work_order_contacts = self._generate_partner(
|
||||
parent=so.partner_id
|
||||
) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({"work_order_contacts": [(6, 0, work_order_contacts.ids)]})
|
||||
|
||||
so.action_confirm()
|
||||
task = sol.task_id
|
||||
task.write({'work_order_contacts': [Command.link(self._generate_partner(parent=so.partner_id).id)]})
|
||||
task.write(
|
||||
{
|
||||
"work_order_contacts": [
|
||||
Command.link(self._generate_partner(parent=so.partner_id).id)
|
||||
]
|
||||
}
|
||||
)
|
||||
for subtask in task._get_all_subtasks():
|
||||
self.assertEqual(subtask.work_order_contacts, task.work_order_contacts)
|
||||
with Form(task) as task_form:
|
||||
|
|
@ -111,12 +129,20 @@ class TaskTest(BemadeFSMBaseTest):
|
|||
|
||||
def test_task_gets_site_contacts_from_parent(self):
|
||||
so, sol = self._generate_so_with_multilevel_task_template()
|
||||
site_contacts = self._generate_partner(parent=so.partner_id) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({'site_contacts': [(6, 0, site_contacts.ids)]})
|
||||
site_contacts = self._generate_partner(
|
||||
parent=so.partner_id
|
||||
) | self._generate_partner(parent=so.partner_id)
|
||||
so.write({"site_contacts": [(6, 0, site_contacts.ids)]})
|
||||
|
||||
so.action_confirm()
|
||||
task = sol.task_id
|
||||
task.write({'site_contacts': [Command.link(self._generate_partner(parent=so.partner_id).id)]})
|
||||
task.write(
|
||||
{
|
||||
"site_contacts": [
|
||||
Command.link(self._generate_partner(parent=so.partner_id).id)
|
||||
]
|
||||
}
|
||||
)
|
||||
for subtask in task._get_all_subtasks():
|
||||
self.assertEqual(subtask.site_contacts, task.site_contacts)
|
||||
with Form(task) as task_form:
|
||||
|
|
|
|||
|
|
@ -4,34 +4,36 @@ from odoo.tests import Form
|
|||
|
||||
class TestTaskReport(BemadeFSMBaseTest):
|
||||
def test_split_time_materials_setting(self):
|
||||
with Form(self.env['res.config.settings']) as settings:
|
||||
with Form(self.env["res.config.settings"]) as settings:
|
||||
settings.separate_time_on_work_orders = True
|
||||
|
||||
with Form(self.env['res.config.settings']) as new_settings:
|
||||
with Form(self.env["res.config.settings"]):
|
||||
self.assertTrue(settings.separate_time_on_work_orders)
|
||||
|
||||
so = self._generate_sale_order()
|
||||
service_product = self._generate_product()
|
||||
material_product = self._generate_product(
|
||||
name="Material Product",
|
||||
product_type='product',
|
||||
service_tracking='no',
|
||||
product_type="product",
|
||||
service_tracking="no",
|
||||
)
|
||||
visit = self._generate_visit(sale_order=so)
|
||||
sol = self._generate_sale_order_line(sale_order=so, product=service_product)
|
||||
sol2 = self._generate_sale_order_line(sale_order=so, product=material_product)
|
||||
self._generate_sale_order_line(sale_order=so, product=service_product)
|
||||
self._generate_sale_order_line(sale_order=so, product=material_product)
|
||||
so.action_confirm()
|
||||
task = visit.task_id
|
||||
|
||||
html_content = self.env['ir.actions.report']._render(
|
||||
'industry_fsm_report.worksheet_custom',
|
||||
[task.id],
|
||||
)[0].decode('utf-8').split('\n')
|
||||
html_content = (
|
||||
self.env["ir.actions.report"]
|
||||
._render(
|
||||
"industry_fsm_report.worksheet_custom",
|
||||
[task.id],
|
||||
)[0]
|
||||
.decode("utf-8")
|
||||
.split("\n")
|
||||
)
|
||||
|
||||
strings_to_find = [
|
||||
"<h2>Materials</h2>",
|
||||
"<span>Material Product</span>"
|
||||
]
|
||||
strings_to_find = ["<h2>Materials</h2>", "<span>Material Product</span>"]
|
||||
|
||||
for line in strings_to_find:
|
||||
line_found = False
|
||||
|
|
|
|||
|
|
@ -1,35 +1,34 @@
|
|||
from .test_bemade_fsm_common import BemadeFSMBaseTest
|
||||
from odoo.tests.common import HttpCase, tagged, Form
|
||||
from odoo.tests.common import tagged, Form
|
||||
from odoo.exceptions import MissingError
|
||||
from odoo import Command
|
||||
from odoo.tools import mute_logger
|
||||
from psycopg2.errors import ForeignKeyViolation
|
||||
import psycopg2
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install')
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestTaskTemplate(BemadeFSMBaseTest):
|
||||
|
||||
def test_delete_task_template(self):
|
||||
"""User should never be able to delete a task template used on a product"""
|
||||
task_template = self._generate_task_template(names=['Template 1'])
|
||||
product = self._generate_product(name="Test Product 1", task_template=task_template)
|
||||
with self.assertRaises(ForeignKeyViolation):
|
||||
with mute_logger('odoo.sql_db'):
|
||||
task_template = self._generate_task_template(names=["Template 1"])
|
||||
self._generate_product(name="Test Product 1", task_template=task_template)
|
||||
with self.assertRaises(psycopg2.errors.ForeignKeyViolation):
|
||||
with mute_logger("odoo.sql_db"):
|
||||
task_template.unlink()
|
||||
|
||||
def test_delete_subtask_template(self):
|
||||
""" Deletion of a child task should be OK even if the parent is on a product. Children of the deleted
|
||||
subtask should be deleted."""
|
||||
parent_task = self._generate_task_template(structure=[2, 1],
|
||||
names=['Parent Template', 'Child Template',
|
||||
'Grandchild Template'])
|
||||
"""Deletion of a child task should be OK even if the parent is on a product.
|
||||
Children of the deleted subtask should be deleted."""
|
||||
parent_task = self._generate_task_template(
|
||||
structure=[2, 1],
|
||||
names=["Parent Template", "Child Template", "Grandchild Template"],
|
||||
)
|
||||
grandchild_task = parent_task.subtasks[0].subtasks[0]
|
||||
|
||||
parent_task.subtasks[0].unlink()
|
||||
|
||||
# Reading deleted child's name field should be impossible
|
||||
with self.assertRaises(MissingError):
|
||||
test = grandchild_task.name
|
||||
_ = grandchild_task.name
|
||||
|
||||
def test_dissociating_customer_resets_equipment_appropriately(self):
|
||||
partner1 = self._generate_partner()
|
||||
|
|
@ -38,7 +37,8 @@ class TestTaskTemplate(BemadeFSMBaseTest):
|
|||
task = self._generate_task_template(customer=partner1, equipment=equipment1)
|
||||
form = Form(task)
|
||||
|
||||
# Switching the partner should trigger on_change that makes sure equipments are linked to the new partner
|
||||
# Switching the partner should trigger on_change that makes sure equipments are
|
||||
# linked to the new partner
|
||||
form.customer = partner2
|
||||
form.save()
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ class TestTaskTemplate(BemadeFSMBaseTest):
|
|||
|
||||
def test_child_task_names_are_short_version(self):
|
||||
so, visit, sol1, sol2 = self._generate_so_with_one_visit_two_lines()
|
||||
template = self._generate_task_template(names=['Task'])
|
||||
template = self._generate_task_template(names=["Task"])
|
||||
product = self._generate_product(task_template=template)
|
||||
sol1.name = "Short Name 1"
|
||||
sol2.name = "Short Name 2"
|
||||
|
|
@ -60,14 +60,21 @@ class TestTaskTemplate(BemadeFSMBaseTest):
|
|||
|
||||
def test_task_creation_directly_from_template(self):
|
||||
project = self.env.ref("industry_fsm.fsm_project")
|
||||
template = self._generate_task_template(names=['Task', 'Child', 'Grandchild'], structure=[2, 1])
|
||||
template = self._generate_task_template(
|
||||
names=["Task", "Child", "Grandchild"], structure=[2, 1]
|
||||
)
|
||||
|
||||
task = template.create_task_from_self(project, "My new task")
|
||||
|
||||
self.assertEqual(len(task.child_ids), len(template.subtasks))
|
||||
self.assertEqual(len(task.child_ids[0].child_ids), len(template.subtasks[0].subtasks))
|
||||
self.assertEqual(len(task.child_ids[1].child_ids), len(template.subtasks[1].subtasks))
|
||||
self.assertEqual(
|
||||
len(task.child_ids[0].child_ids), len(template.subtasks[0].subtasks)
|
||||
)
|
||||
self.assertEqual(
|
||||
len(task.child_ids[1].child_ids), len(template.subtasks[1].subtasks)
|
||||
)
|
||||
self.assertEqual(task.name, "My new task")
|
||||
self.assertEqual(task.child_ids[0].name, template.subtasks[0].name)
|
||||
self.assertTrue(all([t.project_id == project for t in task | task._get_all_subtasks()]))
|
||||
|
||||
self.assertTrue(
|
||||
all([t.project_id == project for t in task | task._get_all_subtasks()])
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,32 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<!-- Equipment Form View -->
|
||||
<record id="equipment_view_form" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.equipment.form</field>
|
||||
<field name="model">bemade_fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Equipment">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group name="left">
|
||||
<field name="pid_tag"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field name="location_notes"/>
|
||||
<field name="pid_tag" />
|
||||
<field name="name" />
|
||||
<field name="description" />
|
||||
<field name="location_notes" />
|
||||
</group>
|
||||
<group name="right">
|
||||
<field name="partner_location_id"
|
||||
groups="account.group_delivery_invoice_address"
|
||||
context="{'default_type': 'delivery', 'show_address': 1}"
|
||||
options='{"always_reload": True}'/>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'no_open': False}"/>
|
||||
<field
|
||||
name="partner_location_id"
|
||||
groups="account.group_delivery_invoice_address"
|
||||
context="{'default_type': 'delivery', 'show_address': 1}"
|
||||
options='{"always_reload": True}'
|
||||
/>
|
||||
<field
|
||||
name="tag_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_open': False}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers" />
|
||||
<field name="activity_ids" widget="mail_activity" />
|
||||
<field name="message_ids" widget="mail_thread" />
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
|
|
@ -36,12 +42,16 @@
|
|||
<field name="name">bemade_fsm.equipment.tree</field>
|
||||
<field name="model">bemade_fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Equipment" editable="bottom">
|
||||
<field name="pid_tag"/>
|
||||
<field name="name"/>
|
||||
<field name="description"/>
|
||||
<field name="tag_ids" widget="many2many_tags" options="{'no_open': False}"/>
|
||||
<field name="partner_location_id"/>
|
||||
<tree editable="bottom">
|
||||
<field name="pid_tag" />
|
||||
<field name="name" />
|
||||
<field name="description" />
|
||||
<field
|
||||
name="tag_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_open': False}"
|
||||
/>
|
||||
<field name="partner_location_id" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
|
|||
|
|
@ -1,32 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<menuitem id="project_task_template_menu"
|
||||
name="Task Templates"
|
||||
parent="project.menu_main_pm"
|
||||
action="task_template_act_window"
|
||||
groups="project.group_project_manager,project.group_project_user"/>
|
||||
<menuitem id="service_task_template_meny"
|
||||
name="Task Templates"
|
||||
action="task_template_act_window"
|
||||
parent="industry_fsm.fsm_menu_root"
|
||||
groups="project.group_project_manager,industry_fsm.group_fsm_manager"/>
|
||||
<menuitem id="menu_service_client"
|
||||
name="Clients"
|
||||
sequence="10"
|
||||
parent="industry_fsm.fsm_menu_root"
|
||||
groups="industry_fsm.group_fsm_user"/>
|
||||
<menuitem id="menu_service_client_clients"
|
||||
name="Clients"
|
||||
action="base.action_partner_customer_form"
|
||||
sequence="20"
|
||||
parent="menu_service_client"
|
||||
groups="industry_fsm.group_fsm_user"/>
|
||||
<menuitem id="menu_service_client_equipment"
|
||||
name="Client Equipment"
|
||||
action="action_window_equipment"
|
||||
sequence="21"
|
||||
parent="menu_service_client"
|
||||
groups="industry_fsm.group_fsm_user"/>
|
||||
</data>
|
||||
</odoo>
|
||||
<menuitem
|
||||
id="project_task_template_menu"
|
||||
name="Task Templates"
|
||||
parent="project.menu_main_pm"
|
||||
action="task_template_act_window"
|
||||
groups="project.group_project_manager,project.group_project_user"
|
||||
/>
|
||||
<menuitem
|
||||
id="service_task_template_meny"
|
||||
name="Task Templates"
|
||||
action="task_template_act_window"
|
||||
parent="industry_fsm.fsm_menu_root"
|
||||
groups="project.group_project_manager,industry_fsm.group_fsm_manager"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_service_client"
|
||||
name="Clients"
|
||||
sequence="10"
|
||||
parent="industry_fsm.fsm_menu_root"
|
||||
groups="industry_fsm.group_fsm_user"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_service_client_clients"
|
||||
name="Clients"
|
||||
action="base.action_partner_customer_form"
|
||||
sequence="20"
|
||||
parent="menu_service_client"
|
||||
groups="industry_fsm.group_fsm_user"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_service_client_equipment"
|
||||
name="Client Equipment"
|
||||
action="action_window_equipment"
|
||||
sequence="21"
|
||||
parent="menu_service_client"
|
||||
groups="industry_fsm.group_fsm_user"
|
||||
/>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="product_template_form_inherit" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.product.template.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field
|
||||
name="inherit_id"
|
||||
ref="sale_project.product_template_form_view_invoice_policy_inherit_sale_project"
|
||||
/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='project_id']" position="after">
|
||||
<field
|
||||
name="task_template_id"
|
||||
invisible="service_tracking not in ('task_global_project', 'task_in_project')"
|
||||
domain="[('parent', '=', False)]"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_form_inherit" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.product.template.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="sale_project.product_template_form_view_invoice_policy_inherit_sale_project"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='project_id']" position="after">
|
||||
<field name="task_template_id"
|
||||
invisible="service_tracking not in ('task_global_project', 'task_in_project')"
|
||||
domain="[('parent', '=', False)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- BV: Did comment that one cause can not match inherit_id and don't understand it use -->
|
||||
<!-- <record id="product_search_form_view_inherit_bemade_fsm" model="ir.ui.view">-->
|
||||
<!-- <field name="name">bemade_fsm.product_search_form_view_inherit_bemade_fsm</field>-->
|
||||
<!-- <field name="model">product.product</field>-->
|
||||
<!-- <field name="inherit_id" ref="industry_fsm_sale.product_search_form_view_inherit_fsm_sale"/>-->
|
||||
<!-- <field name="arch" type="xml">-->
|
||||
<!-- <xpath expr="//searchpanel//field[@name='categ_id']" position="attributes">-->
|
||||
<!-- <attribute name="limit">0</attribute>-->
|
||||
<!-- </xpath>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
</data>
|
||||
</odoo>
|
||||
<!-- BV: Did comment that one cause can not match inherit_id and don't understand it use -->
|
||||
<!-- <record id="product_search_form_view_inherit_bemade_fsm" model="ir.ui.view">-->
|
||||
<!-- <field name="name">bemade_fsm.product_search_form_view_inherit_bemade_fsm</field>-->
|
||||
<!-- <field name="model">product.product</field>-->
|
||||
<!-- <field name="inherit_id" ref="industry_fsm_sale.product_search_form_view_inherit_fsm_sale"/>-->
|
||||
<!-- <field name="arch" type="xml">-->
|
||||
<!-- <xpath expr="//searchpanel//field[@name='categ_id']" position="attributes">-->
|
||||
<!-- <attribute name="limit">0</attribute>-->
|
||||
<!-- </xpath>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<record id="act_res_partner_2_equipment" model="ir.actions.act_window">
|
||||
<field name="name">Equipments</field>
|
||||
|
|
@ -11,44 +11,71 @@
|
|||
<record id="partner_equipment_location_view_form" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.partner.equipment.location.form</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="inherit_id" ref="base.view_partner_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='internal_notes']" position="before">
|
||||
<field name="is_site_contact" invisible="True"/>
|
||||
<field name="is_service_site" invisible="True"/>
|
||||
<page name="field_service" string="Service Site Responsibilities" invisible="is_service_site or is_company">
|
||||
<field name="owned_equipment_ids" invisible="True"/>
|
||||
<field name="is_site_contact" invisible="True" />
|
||||
<field name="is_service_site" invisible="True" />
|
||||
<page
|
||||
name="field_service"
|
||||
string="Service Site Responsibilities"
|
||||
invisible="is_service_site or is_company"
|
||||
>
|
||||
<field name="owned_equipment_ids" invisible="True" />
|
||||
<group>
|
||||
<field name="site_ids">
|
||||
<field name="site_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="name" widget="res_partner_many2one"/>
|
||||
<field name="name" widget="res_partner_many2one" />
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
<page name="service_equipment" string="Service Site Info" invisible="is_site_contact">
|
||||
<field name="equipment_ids"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
||||
readonly="False"/>
|
||||
<field name="site_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||
<field name="work_order_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||
<field name="owned_equipment_ids"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
||||
readonly="True"/>
|
||||
<page
|
||||
name="service_equipment"
|
||||
string="Service Site Info"
|
||||
invisible="is_site_contact"
|
||||
>
|
||||
<field
|
||||
name="equipment_ids"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
||||
readonly="False"
|
||||
/>
|
||||
<field
|
||||
name="site_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"
|
||||
/>
|
||||
<field
|
||||
name="work_order_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"
|
||||
/>
|
||||
<field
|
||||
name="owned_equipment_ids"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_equipment_view_tree'}"
|
||||
readonly="True"
|
||||
/>
|
||||
</page>
|
||||
</xpath>
|
||||
<div name="button_box" position="inside">
|
||||
<button class="oe_stat_button" type="action" name="%(bemade_fsm.act_res_partner_2_equipment)d"
|
||||
icon="fa-tachometer">
|
||||
<field string="Equipments" name="equipment_count" widget="statinfo"/>
|
||||
<button
|
||||
class="oe_stat_button"
|
||||
type="action"
|
||||
name="%(bemade_fsm.act_res_partner_2_equipment)d"
|
||||
icon="fa-tachometer"
|
||||
>
|
||||
<field
|
||||
string="Equipments"
|
||||
name="equipment_count"
|
||||
widget="statinfo"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<field name="lang" position="after">
|
||||
</field>
|
||||
<xpath expr="/form//field[@name='child_ids']/form//field[@name='comment']" position="after">
|
||||
<field name="is_site_contact"/>
|
||||
<xpath
|
||||
expr="/form//field[@name='child_ids']/form//field[@name='comment']"
|
||||
position="after"
|
||||
>
|
||||
<field name="is_site_contact" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
|
@ -58,9 +85,9 @@
|
|||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="name" />
|
||||
<field name="email" widget="email"/>
|
||||
<field name="phone" widget="phone"/>
|
||||
<field name="mobile" widget="phone"/>
|
||||
<field name="email" widget="email" />
|
||||
<field name="phone" widget="phone" />
|
||||
<field name="mobile" widget="phone" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
@ -69,13 +96,15 @@
|
|||
<field name="model">bemade_fsm.equipment</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="pid_tag"/>
|
||||
<field name="name"/>
|
||||
<field name="location_notes"/>
|
||||
<button name="action_view_equipment"
|
||||
type="object"
|
||||
string="Details"
|
||||
icon="fa-external-link"/>
|
||||
<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>
|
||||
</record>
|
||||
|
|
|
|||
|
|
@ -1,55 +1,65 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?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="fsm_visits" string="Service Visits">
|
||||
<field name="visit_ids"
|
||||
context="{'tree_view_ref': 'bemade_fsm.bemade_fsm_visit_tree'}"/>
|
||||
</group>
|
||||
<group name="field_service_info" string="Contacts and Equipment">
|
||||
<field name="valid_equipment_ids" invisible="1"/>
|
||||
<field name="summary_equipment_ids"
|
||||
context="{'default_partner_location_id': partner_shipping_id,}"
|
||||
widget="many2many_tags"
|
||||
groups="account.group_delivery_invoice_address"/>
|
||||
<field name="site_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||
<field name="work_order_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||
<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="fsm_visits" string="Service Visits">
|
||||
<field
|
||||
name="visit_ids"
|
||||
context="{'tree_view_ref': 'bemade_fsm.bemade_fsm_visit_tree'}"
|
||||
/>
|
||||
</group>
|
||||
<group name="field_service_info" string="Contacts and Equipment">
|
||||
<field name="valid_equipment_ids" invisible="1" />
|
||||
<field
|
||||
name="summary_equipment_ids"
|
||||
context="{'default_partner_location_id': partner_shipping_id,}"
|
||||
widget="many2many_tags"
|
||||
groups="account.group_delivery_invoice_address"
|
||||
/>
|
||||
<field
|
||||
name="site_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"
|
||||
/>
|
||||
<field
|
||||
name="work_order_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"
|
||||
/>
|
||||
|
||||
<field name="default_equipment_ids"
|
||||
context="{'default_partner_location_id': partner_shipping_id,}"
|
||||
widget="many2many_tags"
|
||||
domain="[('id', 'in', valid_equipment_ids)]"
|
||||
groups="account.group_delivery_invoice_address"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//tree//field[@name='name']" position="after">
|
||||
<field name="valid_equipment_ids" invisible="1"/>
|
||||
<field name="equipment_ids"
|
||||
widget="many2many_tags"
|
||||
domain="[('id', 'in', valid_equipment_ids)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bemade_fsm_visit_tree" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.visit.tree</field>
|
||||
<field name="model">bemade_fsm.visit</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="label"/>
|
||||
<field name="approx_date"/>
|
||||
<field name="is_completed" widget="boolean"/>
|
||||
<field name="is_invoiced" widget="boolean"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
<field
|
||||
name="default_equipment_ids"
|
||||
context="{'default_partner_location_id': partner_shipping_id,}"
|
||||
widget="many2many_tags"
|
||||
domain="[('id', 'in', valid_equipment_ids)]"
|
||||
groups="account.group_delivery_invoice_address"
|
||||
/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//tree//field[@name='name']" position="after">
|
||||
<field name="valid_equipment_ids" invisible="1" />
|
||||
<field
|
||||
name="equipment_ids"
|
||||
widget="many2many_tags"
|
||||
domain="[('id', 'in', valid_equipment_ids)]"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="bemade_fsm_visit_tree" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.visit.tree</field>
|
||||
<field name="model">bemade_fsm.visit</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="label" />
|
||||
<field name="approx_date" />
|
||||
<field name="is_completed" widget="boolean" />
|
||||
<field name="is_invoiced" widget="boolean" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,105 +1,127 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="task_template_form_view" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.task_template.form</field>
|
||||
<field name="model">project.task.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Task Template">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="Title"/>
|
||||
</h1>
|
||||
</div>
|
||||
<record id="task_template_form_view" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.task_template.form</field>
|
||||
<field name="model">project.task.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Task Template">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" />
|
||||
<h1>
|
||||
<field name="name" placeholder="Title" />
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="project"/>
|
||||
<field name="assignees" widget="many2many_avatar_user"/>
|
||||
<field name="parent"/>
|
||||
<field name="planned_hours"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="customer"/>
|
||||
<field name="equipment_ids"
|
||||
domain="[('partner_location_id', '=', customer)]"
|
||||
context="{'tree_view_ref': 'bemade_fsm.equipment_view_tree'}"/>
|
||||
<field name="tags" widget="many2many_tags"/>
|
||||
<field name="company_id" />
|
||||
</group>
|
||||
<field name="project" />
|
||||
<field name="assignees" widget="many2many_avatar_user" />
|
||||
<field name="parent" />
|
||||
<field name="planned_hours" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="customer" />
|
||||
<field
|
||||
name="equipment_ids"
|
||||
domain="[('partner_location_id', '=', customer)]"
|
||||
context="{'tree_view_ref': 'bemade_fsm.equipment_view_tree'}"
|
||||
/>
|
||||
<field name="tags" widget="many2many_tags" />
|
||||
<field name="company_id" />
|
||||
</group>
|
||||
<notebook>
|
||||
<page name="description_page" string="Description">
|
||||
<field name="description" type="html" options="{'collaborative': true}"/>
|
||||
</page>
|
||||
<page name="subtasks_page" string="Subtasks">
|
||||
<field name="subtasks">
|
||||
<tree editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="customer"/>
|
||||
<field name="assignees" widget="many2many_avatar_user"/>
|
||||
<button name="action_open_task" type="object" title="View Task"
|
||||
string="View Task" class="btn btn-link pull-right"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="task_template_tree_view" model="ir.ui.view">
|
||||
<field name="name">project.task_template.tree</field>
|
||||
<field name="model">project.task.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Task Template">
|
||||
<field name="name"/>
|
||||
<field name="assignees" widget="many2many_avatar_user"/>
|
||||
<field name="project"/>
|
||||
<field name="parent"/>
|
||||
<field name="planned_hours"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="task_template_search_view" model="ir.ui.view">
|
||||
<field name="name">project.task_template.search</field>
|
||||
<field name="model">project.task.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Task Template">
|
||||
<field name="name"/>
|
||||
<field name="project"/>
|
||||
<field name="assignees"/>
|
||||
<field name="parent"/>
|
||||
<field name="subtasks"/>
|
||||
<field name="planned_hours"/>
|
||||
<group expand="1" string="Group By">
|
||||
<filter string="Project" name="groupby_project" domain="[]"
|
||||
context="{'group_by':'project'}"/>
|
||||
<filter string="Parent Task" name="groupby_parent" domain="[]"
|
||||
context="{'group_by':'parent'}"/>
|
||||
<filter string="Customer" name="groupby_customer" domain="[]"
|
||||
context="{'group_by':'customer'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<notebook>
|
||||
<page name="description_page" string="Description">
|
||||
<field
|
||||
name="description"
|
||||
type="html"
|
||||
options="{'collaborative': true}"
|
||||
/>
|
||||
</page>
|
||||
<page name="subtasks_page" string="Subtasks">
|
||||
<field name="subtasks">
|
||||
<tree editable="bottom">
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="name" />
|
||||
<field name="customer" />
|
||||
<field
|
||||
name="assignees"
|
||||
widget="many2many_avatar_user"
|
||||
/>
|
||||
<button
|
||||
name="action_open_task"
|
||||
type="object"
|
||||
title="View Task"
|
||||
string="View Task"
|
||||
class="btn btn-link pull-right"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="task_template_act_window" model="ir.actions.act_window">
|
||||
<field name="name">Task Template</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">project.task.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
There are no task templates, click above to create one.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<record id="task_template_tree_view" model="ir.ui.view">
|
||||
<field name="name">project.task_template.tree</field>
|
||||
<field name="model">project.task.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name" />
|
||||
<field name="assignees" widget="many2many_avatar_user" />
|
||||
<field name="project" />
|
||||
<field name="parent" />
|
||||
<field name="planned_hours" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
<record id="task_template_search_view" model="ir.ui.view">
|
||||
<field name="name">project.task_template.search</field>
|
||||
<field name="model">project.task.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Task Template">
|
||||
<field name="name" />
|
||||
<field name="project" />
|
||||
<field name="assignees" />
|
||||
<field name="parent" />
|
||||
<field name="subtasks" />
|
||||
<field name="planned_hours" />
|
||||
<group expand="1" string="Group By">
|
||||
<filter
|
||||
string="Project"
|
||||
name="groupby_project"
|
||||
domain="[]"
|
||||
context="{'group_by':'project'}"
|
||||
/>
|
||||
<filter
|
||||
string="Parent Task"
|
||||
name="groupby_parent"
|
||||
domain="[]"
|
||||
context="{'group_by':'parent'}"
|
||||
/>
|
||||
<filter
|
||||
string="Customer"
|
||||
name="groupby_customer"
|
||||
domain="[]"
|
||||
context="{'group_by':'customer'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="task_template_act_window" model="ir.actions.act_window">
|
||||
<field name="name">Task Template</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">project.task.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
There are no task templates, click above to create one.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,228 +1,222 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="bemade_fsm_project_task_form_inherit" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.project_task.form</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.view_task_form2_inherit"/>
|
||||
<field name="priority" eval="8"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']/.." position="before">
|
||||
<h1 class="d-flex flex-row justify-content-between">
|
||||
<field name="work_order_number" invisible="work_order_number == False"/>
|
||||
</h1>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='extra_info']" position="after">
|
||||
<page string="Field Service" name="field_service" translate="True">
|
||||
<group name="equipment_and_contacts">
|
||||
<field name="equipment_ids"
|
||||
domain="[('partner_location_id', '=', partner_id)]"
|
||||
context="{'tree_view_ref': 'bemade_fsm.equipment_view_tree'}"/>
|
||||
<field name="site_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||
<field name="work_order_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
<button name="action_fsm_validate" class='btn-primary'
|
||||
position="attributes">
|
||||
<attribute name="string">Mark as Delivered</attribute>
|
||||
<attribute name="groups">industry_fsm.group_fsm_manager</attribute>
|
||||
</button>
|
||||
<button name="action_fsm_validate" class='btn-secondary'
|
||||
position="attributes">
|
||||
<attribute name="string">Mark as Delivered</attribute>
|
||||
<attribute name="groups">industry_fsm.group_fsm_manager</attribute>
|
||||
</button>
|
||||
<record id="bemade_fsm_project_task_form_inherit" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.project_task.form</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.view_task_form2_inherit" />
|
||||
<field name="priority" eval="8" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']/.." position="before">
|
||||
<h1 class="d-flex flex-row justify-content-between">
|
||||
<field
|
||||
name="work_order_number"
|
||||
invisible="work_order_number == False"
|
||||
/>
|
||||
</h1>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='extra_info']" position="after">
|
||||
<page string="Field Service" name="field_service" translate="True">
|
||||
<group name="equipment_and_contacts">
|
||||
<field
|
||||
name="equipment_ids"
|
||||
domain="[('partner_location_id', '=', partner_id)]"
|
||||
context="{'tree_view_ref': 'bemade_fsm.equipment_view_tree'}"
|
||||
/>
|
||||
<field
|
||||
name="site_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"
|
||||
/>
|
||||
<field
|
||||
name="work_order_contacts"
|
||||
context="{'tree_view_ref': 'bemade_fsm.fsm_contacts_view_tree'}"
|
||||
/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
<button
|
||||
name="action_fsm_validate"
|
||||
class='btn-primary'
|
||||
position="attributes"
|
||||
>
|
||||
<attribute name="string">Mark as Delivered</attribute>
|
||||
<attribute name="groups">industry_fsm.group_fsm_manager</attribute>
|
||||
</button>
|
||||
<button
|
||||
name="action_fsm_validate"
|
||||
class='btn-secondary'
|
||||
position="attributes"
|
||||
>
|
||||
<attribute name="string">Mark as Delivered</attribute>
|
||||
<attribute name="groups">industry_fsm.group_fsm_manager</attribute>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_task_form2_inherit" model="ir.ui.view">
|
||||
<field name="inherit_id" ref="project.view_task_form2" />
|
||||
<field name="model">project.task</field>
|
||||
<field name="name">bemade_fsm.project_task.form2</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath
|
||||
expr="//field[@name='child_ids']/tree//field[@name='name']"
|
||||
position="after"
|
||||
>
|
||||
<field name="description" string="Description/Comments" />
|
||||
</xpath>
|
||||
<field name="user_ids" position="after">
|
||||
<field name="propagate_assignment" />
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_task_form2_inherit" model="ir.ui.view">
|
||||
<field name="inherit_id" ref="project.view_task_form2"/>
|
||||
<field name="model">project.task</field>
|
||||
<field name="name">bemade_fsm.project_task.form2</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='child_ids']/tree//field[@name='name']" position="after">
|
||||
<field name="description" string="Description/Comments"/>
|
||||
</xpath>
|
||||
<field name="user_ids" position="after">
|
||||
<field name="propagate_assignment"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Add parent_id = false to domain for My Tasks, All Tasks: To Schedule, All Tasks and To Invoice-->
|
||||
<record id="industry_fsm.project_task_action_fsm" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="project_task_view_list_fsm_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.view.list.fsm.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_list_fsm" />
|
||||
<field name="arch" type="xml">
|
||||
<tree position="attributes">
|
||||
<attribute name="js_class">project_list</attribute>
|
||||
</tree>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="work_order_number" optional="show" />
|
||||
</field>
|
||||
</record>
|
||||
<!-- Add parent_id = false to domain for My Tasks, All Tasks: To Schedule, All Tasks and To Invoice-->
|
||||
<record id="industry_fsm.project_task_action_fsm" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_map"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_to_schedule_fsm"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_all_fsm"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm_sale.project_task_action_to_invoice_fsm"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_planning_groupby_user"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_planning_groupby_project"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="industry_fsm_report.project_task_action_fsm_planning_groupby_worksheet"
|
||||
model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
</record>
|
||||
<record id="project_task_view_calendar_fsm_no_worksheet" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.project_task_view_calendar_no_worksheet</field>
|
||||
<field name="inherit_id"
|
||||
ref="industry_fsm_report.project_task_view_calendar_fsm_worksheet"/>
|
||||
<field name="model">project.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='worksheet_template_id']"
|
||||
position="replace"></xpath>
|
||||
<xpath expr="//calendar" position="attributes">
|
||||
<attribute name="color">user_ids</attribute>
|
||||
</xpath>
|
||||
<field name="company_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
</record>
|
||||
<record id="project_task_view_list_fsm_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.view.list.fsm.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_list_fsm"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree position="attributes">
|
||||
<attribute name="js_class">project_list</attribute>
|
||||
</tree>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="work_order_number" optional="show"/>
|
||||
</field>
|
||||
<field name="company_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
<field name="worksheet_template_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
<field name="project_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
<field name="worksheet_template_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_map"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
<field name="project_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_to_schedule_fsm"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_all_fsm"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm_sale.project_task_action_to_invoice_fsm"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False),
|
||||
('invoice_status', '=', 'to invoice')]
|
||||
</field>
|
||||
</record>
|
||||
<!-- Add parent_id = false to domain for planning actions as well -->
|
||||
<record id="industry_fsm.project_task_action_fsm_planning_groupby_user"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_planning_groupby_project"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm_report.project_task_action_fsm_planning_groupby_worksheet"
|
||||
model="ir.actions.act_window">
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<!-- <record id="project_task_view_calendar_fsm" model="ir.ui.view">-->
|
||||
<!-- <field name="name">bemade_fsm.project_task_view_calendar_fsm</field>-->
|
||||
<!-- <field name="inherit_id" ref="industry_fsm.project_task_view_calendar_fsm"/>-->
|
||||
<!-- <field name="model">project.task</field>-->
|
||||
<!-- <field name="arch" type="xml">-->
|
||||
<!-- <field name="user_ids" position="attributes">-->
|
||||
<!-- <attribute name="filters">1</attribute>-->
|
||||
<!-- <attribute name="color">user_ids</attribute>-->
|
||||
<!-- </field>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
<record id="project_task_view_calendar_fsm_no_worksheet" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.project_task_view_calendar_no_worksheet</field>
|
||||
<field name="inherit_id"
|
||||
ref="industry_fsm_report.project_task_view_calendar_fsm_worksheet"/>
|
||||
<field name="model">project.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='worksheet_template_id']"
|
||||
position="replace"></xpath>
|
||||
<xpath expr="//calendar" position="attributes">
|
||||
<attribute name="color">user_ids</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="project_task_view_search_fsm_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.view.search.fsm.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_search_fsm"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="schedule" position="attributes">
|
||||
<attribute name="domain">
|
||||
[
|
||||
'&',
|
||||
('fsm_done', '=', False),
|
||||
'|',
|
||||
('user_ids', '=', False),
|
||||
'&',
|
||||
('planned_date_start', '=', False),
|
||||
('date_deadline', '=', False),
|
||||
]
|
||||
</attribute>
|
||||
</filter>
|
||||
<filter name="my_tasks" position="after">
|
||||
<filter name="is_parent_task"
|
||||
string="Parent Task"
|
||||
domain="[('parent_id', '=', False)]"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_fsm_map" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record
|
||||
id="industry_fsm.project_task_action_to_schedule_fsm"
|
||||
model="ir.actions.act_window"
|
||||
>
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record id="industry_fsm.project_task_action_all_fsm" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record
|
||||
id="industry_fsm_sale.project_task_action_to_invoice_fsm"
|
||||
model="ir.actions.act_window"
|
||||
>
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False),
|
||||
('invoice_status', '=', 'to invoice')]
|
||||
</field>
|
||||
</record>
|
||||
<!-- Add parent_id = false to domain for planning actions as well -->
|
||||
<record
|
||||
id="industry_fsm.project_task_action_fsm_planning_groupby_user"
|
||||
model="ir.actions.act_window"
|
||||
>
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record
|
||||
id="industry_fsm.project_task_action_fsm_planning_groupby_project"
|
||||
model="ir.actions.act_window"
|
||||
>
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<record
|
||||
id="industry_fsm_report.project_task_action_fsm_planning_groupby_worksheet"
|
||||
model="ir.actions.act_window"
|
||||
>
|
||||
<field name="context">{'search_default_is_parent_task': True}</field>
|
||||
<field name="domain">[('is_fsm', '=', True),
|
||||
('project_id', '!=', False),
|
||||
('display_in_project', '=', True),
|
||||
('parent_id', '=', False)]
|
||||
</field>
|
||||
</record>
|
||||
<!-- <record id="project_task_view_calendar_fsm" model="ir.ui.view">-->
|
||||
<!-- <field name="name">bemade_fsm.project_task_view_calendar_fsm</field>-->
|
||||
<!-- <field name="inherit_id" ref="industry_fsm.project_task_view_calendar_fsm"/>-->
|
||||
<!-- <field name="model">project.task</field>-->
|
||||
<!-- <field name="arch" type="xml">-->
|
||||
<!-- <field name="user_ids" position="attributes">-->
|
||||
<!-- <attribute name="filters">1</attribute>-->
|
||||
<!-- <attribute name="color">user_ids</attribute>-->
|
||||
<!-- </field>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
<record id="project_task_view_calendar_fsm_no_worksheet" model="ir.ui.view">
|
||||
<field name="name">bemade_fsm.project_task_view_calendar_no_worksheet</field>
|
||||
<field
|
||||
name="inherit_id"
|
||||
ref="industry_fsm_report.project_task_view_calendar_fsm_worksheet"
|
||||
/>
|
||||
<field name="model">project.task</field>
|
||||
<field name="priority" eval="100" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='worksheet_template_id']" position="replace" />
|
||||
<xpath expr="//calendar" position="attributes">
|
||||
<attribute name="color">user_ids</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="project_task_view_search_fsm_inherit" model="ir.ui.view">
|
||||
<field name="name">project.task.view.search.fsm.inherit</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="industry_fsm.project_task_view_search_fsm" />
|
||||
<field name="arch" type="xml">
|
||||
<filter name="schedule" position="attributes">
|
||||
<attribute name="domain">
|
||||
[
|
||||
'&',
|
||||
('fsm_done', '=', False),
|
||||
'|',
|
||||
('user_ids', '=', False),
|
||||
'&',
|
||||
('planned_date_start', '=', False),
|
||||
('date_deadline', '=', False),
|
||||
]
|
||||
</attribute>
|
||||
</filter>
|
||||
<filter name="my_tasks" position="after">
|
||||
<filter
|
||||
name="is_parent_task"
|
||||
string="Parent Task"
|
||||
domain="[('parent_id', '=', False)]"
|
||||
/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class NewTaskFromTemplateWizard(models.TransientModel):
|
||||
|
|
@ -7,45 +6,52 @@ class NewTaskFromTemplateWizard(models.TransientModel):
|
|||
_description = "Create Task from Template Wizard"
|
||||
|
||||
project_id = fields.Many2one(
|
||||
comodel_name='project.project',
|
||||
string='Project',
|
||||
help='The project the new task should be created in.',
|
||||
comodel_name="project.project",
|
||||
string="Project",
|
||||
help="The project the new task should be created in.",
|
||||
required=True,
|
||||
)
|
||||
|
||||
task_template_id = fields.Many2one(
|
||||
comodel_name='project.task.template',
|
||||
string='Task Template',
|
||||
help='The template to use when creating the new task.',
|
||||
comodel_name="project.task.template",
|
||||
string="Task Template",
|
||||
help="The template to use when creating the new task.",
|
||||
required=True,
|
||||
)
|
||||
|
||||
new_task_title = fields.Char(
|
||||
help='The title (name) for the newly created task. If left blank, the name of the template will be used.',
|
||||
help=(
|
||||
"The title (name) for the newly created task. If left blank, the name of"
|
||||
" the template will be used."
|
||||
),
|
||||
)
|
||||
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
active_id = self.env.context.get('active_id', False)
|
||||
active_model = self.env.context.get('active_model', False)
|
||||
active_id = self.env.context.get("active_id", False)
|
||||
active_model = self.env.context.get("active_model", False)
|
||||
if not active_model:
|
||||
params = self.env.context.get('params', False)
|
||||
active_model = params and params.get('model', False)
|
||||
if active_model == 'project.task.template' and active_id and 'task_template_id' in fields_list:
|
||||
res.update({'task_template_id': active_id})
|
||||
if active_model == 'project.task' and 'project_id' in fields_list:
|
||||
res.update({'project_id': self.env.ref('industry_fsm.fsm_project').id})
|
||||
params = self.env.context.get("params", False)
|
||||
active_model = params and params.get("model", False)
|
||||
if (
|
||||
active_model == "project.task.template"
|
||||
and active_id
|
||||
and "task_template_id" in fields_list
|
||||
):
|
||||
res.update({"task_template_id": active_id})
|
||||
if active_model == "project.task" and "project_id" in fields_list:
|
||||
res.update({"project_id": self.env.ref("industry_fsm.fsm_project").id})
|
||||
return res
|
||||
|
||||
def action_create_task_from_template(self):
|
||||
self.ensure_one()
|
||||
task = self.task_template_id.create_task_from_self(self.project_id, self.new_task_title)
|
||||
task = self.task_template_id.create_task_from_self(
|
||||
self.project_id, self.new_task_title
|
||||
)
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'project.task',
|
||||
'res_id': task.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current',
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "project.task",
|
||||
"res_id": task.id,
|
||||
"view_mode": "form",
|
||||
"target": "current",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from odoo import models, fields, api
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
"res.company",
|
||||
default=lambda self: self.env.company or self.env.user.company_id,
|
||||
)
|
||||
separate_time_on_work_orders = fields.Boolean(
|
||||
|
|
|
|||
Loading…
Reference in a new issue