From bde83307c918696ccb179d435cbc66f0284e9d10 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Wed, 30 Nov 2016 13:45:34 -0800 Subject: [PATCH 1/6] [IMP] contract: Add templating * Add template functionality for contracts --- contract/README.rst | 1 + contract/__manifest__.py | 1 + contract/models/__init__.py | 7 +- contract/models/account_analytic_contract.py | 66 ++++++++ .../models/account_analytic_invoice_line.py | 89 ++++++++++ ...ontract.py => account_anayltic_account.py} | 155 +++--------------- .../models/{invoice.py => account_invoice.py} | 0 contract/security/ir.model.access.csv | 7 +- contract/views/contract.xml | 3 +- 9 files changed, 193 insertions(+), 136 deletions(-) create mode 100644 contract/models/account_analytic_contract.py create mode 100644 contract/models/account_analytic_invoice_line.py rename contract/models/{contract.py => account_anayltic_account.py} (58%) rename contract/models/{invoice.py => account_invoice.py} (100%) diff --git a/contract/README.rst b/contract/README.rst index df13f6bfd7..449191a4cc 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -66,6 +66,7 @@ Contributors * Pedro M. Baeza * Carlos Dauden * Angel Moya +* Dave Lasley Maintainer ---------- diff --git a/contract/__manifest__.py b/contract/__manifest__.py index 173be551c9..172339dcfc 100644 --- a/contract/__manifest__.py +++ b/contract/__manifest__.py @@ -10,6 +10,7 @@ 'license': 'AGPL-3', 'author': "OpenERP SA," "Tecnativa," + "LasLabs, " "Odoo Community Association (OCA)", 'website': 'https://github.com/oca/contract', 'depends': ['base', 'account', 'analytic'], diff --git a/contract/models/__init__.py b/contract/models/__init__.py index 8deef4105b..af0dd623b4 100644 --- a/contract/models/__init__.py +++ b/contract/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# © 2016 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import contract -from . import invoice +from . import account_analytic_contract +from . import account_anayltic_account +from . import account_analytic_invoice_line +from . import account_invoice diff --git a/contract/models/account_analytic_contract.py b/contract/models/account_analytic_contract.py new file mode 100644 index 0000000000..eca4fca6d4 --- /dev/null +++ b/contract/models/account_analytic_contract.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# © 2004-2010 OpenERP SA +# © 2014 Angel Moya +# © 2015 Pedro M. Baeza +# © 2016 Carlos Dauden +# Copyright 2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models +from odoo.addons import decimal_precision as dp +from odoo.exceptions import ValidationError +from odoo.tools.translate import _ + + +class AccountAnalyticContract(models.Model): + _name = 'account.analytic.contract' + + pricelist_id = fields.Many2one( + comodel_name='product.pricelist', + string='Pricelist') + recurring_invoice_line_ids = fields.One2many( + comodel_name='account.analytic.invoice.line', + inverse_name='analytic_account_id', + copy=True, + string='Invoice Lines') + recurring_rule_type = fields.Selection( + [('daily', 'Day(s)'), + ('weekly', 'Week(s)'), + ('monthly', 'Month(s)'), + ('monthlylastday', 'Month(s) last day'), + ('yearly', 'Year(s)'), + ], + default='monthly', + string='Recurrency', + help="Specify Interval for automatic invoice generation.") + recurring_invoicing_type = fields.Selection( + [('pre-paid', 'Pre-paid'), + ('post-paid', 'Post-paid'), + ], + default='pre-paid', + string='Invoicing type', + help="Specify if process date is 'from' or 'to' invoicing date") + recurring_interval = fields.Integer( + default=1, + string='Repeat Every', + help="Repeat every (Days/Week/Month/Year)") + journal_id = fields.Many2one( + 'account.journal', + string='Journal', + default=lambda s: s._default_journal(), + domain="[('type', '=', 'sale'),('company_id', '=', company_id)]") + + @api.model + def _default_journal(self): + company_id = self.env.context.get( + 'company_id', self.env.user.company_id.id) + domain = [ + ('type', '=', 'sale'), + ('company_id', '=', company_id)] + return self.env['account.journal'].search(domain, limit=1) + + @api.onchange('partner_id') + def _onchange_partner_id(self): + self.pricelist_id = self.partner_id.property_product_pricelist.id diff --git a/contract/models/account_analytic_invoice_line.py b/contract/models/account_analytic_invoice_line.py new file mode 100644 index 0000000000..e2f20048aa --- /dev/null +++ b/contract/models/account_analytic_invoice_line.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# © 2004-2010 OpenERP SA +# © 2014 Angel Moya +# © 2015 Pedro M. Baeza +# © 2016 Carlos Dauden +# Copyright 2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models +from odoo.addons import decimal_precision as dp +from odoo.exceptions import ValidationError +from odoo.tools.translate import _ + + +class AccountAnalyticInvoiceLine(models.Model): + _name = 'account.analytic.invoice.line' + + product_id = fields.Many2one( + 'product.product', string='Product', required=True) + analytic_account_id = fields.Many2one( + 'account.analytic.account', string='Analytic Account') + name = fields.Text(string='Description', required=True) + quantity = fields.Float(default=1.0, required=True) + uom_id = fields.Many2one( + 'product.uom', string='Unit of Measure', required=True) + price_unit = fields.Float('Unit Price', required=True) + price_subtotal = fields.Float( + compute='_compute_price_subtotal', + digits=dp.get_precision('Account'), + string='Sub Total') + discount = fields.Float( + string='Discount (%)', + digits=dp.get_precision('Discount'), + help='Discount that is applied in generated invoices.' + ' It should be less or equal to 100') + + @api.multi + @api.depends('quantity', 'price_unit', 'discount') + def _compute_price_subtotal(self): + for line in self: + subtotal = line.quantity * line.price_unit + discount = line.discount / 100 + subtotal *= 1 - discount + if line.analytic_account_id.pricelist_id: + cur = line.analytic_account_id.pricelist_id.currency_id + line.price_subtotal = cur.round(subtotal) + else: + line.price_subtotal = subtotal + + @api.multi + @api.constrains('discount') + def _check_discount(self): + for line in self: + if line.discount > 100: + raise ValidationError( + _("Discount should be less or equal to 100")) + + @api.multi + @api.onchange('product_id') + def _onchange_product_id(self): + if not self.product_id: + return {'domain': {'uom_id': []}} + + vals = {} + domain = {'uom_id': [ + ('category_id', '=', self.product_id.uom_id.category_id.id)]} + if not self.uom_id or (self.product_id.uom_id.category_id.id != + self.uom_id.category_id.id): + vals['uom_id'] = self.product_id.uom_id + + product = self.product_id.with_context( + lang=self.analytic_account_id.partner_id.lang, + partner=self.analytic_account_id.partner_id.id, + quantity=self.quantity, + date=self.analytic_account_id.recurring_next_date, + pricelist=self.analytic_account_id.pricelist_id.id, + uom=self.uom_id.id + ) + + name = product.name_get()[0][1] + if product.description_sale: + name += '\n' + product.description_sale + vals['name'] = name + + vals['price_unit'] = product.price + self.update(vals) + return {'domain': domain} diff --git a/contract/models/contract.py b/contract/models/account_anayltic_account.py similarity index 58% rename from contract/models/contract.py rename to contract/models/account_anayltic_account.py index 89bd5fbec9..4edb49bbd1 100644 --- a/contract/models/contract.py +++ b/contract/models/account_anayltic_account.py @@ -3,151 +3,48 @@ # © 2014 Angel Moya # © 2015 Pedro M. Baeza # © 2016 Carlos Dauden +# Copyright 2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from dateutil.relativedelta import relativedelta -import logging from odoo import api, fields, models from odoo.addons import decimal_precision as dp from odoo.exceptions import ValidationError from odoo.tools.translate import _ -_logger = logging.getLogger(__name__) - - -class AccountAnalyticInvoiceLine(models.Model): - _name = 'account.analytic.invoice.line' - - product_id = fields.Many2one( - 'product.product', string='Product', required=True) - analytic_account_id = fields.Many2one( - 'account.analytic.account', string='Analytic Account') - name = fields.Text(string='Description', required=True) - quantity = fields.Float(default=1.0, required=True) - uom_id = fields.Many2one( - 'product.uom', string='Unit of Measure', required=True) - price_unit = fields.Float('Unit Price', required=True) - price_subtotal = fields.Float( - compute='_compute_price_subtotal', - digits=dp.get_precision('Account'), - string='Sub Total') - discount = fields.Float( - string='Discount (%)', - digits=dp.get_precision('Discount'), - help='Discount that is applied in generated invoices.' - ' It should be less or equal to 100') - - @api.multi - @api.depends('quantity', 'price_unit', 'discount') - def _compute_price_subtotal(self): - for line in self: - subtotal = line.quantity * line.price_unit - discount = line.discount / 100 - subtotal *= 1 - discount - if line.analytic_account_id.pricelist_id: - cur = line.analytic_account_id.pricelist_id.currency_id - line.price_subtotal = cur.round(subtotal) - else: - line.price_subtotal = subtotal - - @api.multi - @api.constrains('discount') - def _check_discount(self): - for line in self: - if line.discount > 100: - raise ValidationError( - _("Discount should be less or equal to 100")) - - @api.multi - @api.onchange('product_id') - def _onchange_product_id(self): - if not self.product_id: - return {'domain': {'uom_id': []}} - - vals = {} - domain = {'uom_id': [ - ('category_id', '=', self.product_id.uom_id.category_id.id)]} - if not self.uom_id or (self.product_id.uom_id.category_id.id != - self.uom_id.category_id.id): - vals['uom_id'] = self.product_id.uom_id - - product = self.product_id.with_context( - lang=self.analytic_account_id.partner_id.lang, - partner=self.analytic_account_id.partner_id.id, - quantity=self.quantity, - date=self.analytic_account_id.recurring_next_date, - pricelist=self.analytic_account_id.pricelist_id.id, - uom=self.uom_id.id - ) - - name = product.name_get()[0][1] - if product.description_sale: - name += '\n' + product.description_sale - vals['name'] = name - - vals['price_unit'] = product.price - self.update(vals) - return {'domain': domain} - class AccountAnalyticAccount(models.Model): - _inherit = 'account.analytic.account' - - @api.model - def _default_journal(self): - company_id = self.env.context.get( - 'company_id', self.env.user.company_id.id) - domain = [ - ('type', '=', 'sale'), - ('company_id', '=', company_id)] - return self.env['account.journal'].search(domain, limit=1) - - pricelist_id = fields.Many2one( - comodel_name='product.pricelist', - string='Pricelist') + _name = 'account.analytic.account' + _inherit = ['account.analytic.account', + 'account.analytic.contract', + ] + + contract_template_id = fields.Many2one( + string='Contract Template', + comodel_name='account.analytic.contract', + ) date_start = fields.Date(default=fields.Date.context_today) - recurring_invoice_line_ids = fields.One2many( - comodel_name='account.analytic.invoice.line', - inverse_name='analytic_account_id', - copy=True, - string='Invoice Lines') recurring_invoices = fields.Boolean( - string='Generate recurring invoices automatically') - recurring_rule_type = fields.Selection( - [('daily', 'Day(s)'), - ('weekly', 'Week(s)'), - ('monthly', 'Month(s)'), - ('monthlylastday', 'Month(s) last day'), - ('yearly', 'Year(s)'), - ], - default='monthly', - string='Recurrency', - help="Specify Interval for automatic invoice generation.") - recurring_invoicing_type = fields.Selection( - [('pre-paid', 'Pre-paid'), - ('post-paid', 'Post-paid'), - ], - default='pre-paid', - string='Invoicing type', - help="Specify if process date is 'from' or 'to' invoicing date") - recurring_interval = fields.Integer( - default=1, - string='Repeat Every', - help="Repeat every (Days/Week/Month/Year)") + string='Generate recurring invoices automatically', + ) recurring_next_date = fields.Date( default=fields.Date.context_today, copy=False, - string='Date of Next Invoice') - journal_id = fields.Many2one( - 'account.journal', - string='Journal', - default=_default_journal, - domain="[('type', '=', 'sale'),('company_id', '=', company_id)]") - - @api.onchange('partner_id') - def _onchange_partner_id(self): - self.pricelist_id = self.partner_id.property_product_pricelist.id + string='Date of Next Invoice', + ) + + @api.onchange('contract_template_id') + def _onchange_contract_template_id(self): + """ It updates contract fields with that of the template """ + contract = self.contract_template_id + for field_name, field in contract._fields.iteritems(): + if any(( + field.compute, field.related, field.automatic, + field.readonly, field.company_dependent, + )): + continue + self[field_name] = self.contract_template_id[field_name] @api.onchange('recurring_invoices') def _onchange_recurring_invoices(self): diff --git a/contract/models/invoice.py b/contract/models/account_invoice.py similarity index 100% rename from contract/models/invoice.py rename to contract/models/account_invoice.py diff --git a/contract/security/ir.model.access.csv b/contract/security/ir.model.access.csv index d477bb7868..6939770a39 100644 --- a/contract/security/ir.model.access.csv +++ b/contract/security/ir.model.access.csv @@ -1,4 +1,5 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"account_analytic_invoice_line_manager","Recurring manager","model_account_analytic_invoice_line","account.group_account_manager",1,1,1,1 -"account_analytic_invoice_line_user","Recurring user","model_account_analytic_invoice_line","account.group_account_user",1,0,0,0 - +"account_analytic_contract_manager","Recurring manager","model_account_analytic_contract","account.group_account_manager",1,1,1,1 +"account_analytic_contract_user","Recurring user","model_account_analytic_contract","account.group_account_user",1,0,0,0 +"account_analytic_contract_manager","Recurring manager","model_account_analytic_contract","account.group_account_manager",1,1,1,1 +"account_analytic_contract_user","Recurring user","model_account_analytic_contract","account.group_account_user",1,0,0,0 diff --git a/contract/views/contract.xml b/contract/views/contract.xml index d9360d58ac..6541c39727 100644 --- a/contract/views/contract.xml +++ b/contract/views/contract.xml @@ -27,11 +27,12 @@ attrs="{'invisible': [('recurring_invoices','!=',True)]}" string="Create invoices" class="oe_link" groups="base.group_no_one"/> -