Browse Source

[ADD] - Add renewal process with termination notice

pull/208/head
sbejaoui 6 years ago
committed by Thomas Binsfeld
parent
commit
d12a9110ee
  1. 9
      product_contract/models/abstract_contract_line.py
  2. 20
      product_contract/models/product_template.py
  3. 25
      product_contract/models/sale_order_line.py
  4. 27
      product_contract/tests/test_sale_order.py
  5. 41
      product_contract/views/product_template.xml
  6. 6
      product_contract/views/sale_order.xml

9
product_contract/models/abstract_contract_line.py

@ -16,3 +16,12 @@ class AccountAbstractAnalyticContractLine(models.AbstractModel):
) )
self.recurring_interval = self.product_id.recurring_interval self.recurring_interval = self.product_id.recurring_interval
self.date_start = fields.Date.today() self.date_start = fields.Date.today()
self.is_auto_renew = self.product_id.is_auto_renew
self.auto_renew_interval = self.product_id.auto_renew_interval
self.auto_renew_rule_type = self.product_id.auto_renew_rule_type
self.termination_notice_interval = (
self.product_id.termination_notice_interval
)
self.termination_notice_rule_type = (
self.product_id.termination_notice_rule_type
)

20
product_contract/models/product_template.py

@ -37,6 +37,26 @@ class ProductTemplate(models.Model):
string='Repeat Every', string='Repeat Every',
help="Repeat every (Days/Week/Month/Year)", help="Repeat every (Days/Week/Month/Year)",
) )
is_auto_renew = fields.Boolean(string="Auto Renew", default=False)
auto_renew_interval = fields.Integer(
default=1,
string='Renew Every',
help="Renew every (Days/Week/Month/Year)",
)
auto_renew_rule_type = fields.Selection(
[('monthly', 'Month(s)'), ('yearly', 'Year(s)')],
default='yearly',
string='Renewal type',
help="Specify Interval for automatic renewal.",
)
termination_notice_interval = fields.Integer(
default=1, string='Termination Notice Before'
)
termination_notice_rule_type = fields.Selection(
[('daily', 'Day(s)'), ('weekly', 'Week(s)'), ('monthly', 'Month(s)')],
default='monthly',
string='Termination Notice type',
)
@api.onchange('is_contract') @api.onchange('is_contract')
def _change_is_contract(self): def _change_is_contract(self):

25
product_contract/models/sale_order_line.py

@ -47,8 +47,8 @@ class SaleOrderLine(models.Model):
help="Repeat every (Days/Week/Month/Year)", help="Repeat every (Days/Week/Month/Year)",
copy=False, copy=False,
) )
date_start = fields.Date(string='Date Start')
date_end = fields.Date(string='Date End', index=True)
date_start = fields.Date(string='Date Start',)
date_end = fields.Date(string='Date End',)
contract_line_id = fields.Many2one( contract_line_id = fields.Many2one(
comodel_name="account.analytic.invoice.line", comodel_name="account.analytic.invoice.line",
@ -56,6 +56,11 @@ class SaleOrderLine(models.Model):
required=False, required=False,
copy=False, copy=False,
) )
is_auto_renew = fields.Boolean(
string="Auto Renew",
related="product_id.is_auto_renew",
readonly=True,
)
@api.onchange('product_id') @api.onchange('product_id')
def onchange_product(self): def onchange_product(self):
@ -65,7 +70,14 @@ class SaleOrderLine(models.Model):
self.product_id.recurring_invoicing_type self.product_id.recurring_invoicing_type
) )
self.recurring_interval = self.product_id.recurring_interval self.recurring_interval = self.product_id.recurring_interval
self.date_start = fields.Date.today()
self.date_start = self.date_start or fields.Date.today()
if self.product_id.is_auto_renew:
self.date_end = self.date_start + self.env[
'account.analytic.invoice.line'
].get_relative_delta(
self.product_id.auto_renew_rule_type,
self.product_id.auto_renew_interval,
)
@api.multi @api.multi
def _prepare_contract_line_values(self, contract): def _prepare_contract_line_values(self, contract):
@ -91,6 +103,13 @@ class SaleOrderLine(models.Model):
'recurring_interval': self.recurring_interval, 'recurring_interval': self.recurring_interval,
'recurring_invoicing_type': self.recurring_invoicing_type, 'recurring_invoicing_type': self.recurring_invoicing_type,
'recurring_rule_type': self.recurring_rule_type, 'recurring_rule_type': self.recurring_rule_type,
'is_auto_renew': self.product_id.is_auto_renew,
'auto_renew_interval': self.product_id.auto_renew_interval,
'auto_renew_rule_type': self.product_id.auto_renew_rule_type,
'termination_notice_interval':
self.product_id.termination_notice_interval,
'termination_notice_rule_type':
self.product_id.termination_notice_rule_type,
'contract_id': contract.id, 'contract_id': contract.id,
'sale_order_line_id': self.id, 'sale_order_line_id': self.id,
} }

27
product_contract/tests/test_sale_order.py

@ -40,6 +40,7 @@ class TestSaleOrder(TransactionCase):
self.product1.write( self.product1.write(
{ {
'is_contract': True, 'is_contract': True,
'is_auto_renew': True,
'contract_template_id': self.contract_template1.id, 'contract_template_id': self.contract_template1.id,
} }
) )
@ -52,6 +53,7 @@ class TestSaleOrder(TransactionCase):
self.order_line1 = self.sale.order_line.filtered( self.order_line1 = self.sale.order_line.filtered(
lambda l: l.product_id == self.product1 lambda l: l.product_id == self.product1
) )
self.order_line1.date_start = '2018-01-01'
self.contract = self.env["account.analytic.account"].create( self.contract = self.env["account.analytic.account"].create(
{ {
"name": "Test Contract 2", "name": "Test Contract 2",
@ -88,9 +90,14 @@ class TestSaleOrder(TransactionCase):
contract""" contract"""
self.assertTrue(self.sale.is_contract) self.assertTrue(self.sale.is_contract)
def test_action_confirm_auto_renew_without_date_end(self):
with self.assertRaises(ValidationError):
self.sale.action_confirm()
def test_action_confirm(self): def test_action_confirm(self):
""" It should create a contract for each contract template used in """ It should create a contract for each contract template used in
order_line """ order_line """
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
contracts = self.sale.order_line.mapped('contract_id') contracts = self.sale.order_line.mapped('contract_id')
self.assertEqual(len(contracts), 2) self.assertEqual(len(contracts), 2)
@ -102,12 +109,14 @@ class TestSaleOrder(TransactionCase):
def test_sale_contract_count(self): def test_sale_contract_count(self):
"""It should count contracts as many different contract template used """It should count contracts as many different contract template used
in order_line""" in order_line"""
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
self.assertEqual(self.sale.contract_count, 2) self.assertEqual(self.sale.contract_count, 2)
def test_onchange_product(self): def test_onchange_product(self):
""" It should get recurrence invoicing info to the sale line from """ It should get recurrence invoicing info to the sale line from
its product """ its product """
self.order_line1.onchange_product()
self.assertEqual( self.assertEqual(
self.order_line1.recurring_rule_type, self.order_line1.recurring_rule_type,
self.product1.recurring_rule_type, self.product1.recurring_rule_type,
@ -120,6 +129,10 @@ class TestSaleOrder(TransactionCase):
self.order_line1.recurring_invoicing_type, self.order_line1.recurring_invoicing_type,
self.product1.recurring_invoicing_type, self.product1.recurring_invoicing_type,
) )
self.assertEqual(
self.order_line1.date_end,
Date.to_date('2019-01-01'),
)
def test_check_contract_sale_partner(self): def test_check_contract_sale_partner(self):
"""Can't link order line to a partner contract different then the """Can't link order line to a partner contract different then the
@ -155,6 +168,7 @@ class TestSaleOrder(TransactionCase):
def test_sale_order_line_invoice_status(self): def test_sale_order_line_invoice_status(self):
"""Sale order line for contract product should have nothing to """Sale order line for contract product should have nothing to
invoice as status""" invoice as status"""
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
self.assertEqual(self.order_line1.invoice_status, 'no') self.assertEqual(self.order_line1.invoice_status, 'no')
@ -164,6 +178,7 @@ class TestSaleOrder(TransactionCase):
self.sale.order_line.filtered( self.sale.order_line.filtered(
lambda line: not line.product_id.is_contract lambda line: not line.product_id.is_contract
).unlink() ).unlink()
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
self.assertEqual(self.sale.invoice_status, 'no') self.assertEqual(self.sale.invoice_status, 'no')
@ -171,6 +186,7 @@ class TestSaleOrder(TransactionCase):
"""Should not invoice contract product on sale order create invoice""" """Should not invoice contract product on sale order create invoice"""
self.product2.is_contract = False self.product2.is_contract = False
self.product2.invoice_policy = 'order' self.product2.invoice_policy = 'order'
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
self.sale.action_invoice_create() self.sale.action_invoice_create()
self.assertEqual(len(self.sale.invoice_ids), 1) self.assertEqual(len(self.sale.invoice_ids), 1)
@ -181,6 +197,7 @@ class TestSaleOrder(TransactionCase):
def test_link_contract_invoice_to_sale_order(self): def test_link_contract_invoice_to_sale_order(self):
"""It should link contract invoice to sale order""" """It should link contract invoice to sale order"""
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
invoice = self.order_line1.contract_id.recurring_create_invoice() invoice = self.order_line1.contract_id.recurring_create_invoice()
self.assertTrue(invoice in self.sale.invoice_ids) self.assertTrue(invoice in self.sale.invoice_ids)
@ -189,8 +206,14 @@ class TestSaleOrder(TransactionCase):
"""Should stop contract line at sale order line start date""" """Should stop contract line at sale order line start date"""
self.order_line1.contract_id = self.contract self.order_line1.contract_id = self.contract
self.order_line1.contract_line_id = self.contract_line self.order_line1.contract_line_id = self.contract_line
self.order_line1.date_start = "2018-01-01"
self.contract_line.date_end = "2019-01-01"
self.contract_line.is_auto_renew = "2019-01-01"
self.order_line1.date_start = "2018-06-01"
self.order_line1.onchange_product()
self.sale.action_confirm() self.sale.action_confirm()
self.assertEqual( self.assertEqual(
self.contract_line.date_end, Date.to_date("2018-01-01")
self.contract_line.date_end, Date.to_date("2018-06-01")
)
self.assertFalse(
self.contract_line.is_auto_renew
) )

41
product_contract/views/product_template.xml

@ -19,24 +19,45 @@
<label for="is_contract"/> <label for="is_contract"/>
</div> </div>
</xpath> </xpath>
<xpath expr="//group[@name='group_standard_price']"
position="inside">
<xpath expr="//notebook" position="inside">
<page string="Contract"
attrs="{'invisible': [('is_contract', '=', False)],}">
<group>
<field name="contract_template_id" <field name="contract_template_id"
attrs="{'invisible': [('is_contract', '=', False)],
'required':[('is_contract', '=', True)]}"/>
attrs="{'required':[('is_contract', '=', True)]}"/>
<field name="recurring_invoicing_type" <field name="recurring_invoicing_type"
attrs="{'invisible': [('is_contract', '=', False)],
'required':[('is_contract', '=', True)]}"/>
attrs="{'required':[('is_contract', '=', True)]}"/>
<label for="recurring_interval" attrs="{'invisible': [('is_contract', '=', False)],
'required':[('is_contract', '=', True)]}"/>
<div attrs="{'invisible': [('is_contract', '=', False)],
'required':[('is_contract', '=', True)]}">
<label for="recurring_interval" attrs="{'required':[('is_contract', '=', True)]}"/>
<div attrs="{'required':[('is_contract', '=', True)]}">
<field name="recurring_interval" <field name="recurring_interval"
class="oe_inline" nolabel="1"/> class="oe_inline" nolabel="1"/>
<field name="recurring_rule_type" <field name="recurring_rule_type"
class="oe_inline" nolabel="1"/> class="oe_inline" nolabel="1"/>
</div> </div>
</group>
<group>
<field name="is_auto_renew"/>
<label for="auto_renew_interval" attrs="{'invisible': [('is_auto_renew', '=', False)],
'required':[('is_contract', '=', True)]}"/>
<div attrs="{'invisible': [('is_auto_renew', '=', False)],
'required':[('is_auto_renew', '=', True)]}">
<field name="auto_renew_interval"
class="oe_inline" nolabel="1"/>
<field name="auto_renew_rule_type"
class="oe_inline" nolabel="1"/>
</div>
<label for="termination_notice_interval" attrs="{'invisible': [('is_auto_renew', '=', False)],
'required':[('is_contract', '=', True)]}"/>
<div attrs="{'invisible': [('is_auto_renew', '=', False)],
'required':[('is_auto_renew', '=', True)]}">
<field name="termination_notice_interval"
class="oe_inline" nolabel="1"/>
<field name="termination_notice_rule_type"
class="oe_inline" nolabel="1"/>
</div>
</group>
</page>
</xpath> </xpath>
</field> </field>
</record> </record>

6
product_contract/views/sale_order.xml

@ -34,6 +34,8 @@
<field name="contract_line_id" <field name="contract_line_id"
attrs="{'invisible': [('is_contract', '=', False)]}" attrs="{'invisible': [('is_contract', '=', False)]}"
domain="[('contract_id','=',contract_id)]"/> domain="[('contract_id','=',contract_id)]"/>
<field name="is_auto_renew"
invisible="1"/>
</xpath> </xpath>
<xpath expr="//field[@name='order_line']/form//field[@name='tax_id']/parent::group" <xpath expr="//field[@name='order_line']/form//field[@name='tax_id']/parent::group"
@ -58,10 +60,10 @@
</div> </div>
</group> </group>
<group attrs="{'invisible': [('is_contract', '=', False)]}"> <group attrs="{'invisible': [('is_contract', '=', False)]}">
<field name="date_start"/>
<field name="date_start" attrs="{'required': [('is_contract', '=', True)]}"/>
</group> </group>
<group attrs="{'invisible': [('is_contract', '=', False)]}"> <group attrs="{'invisible': [('is_contract', '=', False)]}">
<field name="date_end"/>
<field name="date_end" attrs="{'required': [('is_auto_renew', '=', True)]}"/>
</group> </group>
</xpath> </xpath>
<xpath expr="//field[@name='order_line']/tree//field[@name='price_total']" <xpath expr="//field[@name='order_line']/tree//field[@name='price_total']"

Loading…
Cancel
Save