mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
@@ -74,7 +74,8 @@ Known issues / Roadmap
|
||||
======================
|
||||
|
||||
* Recover states and others functional fields in Contracts.
|
||||
|
||||
* Depending on the contract lines, some sections of contract
|
||||
line have no meaning that they are propagated to certain invoices
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
'wizards/contract_manually_create_invoice.xml',
|
||||
'wizards/contract_contract_terminate.xml',
|
||||
'views/contract_tag.xml',
|
||||
'views/assets.xml',
|
||||
'views/abstract_contract_line.xml',
|
||||
'views/contract.xml',
|
||||
'views/contract_line.xml',
|
||||
|
||||
@@ -17,13 +17,13 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
_description = 'Abstract Recurring Contract Line'
|
||||
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string='Product', required=True
|
||||
'product.product', string='Product'
|
||||
)
|
||||
|
||||
name = fields.Text(string='Description', required=True)
|
||||
quantity = fields.Float(default=1.0, required=True)
|
||||
uom_id = fields.Many2one(
|
||||
'uom.uom', string='Unit of Measure', required=True
|
||||
'uom.uom', string='Unit of Measure'
|
||||
)
|
||||
automatic_price = fields.Boolean(
|
||||
string="Auto-price?",
|
||||
@@ -125,6 +125,9 @@ class ContractAbstractContractLine(models.AbstractModel):
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
display_type = fields.Selection([
|
||||
('line_section', "Section"),
|
||||
('line_note', "Note")], default=False, help="Technical field for UX purpose.")
|
||||
|
||||
@api.model
|
||||
def _get_default_recurring_invoicing_offset(
|
||||
|
||||
@@ -146,6 +146,9 @@ class ContractLine(models.Model):
|
||||
def _compute_state(self):
|
||||
today = fields.Date.context_today(self)
|
||||
for rec in self:
|
||||
if rec.display_type:
|
||||
rec.state = False
|
||||
continue
|
||||
if rec.is_canceled:
|
||||
rec.state = 'canceled'
|
||||
continue
|
||||
@@ -248,6 +251,8 @@ class ContractLine(models.Model):
|
||||
]
|
||||
if state == 'canceled':
|
||||
return [('is_canceled', '=', True)]
|
||||
if not state:
|
||||
return [('display_type', '!=', False)]
|
||||
|
||||
@api.model
|
||||
def _search_state(self, operator, value):
|
||||
@@ -258,11 +263,8 @@ class ContractLine(models.Model):
|
||||
'upcoming-close',
|
||||
'closed',
|
||||
'canceled',
|
||||
False,
|
||||
]
|
||||
if operator == '!=' and not value:
|
||||
return []
|
||||
if operator == '=' and not value:
|
||||
return [('id', '=', False)]
|
||||
if operator == '=':
|
||||
return self._get_state_domain(value)
|
||||
if operator == '!=':
|
||||
@@ -275,8 +277,6 @@ class ContractLine(models.Model):
|
||||
return domain
|
||||
if operator == 'in':
|
||||
domain = []
|
||||
if not value:
|
||||
return [('id', '=', False)]
|
||||
for state in value:
|
||||
if domain:
|
||||
domain.insert(0, '|')
|
||||
@@ -284,6 +284,8 @@ class ContractLine(models.Model):
|
||||
return domain
|
||||
|
||||
if operator == 'not in':
|
||||
if set(value) == set(states):
|
||||
return [('id', '=', False)]
|
||||
return self._search_state(
|
||||
'in', [state for state in states if state not in value]
|
||||
)
|
||||
@@ -648,15 +650,15 @@ class ContractLine(models.Model):
|
||||
|
||||
@api.depends('recurring_next_date', 'date_start', 'date_end')
|
||||
def _compute_create_invoice_visibility(self):
|
||||
# TODO: depending on the lines, and their order, some sections
|
||||
# have no meaning in certain invoices
|
||||
today = fields.Date.context_today(self)
|
||||
for rec in self:
|
||||
if rec.date_start:
|
||||
if today < rec.date_start:
|
||||
rec.create_invoice_visibility = False
|
||||
else:
|
||||
rec.create_invoice_visibility = bool(
|
||||
rec.recurring_next_date
|
||||
)
|
||||
if (not rec.display_type and
|
||||
rec.date_start and today >= rec.date_start):
|
||||
rec.create_invoice_visibility = bool(rec.recurring_next_date)
|
||||
else:
|
||||
rec.create_invoice_visibility = False
|
||||
|
||||
@api.multi
|
||||
def _prepare_invoice_line(self, invoice_id=False, invoice_values=False):
|
||||
@@ -665,6 +667,7 @@ class ContractLine(models.Model):
|
||||
self.last_date_invoiced, self.recurring_next_date
|
||||
)
|
||||
invoice_line_vals = {
|
||||
'display_type': self.display_type,
|
||||
'product_id': self.product_id.id,
|
||||
'quantity': self._get_quantity_to_invoice(*dates),
|
||||
'uom_id': self.uom_id.id,
|
||||
@@ -1299,10 +1302,11 @@ class ContractLine(models.Model):
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
"""stop unlink uncnacled lines"""
|
||||
if not all(self.mapped('is_canceled')):
|
||||
raise ValidationError(
|
||||
_("Contract line must be canceled before delete")
|
||||
)
|
||||
for record in self:
|
||||
if not (record.is_canceled or record.display_type):
|
||||
raise ValidationError(
|
||||
_("Contract line must be canceled before delete")
|
||||
)
|
||||
return super().unlink()
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
* Recover states and others functional fields in Contracts.
|
||||
|
||||
* Depending on the contract lines, some sections of contract
|
||||
line have no meaning that they are propagated to certain invoices
|
||||
|
||||
@@ -5,27 +5,27 @@
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-call="web.external_layout">
|
||||
<t t-set="o" t-value="o.with_context(lang=o.partner_id.lang)" />
|
||||
<t t-set="address">
|
||||
<p id="partner_info"><strong>Partner:</strong></p>
|
||||
<div t-field="o.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "mobile", "fax", "email"], "no_marker": true, "phone_icons": true}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
</t>
|
||||
<div class="page">
|
||||
<div class="oe_structure"/>
|
||||
<div class="row" id="partner_info">
|
||||
<div class="col-xs-5 col-xs-offset-7">
|
||||
<p id="partner_info"><strong>Partner:</strong></p>
|
||||
<div t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "mobile", "fax", "email"], "no_marker": true, "phone_icons": true}'/>
|
||||
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="header_info">
|
||||
<div class="col-xs-3">
|
||||
<div class="col-3">
|
||||
<strong>Responsible: </strong><p t-field="o.user_id"/>
|
||||
<strong>Contract: </strong><p t-field="o.code"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="invoice_info">
|
||||
<t t-set="total" t-value="0"/>
|
||||
<div class="col-xs-12">
|
||||
<div class="col-12">
|
||||
<t t-set="total" t-value="0"/>
|
||||
<p id="services_info"><strong>Recurring Items</strong></p>
|
||||
<table class="table table-condensed">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><strong>Description</strong></th>
|
||||
@@ -36,30 +36,61 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="o.contract_line_ids" t-as="l">
|
||||
<td>
|
||||
<span t-field="l.name"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.quantity"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_subtotal" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="l.date_start"/>
|
||||
</td>
|
||||
<t t-set="total" t-value="total + l.price_subtotal"/>
|
||||
</tr>
|
||||
<t t-set="current_subtotal" t-value="0"/>
|
||||
<t t-foreach="o.contract_line_ids" t-as="l">
|
||||
<t t-set="current_subtotal" t-value="current_subtotal + l.price_subtotal"/>
|
||||
<tr t-att-class="'bg-200 font-weight-bold o_line_section' if l.display_type == 'line_section' else 'font-italic o_line_note' if l.display_type == 'line_note' else ''">
|
||||
<t t-if="not l.display_type">
|
||||
<td>
|
||||
<span t-field="l.name"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.quantity"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_subtotal" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.date_start"/>
|
||||
</td>
|
||||
<t t-set="total" t-value="total + l.price_subtotal"/>
|
||||
</t>
|
||||
<t t-if="l.display_type == 'line_section'">
|
||||
<td colspan="99">
|
||||
<span t-field="l.name"/>
|
||||
</td>
|
||||
<t t-set="current_section" t-value="l"/>
|
||||
<t t-set="current_subtotal" t-value="0"/>
|
||||
</t>
|
||||
<t t-if="l.display_type == 'line_note'">
|
||||
<td colspan="99">
|
||||
<span t-field="l.name"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<t t-if="current_section and (l_last or o.contract_line_ids[l_index+1].display_type == 'line_section')">
|
||||
<tr class="is-subtotal text-right">
|
||||
<td colspan="99">
|
||||
<strong class="mr16">Subtotal</strong>
|
||||
<span
|
||||
t-esc="current_subtotal"
|
||||
t-options='{"widget": "monetary", "display_currency": o.currency_id}'
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-xs-4 pull-right">
|
||||
<table class="table table-condensed">
|
||||
<tr class="border-black">
|
||||
</div>
|
||||
<div id="total" class="row" name="total">
|
||||
<div class="col-4 ml-auto">
|
||||
<table class="table table-sm">
|
||||
<tr class="border-black o_subtotal" style="">
|
||||
<td><strong>Total</strong></td>
|
||||
<td class="text-right">
|
||||
<span t-esc="total" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
|
||||
@@ -68,10 +99,13 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="note">
|
||||
<strong>Notes: </strong>
|
||||
<br></br>
|
||||
<p t-field="o.note"/>
|
||||
<div>
|
||||
<div class="row mt-4" id="note">
|
||||
<div><strong>Notes: </strong></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p t-field="o.note"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -425,6 +425,8 @@ To use it, just select the template on the contract and fields will be filled au
|
||||
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
|
||||
<ul class="simple">
|
||||
<li>Recover states and others functional fields in Contracts.</li>
|
||||
<li>Depending on the contract lines, some sections of contract
|
||||
line have no meaning that they are propagated to certain invoices</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
|
||||
38
contract/static/src/js/section_and_note_fields_backend.js
Normal file
38
contract/static/src/js/section_and_note_fields_backend.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
*/
|
||||
/*
|
||||
If in the sub-tree view where the sections and notes are to be used
|
||||
there are fields that have defined in the XML attrs = {'invisible': ....}
|
||||
and this condition is met, then an extra space appears in the rows
|
||||
corresponding to the sections and lines.
|
||||
This js was written to deal with that problem, but a solution based on
|
||||
this can be applied directly to Odoo*/
|
||||
odoo.define('contract.section_and_note_backend', function (require) {
|
||||
"use strict";
|
||||
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var section_and_note_one2many = fieldRegistry.get('section_and_note_one2many');
|
||||
|
||||
section_and_note_one2many.include({
|
||||
_getRenderer: function () {
|
||||
var result = this._super.apply(this, arguments);
|
||||
if (this.view.arch.tag === 'tree') {
|
||||
result.include({
|
||||
_renderBodyCell: function (record, node, index, options) {
|
||||
var $cell = this._super.apply(this, arguments);
|
||||
|
||||
var isSection = record.data.display_type === 'line_section';
|
||||
var isNote = record.data.display_type === 'line_note';
|
||||
|
||||
if (isSection || isNote) {
|
||||
$cell.removeClass('o_invisible_modifier');
|
||||
}
|
||||
return $cell;
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -42,9 +42,16 @@ class TestContractBase(common.SavepointCase):
|
||||
'recurring_rule_type': 'yearly',
|
||||
'recurring_interval': 1,
|
||||
}
|
||||
cls.section_template_vals = {
|
||||
'display_type': 'line_section',
|
||||
'name': 'Test section',
|
||||
}
|
||||
cls.template_vals = {
|
||||
'name': 'Test Contract Template',
|
||||
'contract_line_ids': [(0, 0, cls.line_template_vals)],
|
||||
'contract_line_ids': [
|
||||
(0, 0, cls.section_template_vals),
|
||||
(0, 0, cls.line_template_vals),
|
||||
],
|
||||
}
|
||||
cls.template = cls.env['contract.template'].create(
|
||||
cls.template_vals
|
||||
@@ -403,6 +410,14 @@ class TestContract(TestContractBase):
|
||||
self.contract._onchange_contract_template_id()
|
||||
res = {
|
||||
'contract_line_ids': [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
'display_type': 'line_section',
|
||||
'name': 'Test section',
|
||||
}
|
||||
),
|
||||
(
|
||||
0,
|
||||
0,
|
||||
@@ -434,15 +449,19 @@ class TestContract(TestContractBase):
|
||||
)
|
||||
self.contract.contract_template_id = self.template
|
||||
self.contract._onchange_contract_template_id()
|
||||
self.assertEqual(len(self.contract.contract_line_ids), 1)
|
||||
|
||||
for key, value in self.line_template_vals.items():
|
||||
test_value = self.contract.contract_line_ids[0][key]
|
||||
try:
|
||||
test_value = test_value.id
|
||||
except AttributeError:
|
||||
pass
|
||||
self.assertEqual(test_value, value)
|
||||
self.assertEqual(len(self.contract.contract_line_ids), 2)
|
||||
for index, vals in [
|
||||
(0, self.section_template_vals),
|
||||
(1, self.line_template_vals)
|
||||
]:
|
||||
contract_line = self.contract.contract_line_ids[index]
|
||||
for key, value in vals.items():
|
||||
test_value = contract_line[key]
|
||||
try:
|
||||
test_value = test_value.id
|
||||
except AttributeError:
|
||||
pass
|
||||
self.assertEqual(test_value, value)
|
||||
|
||||
def test_send_mail_contract(self):
|
||||
result = self.contract.action_contract_send()
|
||||
@@ -2098,6 +2117,12 @@ class TestContract(TestContractBase):
|
||||
'is_canceled': True,
|
||||
}
|
||||
)
|
||||
# section
|
||||
lines |= self.env['contract.line'].create({
|
||||
"contract_id": self.contract.id,
|
||||
"display_type": "line_section",
|
||||
"name": "Test section",
|
||||
})
|
||||
states = [
|
||||
'upcoming',
|
||||
'in-progress',
|
||||
@@ -2105,48 +2130,29 @@ class TestContract(TestContractBase):
|
||||
'upcoming-close',
|
||||
'closed',
|
||||
'canceled',
|
||||
False,
|
||||
]
|
||||
self.assertEqual(set(lines.mapped('state')), set(states))
|
||||
# Test search method
|
||||
for state in states:
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', '=', state)]
|
||||
)
|
||||
self.assertEqual(len(set(lines.mapped('state'))), 1, state)
|
||||
self.assertEqual(lines.mapped('state')[0], state, state)
|
||||
|
||||
for state in states:
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', '!=', state)]
|
||||
)
|
||||
self.assertFalse(state in lines.mapped('state'))
|
||||
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', 'in', states)]
|
||||
)
|
||||
lines = self.env['contract.line'].search([('state', '=', state)])
|
||||
self.assertTrue(lines, state)
|
||||
self.assertTrue(state in lines.mapped('state'), state)
|
||||
lines = self.env['contract.line'].search([('state', '!=', state)])
|
||||
self.assertFalse(state in lines.mapped('state'), state)
|
||||
lines = self.env['contract.line'].search([('state', 'in', states)])
|
||||
self.assertEqual(set(lines.mapped('state')), set(states))
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', 'in', [])]
|
||||
)
|
||||
lines = self.env['contract.line'].search([('state', 'in', [])])
|
||||
self.assertFalse(lines.mapped('state'))
|
||||
with self.assertRaises(TypeError):
|
||||
self.env['contract.line'].search(
|
||||
[('state', 'in', 'upcoming')]
|
||||
)
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', 'not in', [])]
|
||||
)
|
||||
self.env['contract.line'].search([('state', 'in', 'upcoming')])
|
||||
lines = self.env['contract.line'].search([('state', 'not in', [])])
|
||||
self.assertEqual(set(lines.mapped('state')), set(states))
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', 'not in', states)]
|
||||
)
|
||||
lines = self.env['contract.line'].search([('state', 'not in', states)])
|
||||
self.assertFalse(lines.mapped('state'))
|
||||
lines = self.env['contract.line'].search(
|
||||
[('state', 'not in', ['upcoming', 'in-progress'])]
|
||||
)
|
||||
self.assertEqual(
|
||||
set(lines.mapped('state')),
|
||||
set(['to-renew', 'upcoming-close', 'closed', 'canceled']),
|
||||
)
|
||||
state2 = ['upcoming', 'in-progress']
|
||||
lines = self.env['contract.line'].search([('state', 'not in', state2)])
|
||||
self.assertEqual(set(lines.mapped('state')), set(states) - set(state2))
|
||||
|
||||
def test_check_auto_renew_contract_line_with_successor(self):
|
||||
"""
|
||||
@@ -2331,6 +2337,12 @@ class TestContract(TestContractBase):
|
||||
)
|
||||
self.assertFalse(self.acct_line.create_invoice_visibility)
|
||||
self.assertFalse(self.contract.create_invoice_visibility)
|
||||
section = self.env['contract.line'].create({
|
||||
"contract_id": self.contract.id,
|
||||
"display_type": "line_section",
|
||||
"name": "Test section",
|
||||
})
|
||||
self.assertFalse(section.create_invoice_visibility)
|
||||
|
||||
def test_invoice_contract_without_lines(self):
|
||||
self.contract.contract_line_ids.cancel()
|
||||
|
||||
@@ -7,24 +7,38 @@
|
||||
<field name="model">contract.abstract.contract.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header/>
|
||||
<field name="display_type" invisible="1"/>
|
||||
<header attrs="{'invisible': [('display_type', '!=', False)]}"/>
|
||||
<sheet>
|
||||
<group col="4">
|
||||
<field colspan="4" name="product_id"/>
|
||||
<field colspan="4" name="name"/>
|
||||
<field colspan="2" name="quantity"/>
|
||||
<field colspan="2" name="uom_id"/>
|
||||
<field name="specific_price" invisible="1"/>
|
||||
<group col="4" attrs="{'invisible': [('display_type', '!=', False)]}">
|
||||
<field colspan="4"
|
||||
name="product_id"
|
||||
attrs="{'required': [('display_type', '=', False)]}"/>
|
||||
<label for="quantity"/>
|
||||
<div class="o_row">
|
||||
<field name="quantity" class="oe_inline"/>
|
||||
<field name="uom_id"
|
||||
class="oe_inline"
|
||||
groups="uom.group_uom"
|
||||
attrs="{'required': [('display_type', '=', False)]}"/>
|
||||
</div>
|
||||
<field colspan="2" name="automatic_price"/>
|
||||
<field name="specific_price" invisible="1"/>
|
||||
<field colspan="2" name="price_unit"
|
||||
attrs="{'readonly': [('automatic_price', '=', True)]}"/>
|
||||
<field colspan="2" name="discount" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group col="4">
|
||||
|
||||
<label for="name" string="Description" attrs="{'invisible': [('display_type', '!=', False)]}"/>
|
||||
<label for="name" string="Section" attrs="{'invisible': [('display_type', '!=', 'line_section')]}"/>
|
||||
<label for="name" string="Note" attrs="{'invisible': [('display_type', '!=', 'line_note')]}"/>
|
||||
<field name="name" nolabel="1"/>
|
||||
|
||||
<group col="4" attrs="{'invisible': [('display_type', '!=', False)]}">
|
||||
<field colspan="2" name="is_auto_renew"/>
|
||||
<field colspan="2" name="is_canceled" invisible="1"/>
|
||||
</group>
|
||||
<group attrs="{'invisible':[('is_auto_renew', '=', False)]}">
|
||||
<group attrs="{'invisible':['|', ('is_auto_renew', '=', False), ('display_type', '!=', False)]}">
|
||||
<group>
|
||||
<label for="auto_renew_interval"/>
|
||||
<div>
|
||||
@@ -48,10 +62,10 @@
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group name="recurrence_info">
|
||||
<group name="recurrence_info" attrs="{'invisible': [('display_type', '!=', False)]}">
|
||||
<group>
|
||||
<label for="recurring_interval"/>
|
||||
<div>
|
||||
<div class="o_row">
|
||||
<field name="recurring_interval"
|
||||
class="oe_inline" nolabel="1"/>
|
||||
<field name="recurring_rule_type"
|
||||
|
||||
10
contract/views/assets.xml
Normal file
10
contract/views/assets.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<template id="assets_backend" name="contract assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/contract/static/src/js/section_and_note_fields_backend.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -96,8 +96,75 @@
|
||||
<notebook>
|
||||
<page name="recurring_invoice_line"
|
||||
string="Recurring Invoices">
|
||||
<field name="contract_line_ids" attrs="{'readonly': [('is_terminated','=',True)]}"
|
||||
context="{'default_contract_type': contract_type}"/>
|
||||
<field name="contract_line_ids"
|
||||
attrs="{'readonly': [('is_terminated','=',True)]}"
|
||||
widget="section_and_note_one2many"
|
||||
context="{'default_contract_type': contract_type}">
|
||||
<tree decoration-muted="is_canceled"
|
||||
decoration-info="create_invoice_visibility and not is_canceled">
|
||||
<control>
|
||||
<create string="Add a line"/>
|
||||
<create string="Add a section" context="{'default_display_type': 'line_section'}"/>
|
||||
<create string="Add a note" context="{'default_display_type': 'line_note'}"/>
|
||||
</control>
|
||||
<field name="display_type" invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_id"/>
|
||||
<field name="name" widget="section_and_note_text"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom_id"/>
|
||||
<field name="automatic_price" attrs="{'column_invisible': [('parent.contract_type', '=', 'purchase')]}"/>
|
||||
<field name="price_unit"
|
||||
attrs="{'readonly': [('automatic_price', '=', True)]}"/>
|
||||
<field name="specific_price" invisible="1"/>
|
||||
<field name="discount" groups="base.group_no_one"/>
|
||||
<field name="price_subtotal"/>
|
||||
<field name="recurring_interval" invisible="1"/>
|
||||
<field name="recurring_rule_type" invisible="1"/>
|
||||
<field name="recurring_invoicing_type" invisible="1"/>
|
||||
<field name="date_start" required="1"/>
|
||||
<field name="date_end"/>
|
||||
<field name="recurring_next_date" required="1"/>
|
||||
<field name="last_date_invoiced" groups="base.group_no_one"/>
|
||||
<field name="create_invoice_visibility" invisible="1"/>
|
||||
<field name="is_plan_successor_allowed" invisible="1"/>
|
||||
<field name="is_stop_plan_successor_allowed" invisible="1"/>
|
||||
<field name="is_stop_allowed" invisible="1"/>
|
||||
<field name="is_cancel_allowed" invisible="1"/>
|
||||
<field name="is_un_cancel_allowed" invisible="1"/>
|
||||
<field name="is_auto_renew" invisible="1"/>
|
||||
<field name="is_canceled" invisible="1"/>
|
||||
<button name="action_plan_successor"
|
||||
string="Plan Start" type="object"
|
||||
icon="fa-calendar text-success"
|
||||
attrs="{'invisible': [('is_plan_successor_allowed', '=', False)]}"/>
|
||||
<button name="action_stop_plan_successor"
|
||||
string="Stop Plan Successor"
|
||||
type="object"
|
||||
icon="fa-pause text-muted"
|
||||
attrs="{'invisible': [('is_stop_plan_successor_allowed', '=', False)]}"/>
|
||||
<button name="action_stop" string="Stop"
|
||||
type="object"
|
||||
icon="fa-stop text-danger"
|
||||
attrs="{'invisible': [('is_stop_allowed', '=', False)]}"/>
|
||||
<button name="cancel" string="Cancel"
|
||||
type="object"
|
||||
icon="fa-ban text-danger"
|
||||
confirm="Are you sure you want to cancel this line"
|
||||
attrs="{'invisible': [('is_cancel_allowed', '=', False)]}"/>
|
||||
<button name="action_uncancel"
|
||||
string="Un-cancel" type="object"
|
||||
icon="fa-ban text-success"
|
||||
attrs="{'invisible': [('is_un_cancel_allowed', '=', False)]}"/>
|
||||
<button name="renew" string="Renew"
|
||||
type="object"
|
||||
icon="fa-fast-forward text-success"
|
||||
groups="base.group_no_one"
|
||||
attrs="{'invisible': [('is_auto_renew', '=', False)]}"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="note"/>
|
||||
</page>
|
||||
<page name="info" string="Other Information">
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
</group>
|
||||
</group>
|
||||
<group name="recurrence_info" position="after">
|
||||
<group name="analytic" groups="analytic.group_analytic_accounting,analytic.group_analytic_tags">
|
||||
<group name="analytic"
|
||||
groups="analytic.group_analytic_accounting,analytic.group_analytic_tags"
|
||||
attrs="{'invisible': [('display_type', '!=', False)]}">
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
|
||||
</group>
|
||||
@@ -77,6 +79,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- TODO: Delete this view in migration to v13 or further migrations -->
|
||||
<!--TREE view-->
|
||||
<record id="contract_line_tree_view" model="ir.ui.view">
|
||||
<field name="name">contract.line tree view (in contract)</field>
|
||||
|
||||
@@ -19,11 +19,19 @@
|
||||
</group>
|
||||
</group>
|
||||
<group name="group_invoice_lines" string="Contract Template Lines">
|
||||
<field name="contract_line_ids" nolabel="1">
|
||||
<field name="contract_line_ids"
|
||||
widget="section_and_note_one2many"
|
||||
nolabel="1">
|
||||
<tree>
|
||||
<control>
|
||||
<create string="Add a line"/>
|
||||
<create string="Add a section" context="{'default_display_type': 'line_section'}"/>
|
||||
<create string="Add a note" context="{'default_display_type': 'line_note'}"/>
|
||||
</control>
|
||||
<field name="display_type" invisible="1"/>
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="product_id" />
|
||||
<field name="name" />
|
||||
<field name="name" widget="section_and_note_text"/>
|
||||
<field name="quantity" />
|
||||
<field name="uom_id" />
|
||||
<field name="automatic_price" attrs="{'column_invisible': [('parent.contract_type','=','purchase')]}"/>
|
||||
|
||||
@@ -9,4 +9,14 @@
|
||||
groups="sales_team.group_sale_salesman"
|
||||
/>
|
||||
|
||||
<record id="contract_contract_form_view" model="ir.ui.view">
|
||||
<field name="name">contract.contract.form.contract.sale</field>
|
||||
<field name="model">contract.contract</field>
|
||||
<field name="inherit_id" ref="contract.contract_contract_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='contract_line_ids']/tree/field[@name='discount']" position="attributes">
|
||||
<attribute name="groups" add="sale.group_discount_per_so_line" separator=","/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<field name="model">contract.abstract.contract.line</field>
|
||||
<field name="inherit_id" ref="contract.contract_abstract_contract_line_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='quantity']" position="before">
|
||||
<xpath expr="//label[@for='quantity']" position="before">
|
||||
<field name="qty_type"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='quantity']" position="after">
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
<xpath expr="//field[@name='code']" position="after">
|
||||
<field name="skip_zero_qty"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='contract_line_ids']/tree/field[@name='quantity']" position="before">
|
||||
<field name="qty_type"/>
|
||||
<field
|
||||
name="qty_formula_id"
|
||||
attrs="{'invisible': [('qty_type', '!=', 'variable')]}"
|
||||
/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='contract_line_ids']/tree/field[@name='quantity']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('qty_type', '!=', 'fixed')]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user