diff --git a/agreement/README.rst b/agreement/README.rst new file mode 100644 index 000000000..21cd7854d --- /dev/null +++ b/agreement/README.rst @@ -0,0 +1,21 @@ +**This file is going to be generated by oca-gen-addon-readme.** + +*Manual changes will be overwritten.* + +Please provide content in the ``readme`` directory: + +* **DESCRIPTION.rst** (required) +* INSTALL.rst (optional) +* CONFIGURE.rst (optional) +* **USAGE.rst** (optional, highly recommended) +* DEVELOP.rst (optional) +* ROADMAP.rst (optional) +* HISTORY.rst (optional, recommended) +* **CONTRIBUTORS.rst** (optional, highly recommended) +* CREDITS.rst (optional) + +Content of this README will also be drawn from the addon manifest, +from keys such as name, authors, maintainers, development_status, +and license. + +A good, one sentence summary in the manifest is also highly recommended. diff --git a/agreement/__init__.py b/agreement/__init__.py new file mode 100644 index 000000000..073035d16 --- /dev/null +++ b/agreement/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/agreement/__manifest__.py b/agreement/__manifest__.py new file mode 100644 index 000000000..102098bf5 --- /dev/null +++ b/agreement/__manifest__.py @@ -0,0 +1,43 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Agreements', + 'summary': 'Manage Agreements, LOI and Contracts', + 'author': 'Pavlov Media, ' + 'Open Source Integrators, ' + 'Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/contract', + 'category': 'Partner', + 'license': 'AGPL-3', + 'version': '11.0.0.0.1', + 'depends': [ + 'account', + 'contacts', + 'mail', + 'product', + 'sale_management', + ], + 'data': [ + 'data/ir_sequence.xml', + 'data/module_category.xml', + 'data/agreement_stage.xml', + 'security/res_groups.xml', + 'security/ir.model.access.csv', + 'views/res_config_settings.xml', + 'views/reports.xml', + 'views/agreement.xml', + 'views/agreement_clause.xml', + 'views/agreement_section.xml', + 'views/agreement_stages.xml', + 'views/agreement_type.xml', + 'views/agreement_subtype.xml', + 'views/agreement_renewaltype.xml', + 'views/agreement_increasetype.xml', + 'views/res_partner.xml', + 'views/menu.xml', + ], + 'application': True, + 'development_status': 'Beta', + 'maintainers': 'max3903', +} diff --git a/agreement/data/agreement_stage.xml b/agreement/data/agreement_stage.xml new file mode 100644 index 000000000..619aa1f72 --- /dev/null +++ b/agreement/data/agreement_stage.xml @@ -0,0 +1,49 @@ + + + + + New + 10 + + + + Draft + 20 + + + + Reviewed + 30 + + + + Negotiation + 40 + + + + Out for Customer Signature + 50 + + + + Waiting Internal Signature + 60 + + + + Active + 70 + + + + Expired + 80 + + + + Cancelled + 100 + True + + diff --git a/agreement/data/ir_sequence.xml b/agreement/data/ir_sequence.xml new file mode 100644 index 000000000..d2104c559 --- /dev/null +++ b/agreement/data/ir_sequence.xml @@ -0,0 +1,13 @@ + + + + + + Agreements + agreement + AG + 3 + + + + diff --git a/agreement/data/module_category.xml b/agreement/data/module_category.xml new file mode 100644 index 000000000..d3c2ee7a7 --- /dev/null +++ b/agreement/data/module_category.xml @@ -0,0 +1,9 @@ + + + + + Agreement + 80 + + + diff --git a/agreement/models/__init__.py b/agreement/models/__init__.py new file mode 100644 index 000000000..faf7817b0 --- /dev/null +++ b/agreement/models/__init__.py @@ -0,0 +1,15 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ( + res_config_settings, + agreement, + agreement_clause, + agreement_section, + agreement_stage, + agreement_type, + agreement_subtype, + res_partner, + product_template, + agreement_renewaltype, + agreement_increasetype, +) diff --git a/agreement/models/agreement.py b/agreement/models/agreement.py new file mode 100644 index 000000000..05a3cf21f --- /dev/null +++ b/agreement/models/agreement.py @@ -0,0 +1,402 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, fields, _ + + +class Agreement(models.Model): + _name = 'agreement' + _inherit = ['mail.thread'] + + def _default_stage_id(self): + return self.env.ref('agreement.agreement_stage_new') + + # General + name = fields.Char( + string="Title", + required=True + ) + is_template = fields.Boolean( + string="Is a Template?", + default=False, + copy=False, + help="Make this agreement a template." + ) + 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", + track_visibility='onchange', + help="Description of the agreement" + ) + start_date = fields.Date( + string="Start Date", + track_visibility='onchange', + help="When the agreement starts." + ) + end_date = fields.Date( + string="End Date", + track_visibility='onchange', + help="When the agreement ends." + ) + color = fields.Integer() + 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="Company Signed Date", + track_visibility='onchange', + help="Date the contract was signed by Company." + ) + customer_signed_date = fields.Date( + string="Customer Signed Date", + track_visibility='onchange', + help="Date the contract was signed by Customer." + ) + customer_term = fields.Integer( + string="Customer Term (Months)", + track_visibility='onchange', + help="Number of months this agreement/contract is in effect with " + "customer." + ) + vendor_term = fields.Integer( + string="Vendor Term (Months)", + track_visibility='onchange', + help="Number of months this agreement/contract is in effect with " + "vendor." + ) + expiration_notice = fields.Integer( + string="Exp. Notice (Days)", + track_visibility='onchange', + help="Number of Days before expiration to be notified." + ) + change_notice = fields.Integer( + string="Change Notice (Days)", + track_visibility='onchange', + help="Number of Days to be notified before changes." + ) + special_terms = fields.Text( + string="Special Terms", + track_visibility='onchange', + help="Any terms that you have agreed to and want to track on the " + "agreement/contract." + ) + contract_value = fields.Monetary( + compute='_compute_contract_value', + string="Contract Value", + help="Total value of the contract over ther entire term.", + store=True + ) + reference = fields.Char( + string="Reference", + required=True, + default=lambda self: _('New'), + track_visibility='onchange', + help="ID used for internal contract tracking.") + total_company_mrc = fields.Monetary( + 'Company MRC', + currency_field='currency_id', + help="Total company monthly recurring costs." + ) + total_customer_mrc = fields.Monetary( + 'Customer MRC', + currency_field='currency_id', + help="Total custemer monthly recurring costs." + ) + total_company_nrc = fields.Monetary( + 'Company NRC', + currency_field='currency_id', + help="Total company non-recurring costs." + ) + total_customer_nrc = fields.Monetary( + 'Customer NRC', + currency_field='currency_id', + help="Total custemer non-monthly recurring costs." + ) + increase_type_id = fields.Many2one( + 'agreement.increasetype', + string="Increase Type", + track_visibility='onchange', + help="The amount that certain rates may increase." + ) + termination_requested = fields.Date( + string="Termination Requested Date", + track_visibility='onchange', + help="Date that a request for termination was received." + ) + termination_date = fields.Date( + string="Termination Date", + track_visibility='onchange', + help="Date that the contract was terminated." + ) + reviewed_date = fields.Date( + string="Reviewed Date", + track_visibility='onchange' + ) + reviewed_user_id = fields.Many2one( + 'res.users', + string="Reviewed By", + track_visibility='onchange' + ) + approved_date = fields.Date( + string="Approved Date", + track_visibility='onchange' + ) + approved_user_id = fields.Many2one( + 'res.users', + string="Approved By", + track_visibility='onchange' + ) + + currency_id = fields.Many2one( + 'res.currency', + string='Currency' + ) + customer_id = fields.Many2one( + 'res.partner', + string="Customer", + copy=True, + help="The customer this agreement is related to (If Applicable)." + ) + vendor_id = fields.Many2one( + 'res.partner', + string="Vendor", + copy=True, + help="The vendor this agreement is related to (If Applicable)." + ) + customer_contact_id = fields.Many2one( + 'res.partner', + string="Customer Contact", + copy=True, + help="The primary customer contact (If Applicable)." + ) + customer_contact_phone = fields.Char( + related='customer_contact_id.phone', + string="Phone" + ) + customer_contact_email = fields.Char( + related='customer_contact_id.email', + string="Email" + ) + vendor_contact_id = fields.Many2one( + 'res.partner', + string="Vendor Contact", + copy=True, + help="The primary vendor contact (If Applicable)." + ) + vendor_contact_phone = fields.Char( + related='vendor_contact_id.phone', + string="Phone" + ) + vendor_contact_email = fields.Char( + related='vendor_contact_id.email', + string="Email" + ) + agreement_type_id = fields.Many2one( + 'agreement.type', + string="Agreement Type", + track_visibility='onchange', + help="Select the type of agreement." + ) + agreement_subtype_id = fields.Many2one( + 'agreement.subtype', + string="Agreement Sub-type", + track_visibility='onchange', + help="Select the sub-type of this agreement. Sub-Types are related to " + "agreement types." + ) + product_ids = fields.Many2many( + 'product.template', + string="Products & Services") + sale_order_id = fields.Many2one( + 'sale.order', + string="Sales Order", + track_visibility='onchange', + copy=False, + help="Select the Sales Order that this agreement is related to." + ) + payment_term_id = fields.Many2one( + 'account.payment.term', + string="Payment Term", + track_visibility='onchange', + help="Terms of payments." + ) + assigned_user_id = fields.Many2one( + 'res.users', + string="Assigned To", + track_visibility='onchange', + help="Select the user who manages this agreement." + ) + company_signed_user_id = fields.Many2one( + 'res.users', + string="Company Signed By", + track_visibility='onchange', + help="The user at our company who authorized/signed the agreement or " + "contract." + ) + customer_signed_user_id = fields.Many2one( + 'res.partner', + string="Customer Signed By", + track_visibility='onchange', + help="Contact on the account that signed the agreement/contract." + ) + parent_agreement_id = fields.Many2one( + '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( + 'agreement.renewaltype', + string="Renewal Type", + track_visibility='onchange', + help="Describes what happens after the contract expires." + ) + order_lines_services_ids = fields.One2many( + related='sale_order_id.order_line', + string="Service Order Lines", + copy=False + ) + sections_ids = fields.One2many( + 'agreement.section', + 'agreement_id', + string="Sections", + copy=True + ) + clauses_ids = fields.One2many( + 'agreement.clause', + 'agreement_id', + string="Clauses", + copy=True + ) + previous_version_agreements_ids = fields.One2many( + 'agreement', + 'parent_agreement_id', + string="Child Agreements", + copy=False, + domain=[('active', '=', False)] + ) + child_agreements_ids = fields.One2many( + 'agreement', + 'parent_agreement_id', + string="Child Agreements", + copy=False, + domain=[('active', '=', True)] + ) + products_ids = fields.Many2many( + 'product.template', + string="Products", + copy=False + ) + state = fields.Selection([ + ('draft', 'Draft'), + ('active', 'Active'), + ('inactive', 'Inactive')], + default='draft', + track_visibility='always' + ) + notification_address_id = fields.Many2one( + '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", + track_visibility='always' + ) + + # compute contract_value field + @api.depends('total_customer_mrc', 'total_customer_nrc', 'customer_term') + def _compute_contract_value(self): + for record in self: + record.contract_value =\ + (record.total_customer_mrc * record.customer_term) +\ + record.total_customer_nrc + + # compute total_company_mrc field + @api.depends('order_lines_services_ids', 'sale_order_id') + def _compute_company_mrc(self): + order_lines = self.env['sale.order.line'].search( + [('is_service', '=', True)]) + amount_total = sum(order_lines.mapped('purchase_price')) + for record in self: + record.total_company_mrc = amount_total + + # Used for Kanban grouped_by view + @api.model + def _read_group_stage_ids(self, stages, domain, order): + stage_ids = self.env['agreement.stage'].search([]) + return stage_ids + + stage_id = fields.Many2one( + 'agreement.stage', + string="Stage", + group_expand='_read_group_stage_ids', + help="Select the current stage of the agreement.", + track_visibility='onchange', + index=True, + default=lambda self: self._default_stage_id(), + ) + + # Create New Version Button + @api.multi + 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': '{0} - 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'} + res = self.copy(default=default_vals) + 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('reference', _('New')) == _('New'): + vals['reference'] = \ + self.env['ir.sequence'].next_by_code('agreement') or _('New') + return super(Agreement, self).create(vals) + + # Increments the revision on each save action + @api.multi + def write(self, vals): + vals['revision'] = self.revision + 1 + return super(Agreement, self).write(vals) diff --git a/agreement/models/agreement_clause.py b/agreement/models/agreement_clause.py new file mode 100644 index 000000000..765d951fa --- /dev/null +++ b/agreement/models/agreement_clause.py @@ -0,0 +1,80 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, api + + +# Main Agreement clause Records Model +class AgreementClause(models.Model): + _name = 'agreement.clause' + _order = 'clause_sequence' + + # General + name = fields.Char( + string="Title", + required=True + ) + clause_sequence = fields.Integer( + string="Sequence" + ) + agreement_id = fields.Many2one( + 'agreement', + string="Agreement", + ondelete="cascade" + ) + section_id = fields.Many2one( + 'agreement.section', + string="Section", + ondelete="cascade" + ) + content = fields.Html( + string="Clause Content" + ) + active = fields.Boolean( + string="Active", + default=True, + help="If unchecked, it will allow you to hide the agreement without " + "removing it." + ) + + # Placeholder fields + model_id = fields.Many2one( + 'ir.model', + string="Applies to", + help="The type of document this template can be used with." + ) + model_object_field_id = fields.Many2one( + '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)." + ) + null_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.model + def create(self, vals): + seq = self.env['ir.sequence'].next_by_code('agreement.clause') or '/' + vals['clause_sequence'] = seq + return super(AgreementClause, self).create(vals) diff --git a/agreement/models/agreement_increasetype.py b/agreement/models/agreement_increasetype.py new file mode 100644 index 000000000..1988112bf --- /dev/null +++ b/agreement/models/agreement_increasetype.py @@ -0,0 +1,26 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +# Main Agreement Increase Type Records Model +class AgreementIncreaseType(models.Model): + _name = 'agreement.increasetype' + + # General + name = fields.Char( + string="Title", + required=True, + help="Increase types describe any increases that may happen during " + "the contract." + ) + description = fields.Text( + string="Description", + required=True, + help="Description of the renewal type." + ) + increase_percent = fields.Integer( + string="Increase Percentage", + help="Percentage that the amount will increase." + ) diff --git a/agreement/models/agreement_renewaltype.py b/agreement/models/agreement_renewaltype.py new file mode 100644 index 000000000..f8dfc00a2 --- /dev/null +++ b/agreement/models/agreement_renewaltype.py @@ -0,0 +1,22 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +# Main Agreement Section Records Model +class AgreementRenewalType(models.Model): + _name = 'agreement.renewaltype' + + # General + name = fields.Char( + string="Title", + required=True, + help="Renewal types describe what happens after the " + "agreement/contract expires." + ) + description = fields.Text( + string="Description", + required=True, + help="Description of the renewal type." + ) diff --git a/agreement/models/agreement_section.py b/agreement/models/agreement_section.py new file mode 100644 index 000000000..09c9acdd1 --- /dev/null +++ b/agreement/models/agreement_section.py @@ -0,0 +1,80 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, api + + +# Main Agreement Section Records Model +class AgreementSection(models.Model): + _name = 'agreement.section' + _order = 'section_sequence' + + # General + name = fields.Char( + string="Title", + required=True + ) + section_sequence = fields.Integer( + string="Sequence" + ) + agreement_id = fields.Many2one( + 'agreement', + string="Agreement", + ondelete="cascade" + ) + clauses_ids = fields.One2many( + 'agreement.clause', + 'section_id', + string="Clauses" + ) + content = fields.Html( + string="Section Content" + ) + active = fields.Boolean( + string="Active", + default=True, + help="If unchecked, it will allow you to hide the agreement without " + "removing it." + ) + + # Placeholder fields + model_id = fields.Many2one( + 'ir.model', + string="Applies to", + help="The type of document this template can be used with." + ) + model_object_field_id = fields.Many2one( + '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)." + ) + null_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.model + def create(self, vals): + seq = self.env['ir.sequence'].next_by_code('agreement.section') or '/' + vals['section_sequence'] = seq + return super(AgreementSection, self).create(vals) diff --git a/agreement/models/agreement_stage.py b/agreement/models/agreement_stage.py new file mode 100644 index 000000000..beb8dd438 --- /dev/null +++ b/agreement/models/agreement_stage.py @@ -0,0 +1,30 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +# Main Agreement Section Records Model +class AgreementStage(models.Model): + _name = 'agreement.stage' + _order = 'sequence' + + # General + name = fields.Char( + string="Stage Name", + required=True + ) + description = fields.Text( + string="Description", + required=False + ) + sequence = fields.Integer( + string="Sequence", + default="1", + required=False + ) + fold = fields.Boolean( + string="Is Folded", + required=False, + help="This stage is folded in the kanban view by deafault." + ) diff --git a/agreement/models/agreement_status.py b/agreement/models/agreement_status.py new file mode 100644 index 000000000..6d6a190ca --- /dev/null +++ b/agreement/models/agreement_status.py @@ -0,0 +1,15 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +# Main Agreement Status Records Model +class AgreementStatus(models.Model): + _name = 'agreement.type' + + # General + name = fields.Char( + string="Title", + required=True + ) diff --git a/agreement/models/agreement_subtype.py b/agreement/models/agreement_subtype.py new file mode 100644 index 000000000..1959e02a9 --- /dev/null +++ b/agreement/models/agreement_subtype.py @@ -0,0 +1,19 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +# Main Agreement Section Records Model +class AgreementSubtype(models.Model): + _name = 'agreement.subtype' + + # General + name = fields.Char( + string="Title", + required=True + ) + agreement_type_id = fields.Many2one( + 'agreement.type', + string="Agreement Type" + ) diff --git a/agreement/models/agreement_type.py b/agreement/models/agreement_type.py new file mode 100644 index 000000000..b87cc4899 --- /dev/null +++ b/agreement/models/agreement_type.py @@ -0,0 +1,20 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +# Main Agreement Section Records Model +class AgreementType(models.Model): + _name = 'agreement.type' + + # General + name = fields.Char( + string="Title", + required=True + ) + agreement_subtypes_ids = fields.One2many( + 'agreement.subtype', + 'agreement_type_id', + string="Agreement" + ) diff --git a/agreement/models/product_template.py b/agreement/models/product_template.py new file mode 100644 index 000000000..cfd3516b2 --- /dev/null +++ b/agreement/models/product_template.py @@ -0,0 +1,13 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +class Product(models.Model): + _inherit = 'product.template' + + agreements_ids = fields.Many2many( + 'agreement', + string="Agreements" + ) diff --git a/agreement/models/res_config_settings.py b/agreement/models/res_config_settings.py new file mode 100644 index 000000000..2c78a5f1b --- /dev/null +++ b/agreement/models/res_config_settings.py @@ -0,0 +1,8 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' diff --git a/agreement/models/res_partner.py b/agreement/models/res_partner.py new file mode 100644 index 000000000..d8cb299bd --- /dev/null +++ b/agreement/models/res_partner.py @@ -0,0 +1,10 @@ +# Copyright (C) 2018 - TODAY, Pavlov Media +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +class Partner(models.Model): + _inherit = 'res.partner' + agreement_ids = fields.One2many('agreement', 'customer_id', + string="Agreements") diff --git a/agreement/readme/CONFIGURE.rst b/agreement/readme/CONFIGURE.rst new file mode 100644 index 000000000..3871fc34e --- /dev/null +++ b/agreement/readme/CONFIGURE.rst @@ -0,0 +1,6 @@ +To configure this module: + +* Go to Agreement > Configuration > Templates +* Create a new template with sections and clauses and their respective content +* Go to Agreement > Configuration > Stages +* Create and reorder stages to match your process diff --git a/agreement/readme/CONTRIBUTORS.rst b/agreement/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..94d667e1c --- /dev/null +++ b/agreement/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Patrick Wilson +* Bhavesh Odedra +* Wolfgang Hall +* Maxime Chambreuil \ No newline at end of file diff --git a/agreement/readme/CREDITS.rst b/agreement/readme/CREDITS.rst new file mode 100644 index 000000000..b4198038d --- /dev/null +++ b/agreement/readme/CREDITS.rst @@ -0,0 +1,4 @@ +The development of this module has been financially supported by: + +* Pavlov Media +* Open Source Integrators \ No newline at end of file diff --git a/agreement/readme/DESCRIPTION.rst b/agreement/readme/DESCRIPTION.rst new file mode 100644 index 000000000..0680b36cf --- /dev/null +++ b/agreement/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This module allows you to manage agreements, letter of intent and contract content. +The module is meant to be used by the legal team of a company and to allow them +to define sections, clauses and templates with their respective content that can +be dynamic. + +Based on the template, an agreement can be created and the pdf document generated. + +The agreement would go through a workflow to finally become a contract with the +customer signature. diff --git a/agreement/readme/ROADMAP.rst b/agreement/readme/ROADMAP.rst new file mode 100644 index 000000000..58ac79c67 --- /dev/null +++ b/agreement/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +* Split the module to remove the dependencies on sale and account and provide + the same feature in extra modules (agreement_sale, agreement_account, + agreement_purchase) diff --git a/agreement/readme/USAGE.rst b/agreement/readme/USAGE.rst new file mode 100644 index 000000000..cae6e70e9 --- /dev/null +++ b/agreement/readme/USAGE.rst @@ -0,0 +1,7 @@ +To use this module: + +* Go to Agreement > Agrements +* Create a new agreement +* Select a template +* Follow the process to get the required approval +* Send the invitation to the customer to review and sign the agreement \ No newline at end of file diff --git a/agreement/security/ir.model.access.csv b/agreement/security/ir.model.access.csv new file mode 100644 index 000000000..13b3a0ed6 --- /dev/null +++ b/agreement/security/ir.model.access.csv @@ -0,0 +1,17 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_agreement_allusers,agreement all users,model_agreement,agreement.group_agreement_user,1,1,1,0 +access_agreement_allusers,agreement all users,model_agreement,agreement.group_agreement_manager,1,1,1,1 +access_agreement_section_allusers,section all users,model_agreement_section,agreement.group_agreement_user,1,1,1,0 +access_agreement_section_allusers,section all users,model_agreement_section,agreement.group_agreement_manager,1,1,1,1 +access_agreement_clause_allusers,clause all users,model_agreement_clause,agreement.group_agreement_user,1,1,1,0 +access_agreement_clause_allusers,clause all users,model_agreement_clause,agreement.group_agreement_manager,1,1,1,1 +access_agreement_stage_allusers,stage all users,model_agreement_stage,agreement.group_agreement_user,1,0,0,0 +access_agreement_stage_allusers,stage all users,model_agreement_stage,agreement.group_agreement_manager,1,1,1,1 +access_agreement_type_allusers,type all users,model_agreement_type,agreement.group_agreement_user,1,0,0,0 +access_agreement_type_allusers,type all users,model_agreement_type,agreement.group_agreement_manager,1,1,1,1 +access_agreement_subtype_allusers,subtype all users,model_agreement_subtype,agreement.group_agreement_user,1,0,0,0 +access_agreement_subtype_allusers,subtype all users,model_agreement_subtype,agreement.group_agreement_manager,1,1,1,1 +access_agreement_renewaltype_allusers,renewaltype all users,model_agreement_renewaltype,agreement.group_agreement_user,1,0,0,0 +access_agreement_renewaltype_allusers,renewaltype all users,model_agreement_renewaltype,agreement.group_agreement_manager,1,1,1,1 +access_agreement_increasetype_allusers,increasetype all users,model_agreement_increasetype,agreement.group_agreement_user,1,0,0,0 +access_agreement_increasetype_allusers,increasetype all users,model_agreement_increasetype,agreement.group_agreement_manager,1,1,1,1 diff --git a/agreement/security/res_groups.xml b/agreement/security/res_groups.xml new file mode 100644 index 000000000..a0917b7bc --- /dev/null +++ b/agreement/security/res_groups.xml @@ -0,0 +1,19 @@ + + + + + + User + + + + + + + Manager + + + + + + diff --git a/agreement/static/description/icon.png b/agreement/static/description/icon.png new file mode 100644 index 000000000..23ce93135 Binary files /dev/null and b/agreement/static/description/icon.png differ diff --git a/agreement/views/agreement.xml b/agreement/views/agreement.xml new file mode 100644 index 000000000..77e717a76 --- /dev/null +++ b/agreement/views/agreement.xml @@ -0,0 +1,329 @@ + + + + + + Agreement List + agreement + + + + + + + + + + + + + + + + Agreement Form + agreement + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This section is a place where financial records will show the current performance of this agreement. + Perhaps include invoices with total vs costs? + + + + + Reviewed by on . + Approved by on . + + + + + + + + + + + + + + + Agreement Kanban + agreement + + + + + + + + + + + + + + + + oe_kanban_text_red + + + + + + + + + + + Edit + + Record's Colour + + + + + + + + - + + + + V: + + + + + + + + + + + + + + + + + Agreement Search + agreement + + + + + + + + + + + + + + Order Lines Search + sale.order.line + search + + + + + + + + + + + Agreements + agreement + [('is_template', '=', False)] + kanban,tree,form + + + + Agreements + agreement + [('is_template', '=', False)] + tree,form + + + + Templates + agreement + [('is_template', '=', True)] + tree,kanban,form + + + diff --git a/agreement/views/agreement_clause.xml b/agreement/views/agreement_clause.xml new file mode 100644 index 000000000..c25cc404d --- /dev/null +++ b/agreement/views/agreement_clause.xml @@ -0,0 +1,95 @@ + + + + + + agreement_clause_sequencer + agreement.clause + 0 + 1 + + + + + Agreement Clause List + agreement.clause + + + + + + + + + + + + + + + Agreement clause Form + agreement.clause + + + + + + + + + + + + + + + THIS IS UNDER DEVELOPEMENT: The purpose of this section is to be able to create dynamic fields inside your content. + + + + + + + + + + + Sequence # + + + + + + + + Agreement Clause Search + agreement.clause + + + + + + + + + + + Agreement Clause Pivot + agreement.clause + + + + + + + + + + + + Agreement Clauses + agreement.clause + tree,pivot,form + + + diff --git a/agreement/views/agreement_increasetype.xml b/agreement/views/agreement_increasetype.xml new file mode 100644 index 000000000..db59d4f54 --- /dev/null +++ b/agreement/views/agreement_increasetype.xml @@ -0,0 +1,45 @@ + + + + + + Agreement Increase Type List + agreement.increasetype + + + + + + + + + + + + Agreement Increase Type Form + agreement.increasetype + + + + + + + + + + + + + + + + + + + + Agreement Increase Type + agreement.increasetype + tree,form + + + diff --git a/agreement/views/agreement_renewaltype.xml b/agreement/views/agreement_renewaltype.xml new file mode 100644 index 000000000..6cf7d006d --- /dev/null +++ b/agreement/views/agreement_renewaltype.xml @@ -0,0 +1,41 @@ + + + + + + Agreement Renewal Type List + agreement.renewaltype + + + + + + + + + + + Agreement Renewal Type Form + agreement.renewaltype + + + + + + + + + + + + + + + + + Agreement Renewal Type + agreement.renewaltype + tree,form + + + diff --git a/agreement/views/agreement_section.xml b/agreement/views/agreement_section.xml new file mode 100644 index 000000000..3e3e5f51a --- /dev/null +++ b/agreement/views/agreement_section.xml @@ -0,0 +1,89 @@ + + + + + + agreement_section_sequencer + agreement.section + 0 + 1 + + + + + Agreement Section List + agreement.section + + + + + + + + + + + + + Agreement Section Form + agreement.section + + + + + + + + + + + + + + + + + + + + + + + + + THIS IS UNDER DEVELOPEMENT: The purpose of this section is to be able to create dynamic fields inside your content. + + + + + + + + + + + Sequence # + + + + + + + + Agreement Section Search + agreement.section + + + + + + + + + + Agreement Sections + agreement.section + tree,form + + + diff --git a/agreement/views/agreement_stages.xml b/agreement/views/agreement_stages.xml new file mode 100644 index 000000000..66f7b094f --- /dev/null +++ b/agreement/views/agreement_stages.xml @@ -0,0 +1,43 @@ + + + + + + Agreement Stage List + agreement.stage + + + + + + + + + + + Agreement Stage Form + agreement.stage + + + + + + + + + + + + + + + + + + + Agreement Stage + agreement.stage + tree,form + + + diff --git a/agreement/views/agreement_subtype.xml b/agreement/views/agreement_subtype.xml new file mode 100644 index 000000000..51ef1986c --- /dev/null +++ b/agreement/views/agreement_subtype.xml @@ -0,0 +1,42 @@ + + + + + + Agreement Subtype List + agreement.subtype + + + + + + + + + + + Agreement Sub Type Form + agreement.subtype + + + + + + + + + + + + + + + + + Agreement Sub-Types + agreement.subtype + tree,form + + + diff --git a/agreement/views/agreement_type.xml b/agreement/views/agreement_type.xml new file mode 100644 index 000000000..e551d1a42 --- /dev/null +++ b/agreement/views/agreement_type.xml @@ -0,0 +1,41 @@ + + + + + + Agreement Type List + agreement.type + + + + + + + + + + + Agreement Type Form + agreement.type + + + + + + + + + + + + + + + + + Agreement Type + agreement.type + tree,form + + + diff --git a/agreement/views/menu.xml b/agreement/views/menu.xml new file mode 100644 index 000000000..59ac12b88 --- /dev/null +++ b/agreement/views/menu.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/agreement/views/reports.xml b/agreement/views/reports.xml new file mode 100644 index 000000000..ff132dca7 --- /dev/null +++ b/agreement/views/reports.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + Parties + Company Information + Customer Information + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/agreement/views/res_config_settings.xml b/agreement/views/res_config_settings.xml new file mode 100644 index 000000000..f79b304b1 --- /dev/null +++ b/agreement/views/res_config_settings.xml @@ -0,0 +1,28 @@ + + + + + res.config.settings.view.form.agreement + res.config.settings + + + + + + + + + + + + + diff --git a/agreement/views/res_partner.xml b/agreement/views/res_partner.xml new file mode 100644 index 000000000..4b37ef54c --- /dev/null +++ b/agreement/views/res_partner.xml @@ -0,0 +1,17 @@ + + + + + res.partner + + + + + + + + + + + +
This section is a place where financial records will show the current performance of this agreement.
Perhaps include invoices with total vs costs?
Reviewed by on .
Approved by on .