mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
[IMP][10.0] contract: Add templating (#42)
Add template functionality for contracts
This commit is contained in:
committed by
Pedro M. Baeza
parent
c5d3267ee7
commit
3e876611f5
@@ -67,6 +67,7 @@ Contributors
|
|||||||
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||||
* Carlos Dauden <carlos.dauden@tecnativa.com>
|
* Carlos Dauden <carlos.dauden@tecnativa.com>
|
||||||
* Angel Moya <angel.moya@domatix.com>
|
* Angel Moya <angel.moya@domatix.com>
|
||||||
|
* Dave Lasley <dave@laslabs.com>
|
||||||
|
|
||||||
Maintainer
|
Maintainer
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -4,19 +4,21 @@
|
|||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Contracts Management recurring',
|
'name': 'Contracts Management - Recurring',
|
||||||
'version': '10.0.1.0.0',
|
'version': '10.0.1.0.1',
|
||||||
'category': 'Contract Management',
|
'category': 'Contract Management',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'author': "OpenERP SA,"
|
'author': "OpenERP SA, "
|
||||||
"Tecnativa,"
|
"Tecnativa, "
|
||||||
|
"LasLabs, "
|
||||||
"Odoo Community Association (OCA)",
|
"Odoo Community Association (OCA)",
|
||||||
'website': 'https://github.com/oca/contract',
|
'website': 'https://github.com/oca/contract',
|
||||||
'depends': ['base', 'account', 'analytic'],
|
'depends': ['base', 'account', 'analytic'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'data/contract_cron.xml',
|
'data/contract_cron.xml',
|
||||||
'views/contract.xml',
|
'views/account_analytic_account_view.xml',
|
||||||
|
'views/account_analytic_contract_view.xml',
|
||||||
'views/account_invoice_view.xml',
|
'views/account_invoice_view.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from . import contract
|
from . import account_analytic_contract
|
||||||
from . import invoice
|
from . import account_analytic_account
|
||||||
|
from . import account_analytic_invoice_line
|
||||||
|
from . import account_invoice
|
||||||
|
|||||||
@@ -3,157 +3,58 @@
|
|||||||
# © 2014 Angel Moya <angel.moya@domatix.com>
|
# © 2014 Angel Moya <angel.moya@domatix.com>
|
||||||
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||||
# © 2016 Carlos Dauden <carlos.dauden@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).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
import logging
|
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from odoo.addons import decimal_precision as dp
|
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
from odoo.tools.translate import _
|
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):
|
class AccountAnalyticAccount(models.Model):
|
||||||
_inherit = 'account.analytic.account'
|
_name = 'account.analytic.account'
|
||||||
|
_inherit = ['account.analytic.account',
|
||||||
|
'account.analytic.contract',
|
||||||
|
]
|
||||||
|
|
||||||
@api.model
|
contract_template_id = fields.Many2one(
|
||||||
def _default_journal(self):
|
string='Contract Template',
|
||||||
company_id = self.env.context.get(
|
comodel_name='account.analytic.contract',
|
||||||
'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)
|
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(
|
recurring_invoices = fields.Boolean(
|
||||||
string='Generate recurring invoices automatically')
|
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(
|
recurring_next_date = fields.Date(
|
||||||
default=fields.Date.context_today,
|
default=fields.Date.context_today,
|
||||||
copy=False,
|
copy=False,
|
||||||
string='Date of Next Invoice')
|
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')
|
@api.onchange('contract_template_id')
|
||||||
def _onchange_partner_id(self):
|
def _onchange_contract_template_id(self):
|
||||||
self.pricelist_id = self.partner_id.property_product_pricelist.id
|
""" 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')
|
@api.onchange('recurring_invoices')
|
||||||
def _onchange_recurring_invoices(self):
|
def _onchange_recurring_invoices(self):
|
||||||
if self.date_start and self.recurring_invoices:
|
if self.date_start and self.recurring_invoices:
|
||||||
self.recurring_next_date = self.date_start
|
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
|
@api.model
|
||||||
def get_relative_delta(self, recurring_rule_type, interval):
|
def get_relative_delta(self, recurring_rule_type, interval):
|
||||||
if recurring_rule_type == 'daily':
|
if recurring_rule_type == 'daily':
|
||||||
71
contract/models/account_analytic_contract.py
Normal file
71
contract/models/account_analytic_contract.py
Normal 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)
|
||||||
87
contract/models/account_analytic_invoice_line.py
Normal file
87
contract/models/account_analytic_invoice_line.py
Normal 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}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||||
|
"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_invoice_line_manager","Recurring manager","model_account_analytic_invoice_line","account.group_account_manager",1,1,1,1
|
"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_invoice_line_user","Recurring user","model_account_analytic_invoice_line","account.group_account_user",1,0,0,0
|
||||||
|
|
||||||
|
|||||||
|
@@ -15,6 +15,14 @@ class TestContract(TransactionCase):
|
|||||||
self.product.taxes_id += self.env['account.tax'].search(
|
self.product.taxes_id += self.env['account.tax'].search(
|
||||||
[('type_tax_use', '=', 'sale')], limit=1)
|
[('type_tax_use', '=', 'sale')], limit=1)
|
||||||
self.product.description_sale = 'Test description sale'
|
self.product.description_sale = 'Test description sale'
|
||||||
|
self.template_vals = {
|
||||||
|
'recurring_rule_type': 'yearly',
|
||||||
|
'recurring_interval': 12345,
|
||||||
|
'name': 'Test Contract Template',
|
||||||
|
}
|
||||||
|
self.template = self.env['account.analytic.contract'].create(
|
||||||
|
self.template_vals,
|
||||||
|
)
|
||||||
self.contract = self.env['account.analytic.account'].create({
|
self.contract = self.env['account.analytic.account'].create({
|
||||||
'name': 'Test Contract',
|
'name': 'Test Contract',
|
||||||
'partner_id': self.partner.id,
|
'partner_id': self.partner.id,
|
||||||
@@ -136,3 +144,14 @@ class TestContract(TransactionCase):
|
|||||||
journal.write({'type': 'general'})
|
journal.write({'type': 'general'})
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
contract_no_journal.recurring_create_invoice()
|
contract_no_journal.recurring_create_invoice()
|
||||||
|
|
||||||
|
def test_onchange_contract_template_id(self):
|
||||||
|
""" It should change the contract values to match the template. """
|
||||||
|
self.contract.contract_template_id = self.template
|
||||||
|
self.contract._onchange_contract_template_id()
|
||||||
|
res = {
|
||||||
|
'recurring_rule_type': self.contract.recurring_rule_type,
|
||||||
|
'recurring_interval': self.contract.recurring_interval,
|
||||||
|
}
|
||||||
|
del self.template_vals['name']
|
||||||
|
self.assertDictEqual(res, self.template_vals)
|
||||||
|
|||||||
@@ -1,49 +1,55 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="act_recurring_invoices" model="ir.actions.act_window">
|
|
||||||
<field name="context">{'search_default_contract_id':
|
|
||||||
[active_id],
|
|
||||||
'default_contract_id': active_id}
|
|
||||||
</field>
|
|
||||||
<field name="name">Invoices</field>
|
|
||||||
<field name="res_model">account.invoice</field>
|
|
||||||
<field name="view_id" ref="account.invoice_tree" />
|
|
||||||
<field name="search_view_id" ref="account.view_account_invoice_filter"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account_analytic_account_recurring_form_form" model="ir.ui.view">
|
<record id="account_analytic_account_recurring_form_form" model="ir.ui.view">
|
||||||
<field name="name">account.analytic.account.invoice.recurring.form.inherit</field>
|
<field name="name">account.analytic.account.invoice.recurring.form.inherit</field>
|
||||||
<field name="model">account.analytic.account</field>
|
<field name="model">account.analytic.account</field>
|
||||||
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
|
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
|
||||||
<field eval="40" name="priority"/>
|
<field name="mode">primary</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<group name="main" position="after">
|
<group name="main" position="after">
|
||||||
<separator string="Recurring Invoices" attrs="{'invisible': [('recurring_invoices','!=',True)]}"/>
|
<separator string="Recurring Invoices"
|
||||||
|
attrs="{'invisible': [('recurring_invoices','!=',True)]}"
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<field name="recurring_invoices" class="oe_inline"/>
|
<field name="recurring_invoices" class="oe_inline"/>
|
||||||
<label for="recurring_invoices" />
|
<label for="recurring_invoices" />
|
||||||
<button name="recurring_create_invoice" type="object"
|
<button name="recurring_create_invoice"
|
||||||
|
type="object"
|
||||||
attrs="{'invisible': [('recurring_invoices','!=',True)]}"
|
attrs="{'invisible': [('recurring_invoices','!=',True)]}"
|
||||||
string="Create invoices" class="oe_link"
|
string="Create invoices"
|
||||||
groups="base.group_no_one"/>
|
class="oe_link"
|
||||||
<button name="%(contract.act_recurring_invoices)d" type="action"
|
groups="base.group_no_one"
|
||||||
|
/>
|
||||||
|
<button name="contract.act_recurring_invoices"
|
||||||
|
type="action"
|
||||||
attrs="{'invisible': [('recurring_invoices','!=',True)]}"
|
attrs="{'invisible': [('recurring_invoices','!=',True)]}"
|
||||||
string="⇒ Show recurring invoices" class="oe_link"/>
|
string="⇒ Show recurring invoices"
|
||||||
|
class="oe_link"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<group col="4" attrs="{'invisible': [('recurring_invoices','!=',True)]}">
|
<group col="4" attrs="{'invisible': [('recurring_invoices','!=',True)]}">
|
||||||
|
<field name="contract_template_id" />
|
||||||
<field name="journal_id"/>
|
<field name="journal_id"/>
|
||||||
<field name="pricelist_id"/>
|
<field name="pricelist_id"/>
|
||||||
<label for="recurring_interval"/>
|
<label for="recurring_interval"/>
|
||||||
<div>
|
<div>
|
||||||
<field name="recurring_interval" class="oe_inline" attrs="{'required': [('recurring_invoices', '=', True)]}"/>
|
<field name="recurring_interval"
|
||||||
<field name="recurring_rule_type" class="oe_inline" attrs="{'required': [('recurring_invoices', '=', True)]}"/>
|
class="oe_inline"
|
||||||
|
attrs="{'required': [('recurring_invoices', '=', True)]}"
|
||||||
|
/>
|
||||||
|
<field name="recurring_rule_type"
|
||||||
|
class="oe_inline"
|
||||||
|
attrs="{'required': [('recurring_invoices', '=', True)]}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<field name="recurring_invoicing_type"/>
|
<field name="recurring_invoicing_type"/>
|
||||||
<field name="date_start"/>
|
<field name="date_start"/>
|
||||||
<field name="recurring_next_date"/>
|
<field name="recurring_next_date"/>
|
||||||
</group>
|
</group>
|
||||||
<label for="recurring_invoice_line_ids" attrs="{'invisible': [('recurring_invoices','=',False)]}"/>
|
<label for="recurring_invoice_line_ids"
|
||||||
|
attrs="{'invisible': [('recurring_invoices','=',False)]}"
|
||||||
|
/>
|
||||||
<div attrs="{'invisible': [('recurring_invoices','=',False)]}">
|
<div attrs="{'invisible': [('recurring_invoices','=',False)]}">
|
||||||
<field name="recurring_invoice_line_ids">
|
<field name="recurring_invoice_line_ids">
|
||||||
<tree string="Account Analytic Lines" editable="bottom">
|
<tree string="Account Analytic Lines" editable="bottom">
|
||||||
@@ -87,9 +93,15 @@
|
|||||||
<field name="name" position="after">
|
<field name="name" position="after">
|
||||||
<field name="journal_id"/>
|
<field name="journal_id"/>
|
||||||
<field name="pricelist_id"/>
|
<field name="pricelist_id"/>
|
||||||
<filter name="recurring_invoices" string="Recurring Invoices" domain="[('recurring_invoices','=',True)]"/>
|
<filter name="recurring_invoices"
|
||||||
|
string="Recurring Invoices"
|
||||||
|
domain="[('recurring_invoices','=',True)]"
|
||||||
|
/>
|
||||||
<group expand="0" string="Group By...">
|
<group expand="0" string="Group By...">
|
||||||
<filter string="Next Invoice" domain="[]" context="{'group_by':'recurring_next_date'}"/>
|
<filter string="Next Invoice"
|
||||||
|
domain="[]"
|
||||||
|
context="{'group_by':'recurring_next_date'}"
|
||||||
|
/>
|
||||||
</group>
|
</group>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
@@ -109,6 +121,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<menuitem action="action_account_analytic_overdue_all" id="menu_action_account_analytic_overdue_all" sequence="99" parent="account.menu_finance_receivables"/>
|
|
||||||
|
<menuitem id="menu_action_account_analytic_overdue_all"
|
||||||
|
parent="account.menu_finance_receivables"
|
||||||
|
action="action_account_analytic_overdue_all"
|
||||||
|
sequence="99"
|
||||||
|
/>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
118
contract/views/account_analytic_contract_view.xml
Normal file
118
contract/views/account_analytic_contract_view.xml
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="account_analytic_contract_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">Account Analytic Contract Form View</field>
|
||||||
|
<field name="model">account.analytic.contract</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Contract Template">
|
||||||
|
<group name="group_main">
|
||||||
|
<field name="name" />
|
||||||
|
<group name="group_main_left">
|
||||||
|
<field name="journal_id" />
|
||||||
|
<field name="pricelist_id" />
|
||||||
|
</group>
|
||||||
|
<group name="group_main_right">
|
||||||
|
<field name="recurring_invoicing_type" />
|
||||||
|
<label for="recurring_interval" />
|
||||||
|
<div>
|
||||||
|
<field name="recurring_interval"
|
||||||
|
class="oe_inline"
|
||||||
|
required="True"
|
||||||
|
/>
|
||||||
|
<field name="recurring_rule_type"
|
||||||
|
class="oe_inline"
|
||||||
|
required="True"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group name="group_invoice_lines" string="Invoice Lines">
|
||||||
|
<field name="recurring_invoice_line_ids">
|
||||||
|
<tree string="Account Analytic Lines" editable="bottom">
|
||||||
|
<field name="product_id" />
|
||||||
|
<field name="name" />
|
||||||
|
<field name="quantity" />
|
||||||
|
<field name="uom_id" />
|
||||||
|
<field name="price_unit" />
|
||||||
|
<field name="discount" groups="sale.group_discount_per_so_line" />
|
||||||
|
<field name="price_subtotal" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
<group name="group_legend"
|
||||||
|
string="Legend (for the markers inside invoice lines description)"
|
||||||
|
>
|
||||||
|
<p> <strong>#START#</strong>: Start date of the invoiced period</p>
|
||||||
|
<p> <strong>#END#</strong>: End date of the invoiced period</p>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account_analytic_contract_view_tree" model="ir.ui.view">
|
||||||
|
<field name="name">Account Analytic Contract Tree View</field>
|
||||||
|
<field name="model">account.analytic.contract</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Contract Templates">
|
||||||
|
<field name="name" />
|
||||||
|
<field name="recurring_rule_type" />
|
||||||
|
<field name="recurring_interval" />
|
||||||
|
<field name="recurring_invoicing_type" />
|
||||||
|
<field name="pricelist_id" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account_analytic_contract_view_search" model="ir.ui.view">
|
||||||
|
<field name="name">Account Analytic Contract Search View</field>
|
||||||
|
<field name="model">account.analytic.contract</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Contract Templates">
|
||||||
|
<field name="name" />
|
||||||
|
<field name="recurring_rule_type" />
|
||||||
|
<field name="recurring_interval" />
|
||||||
|
<field name="recurring_invoicing_type" />
|
||||||
|
<field name="pricelist_id" />
|
||||||
|
<field name="journal_id" />
|
||||||
|
<filter string="Recurrence"
|
||||||
|
context="{'group_by': 'recurring_rule_type'}"
|
||||||
|
/>
|
||||||
|
<filter string="Invoicing Type"
|
||||||
|
context="{'group_by': 'recurring_invoicing_type'}"
|
||||||
|
/>
|
||||||
|
<filter string="Pricelist"
|
||||||
|
context="{'group_by': 'pricelist_id'}"
|
||||||
|
/>
|
||||||
|
<filter string="Journal"
|
||||||
|
context="{'group_by': 'journal_id'}"
|
||||||
|
/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account_analytic_contract_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Contract Templates</field>
|
||||||
|
<field name="res_model">account.analytic.contract</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="oe_view_nocontent_create">
|
||||||
|
Click to create a new contract template.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_config_contract"
|
||||||
|
name="Contracts"
|
||||||
|
sequence="1"
|
||||||
|
parent="account.menu_finance_configuration"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem id="account_analytic_contract_menu"
|
||||||
|
parent="menu_config_contract"
|
||||||
|
action="account_analytic_contract_action"
|
||||||
|
sequence="1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<!-- Invoice search view with contract -->
|
|
||||||
<record id="view_account_invoice_filter_contract" model="ir.ui.view">
|
<record id="view_account_invoice_filter_contract" model="ir.ui.view">
|
||||||
<field name="name">account.invoice.select.contract</field>
|
<field name="name">account.invoice.select.contract</field>
|
||||||
<field name="model">account.invoice</field>
|
<field name="model">account.invoice</field>
|
||||||
@@ -14,4 +13,15 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="act_recurring_invoices" model="ir.actions.act_window">
|
||||||
|
<field name="context">{'search_default_contract_id':
|
||||||
|
[active_id],
|
||||||
|
'default_contract_id': active_id}
|
||||||
|
</field>
|
||||||
|
<field name="name">Invoices</field>
|
||||||
|
<field name="res_model">account.invoice</field>
|
||||||
|
<field name="view_id" ref="account.invoice_tree" />
|
||||||
|
<field name="search_view_id" ref="account.view_account_invoice_filter"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -77,11 +77,6 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem id="contract.menu_config_contract"
|
|
||||||
name="Contracts"
|
|
||||||
sequence="5"
|
|
||||||
parent="account.menu_finance_configuration"
|
|
||||||
/>
|
|
||||||
<menuitem id="menu_contract_quantity_formula"
|
<menuitem id="menu_contract_quantity_formula"
|
||||||
action="action_contract_quantity_formula"
|
action="action_contract_quantity_formula"
|
||||||
parent="contract.menu_config_contract"
|
parent="contract.menu_config_contract"
|
||||||
|
|||||||
Reference in New Issue
Block a user