# Copyright (C) 2018 - TODAY, Pavlov Media # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models class Agreement(models.Model): _inherit = "agreement" # General name = fields.Char(string="Title", required=True) version = fields.Integer( string="Version", default=1, copy=False, help="The versions are used to keep track of document history and " "previous versions can be referenced.", ) revision = fields.Integer( string="Revision", default=0, copy=False, help="The revision will increase with every save event.", ) description = fields.Text( string="Description", tracking=True, help="Description of the agreement", ) dynamic_description = fields.Text( compute="_compute_dynamic_description", string="Dynamic Description", help="Compute dynamic description", ) start_date = fields.Date( string="Start Date", tracking=True, help="When the agreement starts.", ) end_date = fields.Date( string="End Date", tracking=True, help="When the agreement ends." ) color = fields.Integer(string="Color") active = fields.Boolean( string="Active", default=True, help="If unchecked, it will allow you to hide the agreement without " "removing it.", ) company_signed_date = fields.Date( string="Signed on", tracking=True, help="Date the contract was signed by Company.", ) partner_signed_date = fields.Date( string="Signed on (Partner)", tracking=True, help="Date the contract was signed by the Partner.", ) term = fields.Integer( string="Term (Months)", tracking=True, help="Number of months this agreement/contract is in effect with the " "partner.", ) expiration_notice = fields.Integer( string="Exp. Notice (Days)", tracking=True, help="Number of Days before expiration to be notified.", ) change_notice = fields.Integer( string="Change Notice (Days)", tracking=True, help="Number of Days to be notified before changes.", ) special_terms = fields.Text( string="Special Terms", tracking=True, help="Any terms that you have agreed to and want to track on the " "agreement/contract.", ) dynamic_special_terms = fields.Text( compute="_compute_dynamic_special_terms", string="Dynamic Special Terms", help="Compute dynamic special terms", ) code = fields.Char( string="Reference", required=True, default=lambda self: _("New"), tracking=True, copy=False, help="ID used for internal contract tracking.", ) increase_type_id = fields.Many2one( comodel_name="agreement.increasetype", string="Increase Type", tracking=True, help="The amount that certain rates may increase.", ) termination_requested = fields.Date( string="Termination Requested Date", tracking=True, help="Date that a request for termination was received.", ) termination_date = fields.Date( string="Termination Date", tracking=True, help="Date that the contract was terminated.", ) reviewed_date = fields.Date(string="Reviewed Date", tracking=True) reviewed_user_id = fields.Many2one( comodel_name="res.users", string="Reviewed By", tracking=True ) approved_date = fields.Date(string="Approved Date", tracking=True) approved_user_id = fields.Many2one( comodel_name="res.users", string="Approved By", tracking=True ) currency_id = fields.Many2one(comodel_name="res.currency", string="Currency") partner_id = fields.Many2one( comodel_name="res.partner", string="Partner", required=False, copy=True, help="The customer or vendor this agreement is related to.", ) partner_contact_id = fields.Many2one( comodel_name="res.partner", string="Partner Contact", copy=True, help="The primary partner contact (If Applicable).", ) partner_contact_phone = fields.Char( related="partner_contact_id.phone", string="Partner Phone" ) partner_contact_email = fields.Char( related="partner_contact_id.email", string="Partner Email" ) company_contact_id = fields.Many2one( comodel_name="res.partner", string="Company Contact", copy=True, help="The primary contact in the company.", ) company_contact_phone = fields.Char( related="company_contact_id.phone", string="Phone" ) company_contact_email = fields.Char( related="company_contact_id.email", string="Email" ) use_parties_content = fields.Boolean( string="Use parties content", help="Use custom content for parties" ) company_partner_id = fields.Many2one( related="company_id.partner_id", string="Company's Partner" ) def _get_default_parties(self): deftext = """

Company Information

${object.company_id.partner_id.name or ''}.
${object.company_id.partner_id.street or ''}
${object.company_id.partner_id.state_id.code or ''} ${object.company_id.partner_id.zip or ''} ${object.company_id.partner_id.city or ''}
${object.company_id.partner_id.country_id.name or ''}.

Represented by ${object.company_contact_id.name or ''}.

Partner Information

${object.partner_id.name or ''}.
${object.partner_id.street or ''}
${object.partner_id.state_id.code or ''} ${object.partner_id.zip or ''} ${object.partner_id.city or ''}
${object.partner_id.country_id.name or ''}.

Represented by ${object.partner_contact_id.name or ''}.

""" return deftext parties = fields.Html( string="Parties", tracking=True, default=_get_default_parties, help="Parties of the agreement", ) dynamic_parties = fields.Html( compute="_compute_dynamic_parties", string="Dynamic Parties", help="Compute dynamic parties", ) agreement_type_id = fields.Many2one(tracking=True) agreement_subtype_id = fields.Many2one( comodel_name="agreement.subtype", string="Agreement Sub-type", tracking=True, help="Select the sub-type of this agreement. Sub-Types are related to " "agreement types.", ) product_ids = fields.Many2many( comodel_name="product.template", string="Products & Services" ) assigned_user_id = fields.Many2one( comodel_name="res.users", string="Assigned To", tracking=True, help="Select the user who manages this agreement.", ) company_signed_user_id = fields.Many2one( comodel_name="res.users", string="Signed By", tracking=True, help="The user at our company who authorized/signed the agreement or " "contract.", ) partner_signed_user_id = fields.Many2one( comodel_name="res.partner", string="Signed By (Partner)", tracking=True, help="Contact on the account that signed the agreement/contract.", ) parent_agreement_id = fields.Many2one( comodel_name="agreement", string="Parent Agreement", help="Link this agreement to a parent agreement. For example if this " "agreement is an amendment to another agreement. This list will " "only show other agreements related to the same account.", ) renewal_type_id = fields.Many2one( comodel_name="agreement.renewaltype", string="Renewal Type", tracking=True, help="Describes what happens after the contract expires.", ) recital_ids = fields.One2many( comodel_name="agreement.recital", inverse_name="agreement_id", string="Recitals", copy=True, ) sections_ids = fields.One2many( comodel_name="agreement.section", inverse_name="agreement_id", string="Sections", copy=True, ) clauses_ids = fields.One2many( comodel_name="agreement.clause", inverse_name="agreement_id", string="Clauses" ) appendix_ids = fields.One2many( comodel_name="agreement.appendix", inverse_name="agreement_id", string="Appendices", copy=True, ) previous_version_agreements_ids = fields.One2many( comodel_name="agreement", inverse_name="parent_agreement_id", string="Previous Versions", copy=False, domain=[("active", "=", False)], ) child_agreements_ids = fields.One2many( comodel_name="agreement", inverse_name="parent_agreement_id", string="Child Agreements", copy=False, domain=[("active", "=", True)], ) line_ids = fields.One2many( comodel_name="agreement.line", inverse_name="agreement_id", string="Products/Services", copy=False, ) state = fields.Selection( [("draft", "Draft"), ("active", "Active"), ("inactive", "Inactive")], default="draft", tracking=True, ) notification_address_id = fields.Many2one( comodel_name="res.partner", string="Notification Address", help="The address to send notificaitons to, if different from " "customer address.(Address Type = Other)", ) signed_contract_filename = fields.Char(string="Filename") signed_contract = fields.Binary(string="Signed Document", tracking=True) # Dynamic field editor field_domain = fields.Char( string="Field Expression", default='[["active", "=", True]]' ) default_value = fields.Char( string="Default Value", help="Optional value to use if the target field is empty.", ) copyvalue = fields.Char( string="Placeholder Expression", help="""Final placeholder expression, to be copy-pasted in the desired 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 def _compute_dynamic_description(self): MailTemplates = self.env["mail.template"] for agreement in self: lang = agreement.partner_id.lang or "en_US" description = MailTemplates.with_context(lang=lang)._render_template( agreement.description, "agreement", agreement.id ) agreement.dynamic_description = description def _compute_dynamic_parties(self): MailTemplates = self.env["mail.template"] for agreement in self: lang = agreement.partner_id.lang or "en_US" parties = MailTemplates.with_context(lang=lang)._render_template( agreement.parties, "agreement", agreement.id ) agreement.dynamic_parties = parties def _compute_dynamic_special_terms(self): MailTemplates = self.env["mail.template"] for agreement in self: lang = agreement.partner_id.lang or "en_US" special_terms = MailTemplates.with_context(lang=lang)._render_template( agreement.special_terms, "agreement", agreement.id ) agreement.dynamic_special_terms = special_terms # Used for Kanban grouped_by view @api.model def _read_group_stage_ids(self, stages, domain, order): stage_ids = self.env["agreement.stage"].search( [("stage_type", "=", "agreement")] ) return stage_ids stage_id = fields.Many2one( comodel_name="agreement.stage", string="Stage", group_expand="_read_group_stage_ids", help="Select the current stage of the agreement.", tracking=True, index=True, ) # Create New Version Button def create_new_version(self, vals): for rec in self: if not rec.state == "draft": # Make sure status is draft rec.state = "draft" default_vals = { "name": "{} - OLD VERSION".format(rec.name), "active": False, "parent_agreement_id": rec.id, } # Make a current copy and mark it as old rec.copy(default=default_vals) # Increment the Version rec.version = rec.version + 1 # Reset revision to 0 since it's a new version vals["revision"] = 0 return super(Agreement, self).write(vals) def create_new_agreement(self): default_vals = { "name": "NEW", "active": True, "version": 1, "revision": 0, "state": "draft", "stage_id": self.env.ref("agreement_legal.agreement_stage_new").id, } res = self.copy(default=default_vals) res.sections_ids.mapped("clauses_ids").write({"agreement_id": res.id}) return { "res_model": "agreement", "type": "ir.actions.act_window", "view_mode": "form", "view_type": "form", "res_id": res.id, } @api.model def create(self, vals): if vals.get("code", _("New")) == _("New"): vals["code"] = self.env["ir.sequence"].next_by_code("agreement") or _("New") if not vals.get("stage_id"): vals["stage_id"] = self.env.ref("agreement_legal.agreement_stage_new").id return super(Agreement, self).create(vals) # Increments the revision on each save action def write(self, vals): res = True for rec in self: vals["revision"] = rec.revision + 1 res = super(Agreement, rec).write(vals) return res