Skip to content

Commit

Permalink
Merge 60dafd3 into 693fe5a
Browse files Browse the repository at this point in the history
  • Loading branch information
sbejaoui committed Jan 30, 2020
2 parents 693fe5a + 60dafd3 commit ea30102
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 20 deletions.
4 changes: 4 additions & 0 deletions contract/__manifest__.py
Expand Up @@ -20,23 +20,27 @@
'depends': ['base', 'account', 'product'],
"external_dependencies": {"python": ["dateutil"]},
'data': [
'security/groups.xml',
'security/contract_tag.xml',
'security/ir.model.access.csv',
'security/contract_security.xml',
'security/contract_resiliate_reason.xml',
'report/report_contract.xml',
'report/contract_views.xml',
'data/contract_cron.xml',
'data/contract_renew_cron.xml',
'data/mail_template.xml',
'wizards/contract_line_wizard.xml',
'wizards/contract_manually_create_invoice.xml',
'wizards/contract_contract_resiliate.xml',
'views/abstract_contract_line.xml',
'views/contract.xml',
'views/contract_line.xml',
'views/contract_template.xml',
'views/contract_template_line.xml',
'views/res_partner_view.xml',
'views/res_config_settings.xml',
'views/contract_resiliate_reason.xml',
],
'installable': True,
}
1 change: 1 addition & 0 deletions contract/models/__init__.py
Expand Up @@ -12,3 +12,4 @@
from . import contract_tag
from . import res_company
from . import res_config_settings
from . import contract_resiliate_reason
51 changes: 50 additions & 1 deletion contract/models/contract.py
Expand Up @@ -7,7 +7,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError, UserError
from odoo.tools.translate import _


Expand Down Expand Up @@ -92,6 +92,15 @@ class ContractContract(models.Model):
index=True
)
tag_ids = fields.Many2many(comodel_name="contract.tag", string="Tags")
is_resiliated = fields.Boolean(string="Resiliated", readonly=True)
resiliate_reason_id = fields.Many2one(
comodel_name="contract.resiliate.reason",
string="Resiliate Reason",
ondelete="restrict",
readonly=True
)
resiliate_comment = fields.Text(string="Resiliate Comment", readonly=True)
resiliate_date = fields.Date(string="Resiliate Date", readonly=True)

@api.multi
def _inverse_partner_id(self):
Expand Down Expand Up @@ -457,3 +466,43 @@ def cron_recurring_create_invoice(self, date_ref=None):
domain = self._get_contracts_to_invoice_domain(date_ref)
contracts_to_invoice = self.search(domain)
return contracts_to_invoice._recurring_create_invoice(date_ref)

@api.multi
def action_resiliate_contract(self):
self.ensure_one()
context = {"default_contract_id": self.id}
return {
'type': 'ir.actions.act_window',
'name': _('Resiliate Contract'),
'res_model': 'contract.contract.resiliate',
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
'context': context,
}

@api.multi
def _resiliate_contract(
self, resiliate_reason_id, resiliate_comment, resiliate_date
):
self.ensure_one()
if not self.env.user.has_group("contract.can_resiliate_contract"):
raise UserError(_('You are not allowed to resiliate contracts.'))
self.contract_line_ids.filtered('is_stop_allowed').stop(resiliate_date)
self.write({
'is_resiliated': True,
'resiliate_reason_id': resiliate_reason_id.id,
'resiliate_comment': resiliate_comment,
'resiliate_date': resiliate_date,
})
return True

@api.multi
def action_cancel_contract_resiliation(self):
self.ensure_one()
self.write({
'is_resiliated': False,
'resiliate_reason_id': False,
'resiliate_comment': False,
'resiliate_date': False,
})
26 changes: 19 additions & 7 deletions contract/models/contract_line.py
Expand Up @@ -292,9 +292,19 @@ def _search_state(self, operator, value):
'successor_contract_line_id',
'predecessor_contract_line_id',
'is_canceled',
'contract_id.is_resiliated',
)
def _compute_allowed(self):
for rec in self:
if rec.contract_id.is_resiliated:
rec.update({
'is_plan_successor_allowed': False,
'is_stop_plan_successor_allowed': False,
'is_stop_allowed': False,
'is_cancel_allowed': False,
'is_un_cancel_allowed': False,
})
continue
if rec.date_start:
allowed = get_allowed(
rec.date_start,
Expand All @@ -306,13 +316,14 @@ def _compute_allowed(self):
rec.is_canceled,
)
if allowed:
rec.is_plan_successor_allowed = allowed.plan_successor
rec.is_stop_plan_successor_allowed = (
allowed.stop_plan_successor
)
rec.is_stop_allowed = allowed.stop
rec.is_cancel_allowed = allowed.cancel
rec.is_un_cancel_allowed = allowed.uncancel
rec.update({
'is_plan_successor_allowed': allowed.plan_successor,
'is_stop_plan_successor_allowed':
allowed.stop_plan_successor,
'is_stop_allowed': allowed.stop,
'is_cancel_allowed': allowed.cancel,
'is_un_cancel_allowed': allowed.uncancel,
})

@api.constrains('is_auto_renew', 'successor_contract_line_id', 'date_end')
def _check_allowed(self):
Expand Down Expand Up @@ -1246,6 +1257,7 @@ def renew(self):
@api.model
def _contract_line_to_renew_domain(self):
return [
('contract_id.is_resiliated', '=', False),
('is_auto_renew', '=', True),
('is_canceled', '=', False),
('termination_notice_date', '<=', fields.Date.context_today(self)),
Expand Down
12 changes: 12 additions & 0 deletions contract/models/contract_resiliate_reason.py
@@ -0,0 +1,12 @@
# Copyright 2020 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ContractResiliateReason(models.Model):

_name = 'contract.resiliate.reason'
_description = 'Contract Resiliation Reason'

name = fields.Char(required=True)
27 changes: 27 additions & 0 deletions contract/security/contract_resiliate_reason.xml
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 ACSONE SA/NV
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->

<odoo>

<record model="ir.model.access" id="contract_resiliate_reason_access_manager">
<field name="name">contract.resiliate.reason access manager</field>
<field name="model_id" ref="model_contract_resiliate_reason"/>
<field name="group_id" ref="account.group_account_manager"/>
<field name="perm_read" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_unlink" eval="1"/>
</record>

<record model="ir.model.access" id="contract_resiliate_reason_access_user">
<field name="name">contract.resiliate.reason access user</field>
<field name="model_id" ref="model_contract_resiliate_reason"/>
<field name="group_id" ref="account.group_account_invoice"/>
<field name="perm_read" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_unlink" eval="1"/>
</record>

</odoo>
12 changes: 12 additions & 0 deletions contract/security/groups.xml
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 ACSONE SA/NV
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->

<odoo>

<record id="can_resiliate_contract" model="res.groups">
<field name="name">Contract: Can Resiliate Contracts</field>
<field name="implied_ids" eval="[(4, ref('account.group_account_invoice'))]"/>
</record>

</odoo>
53 changes: 52 additions & 1 deletion contract/tests/test_contract.py
Expand Up @@ -6,7 +6,7 @@
from datetime import timedelta
from dateutil.relativedelta import relativedelta
from odoo import fields
from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError, UserError
from odoo.tests import common


Expand Down Expand Up @@ -110,6 +110,9 @@ def setUpClass(cls):
)
cls.acct_line.product_id.is_auto_renew = True
cls.contract.company_id.create_new_line_at_contract_line_renew = True
cls.resiliate_reason = cls.env['contract.resiliate.reason'].create({
'name': 'resiliate_reason'
})


class TestContract(TestContractBase):
Expand Down Expand Up @@ -2364,3 +2367,51 @@ def test_stop_and_update_recurring_invoice_date(self):
self.assertEqual(
self.acct_line.recurring_next_date, to_date('2019-06-01')
)

def test_action_resiliate_contract(self):
action = self.contract.action_resiliate_contract()
wizard = (
self.env[action['res_model']]
.with_context(action['context'])
.create(
{
'resiliate_date': '2018-03-01',
'resiliate_reason_id': self.resiliate_reason.id,
'resiliate_comment': 'resiliate_comment',
}
)
)
self.assertEqual(wizard.contract_id, self.contract)
with self.assertRaises(UserError):
wizard.resiliate_contract()
group_can_resiliate_contract = self.env.ref(
"contract.can_resiliate_contract"
)
group_can_resiliate_contract.users |= self.env.user
wizard.resiliate_contract()
self.assertTrue(self.contract.is_resiliated)
self.assertEqual(self.contract.resiliate_date, to_date('2018-03-01'))
self.assertEqual(
self.contract.resiliate_reason_id.id, self.resiliate_reason.id
)
self.assertEqual(self.contract.resiliate_comment, 'resiliate_comment')
self.contract.action_cancel_contract_resiliation()
self.assertFalse(self.contract.is_resiliated)
self.assertFalse(self.contract.resiliate_reason_id)
self.assertFalse(self.contract.resiliate_comment)

def test_resiliate_date_before_last_date_invoiced(self):
self.contract.recurring_create_invoice()
self.assertEqual(
self.acct_line.last_date_invoiced, to_date('2018-02-14')
)
group_can_resiliate_contract = self.env.ref(
"contract.can_resiliate_contract"
)
group_can_resiliate_contract.users |= self.env.user
with self.assertRaises(ValidationError):
self.contract._resiliate_contract(
self.resiliate_reason,
'resiliate_comment',
to_date('2018-02-13'),
)
41 changes: 31 additions & 10 deletions contract/views/contract.xml
Expand Up @@ -7,16 +7,32 @@
<field name="model">contract.contract</field>
<field name="arch" type="xml">
<form>
<field name="is_resiliated" invisible="1"/>
<div class="alert alert-danger" role="alert" style="margin-bottom:0px;" attrs="{'invisible': [('is_resiliated','=',False)]}">
<p>This contract was resiliated for the reason <strong><field name="resiliate_reason_id" options="{'no_open':True}"/></strong> on <field name="resiliate_date"/>.</p>
<p><field name="resiliate_comment"/></p>
</div>
<header>
<button name="action_contract_send"
type="object"
string="Send by Email"
attrs="{'invisible': [('is_resiliated','=',True)]}"
groups="base.group_user"/>
<button name="recurring_create_invoice"
type="object"
attrs="{'invisible': [('create_invoice_visibility', '=', False)]}"
attrs="{'invisible': ['|', ('create_invoice_visibility', '=', False)]}"
string="Create invoices"
groups="base.group_no_one"/>
<button name="action_resiliate_contract"
type="object"
string="Resiliate Contract"
attrs="{'invisible': [('is_resiliated','=',True)]}"
groups="contract.can_resiliate_contract"/>
<button name="action_cancel_contract_resiliation"
type="object"
string="Cancel Contract Resiliation"
attrs="{'invisible': [('is_resiliated','=',False)]}"
groups="contract.can_resiliate_contract"/>
</header>
<sheet string="Contract">
<div class="oe_button_box" name="button_box">
Expand All @@ -38,56 +54,61 @@
class="oe_edit_only"/>
<h3>
<field name="name" class="oe_inline"
attrs="{'readonly': [('is_resiliated','=',True)]}"
placeholder="e.g. Contract XYZ"/>
</h3>
</div>
<group name="main">
<group>
<field name="commercial_partner_id" invisible="1"/>
<field name="partner_id" required="1"/>
<field name="payment_term_id"/>
<field name="user_id"/>
<field name="partner_id" required="1" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="payment_term_id" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="user_id" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
</group>
<group>
<field name="contract_template_id"
attrs="{'readonly': [('is_resiliated','=',True)]}"
domain="['|', ('contract_type', '=', contract_type), ('contract_type', '=', False)]"
context="{'default_contract_type': contract_type}"/>
<field name="contract_type" invisible="1"
required="1"/>
<field name="fiscal_position_id"/>
<field name="fiscal_position_id" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="tag_ids" widget="many2many_tags"/>
</group>
</group>

<group name="recurring_invoices">
<group>
<field name="journal_id" required="1"/>
<field name="journal_id" required="1" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="recurring_next_date"/>
</group>
<group>
<field name="pricelist_id"/>
<field name="pricelist_id" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="date_end"/>
</group>
</group>
<notebook>
<page name="recurring_invoice_line"
string="Recurring Invoices">
<field name="contract_line_ids"
<field name="contract_line_ids" attrs="{'readonly': [('is_resiliated','=',True)]}"
context="{'default_contract_type': contract_type}"/>
</page>
<page name="info" string="Other Information">
<field name="create_invoice_visibility"
invisible="1"/>
<group>
<field name="code"/>
<field name="group_id"/>
<field name="code" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="group_id" attrs="{'readonly': [('is_resiliated','=',True)]}"/>
<field name="company_id"
attrs="{'readonly': [('is_resiliated','=',True)]}"
options="{'no_create': True}"
groups="base.group_multi_company"/>
<field name="currency_id"
attrs="{'readonly': [('is_resiliated','=',True)]}"
options="{'no_create': True}"
groups="base.group_multi_currency"/>
<field name="invoice_partner_id"
attrs="{'readonly': [('is_resiliated','=',True)]}"
required="1"/>
</group>
<group string="Legend (for the markers inside invoice lines description)"
Expand Down

0 comments on commit ea30102

Please sign in to comment.