[14.0][MIG] agreement_legal (Version 12.0 to 14.0)

[MIG] Black & Cleanup

[IMP] Reviewer Requested Changes

[IMP] Black

[IMP] Reviewer Suggestion

[FIX] Tests

[FIX] Multiple Issues

- Added 'is_old_version' field to better display old versions. Before these were marked as inactive therefore not showing in the agreement.
- 'is_template' field now copies when making a template revision or copying a template.
- New Versions now retain original create date.
- Moved version/revision and created on/by fields out of footer to top in order to have the save buttons when creating children.
- Moved demo data out of data folder into the demo.xml

[FIX] demo data

[FIX] Revert back to only migration

[FIX] Report dynamic field rendering

[FIX] Test
This commit is contained in:
Patrick Wilson
2021-05-04 11:38:03 -06:00
committed by Enric Tobella
parent 884fcd1b93
commit 9cf3d551b0
41 changed files with 892 additions and 364 deletions

View File

@@ -1,14 +1,4 @@
# Copyright (C) 2018 - TODAY, Pavlov Media # Copyright (C) 2018 - TODAY, Pavlov Media
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, SUPERUSER_ID
from . import models from . import models
def post_init_agreement_legal(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, dict())
cr.execute(
"UPDATE agreement SET stage_id = %s WHERE stage_id IS NULL;",
(env.ref("agreement_legal.agreement_stage_new").id,),
)
return True

View File

@@ -36,8 +36,6 @@
"views/menu.xml", "views/menu.xml",
], ],
"demo": ["demo/demo.xml"], "demo": ["demo/demo.xml"],
"qweb": ["static/src/xml/domain_widget_view.xml"],
"post_init_hook": "post_init_agreement_legal",
"application": True, "application": True,
"development_status": "Beta", "development_status": "Beta",
"maintainers": ["max3903", "ygol"], "maintainers": ["max3903", "ygol"],

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record id="agreement_stage_new" model="agreement.stage"> <record id="agreement_stage_new" model="agreement.stage">
<field name="name">New</field> <field name="name">New</field>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record id="agreement_type_agreement" model="agreement.type"> <record id="agreement_type_agreement" model="agreement.type">
<field name="name">Agreement</field> <field name="name">Agreement</field>

View File

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo noupdate="1"> <odoo noupdate="1">
<!-- Sequence for agreement --> <!-- Sequence for agreement -->
<record id="seq_agreement" model="ir.sequence"> <record id="seq_agreement" model="ir.sequence">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record id="agreement" model="ir.module.category"> <record id="agreement" model="ir.module.category">
<field name="name">Agreement</field> <field name="name">Agreement</field>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!-- <!--
© 2019 Ygol Internetwork (yves@ygol.com) © 2019 Ygol Internetwork (yves@ygol.com)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

View File

@@ -7,7 +7,6 @@ from odoo import _, api, fields, models
class Agreement(models.Model): class Agreement(models.Model):
_inherit = "agreement" _inherit = "agreement"
# General
name = fields.Char(string="Title", required=True) name = fields.Char(string="Title", required=True)
version = fields.Integer( version = fields.Integer(
string="Version", string="Version",
@@ -23,7 +22,7 @@ class Agreement(models.Model):
help="The revision will increase with every save event.", help="The revision will increase with every save event.",
) )
description = fields.Text( description = fields.Text(
string="Description", tracking=True, help="Description of the agreement", string="Description", tracking=True, help="Description of the agreement"
) )
dynamic_description = fields.Text( dynamic_description = fields.Text(
compute="_compute_dynamic_description", compute="_compute_dynamic_description",
@@ -31,7 +30,7 @@ class Agreement(models.Model):
help="Compute dynamic description", help="Compute dynamic description",
) )
start_date = fields.Date( start_date = fields.Date(
string="Start Date", tracking=True, help="When the agreement starts.", string="Start Date", tracking=True, help="When the agreement starts."
) )
end_date = fields.Date( end_date = fields.Date(
string="End Date", tracking=True, help="When the agreement ends." string="End Date", tracking=True, help="When the agreement ends."
@@ -89,7 +88,7 @@ class Agreement(models.Model):
help="ID used for internal contract tracking.", help="ID used for internal contract tracking.",
) )
increase_type_id = fields.Many2one( increase_type_id = fields.Many2one(
comodel_name="agreement.increasetype", "agreement.increasetype",
string="Increase Type", string="Increase Type",
tracking=True, tracking=True,
help="The amount that certain rates may increase.", help="The amount that certain rates may increase.",
@@ -105,23 +104,19 @@ class Agreement(models.Model):
help="Date that the contract was terminated.", help="Date that the contract was terminated.",
) )
reviewed_date = fields.Date(string="Reviewed Date", tracking=True) reviewed_date = fields.Date(string="Reviewed Date", tracking=True)
reviewed_user_id = fields.Many2one( reviewed_user_id = fields.Many2one("res.users", string="Reviewed By", tracking=True)
comodel_name="res.users", string="Reviewed By", tracking=True
)
approved_date = fields.Date(string="Approved Date", tracking=True) approved_date = fields.Date(string="Approved Date", tracking=True)
approved_user_id = fields.Many2one( approved_user_id = fields.Many2one("res.users", string="Approved By", tracking=True)
comodel_name="res.users", string="Approved By", tracking=True currency_id = fields.Many2one("res.currency", string="Currency")
)
currency_id = fields.Many2one(comodel_name="res.currency", string="Currency")
partner_id = fields.Many2one( partner_id = fields.Many2one(
comodel_name="res.partner", "res.partner",
string="Partner", string="Partner",
required=False, required=False,
copy=True, copy=True,
help="The customer or vendor this agreement is related to.", help="The customer or vendor this agreement is related to.",
) )
partner_contact_id = fields.Many2one( partner_contact_id = fields.Many2one(
comodel_name="res.partner", "res.partner",
string="Partner Contact", string="Partner Contact",
copy=True, copy=True,
help="The primary partner contact (If Applicable).", help="The primary partner contact (If Applicable).",
@@ -133,7 +128,7 @@ class Agreement(models.Model):
related="partner_contact_id.email", string="Partner Email" related="partner_contact_id.email", string="Partner Email"
) )
company_contact_id = fields.Many2one( company_contact_id = fields.Many2one(
comodel_name="res.partner", "res.partner",
string="Company Contact", string="Company Contact",
copy=True, copy=True,
help="The primary contact in the company.", help="The primary contact in the company.",
@@ -187,89 +182,73 @@ class Agreement(models.Model):
string="Dynamic Parties", string="Dynamic Parties",
help="Compute dynamic parties", help="Compute dynamic parties",
) )
agreement_type_id = fields.Many2one(tracking=True) agreement_type_id = fields.Many2one(tracking=True,)
agreement_subtype_id = fields.Many2one( agreement_subtype_id = fields.Many2one(
comodel_name="agreement.subtype", "agreement.subtype",
string="Agreement Sub-type", string="Agreement Sub-type",
tracking=True, tracking=True,
help="Select the sub-type of this agreement. Sub-Types are related to " help="Select the sub-type of this agreement. Sub-Types are related to "
"agreement types.", "agreement types.",
) )
product_ids = fields.Many2many( product_ids = fields.Many2many("product.template", string="Products & Services")
comodel_name="product.template", string="Products & Services"
)
assigned_user_id = fields.Many2one( assigned_user_id = fields.Many2one(
comodel_name="res.users", "res.users",
string="Assigned To", string="Assigned To",
tracking=True, tracking=True,
help="Select the user who manages this agreement.", help="Select the user who manages this agreement.",
) )
company_signed_user_id = fields.Many2one( company_signed_user_id = fields.Many2one(
comodel_name="res.users", "res.users",
string="Signed By", string="Signed By",
tracking=True, tracking=True,
help="The user at our company who authorized/signed the agreement or " help="The user at our company who authorized/signed the agreement or "
"contract.", "contract.",
) )
partner_signed_user_id = fields.Many2one( partner_signed_user_id = fields.Many2one(
comodel_name="res.partner", "res.partner",
string="Signed By (Partner)", string="Signed By (Partner)",
tracking=True, tracking=True,
help="Contact on the account that signed the agreement/contract.", help="Contact on the account that signed the agreement/contract.",
) )
parent_agreement_id = fields.Many2one( parent_agreement_id = fields.Many2one(
comodel_name="agreement", "agreement",
string="Parent Agreement", string="Parent Agreement",
help="Link this agreement to a parent agreement. For example if this " help="Link this agreement to a parent agreement. For example if this "
"agreement is an amendment to another agreement. This list will " "agreement is an amendment to another agreement. This list will "
"only show other agreements related to the same account.", "only show other agreements related to the same account.",
) )
renewal_type_id = fields.Many2one( renewal_type_id = fields.Many2one(
comodel_name="agreement.renewaltype", "agreement.renewaltype",
string="Renewal Type", string="Renewal Type",
tracking=True, tracking=True,
help="Describes what happens after the contract expires.", help="Describes what happens after the contract expires.",
) )
recital_ids = fields.One2many( recital_ids = fields.One2many(
comodel_name="agreement.recital", "agreement.recital", "agreement_id", string="Recitals", copy=True
inverse_name="agreement_id",
string="Recitals",
copy=True,
) )
sections_ids = fields.One2many( sections_ids = fields.One2many(
comodel_name="agreement.section", "agreement.section", "agreement_id", string="Sections", copy=True
inverse_name="agreement_id",
string="Sections",
copy=True,
)
clauses_ids = fields.One2many(
comodel_name="agreement.clause", inverse_name="agreement_id", string="Clauses"
) )
clauses_ids = fields.One2many("agreement.clause", "agreement_id", string="Clauses")
appendix_ids = fields.One2many( appendix_ids = fields.One2many(
comodel_name="agreement.appendix", "agreement.appendix", "agreement_id", string="Appendices", copy=True
inverse_name="agreement_id",
string="Appendices",
copy=True,
) )
previous_version_agreements_ids = fields.One2many( previous_version_agreements_ids = fields.One2many(
comodel_name="agreement", "agreement",
inverse_name="parent_agreement_id", "parent_agreement_id",
string="Previous Versions", string="Previous Versions",
copy=False, copy=False,
context={"active_test": False}, context={"active_test": False},
) )
child_agreements_ids = fields.One2many( child_agreements_ids = fields.One2many(
comodel_name="agreement", "agreement",
inverse_name="parent_agreement_id", "parent_agreement_id",
string="Child Agreements", string="Child Agreements",
copy=False, copy=False,
domain=[("active", "=", True)], domain=[("active", "=", True)],
) )
line_ids = fields.One2many( line_ids = fields.One2many(
comodel_name="agreement.line", "agreement.line", "agreement_id", string="Products/Services", copy=False
inverse_name="agreement_id",
string="Products/Services",
copy=False,
) )
state = fields.Selection( state = fields.Selection(
[("draft", "Draft"), ("active", "Active"), ("inactive", "Inactive")], [("draft", "Draft"), ("active", "Active"), ("inactive", "Inactive")],
@@ -277,17 +256,32 @@ class Agreement(models.Model):
tracking=True, tracking=True,
) )
notification_address_id = fields.Many2one( notification_address_id = fields.Many2one(
comodel_name="res.partner", "res.partner",
string="Notification Address", string="Notification Address",
help="The address to send notificaitons to, if different from " help="The address to send notificaitons to, if different from "
"customer address.(Address Type = Other)", "customer address.(Address Type = Other)",
) )
signed_contract_filename = fields.Char(string="Filename") signed_contract_filename = fields.Char(string="Filename")
signed_contract = fields.Binary(string="Signed Document", tracking=True) signed_contract = fields.Binary(string="Signed Document", tracking=True)
field_id = fields.Many2one(
# Dynamic field editor "ir.model.fields",
field_domain = fields.Char( string="Field",
string="Field Expression", default='[["active", "=", True]]' help="""Select target field from the related document model. If it is a
relationship field you will be able to select a target field at the
destination of the relationship.""",
)
sub_object_id = fields.Many2one(
"ir.model",
string="Sub-model",
help="""When a relationship field is selected as first field, this
field shows the document model the relationship goes to.""",
)
sub_model_object_field_id = fields.Many2one(
"ir.model.fields",
string="Sub-field",
help="""When a relationship field is selected as first field, this
field lets you select the target field within the destination document
model (sub-model).""",
) )
default_value = fields.Char( default_value = fields.Char(
string="Default Value", string="Default Value",
@@ -299,33 +293,25 @@ class Agreement(models.Model):
template field.""", template field.""",
) )
@api.onchange("field_domain", "default_value")
def onchange_copyvalue(self):
self.copyvalue = False
if self.field_domain:
string_list = self.field_domain.split(",")
if string_list:
field_domain = string_list[0][3:-1]
self.copyvalue = "${{object.{} or {}}}".format(
field_domain, self.default_value or "''"
)
# compute the dynamic content for mako expression # compute the dynamic content for mako expression
def _compute_dynamic_description(self): def _compute_dynamic_description(self):
MailTemplates = self.env["mail.template"] MailTemplates = self.env["mail.template"]
for agreement in self: for agreement in self:
lang = agreement.partner_id.lang or "en_US" lang = agreement.partner_id.lang or "en_US"
description = MailTemplates.with_context(lang=lang)._render_template( description = MailTemplates.with_context(lang=lang)._render_template(
agreement.description, "agreement", agreement.id agreement.description, "agreement", [agreement.id]
) )
agreement.dynamic_description = description des = ""
for i in description:
des += description[i]
agreement.dynamic_description = des
def _compute_dynamic_parties(self): def _compute_dynamic_parties(self):
MailTemplates = self.env["mail.template"] MailTemplates = self.env["mail.template"]
for agreement in self: for agreement in self:
lang = agreement.partner_id.lang or "en_US" lang = agreement.partner_id.lang or "en_US"
parties = MailTemplates.with_context(lang=lang)._render_template( parties = MailTemplates.with_context(lang=lang)._render_template(
agreement.parties, "agreement", agreement.id agreement.parties, "agreement", [agreement.id]
) )
agreement.dynamic_parties = parties agreement.dynamic_parties = parties
@@ -334,10 +320,31 @@ class Agreement(models.Model):
for agreement in self: for agreement in self:
lang = agreement.partner_id.lang or "en_US" lang = agreement.partner_id.lang or "en_US"
special_terms = MailTemplates.with_context(lang=lang)._render_template( special_terms = MailTemplates.with_context(lang=lang)._render_template(
agreement.special_terms, "agreement", agreement.id agreement.special_terms, "agreement", [agreement.id]
) )
agreement.dynamic_special_terms = special_terms agreement.dynamic_special_terms = special_terms
@api.onchange("field_id", "sub_model_object_field_id", "default_value")
def onchange_copyvalue(self):
self.sub_object_id = False
self.copyvalue = False
self.sub_object_id = False
if self.field_id and not self.field_id.relation:
self.copyvalue = "${{object.{} or {}}}".format(
self.field_id.name, self.default_value or "''"
)
self.sub_model_object_field_id = False
if self.field_id and self.field_id.relation:
self.sub_object_id = self.env["ir.model"].search(
[("model", "=", self.field_id.relation)]
)[0]
if self.sub_model_object_field_id:
self.copyvalue = "${{object.{}.{} or {}}}".format(
self.field_id.name,
self.sub_model_object_field_id.name,
self.default_value or "''",
)
# Used for Kanban grouped_by view # Used for Kanban grouped_by view
@api.model @api.model
def _read_group_stage_ids(self, stages, domain, order): def _read_group_stage_ids(self, stages, domain, order):
@@ -347,7 +354,7 @@ class Agreement(models.Model):
return stage_ids return stage_ids
stage_id = fields.Many2one( stage_id = fields.Many2one(
comodel_name="agreement.stage", "agreement.stage",
string="Stage", string="Stage",
group_expand="_read_group_stage_ids", group_expand="_read_group_stage_ids",
help="Select the current stage of the agreement.", help="Select the current stage of the agreement.",
@@ -365,14 +372,19 @@ class Agreement(models.Model):
"name": "{} - OLD VERSION".format(rec.name), "name": "{} - OLD VERSION".format(rec.name),
"active": False, "active": False,
"parent_agreement_id": rec.id, "parent_agreement_id": rec.id,
"version": rec.version,
"code": rec.code + "-V" + str(rec.version),
} }
# Make a current copy and mark it as old # Make a current copy and mark it as old
rec.copy(default=default_vals) rec.copy(default=default_vals)
# Increment the Version
rec.version = rec.version + 1
return super().write({"revision": 0}) return super().write({"revision": 0})
def create_new_agreement(self): def create_new_agreement(self):
self.ensure_one()
default_vals = { default_vals = {
"name": "NEW", "name": "New",
"active": True, "active": True,
"version": 1, "version": 1,
"revision": 0, "revision": 0,
@@ -395,12 +407,9 @@ class Agreement(models.Model):
vals["code"] = self.env["ir.sequence"].next_by_code("agreement") or _("New") vals["code"] = self.env["ir.sequence"].next_by_code("agreement") or _("New")
if not vals.get("stage_id"): if not vals.get("stage_id"):
vals["stage_id"] = self.env.ref("agreement_legal.agreement_stage_new").id vals["stage_id"] = self.env.ref("agreement_legal.agreement_stage_new").id
return super(Agreement, self).create(vals) return super().create(vals)
# Increments the revision on each save action # Increments the revision on each save action
def write(self, vals): def write(self, vals):
res = True vals["revision"] = self.revision + 1
for rec in self: return super().write(vals)
vals["revision"] = rec.revision + 1
res = super(Agreement, rec).write(vals)
return res

View File

@@ -13,7 +13,7 @@ class AgreementAppendix(models.Model):
title = fields.Char( title = fields.Char(
string="Title", string="Title",
required=True, required=True,
help="The title is displayed on the PDF." "The name is not.", help="The title is displayed on the PDF. The name is not.",
) )
sequence = fields.Integer(string="Sequence", default=10) sequence = fields.Integer(string="Sequence", default=10)
content = fields.Html(string="Content") content = fields.Html(string="Content")
@@ -22,9 +22,7 @@ class AgreementAppendix(models.Model):
string="Dynamic Content", string="Dynamic Content",
help="compute dynamic Content", help="compute dynamic Content",
) )
agreement_id = fields.Many2one( agreement_id = fields.Many2one("agreement", string="Agreement", ondelete="cascade")
comodel_name="agreement", string="Agreement", ondelete="cascade"
)
active = fields.Boolean( active = fields.Boolean(
string="Active", string="Active",
default=True, default=True,
@@ -33,8 +31,25 @@ class AgreementAppendix(models.Model):
) )
# Dynamic field editor # Dynamic field editor
field_domain = fields.Char( field_id = fields.Many2one(
string="Field Expression", default='[["active", "=", True]]' "ir.model.fields",
string="Field",
help="""Select target field from the related document model. If it is a
relationship field you will be able to select a target field at the
destination of the relationship.""",
)
sub_object_id = fields.Many2one(
"ir.model",
string="Sub-model",
help="""When a relationship field is selected as first field, this
field shows the document model the relationship goes to.""",
)
sub_model_object_field_id = fields.Many2one(
"ir.model.fields",
string="Sub-field",
help="""When a relationship field is selected as first field, this
field lets you select the target field within the destination document
model (sub-model).""",
) )
default_value = fields.Char( default_value = fields.Char(
string="Default Value", string="Default Value",
@@ -46,16 +61,26 @@ class AgreementAppendix(models.Model):
template field.""", template field.""",
) )
@api.onchange("field_domain", "default_value") @api.onchange("field_id", "sub_model_object_field_id", "default_value")
def onchange_copyvalue(self): def onchange_copyvalue(self):
self.sub_object_id = False
self.copyvalue = False self.copyvalue = False
if self.field_domain: self.sub_object_id = False
string_list = self.field_domain.split(",") if self.field_id and not self.field_id.relation:
if string_list: self.copyvalue = "${{object.{} or {}}}".format(
field_domain = string_list[0][3:-1] self.field_id.name, self.default_value or "''",
self.copyvalue = "${{object.{} or {}}}".format( )
field_domain, self.default_value or "''" self.sub_model_object_field_id = False
) if self.field_id and self.field_id.relation:
self.sub_object_id = self.env["ir.model"].search(
[("model", "=", self.field_id.relation)]
)[0]
if self.sub_model_object_field_id:
self.copyvalue = "${{object.{}.{} or {}}}".format(
self.field_id.name,
self.sub_model_object_field_id.name,
self.default_value or "''",
)
# compute the dynamic content for mako expression # compute the dynamic content for mako expression
def _compute_dynamic_content(self): def _compute_dynamic_content(self):
@@ -67,6 +92,6 @@ class AgreementAppendix(models.Model):
or "en_US" or "en_US"
) )
content = MailTemplates.with_context(lang=lang)._render_template( content = MailTemplates.with_context(lang=lang)._render_template(
appendix.content, "agreement.appendix", appendix.id appendix.content, "agreement.appendix", [appendix.id]
) )
appendix.dynamic_content = content appendix.dynamic_content = content

View File

@@ -11,14 +11,12 @@ class AgreementClause(models.Model):
name = fields.Char(string="Name", required=True) name = fields.Char(string="Name", required=True)
title = fields.Char( title = fields.Char(
string="Title", help="The title is displayed on the PDF." "The name is not." string="Title", help="The title is displayed on the PDF. The name is not."
) )
sequence = fields.Integer(string="Sequence") sequence = fields.Integer(string="Sequence")
agreement_id = fields.Many2one( agreement_id = fields.Many2one("agreement", string="Agreement", ondelete="cascade")
comodel_name="agreement", string="Agreement", ondelete="cascade"
)
section_id = fields.Many2one( section_id = fields.Many2one(
comodel_name="agreement.section", string="Section", ondelete="cascade" "agreement.section", string="Section", ondelete="cascade"
) )
content = fields.Html(string="Clause Content") content = fields.Html(string="Clause Content")
dynamic_content = fields.Html( dynamic_content = fields.Html(
@@ -34,8 +32,25 @@ class AgreementClause(models.Model):
) )
# Dynamic field editor # Dynamic field editor
field_domain = fields.Char( field_id = fields.Many2one(
string="Field Expression", default='[["active", "=", True]]' "ir.model.fields",
string="Field",
help="""Select target field from the related document model. If it is a
relationship field you will be able to select a target field at the
destination of the relationship.""",
)
sub_object_id = fields.Many2one(
"ir.model",
string="Sub-model",
help="""When a relationship field is selected as first field, this
field shows the document model the relationship goes to.""",
)
sub_model_object_field_id = fields.Many2one(
"ir.model.fields",
string="Sub-field",
help="""When a relationship field is selected as first field, this
field lets you select the target field within the destination document
model (sub-model).""",
) )
default_value = fields.Char( default_value = fields.Char(
string="Default Value", string="Default Value",
@@ -47,16 +62,26 @@ class AgreementClause(models.Model):
template field.""", template field.""",
) )
@api.onchange("field_domain", "default_value") @api.onchange("field_id", "sub_model_object_field_id", "default_value")
def onchange_copyvalue(self): def onchange_copyvalue(self):
self.sub_object_id = False
self.copyvalue = False self.copyvalue = False
if self.field_domain: self.sub_object_id = False
string_list = self.field_domain.split(",") if self.field_id and not self.field_id.relation:
if string_list: self.copyvalue = "${{object.{} or {}}}".format(
field_domain = string_list[0][3:-1] self.field_id.name, self.default_value or "''",
self.copyvalue = "${{object.{} or {}}}".format( )
field_domain, self.default_value or "''" self.sub_model_object_field_id = False
) if self.field_id and self.field_id.relation:
self.sub_object_id = self.env["ir.model"].search(
[("model", "=", self.field_id.relation)]
)[0]
if self.sub_model_object_field_id:
self.copyvalue = "${{object.{}.{} or {}}}".format(
self.field_id.name,
self.sub_model_object_field_id.name,
self.default_value or "''",
)
# compute the dynamic content for mako expression # compute the dynamic content for mako expression
def _compute_dynamic_content(self): def _compute_dynamic_content(self):
@@ -66,6 +91,6 @@ class AgreementClause(models.Model):
clause.agreement_id and clause.agreement_id.partner_id.lang or "en_US" clause.agreement_id and clause.agreement_id.partner_id.lang or "en_US"
) )
content = MailTemplates.with_context(lang=lang)._render_template( content = MailTemplates.with_context(lang=lang)._render_template(
clause.content, "agreement.clause", clause.id clause.content, "agreement.clause", [clause.id]
) )
clause.dynamic_content = content clause.dynamic_content = content

View File

@@ -8,15 +8,11 @@ class AgreementLine(models.Model):
_name = "agreement.line" _name = "agreement.line"
_description = "Agreement Lines" _description = "Agreement Lines"
product_id = fields.Many2one(comodel_name="product.product", string="Product") product_id = fields.Many2one("product.product", string="Product")
name = fields.Char(string="Description", required=True) name = fields.Char(string="Description", required=True)
agreement_id = fields.Many2one( agreement_id = fields.Many2one("agreement", string="Agreement", ondelete="cascade")
comodel_name="agreement", string="Agreement", ondelete="cascade"
)
qty = fields.Float(string="Quantity") qty = fields.Float(string="Quantity")
uom_id = fields.Many2one( uom_id = fields.Many2one("uom.uom", string="Unit of Measure", required=True)
comodel_name="uom.uom", string="Unit of Measure", required=True
)
@api.onchange("product_id") @api.onchange("product_id")
def _onchange_product_id(self): def _onchange_product_id(self):

View File

@@ -11,7 +11,7 @@ class AgreementRecital(models.Model):
name = fields.Char(string="Name", required=True) name = fields.Char(string="Name", required=True)
title = fields.Char( title = fields.Char(
string="Title", help="The title is displayed on the PDF." "The name is not." string="Title", help="The title is displayed on the PDF." "The name is not.",
) )
sequence = fields.Integer(string="Sequence", default=10) sequence = fields.Integer(string="Sequence", default=10)
content = fields.Html(string="Content") content = fields.Html(string="Content")
@@ -20,9 +20,7 @@ class AgreementRecital(models.Model):
string="Dynamic Content", string="Dynamic Content",
help="compute dynamic Content", help="compute dynamic Content",
) )
agreement_id = fields.Many2one( agreement_id = fields.Many2one("agreement", string="Agreement", ondelete="cascade")
comodel_name="agreement", string="Agreement", ondelete="cascade"
)
active = fields.Boolean( active = fields.Boolean(
string="Active", string="Active",
default=True, default=True,
@@ -31,8 +29,25 @@ class AgreementRecital(models.Model):
) )
# Dynamic field editor # Dynamic field editor
field_domain = fields.Char( field_id = fields.Many2one(
string="Field Expression", default='[["active", "=", True]]' "ir.model.fields",
string="Field",
help="""Select target field from the related document model. If it is a
relationship field you will be able to select a target field at the
destination of the relationship.""",
)
sub_object_id = fields.Many2one(
"ir.model",
string="Sub-model",
help="""When a relationship field is selected as first field, this
field shows the document model the relationship goes to.""",
)
sub_model_object_field_id = fields.Many2one(
"ir.model.fields",
string="Sub-field",
help="""When a relationship field is selected as first field, this
field lets you select the target field within the destination document
model (sub-model).""",
) )
default_value = fields.Char( default_value = fields.Char(
string="Default Value", string="Default Value",
@@ -44,16 +59,26 @@ class AgreementRecital(models.Model):
template field.""", template field.""",
) )
@api.onchange("field_domain", "default_value") @api.onchange("field_id", "sub_model_object_field_id", "default_value")
def onchange_copyvalue(self): def onchange_copyvalue(self):
self.sub_object_id = False
self.copyvalue = False self.copyvalue = False
if self.field_domain: self.sub_object_id = False
string_list = self.field_domain.split(",") if self.field_id and not self.field_id.relation:
if string_list: self.copyvalue = "${{object.{} or {}}}".format(
field_domain = string_list[0][3:-1] self.field_id.name, self.default_value or "''"
self.copyvalue = "${{object.{} or {}}}".format( )
field_domain, self.default_value or "''" self.sub_model_object_field_id = False
) if self.field_id and self.field_id.relation:
self.sub_object_id = self.env["ir.model"].search(
[("model", "=", self.field_id.relation)]
)[0]
if self.sub_model_object_field_id:
self.copyvalue = "${{object.{}.{} or {}}}".format(
self.field_id.name,
self.sub_model_object_field_id.name,
self.default_value or "''",
)
# compute the dynamic content for mako expression # compute the dynamic content for mako expression
def _compute_dynamic_content(self): def _compute_dynamic_content(self):
@@ -63,6 +88,6 @@ class AgreementRecital(models.Model):
recital.agreement_id and recital.agreement_id.partner_id.lang or "en_US" recital.agreement_id and recital.agreement_id.partner_id.lang or "en_US"
) )
content = MailTemplates.with_context(lang=lang)._render_template( content = MailTemplates.with_context(lang=lang)._render_template(
recital.content, "agreement.recital", recital.id recital.content, "agreement.recital", [recital.id]
) )
recital.dynamic_content = content recital.dynamic_content = content

View File

@@ -14,14 +14,9 @@ class AgreementSection(models.Model):
string="Title", help="The title is displayed on the PDF. The name is not." string="Title", help="The title is displayed on the PDF. The name is not."
) )
sequence = fields.Integer(string="Sequence") sequence = fields.Integer(string="Sequence")
agreement_id = fields.Many2one( agreement_id = fields.Many2one("agreement", string="Agreement", ondelete="cascade")
comodel_name="agreement", string="Agreement", ondelete="cascade"
)
clauses_ids = fields.One2many( clauses_ids = fields.One2many(
comodel_name="agreement.clause", "agreement.clause", "section_id", string="Clauses", copy=True
inverse_name="section_id",
string="Clauses",
copy=True,
) )
content = fields.Html(string="Section Content") content = fields.Html(string="Section Content")
dynamic_content = fields.Html( dynamic_content = fields.Html(
@@ -37,8 +32,25 @@ class AgreementSection(models.Model):
) )
# Dynamic field editor # Dynamic field editor
field_domain = fields.Char( field_id = fields.Many2one(
string="Field Expression", default='[["active", "=", True]]' "ir.model.fields",
string="Field",
help="""Select target field from the related document model. If it is a
relationship field you will be able to select a target field at the
destination of the relationship.""",
)
sub_object_id = fields.Many2one(
"ir.model",
string="Sub-model",
help="""When a relationship field is selected as first field, this
field shows the document model the relationship goes to.""",
)
sub_model_object_field_id = fields.Many2one(
"ir.model.fields",
string="Sub-field",
help="""When a relationship field is selected as first field, this
field lets you select the target field within the destination document
model (sub-model).""",
) )
default_value = fields.Char( default_value = fields.Char(
string="Default Value", string="Default Value",
@@ -50,16 +62,26 @@ class AgreementSection(models.Model):
template field.""", template field.""",
) )
@api.onchange("field_domain", "default_value") @api.onchange("field_id", "sub_model_object_field_id", "default_value")
def onchange_copyvalue(self): def onchange_copyvalue(self):
self.sub_object_id = False
self.copyvalue = False self.copyvalue = False
if self.field_domain: self.sub_object_id = False
string_list = self.field_domain.split(",") if self.field_id and not self.field_id.relation:
if string_list: self.copyvalue = "${{object.{} or {}}}".format(
field_domain = string_list[0][3:-1] self.field_id.name, self.default_value or "''"
self.copyvalue = "${{object.{} or {}}}".format( )
field_domain, self.default_value or "''" self.sub_model_object_field_id = False
) if self.field_id and self.field_id.relation:
self.sub_object_id = self.env["ir.model"].search(
[("model", "=", self.field_id.relation)]
)[0]
if self.sub_model_object_field_id:
self.copyvalue = "${{object.{}.{} or {}}}".format(
self.field_id.name,
self.sub_model_object_field_id.name,
self.default_value or "''",
)
# compute the dynamic content for mako expression # compute the dynamic content for mako expression
def _compute_dynamic_content(self): def _compute_dynamic_content(self):
@@ -69,6 +91,6 @@ class AgreementSection(models.Model):
section.agreement_id and section.agreement_id.partner_id.lang or "en_US" section.agreement_id and section.agreement_id.partner_id.lang or "en_US"
) )
content = MailTemplates.with_context(lang=lang)._render_template( content = MailTemplates.with_context(lang=lang)._render_template(
section.content, "agreement.section", section.id section.content, "agreement.section", [section.id]
) )
section.dynamic_content = content section.dynamic_content = content

View File

@@ -9,6 +9,4 @@ class AgreementSubtype(models.Model):
_description = "Agreement Subtypes" _description = "Agreement Subtypes"
name = fields.Char(string="Name", required=True) name = fields.Char(string="Name", required=True)
agreement_type_id = fields.Many2one( agreement_type_id = fields.Many2one("agreement.type", string="Agreement Type")
comodel_name="agreement.type", string="Agreement Type"
)

View File

@@ -9,7 +9,5 @@ class AgreementType(models.Model):
_description = "Agreement Types" _description = "Agreement Types"
agreement_subtypes_ids = fields.One2many( agreement_subtypes_ids = fields.One2many(
comodel_name="agreement.subtype", "agreement.subtype", "agreement_type_id", string="Subtypes"
inverse_name="agreement_type_id",
string="Subtypes",
) )

View File

@@ -7,4 +7,4 @@ from odoo import fields, models
class Product(models.Model): class Product(models.Model):
_inherit = "product.template" _inherit = "product.template"
agreements_ids = fields.Many2many(comodel_name="agreement", string="Agreements") agreements_ids = fields.Many2many("agreement", string="Agreements")

View File

@@ -7,6 +7,4 @@ from odoo import fields, models
class Partner(models.Model): class Partner(models.Model):
_inherit = "res.partner" _inherit = "res.partner"
agreement_ids = fields.One2many( agreement_ids = fields.One2many("agreement", "partner_id", string="Agreements")
comodel_name="agreement", inverse_name="partner_id", string="Agreements"
)

View File

@@ -1,21 +1,19 @@
<?xml version="1.0" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<report <record id="partner_agreement_contract_document" model="ir.actions.report">
id="partner_agreement_contract_document" <field name="name">Agreement</field>
model="agreement" <field name="model">agreement</field>
string="Contract Document" <field name="report_type">qweb-pdf</field>
name="agreement_legal.report_agreement_document" <field name="report_name">agreement_legal.report_agreement_document</field>
file="agreement_legal.report_agreement_document" <field name="report_file">agreement_legal.report_agreement_document</field>
report_type="qweb-pdf" </record>
/> <record id="partner_agreement_contract_document_preview" model="ir.actions.report">
<report <field name="name">Agreement Preview</field>
id="partner_agreement_contract_document_preview" <field name="model">agreement</field>
model="agreement" <field name="report_type">qweb-html</field>
string="Contract Document Preview" <field name="report_name">agreement_legal.report_agreement_document</field>
name="agreement_legal.report_agreement_document" <field name="report_file">agreement_legal.report_agreement_document</field>
file="agreement_legal.report_agreement_document" </record>
report_type="qweb-html"
/>
<template id="report_agreement_document"> <template id="report_agreement_document">
<t t-name="agreement.report_agreement_document"> <t t-name="agreement.report_agreement_document">
<t t-call="web.html_container"> <t t-call="web.html_container">
@@ -111,7 +109,7 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<t t-if="doc.special_terms"> <t t-if="special_term">
<h2>Special Terms</h2> <h2>Special Terms</h2>
<div name="special_term"> <div name="special_term">
<p t-field="doc.dynamic_special_terms" /> <p t-field="doc.dynamic_special_terms" />

View File

@@ -1,11 +1,11 @@
odoo.define("agreement_legal.domain_widget_ext", function(require) { odoo.define("agreement_legal.domain_widget_ext", function(require) {
"use strict"; "use strict";
const basic_fields = require("web.basic_fields"); var basic_fields = require("web.basic_fields");
const DomainSelector = require("web.DomainSelector"); var DomainSelector = require("web.DomainSelector");
const session = require("web.session"); var session = require("web.session");
const core = require("web.core"); var core = require("web.core");
const qweb = core.qweb; var qweb = core.qweb;
basic_fields.FieldDomain.include({ basic_fields.FieldDomain.include({
/** /**
@@ -26,7 +26,6 @@ odoo.define("agreement_legal.domain_widget_ext", function(require) {
* @override _render from AbstractField * @override _render from AbstractField
* @returns {Deferred} * @returns {Deferred}
*/ */
/* eslint-disable no-unused-vars */
_render: function() { _render: function() {
// If there is no model, only change the non-domain-selector content // If there is no model, only change the non-domain-selector content
if (!this._domainModel) { if (!this._domainModel) {
@@ -35,7 +34,7 @@ odoo.define("agreement_legal.domain_widget_ext", function(require) {
} }
// Convert char value to array value // Convert char value to array value
const value = this.value || "[]"; var value = this.value || "[]";
// Create the domain selector or change the value of the current // Create the domain selector or change the value of the current
// one... // one...

View File

@@ -3,17 +3,6 @@
<t t-name="FieldDomain.content"> <t t-name="FieldDomain.content">
<t t-if="partialUse"> <t t-if="partialUse">
<div t-if="hasModel" class="o_field_domain_panel"> <div t-if="hasModel" class="o_field_domain_panel">
<!--<div t-if="hasModel" class="o_field_domain_panel">
<i class="fa fa-arrow-right" role="img" aria-label="Domain" title="Domain"/>
<button t-if="isValid" class="btn btn-sm btn-secondary o_domain_show_selection_button" type="button">
<t t-esc="nbRecords"/> record(s)
</button>
<span t-else="" class="text-warning" role="alert"><i class="fa fa-exclamation-triangle" role="img" aria-label="Warning" title="Warning"/> Invalid domain</span>
<button t-if="inDialogEdit" class="btn btn-sm btn-primary o_field_domain_dialog_button">Edit Domain</button>
</div>
<div t-else="">Select a model to add a filter.</div>-->
</div> </div>
</t> </t>
<t t-if="!partialUse"> <t t-if="!partialUse">

View File

@@ -1 +1,8 @@
# License LGPLv3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.en.html).
from . import test_agreement from . import test_agreement
from . import test_agreement_appendix
from . import test_agreement_clause
from . import test_agreement_line
from . import test_agreement_recital
from . import test_agreement_section

View File

@@ -1,13 +1,116 @@
# Copyright 2021 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from odoo.addons.agreement.tests import test_agreement
from datetime import timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
class TestAgreement(test_agreement.TestAgreement): class TestAgreement(TransactionCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.test_customer = self.env["res.partner"].create({"name": "TestCustomer"})
self.agreement_type = self.env["agreement.type"].create(
{"name": "Test Agreement Type", "domain": "sale"}
)
self.test_agreement = self.env["agreement"].create(
{
"name": "TestAgreement",
"description": "Test",
"special_terms": "Test",
"partner_id": self.test_customer.id,
"start_date": fields.Date.today(),
"end_date": fields.Date.today() + timedelta(days=365),
"state": "active",
}
)
# TEST 01: Set 'Field' for dynamic placeholder, test onchange method
def test_onchange_copyvalue(self):
agreement_01 = self.test_agreement
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement"), ("name", "=", "active")]
)
agreement_01.field_id = field_01.id
agreement_01.onchange_copyvalue()
self.assertEqual(agreement_01.copyvalue, "${object.active or ''}")
# TEST 02: Set related 'Field' for dynamic placeholder to
# test onchange method
def test_onchange_copyvalue2(self):
agreement_01 = self.test_agreement
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement"), ("name", "=", "agreement_type_id")]
)
sub_field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.type"), ("name", "=", "active")]
)
agreement_01.field_id = field_01.id
agreement_01.onchange_copyvalue()
self.assertEqual(agreement_01.sub_object_id.model, "agreement.type")
agreement_01.sub_model_object_field_id = sub_field_01.id
agreement_01.onchange_copyvalue()
self.assertEqual(
agreement_01.copyvalue, "${object.agreement_type_id.active or ''}"
)
# TEST 03: Create New Version
def test_create_new_version(self):
agreement_01 = self.test_agreement
agreement_01.create_new_version()
old_agreement = self.env["agreement"].search(
[("code", "=", agreement_01.code + "-V1"), ("active", "=", False)]
)
self.assertEqual(len(old_agreement), 1)
new_agreement = self.env["agreement"].search(
[("name", "=", "TestAgreement"), ("version", "=", 2)]
)
self.assertEqual(len(new_agreement), 1)
# TEST 04: Create New Agreement
def test_create_new_agreement(self):
agreement_01 = self.test_agreement
agreement_01.create_new_agreement()
new_agreement = self.env["agreement"].search([("name", "=", "New")])
self.assertEqual(len(new_agreement), 1)
# TEST 05: Test Description Dynamic Field
def test_compute_dynamic_description(self):
agreement_01 = self.test_agreement
agreement_01.description = "${object.name}"
self.assertEqual(
agreement_01.dynamic_description, "TestAgreement",
)
# TEST 06: Test Parties Dynamic Field
def test_compute_dynamic_parties(self):
agreement_01 = self.test_agreement
agreement_01.parties = "${object.name}"
self.assertEqual(
agreement_01.dynamic_parties,
"<p>{" + str(agreement_01.id) + ": '</p><p>TestAgreement</p>'}",
)
# TEST 07: Test Special Terms Dynamic Field
def test_compute_dynamic_special_terms(self):
agreement_01 = self.test_agreement
agreement_01.special_terms = "${object.name}"
self.assertEqual(
agreement_01.dynamic_special_terms,
"{" + str(agreement_01.id) + ": 'TestAgreement'}",
)
# TEST 02: Check Read Stages
def test_read_group_stage_ids(self):
agreement_01 = self.test_agreement
self.assertEqual(
agreement_01._read_group_stage_ids(self.env["agreement.stage"], [], "id"),
self.env["agreement.stage"].search(
[("stage_type", "=", "agreement")], order="id",
),
)
def test_action_create_new_version(self): def test_action_create_new_version(self):
self.agreement.create_new_version() self.test_agreement.create_new_version()
self.assertEqual(self.agreement.state, "draft") self.assertEqual(self.test_agreement.state, "draft")
self.assertEqual(len(self.agreement.previous_version_agreements_ids), 1) self.assertEqual(len(self.test_agreement.previous_version_agreements_ids), 1)

View File

@@ -0,0 +1,69 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from datetime import timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
class TestAgreementAppendices(TransactionCase):
def setUp(self):
super().setUp()
self.test_customer = self.env["res.partner"].create({"name": "TestCustomer"})
self.agreement_type = self.env["agreement.type"].create(
{"name": "Test Agreement Type", "domain": "sale"}
)
self.test_agreement = self.env["agreement"].create(
{
"name": "TestAgreement",
"description": "Test",
"special_terms": "Test",
"partner_id": self.test_customer.id,
"start_date": fields.Date.today(),
"end_date": fields.Date.today() + timedelta(days=365),
}
)
self.test_appendices = self.env["agreement.appendix"].create(
{
"name": "TestAppendices",
"title": "Test",
"content": "Test",
"agreement_id": self.test_agreement.id,
}
)
# TEST 01: Set 'Field' for dynamic placeholder, test onchange method
def test_onchange_copyvalue(self):
appendix_01 = self.test_appendices
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.appendix"), ("name", "=", "active")]
)
appendix_01.field_id = field_01.id
appendix_01.onchange_copyvalue()
self.assertEqual(appendix_01.copyvalue, "${object.active or ''}")
# TEST 02: Set related 'Field' for dynamic placeholder to
# test onchange method
def test_onchange_copyvalue2(self):
appendix_01 = self.test_appendices
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.appendix"), ("name", "=", "agreement_id")]
)
sub_field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement"), ("name", "=", "active")]
)
appendix_01.field_id = field_01.id
appendix_01.onchange_copyvalue()
self.assertEqual(appendix_01.sub_object_id.model, "agreement")
appendix_01.sub_model_object_field_id = sub_field_01.id
appendix_01.onchange_copyvalue()
self.assertEqual(appendix_01.copyvalue, "${object.agreement_id.active or ''}")
# TEST 03: Test Dynamic Field
def test_compute_dynamic_content(self):
appendix_01 = self.test_appendices
appendix_01.content = "${object.name}"
self.assertEqual(
appendix_01.dynamic_content,
"<p>{" + str(appendix_01.id) + ": '</p><p>TestAppendices</p>'}",
)

View File

@@ -0,0 +1,69 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from datetime import timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
class TestAgreementClauses(TransactionCase):
def setUp(self):
super().setUp()
self.test_customer = self.env["res.partner"].create({"name": "TestCustomer"})
self.agreement_type = self.env["agreement.type"].create(
{"name": "Test Agreement Type", "domain": "sale"}
)
self.test_agreement = self.env["agreement"].create(
{
"name": "TestAgreement",
"description": "Test",
"special_terms": "Test",
"partner_id": self.test_customer.id,
"start_date": fields.Date.today(),
"end_date": fields.Date.today() + timedelta(days=365),
}
)
self.test_clause = self.env["agreement.clause"].create(
{
"name": "TestClause",
"title": "Test",
"content": "Test",
"agreement_id": self.test_agreement.id,
}
)
# TEST 01: Set 'Field' for dynamic placeholder, test onchange method
def test_onchange_copyvalue(self):
clause_01 = self.test_clause
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.clause"), ("name", "=", "active")]
)
clause_01.field_id = field_01.id
clause_01.onchange_copyvalue()
self.assertEqual(clause_01.copyvalue, "${object.active or ''}")
# TEST 02: Set related 'Field' for dynamic placeholder to
# test onchange method
def test_onchange_copyvalue2(self):
clause_01 = self.test_clause
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.clause"), ("name", "=", "agreement_id")]
)
sub_field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement"), ("name", "=", "active")]
)
clause_01.field_id = field_01.id
clause_01.onchange_copyvalue()
self.assertEqual(clause_01.sub_object_id.model, "agreement")
clause_01.sub_model_object_field_id = sub_field_01.id
clause_01.onchange_copyvalue()
self.assertEqual(clause_01.copyvalue, "${object.agreement_id.active or ''}")
# TEST 03: Test Dynamic Field
def test_compute_dynamic_content(self):
clause_01 = self.test_clause
clause_01.content = "${object.name}"
self.assertEqual(
clause_01.dynamic_content,
"<p>{" + str(clause_01.id) + ": '</p><p>TestClause</p>'}",
)

View File

@@ -0,0 +1,42 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from datetime import timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
class TestAgreementLine(TransactionCase):
def setUp(self):
super().setUp()
self.test_customer = self.env["res.partner"].create({"name": "TestCustomer"})
self.agreement_type = self.env["agreement.type"].create(
{"name": "Test Agreement Type", "domain": "sale"}
)
self.test_agreement = self.env["agreement"].create(
{
"name": "TestAgreement",
"description": "Test",
"special_terms": "Test",
"partner_id": self.test_customer.id,
"start_date": fields.Date.today(),
"end_date": fields.Date.today() + timedelta(days=365),
}
)
self.test_product1 = self.env["product.product"].create({"name": "TEST1"})
self.test_product2 = self.env["product.product"].create({"name": "TEST2"})
self.test_line = self.env["agreement.line"].create(
{
"product_id": self.test_product1.id,
"name": "Test",
"uom_id": 1,
"agreement_id": self.test_agreement.id,
}
)
# TEST 01: Set line product onchange method
def test_onchange_product_id(self):
line_01 = self.test_line
line_01.product_id = self.test_product2.id
line_01._onchange_product_id()
self.assertEqual(line_01.name, "TEST2")

View File

@@ -0,0 +1,69 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from datetime import timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
class TestAgreementRectical(TransactionCase):
def setUp(self):
super().setUp()
self.test_customer = self.env["res.partner"].create({"name": "TestCustomer"})
self.agreement_type = self.env["agreement.type"].create(
{"name": "Test Agreement Type", "domain": "sale"}
)
self.test_agreement = self.env["agreement"].create(
{
"name": "TestAgreement",
"description": "Test",
"special_terms": "Test",
"partner_id": self.test_customer.id,
"start_date": fields.Date.today(),
"end_date": fields.Date.today() + timedelta(days=365),
}
)
self.test_recital = self.env["agreement.recital"].create(
{
"name": "TestRecital",
"title": "Test",
"content": "Test",
"agreement_id": self.test_agreement.id,
}
)
# TEST 01: Set 'Field' for dynamic placeholder, test onchange method
def test_onchange_copyvalue(self):
recital_01 = self.test_recital
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.recital"), ("name", "=", "active")]
)
recital_01.field_id = field_01.id
recital_01.onchange_copyvalue()
self.assertEqual(recital_01.copyvalue, "${object.active or ''}")
# TEST 02: Set related 'Field' for dynamic placeholder to
# test onchange method
def test_onchange_copyvalue2(self):
recital_01 = self.test_recital
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.recital"), ("name", "=", "agreement_id")]
)
sub_field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement"), ("name", "=", "active")]
)
recital_01.field_id = field_01.id
recital_01.onchange_copyvalue()
self.assertEqual(recital_01.sub_object_id.model, "agreement")
recital_01.sub_model_object_field_id = sub_field_01.id
recital_01.onchange_copyvalue()
self.assertEqual(recital_01.copyvalue, "${object.agreement_id.active or ''}")
# TEST 03: Test Dynamic Field
def test_compute_dynamic_content(self):
recital_01 = self.test_recital
recital_01.content = "${object.name}"
self.assertEqual(
recital_01.dynamic_content,
"<p>{" + str(recital_01.id) + ": '</p><p>TestRecital</p>'}",
)

View File

@@ -0,0 +1,69 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from datetime import timedelta
from odoo import fields
from odoo.tests.common import TransactionCase
class TestAgreementSection(TransactionCase):
def setUp(self):
super().setUp()
self.test_customer = self.env["res.partner"].create({"name": "TestCustomer"})
self.agreement_type = self.env["agreement.type"].create(
{"name": "Test Agreement Type", "domain": "sale"}
)
self.test_agreement = self.env["agreement"].create(
{
"name": "TestAgreement",
"description": "Test",
"special_terms": "Test",
"partner_id": self.test_customer.id,
"start_date": fields.Date.today(),
"end_date": fields.Date.today() + timedelta(days=365),
}
)
self.test_section = self.env["agreement.section"].create(
{
"name": "TestSection",
"title": "Test",
"content": "Test",
"agreement_id": self.test_agreement.id,
}
)
# TEST 01: Set 'Field' for dynamic placeholder, test onchange method
def test_onchange_copyvalue(self):
section_01 = self.test_section
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.section"), ("name", "=", "active")]
)
section_01.field_id = field_01.id
section_01.onchange_copyvalue()
self.assertEqual(section_01.copyvalue, "${object.active or ''}")
# TEST 02: Set related 'Field' for dynamic placeholder to
# test onchange method
def test_onchange_copyvalue2(self):
section_01 = self.test_section
field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement.section"), ("name", "=", "agreement_id")]
)
sub_field_01 = self.env["ir.model.fields"].search(
[("model", "=", "agreement"), ("name", "=", "active")]
)
section_01.field_id = field_01.id
section_01.onchange_copyvalue()
self.assertEqual(section_01.sub_object_id.model, "agreement")
section_01.sub_model_object_field_id = sub_field_01.id
section_01.onchange_copyvalue()
self.assertEqual(section_01.copyvalue, "${object.agreement_id.active or ''}")
# TEST 03: Test Dynamic Field
def test_compute_dynamic_content(self):
section_01 = self.test_section
section_01.content = "${object.name}"
self.assertEqual(
section_01.dynamic_content,
"<p>{" + str(section_01.id) + ": '</p><p>TestSection</p>'}",
)

View File

@@ -1,17 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<template
id="assets_backend"
name="agreement_legal assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script
type="text/javascript"
src="/agreement_legal/static/src/js/domain_widget_ext.js"
/>
</xpath>
</template>
<!-- Agreement List View--> <!-- Agreement List View-->
<record model="ir.ui.view" id="partner_agreement_list_view"> <record model="ir.ui.view" id="partner_agreement_list_view">
<field name="name">Agreement List</field> <field name="name">Agreement List</field>
@@ -59,8 +47,7 @@
/> />
</header> </header>
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box" />
</div>
<div class="oe_title"> <div class="oe_title">
<label <label
for="name" for="name"
@@ -103,21 +90,30 @@
<field name="description" required="True" nolabel="1" /> <field name="description" required="True" nolabel="1" />
</group> </group>
<group class="oe_edit_only"> <group class="oe_edit_only">
<field
name="field_domain"
widget="domain"
nolabel="1"
options="{'model': 'agreement',
'partial_use': True}"
/>
<group> <group>
<field
name="field_id"
domain="[('model_id', '=', active_model),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
/>
<field name="sub_object_id" readonly="1" />
<field
name="sub_model_object_field_id"
domain="[('model_id', '=', sub_object_id),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
attrs="{'readonly':[('sub_object_id', '=', False)],
'required':[('sub_object_id', '!=', False)]}"
/>
<field name="default_value" /> <field name="default_value" />
<field name="copyvalue" /> <field name="copyvalue" />
</group> </group>
<p> <p>
This section (on the left) allows you to add dynamic fields inside the description and special terms. This section (on the left) allows you to add dynamic fields inside the description and special terms.
<ol> <ol>
<li>Select the agreement field using the popup</li> <li>Select the agreement field</li>
<li>Select the sub-field</li>
<li>Enter the default value if the field is empty</li> <li>Enter the default value if the field is empty</li>
<li <li
>Copy and paste the placeholder expression in the description or the special terms</li> >Copy and paste the placeholder expression in the description or the special terms</li>
@@ -388,10 +384,7 @@
<div <div
class="o_kanban_record_subtitle text-muted" class="o_kanban_record_subtitle text-muted"
> >
<field <field name="partner_id" />
name="partner_id"
invisible="context.get('default_partner_id', False)"
/>
<t <t
t-if="record.end_date.raw_value and record.end_date.raw_value lt (new Date())" t-if="record.end_date.raw_value and record.end_date.raw_value lt (new Date())"
t-set="red" t-set="red"
@@ -416,6 +409,7 @@
<span <span
class="fa fa-ellipsis-v" class="fa fa-ellipsis-v"
aria-hidden="true" aria-hidden="true"
title="Icon"
/> />
</a> </a>
<ul <ul

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Appendix List View--> <!-- Agreement Appendix List View-->
<record model="ir.ui.view" id="agreement_appendix_tree"> <record model="ir.ui.view" id="agreement_appendix_tree">
@@ -21,20 +21,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Appendix"> <form string="Appendix">
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box" />
<button <widget
name="toggle_active" name="web_ribbon"
type="object" title="Archived"
class="oe_stat_button" bg_color="bg-danger"
icon="fa-archive" attrs="{'invisible': [('active', '=', True)]}"
> />
<field <field name="active" invisible="1" />
name="active"
widget="boolean_button"
options="{&quot;terminology&quot;: &quot;archive&quot;}"
/>
</button>
</div>
<div class="oe_title"> <div class="oe_title">
<label for="name" class="oe_edit_only" /> <label for="name" class="oe_edit_only" />
<h1> <h1>
@@ -52,21 +46,30 @@
</group> </group>
<field name="content" widget="html" /> <field name="content" widget="html" />
<group class="oe_edit_only"> <group class="oe_edit_only">
<field
name="field_domain"
widget="domain"
nolabel="1"
options="{'model': 'agreement.appendix',
'partial_use': True}"
/>
<group> <group>
<field
name="field_id"
domain="[('model_id', '=', active_model),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
/>
<field name="sub_object_id" readonly="1" />
<field
name="sub_model_object_field_id"
domain="[('model_id', '=', sub_object_id),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
attrs="{'readonly':[('sub_object_id', '=', False)],
'required':[('sub_object_id', '!=', False)]}"
/>
<field name="default_value" /> <field name="default_value" />
<field name="copyvalue" /> <field name="copyvalue" />
</group> </group>
<p> <p>
This section (on the left) allows you to add dynamic fields inside the content. This section (on the left) allows you to add dynamic fields inside the content.
<ol> <ol>
<li>Select the field using the popup</li> <li>Select the appendix field</li>
<li>Select the sub-field</li>
<li>Enter the default value if the field is empty</li> <li>Enter the default value if the field is empty</li>
<li <li
>Copy and paste the placeholder expression in the content</li> >Copy and paste the placeholder expression in the content</li>
@@ -82,7 +85,8 @@
<field name="name">Agreement Appendix Search</field> <field name="name">Agreement Appendix Search</field>
<field name="model">agreement.appendix</field> <field name="model">agreement.appendix</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search string="Appendix">
<field name="name" />
<filter <filter
name="group_agreement" name="group_agreement"
icon="terp-partner" icon="terp-partner"

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Clause List View--> <!-- Agreement Clause List View-->
<record model="ir.ui.view" id="partner_agreement_clause_list_view"> <record model="ir.ui.view" id="partner_agreement_clause_list_view">
@@ -22,20 +22,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Clause"> <form string="Clause">
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box" />
<button <widget
name="toggle_active" name="web_ribbon"
type="object" title="Archived"
class="oe_stat_button" bg_color="bg-danger"
icon="fa-archive" attrs="{'invisible': [('active', '=', True)]}"
> />
<field <field name="active" invisible="1" />
name="active"
widget="boolean_button"
options="{&quot;terminology&quot;: &quot;archive&quot;}"
/>
</button>
</div>
<div class="oe_title"> <div class="oe_title">
<label for="name" class="oe_edit_only" /> <label for="name" class="oe_edit_only" />
<h1> <h1>
@@ -57,21 +51,30 @@
</group> </group>
<field name="content" widget="html" /> <field name="content" widget="html" />
<group class="oe_edit_only"> <group class="oe_edit_only">
<field
name="field_domain"
widget="domain"
nolabel="1"
options="{'model': 'agreement.clause',
'partial_use': True}"
/>
<group> <group>
<field
name="field_id"
domain="[('model_id', '=', active_model),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
/>
<field name="sub_object_id" readonly="1" />
<field
name="sub_model_object_field_id"
domain="[('model_id', '=', sub_object_id),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
attrs="{'readonly':[('sub_object_id', '=', False)],
'required':[('sub_object_id', '!=', False)]}"
/>
<field name="default_value" /> <field name="default_value" />
<field name="copyvalue" /> <field name="copyvalue" />
</group> </group>
<p> <p>
This section (on the left) allows you to add dynamic fields inside the content. This section (on the left) allows you to add dynamic fields inside the content.
<ol> <ol>
<li>Select the field using the popup</li> <li>Select the clause field</li>
<li>Select the sub-field</li>
<li>Enter the default value if the field is empty</li> <li>Enter the default value if the field is empty</li>
<li <li
>Copy and paste the placeholder expression in the content</li> >Copy and paste the placeholder expression in the content</li>
@@ -87,7 +90,8 @@
<field name="name">Agreement Clause Search</field> <field name="name">Agreement Clause Search</field>
<field name="model">agreement.clause</field> <field name="model">agreement.clause</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Agreement Clause Search"> <search string="Clause">
<field name="name" />
<filter <filter
name="group_agreement" name="group_agreement"
icon="terp-partner" icon="terp-partner"

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Increase Type List View--> <!-- Agreement Increase Type List View-->
<record model="ir.ui.view" id="partner_agreement_increasetype_list_view"> <record model="ir.ui.view" id="partner_agreement_increasetype_list_view">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Recital List View--> <!-- Agreement Recital List View-->
<record model="ir.ui.view" id="agreement_recital_tree"> <record model="ir.ui.view" id="agreement_recital_tree">
@@ -21,20 +21,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Recital"> <form string="Recital">
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box" />
<button <widget
name="toggle_active" name="web_ribbon"
type="object" title="Archived"
class="oe_stat_button" bg_color="bg-danger"
icon="fa-archive" attrs="{'invisible': [('active', '=', True)]}"
> />
<field <field name="active" invisible="1" />
name="active"
widget="boolean_button"
options="{&quot;terminology&quot;: &quot;archive&quot;}"
/>
</button>
</div>
<div class="oe_title"> <div class="oe_title">
<label for="name" class="oe_edit_only" /> <label for="name" class="oe_edit_only" />
<h1> <h1>
@@ -52,21 +46,30 @@
</group> </group>
<field name="content" widget="html" /> <field name="content" widget="html" />
<group class="oe_edit_only"> <group class="oe_edit_only">
<field
name="field_domain"
widget="domain"
nolabel="1"
options="{'model': 'agreement.recital',
'partial_use': True}"
/>
<group> <group>
<field
name="field_id"
domain="[('model_id', '=', active_model),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
/>
<field name="sub_object_id" readonly="1" />
<field
name="sub_model_object_field_id"
domain="[('model_id', '=', sub_object_id),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
attrs="{'readonly':[('sub_object_id', '=', False)],
'required':[('sub_object_id', '!=', False)]}"
/>
<field name="default_value" /> <field name="default_value" />
<field name="copyvalue" /> <field name="copyvalue" />
</group> </group>
<p> <p>
This section (on the left) allows you to add dynamic fields inside the content. This section (on the left) allows you to add dynamic fields inside the content.
<ol> <ol>
<li>Select the field using the popup</li> <li>Select the recital field</li>
<li>Select the sub-field</li>
<li>Enter the default value if the field is empty</li> <li>Enter the default value if the field is empty</li>
<li <li
>Copy and paste the placeholder expression in the content</li> >Copy and paste the placeholder expression in the content</li>
@@ -82,7 +85,8 @@
<field name="name">Agreement Recital Search</field> <field name="name">Agreement Recital Search</field>
<field name="model">agreement.recital</field> <field name="model">agreement.recital</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search string="Recitals">
<field name="name" />
<filter <filter
name="group_agreement" name="group_agreement"
icon="terp-partner" icon="terp-partner"

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Renewal Type List View--> <!-- Agreement Renewal Type List View-->
<record model="ir.ui.view" id="partner_agreement_renewaltype_list_view"> <record model="ir.ui.view" id="partner_agreement_renewaltype_list_view">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Sections List View--> <!-- Agreement Sections List View-->
<record model="ir.ui.view" id="partner_agreement_section_list_view"> <record model="ir.ui.view" id="partner_agreement_section_list_view">
@@ -21,20 +21,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Section"> <form string="Section">
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box" />
<button <widget
name="toggle_active" name="web_ribbon"
type="object" title="Archived"
class="oe_stat_button" bg_color="bg-danger"
icon="fa-archive" attrs="{'invisible': [('active', '=', True)]}"
> />
<field <field name="active" invisible="1" />
name="active"
widget="boolean_button"
options="{&quot;terminology&quot;: &quot;archive&quot;}"
/>
</button>
</div>
<div class="oe_title"> <div class="oe_title">
<label for="name" class="oe_edit_only" /> <label for="name" class="oe_edit_only" />
<h1> <h1>
@@ -54,21 +48,30 @@
<page string="Content"> <page string="Content">
<field name='content' nolabel="1" /> <field name='content' nolabel="1" />
<group class="oe_edit_only"> <group class="oe_edit_only">
<field
name="field_domain"
widget="domain"
nolabel="1"
options="{'model': 'agreement.section',
'partial_use': True}"
/>
<group> <group>
<field
name="field_id"
domain="[('model_id', '=', active_model),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
/>
<field name="sub_object_id" readonly="1" />
<field
name="sub_model_object_field_id"
domain="[('model_id', '=', sub_object_id),
('ttype', '!=', 'one2many'),
('ttype', '!=', 'many2many')]"
attrs="{'readonly':[('sub_object_id', '=', False)],
'required':[('sub_object_id', '!=', False)]}"
/>
<field name="default_value" /> <field name="default_value" />
<field name="copyvalue" /> <field name="copyvalue" />
</group> </group>
<p> <p>
This section (on the left) allows you to add dynamic fields inside the content. This section (on the left) allows you to add dynamic fields inside the content.
<ol> <ol>
<li>Select the field using the popup</li> <li>Select the section field</li>
<li>Select the sub-field</li>
<li <li
>Enter the default value if the field is empty</li> >Enter the default value if the field is empty</li>
<li <li
@@ -100,7 +103,8 @@
<field name="name">Agreement Section Search</field> <field name="name">Agreement Section Search</field>
<field name="model">agreement.section</field> <field name="model">agreement.section</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Agreement Section Search"> <search string="Section">
<field name="name" />
<filter <filter
name="group_agreement" name="group_agreement"
string="Agreements" string="Agreements"

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Stage List View--> <!-- Agreement Stage List View-->
<record model="ir.ui.view" id="partner_agreement_stage_list_view"> <record model="ir.ui.view" id="partner_agreement_stage_list_view">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Sub Type List View--> <!-- Agreement Sub Type List View-->
<record model="ir.ui.view" id="partner_agreement_subtype_list_view"> <record model="ir.ui.view" id="partner_agreement_subtype_list_view">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Agreement Type List View--> <!-- Agreement Type List View-->
<record model="ir.ui.view" id="partner_agreement_type_list_view"> <record model="ir.ui.view" id="partner_agreement_type_list_view">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<!-- Remove top menu from agreement module --> <!-- Remove top menu from agreement module -->
<!-- <delete model="ir.ui.menu" id="agreement.agreement_menu" /> --> <!-- <delete model="ir.ui.menu" id="agreement.agreement_menu" /> -->

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record id="res_config_settings_view_form" model="ir.ui.view"> <record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.agreement</field> <field name="name">res.config.settings.view.form.agreement</field>
@@ -49,7 +49,7 @@
<field name="module_agreement_sale" /> <field name="module_agreement_sale" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Sales" /> <label for="module_agreement_sale" string="Sales" />
<div class="text-muted"> <div class="text-muted">
Create an agreement when the sales order is confirmed Create an agreement when the sales order is confirmed
</div> </div>
@@ -63,7 +63,10 @@
<field name="module_agreement_sale_subscription" /> <field name="module_agreement_sale_subscription" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Sale Subscriptions" /> <label
for="module_agreement_sale_subscription"
string="Sale Subscriptions"
/>
<div class="text-muted"> <div class="text-muted">
Link your subscriptions to an agreement Link your subscriptions to an agreement
</div> </div>
@@ -74,7 +77,10 @@
<field name="module_fieldservice_agreement" /> <field name="module_fieldservice_agreement" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Field Service" /> <label
for="module_fieldservice_agreement"
string="Field Service"
/>
<div class="text-muted"> <div class="text-muted">
Link your Field Service orders and equipments to an agreement Link your Field Service orders and equipments to an agreement
</div> </div>
@@ -85,7 +91,10 @@
<field name="module_agreement_stock" /> <field name="module_agreement_stock" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Inventory" /> <label
for="module_agreement_stock"
string="Inventory"
/>
<div class="text-muted"> <div class="text-muted">
Link your transfers to an agreement Link your transfers to an agreement
</div> </div>
@@ -96,7 +105,7 @@
<field name="module_agreement_rma" /> <field name="module_agreement_rma" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Returns" /> <label for="module_agreement_rma" string="Returns" />
<div class="text-muted"> <div class="text-muted">
Link your returns to an agreement Link your returns to an agreement
</div> </div>
@@ -107,7 +116,10 @@
<field name="module_agreement_maintenance" /> <field name="module_agreement_maintenance" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Maintenance" /> <label
for="module_agreement_maintenance"
string="Maintenance"
/>
<div class="text-muted"> <div class="text-muted">
Manage maintenance agreements and contracts Manage maintenance agreements and contracts
</div> </div>
@@ -118,7 +130,10 @@
<field name="module_agreement_mrp" /> <field name="module_agreement_mrp" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Manufacturing" /> <label
for="module_agreement_mrp"
string="Manufacturing"
/>
<div class="text-muted"> <div class="text-muted">
Link your manufacturing orders to an agreement Link your manufacturing orders to an agreement
</div> </div>
@@ -129,7 +144,7 @@
<field name="module_agreement_repair" /> <field name="module_agreement_repair" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Repair" /> <label for="module_agreement_repair" string="Repair" />
<div class="text-muted"> <div class="text-muted">
Link your repair orders to an agreement Link your repair orders to an agreement
</div> </div>
@@ -140,7 +155,10 @@
<field name="module_agreement_project" /> <field name="module_agreement_project" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Project" /> <label
for="module_agreement_project"
string="Project"
/>
<div class="text-muted"> <div class="text-muted">
Link your projects and tasks to an agreement Link your projects and tasks to an agreement
</div> </div>
@@ -151,7 +169,10 @@
<field name="module_agreement_helpdesk" /> <field name="module_agreement_helpdesk" />
</div> </div>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">
<label for="" string="Helpdesk" /> <label
for="module_agreement_helpdesk"
string="Helpdesk"
/>
<div class="text-muted"> <div class="text-muted">
Link your Helpdesk tickets to an agreement Link your Helpdesk tickets to an agreement
</div> </div>
@@ -162,12 +183,11 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<act_window <record id="action_agreement_config" model="ir.actions.act_window">
id="action_agreement_config" <field name="name">Settings</field>
name="Settings" <field name="res_model">res.config.settings</field>
res_model="res.config.settings" <field name="view_mode">form</field>
view_mode="form" <field name="target">inline</field>
target="inline" <field name="context">{'module': 'agreement'}</field>
context="{'module': 'agreement'}" </record>
/>
</odoo> </odoo>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="partner_form"> <record model="ir.ui.view" id="partner_form">
<field name="model">res.partner</field> <field name="model">res.partner</field>