[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 fd369531f0
commit 051c2b7550
12 changed files with 390 additions and 162 deletions

View File

@@ -1,291 +0,0 @@
# -*- 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>
# 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')
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)")
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
@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.model
def get_relative_delta(self, recurring_rule_type, interval):
if recurring_rule_type == 'daily':
return relativedelta(days=interval)
elif recurring_rule_type == 'weekly':
return relativedelta(weeks=interval)
elif recurring_rule_type == 'monthly':
return relativedelta(months=interval)
elif recurring_rule_type == 'monthlylastday':
return relativedelta(months=interval, day=31)
else:
return relativedelta(years=interval)
@api.model
def _insert_markers(self, line, date_start, next_date, date_format):
contract = line.analytic_account_id
if contract.recurring_invoicing_type == 'pre-paid':
date_from = date_start
date_to = next_date - relativedelta(days=1)
else:
date_from = (date_start -
self.get_relative_delta(contract.recurring_rule_type,
contract.recurring_interval) +
relativedelta(days=1))
date_to = date_start
name = line.name
name = name.replace('#START#', date_from.strftime(date_format))
name = name.replace('#END#', date_to.strftime(date_format))
return name
@api.model
def _prepare_invoice_line(self, line, invoice_id):
invoice_line = self.env['account.invoice.line'].new({
'invoice_id': invoice_id,
'product_id': line.product_id.id,
'quantity': line.quantity,
'uom_id': line.uom_id.id,
'discount': line.discount,
})
# Get other invoice line values from product onchange
invoice_line._onchange_product_id()
invoice_line_vals = invoice_line._convert_to_write(invoice_line._cache)
# Insert markers
name = line.name
contract = line.analytic_account_id
if 'old_date' in self.env.context and 'next_date' in self.env.context:
lang_obj = self.env['res.lang']
lang = lang_obj.search(
[('code', '=', contract.partner_id.lang)])
date_format = lang.date_format or '%m/%d/%Y'
name = self._insert_markers(
line, self.env.context['old_date'],
self.env.context['next_date'], date_format)
invoice_line_vals.update({
'name': name,
'account_analytic_id': contract.id,
'price_unit': line.price_unit,
})
return invoice_line_vals
@api.multi
def _prepare_invoice(self):
self.ensure_one()
if not self.partner_id:
raise ValidationError(
_("You must first select a Customer for Contract %s!") %
self.name)
journal = self.journal_id or self.env['account.journal'].search(
[('type', '=', 'sale'),
('company_id', '=', self.company_id.id)],
limit=1)
if not journal:
raise ValidationError(
_("Please define a sale journal for the company '%s'.") %
(self.company_id.name or '',))
currency = (
self.pricelist_id.currency_id or
self.partner_id.property_product_pricelist.currency_id or
self.company_id.currency_id
)
invoice = self.env['account.invoice'].new({
'reference': self.code,
'type': 'out_invoice',
'partner_id': self.partner_id.address_get(
['invoice'])['invoice'],
'currency_id': currency.id,
'journal_id': journal.id,
'date_invoice': self.recurring_next_date,
'origin': self.name,
'company_id': self.company_id.id,
'contract_id': self.id,
'user_id': self.partner_id.user_id.id,
})
# Get other invoice values from partner onchange
invoice._onchange_partner_id()
return invoice._convert_to_write(invoice._cache)
@api.multi
def _create_invoice(self):
self.ensure_one()
invoice_vals = self._prepare_invoice()
invoice = self.env['account.invoice'].create(invoice_vals)
for line in self.recurring_invoice_line_ids:
invoice_line_vals = self._prepare_invoice_line(line, invoice.id)
self.env['account.invoice.line'].create(invoice_line_vals)
invoice.compute_taxes()
return invoice
@api.multi
def recurring_create_invoice(self):
for contract in self:
old_date = fields.Date.from_string(
contract.recurring_next_date or fields.Date.today())
new_date = old_date + self.get_relative_delta(
contract.recurring_rule_type, contract.recurring_interval)
ctx = self.env.context.copy()
ctx.update({
'old_date': old_date,
'next_date': new_date,
# Force company for correct evaluate domain access rules
'force_company': contract.company_id.id,
})
# Re-read contract with correct company
contract.with_context(ctx)._create_invoice()
contract.write({
'recurring_next_date': new_date.strftime('%Y-%m-%d')
})
return True
@api.model
def cron_recurring_create_invoice(self):
contracts = self.search(
[('recurring_next_date', '<=', fields.date.today()),
('recurring_invoices', '=', True)])
return contracts.recurring_create_invoice()