[IMP][10.0] contract: Add templating (#42)

Add template functionality for contracts
This commit is contained in:
Dave Lasley
2017-04-01 08:14:55 -07:00
committed by Pedro M. Baeza
parent eef2c2f8ed
commit dcf3ff0877
12 changed files with 390 additions and 162 deletions

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# 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_analytic_account
from . import account_analytic_invoice_line
from . import account_invoice

View File

@@ -3,157 +3,58 @@
# © 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016-2017 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'
_name = 'account.analytic.account'
_inherit = ['account.analytic.account',
'account.analytic.contract',
]
@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')
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)]")
string='Date of Next Invoice',
)
@api.onchange('partner_id')
def _onchange_partner_id(self):
self.pricelist_id = self.partner_id.property_product_pricelist.id
@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,
field.name in self.NO_SYNC,
)):
continue
self[field_name] = self.contract_template_id[field_name]
@api.onchange('recurring_invoices')
def _onchange_recurring_invoices(self):
if self.date_start and self.recurring_invoices:
self.recurring_next_date = self.date_start
@api.onchange('partner_id')
def _onchange_partner_id(self):
self.pricelist_id = self.partner_id.property_product_pricelist.id
@api.model
def get_relative_delta(self, recurring_rule_type, interval):
if recurring_rule_type == 'daily':

View File

@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# © 2004-2010 OpenERP SA
# © 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016-2017 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class AccountAnalyticContract(models.Model):
_name = 'account.analytic.contract'
# These fields will not be synced to the contract
NO_SYNC = [
'name',
]
name = fields.Char(
required=True,
)
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='Recurrence',
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)

View File

@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# © 2004-2010 OpenERP SA
# © 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
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}