Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from
Apr 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions contract/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Contributors
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
* Carlos Dauden <carlos.dauden@tecnativa.com>
* Angel Moya <angel.moya@domatix.com>
* Dave Lasley <dave@laslabs.com>

Maintainer
----------
Expand Down
12 changes: 7 additions & 5 deletions contract/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
'name': 'Contracts Management recurring',
'version': '10.0.1.0.0',
'name': 'Contracts Management - Recurring',
'version': '10.0.1.0.1',
'category': 'Contract Management',
'license': 'AGPL-3',
'author': "OpenERP SA,"
"Tecnativa,"
'author': "OpenERP SA, "
"Tecnativa, "
"LasLabs, "
"Odoo Community Association (OCA)",
'website': 'https://github.com/oca/contract',
'depends': ['base', 'account', 'analytic'],
'data': [
'security/ir.model.access.csv',
'data/contract_cron.xml',
'views/contract.xml',
'views/account_analytic_account_view.xml',
'views/account_analytic_contract_view.xml',
'views/account_invoice_view.xml',
],
'installable': True,
Expand Down
7 changes: 4 additions & 3 deletions contract/models/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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'

@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,
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':
Expand Down
71 changes: 71 additions & 0 deletions contract/models/account_analytic_contract.py
Original file line number Diff line number Diff line change
@@ -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)
87 changes: 87 additions & 0 deletions contract/models/account_analytic_invoice_line.py
Original file line number Diff line number Diff line change
@@ -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}
File renamed without changes.