Skip to content

Commit

Permalink
[ADD] add new module contract_exception
Browse files Browse the repository at this point in the history
  • Loading branch information
barkat-matthias committed Dec 16, 2024
1 parent c94cd27 commit e9f8a06
Show file tree
Hide file tree
Showing 19 changed files with 390 additions and 0 deletions.
1 change: 1 addition & 0 deletions contract_exception/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models, wizard
22 changes: 22 additions & 0 deletions contract_exception/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Foodles (http://www.foodles.co/)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
{
"name": "Contract Exception",
"version": "14.0.1.0.0",
"category": "Contract Management",
"author": "Odoo Community Association (OCA), Foodles",
"maintainers": [""],
"website": "https://github.com/OCA/contract",
"depends": [
"base_exception",
"contract",
],
"data": [
"security/ir.model.access.csv",
"data/contract_exception_data.xml",
"views/contract_views.xml",
"wizard/contract_exception_confirm_view.xml",
],
"license": "AGPL-3",
"installable": True,
}
16 changes: 16 additions & 0 deletions contract_exception/data/contract_exception_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">
<record forcecreate="True" id="ir_cron_test_contract" model="ir.cron">
<field name="name">Test Contracts</field>
<field name="active" eval="False" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">20</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field name="model_id" ref="contract.model_contract_contract" />
<field name="state">code</field>
<field name="code">model.test_all_contracts()</field>
</record>
</odoo>
5 changes: 5 additions & 0 deletions contract_exception/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import (
contract,
contract_line,
exception_rule,
)
50 changes: 50 additions & 0 deletions contract_exception/models/contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright 2024 Foodles (https://www.foodles.co/).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import api, models


class Contract(models.Model):
_inherit = ["contract.contract", "base.exception"]
_name = "contract.contract"

@api.model
def create(self, vals):
record = super().create(vals)
record._contract_check_exception(vals)
return record

def write(self, vals):
result = super().write(vals)
self._contract_check_exception(vals)
return result

@api.model
def _reverse_field(self):
return "contract_ids"

def _fields_trigger_check_exception(self):
return ["ignore_exception", "contract_line_ids"]

def detect_exceptions(self):
all_exceptions = super().detect_exceptions()
lines = self.mapped("contract_line_ids")
all_exceptions += lines.detect_exceptions()
return all_exceptions

def _contract_check_exception(self, vals):
check_exceptions = any(
field in vals for field in self._fields_trigger_check_exception()
)
if check_exceptions:
self.detect_exceptions()

@api.model
def test_all_contracts(self):
contract_set = self.search([])
contract_set.detect_exceptions()
return True

@api.model
def _get_popup_action(self):
return self.env.ref("contract_exception.action_contract_exception_confirm")
25 changes: 25 additions & 0 deletions contract_exception/models/contract_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2024 Foodles (https://www.foodles.co/).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).


from odoo import api, fields, models


class ContractLine(models.Model):
_inherit = ["contract.line", "base.exception.method"]
_name = "contract.line"

ignore_exception = fields.Boolean(
related="contract_id.ignore_exception", store=True, string="Ignore Exceptions"
)

def _get_main_records(self):
return self.mapped("contract_id")

@api.model
def _reverse_field(self):
return "contract_ids"

def _detect_exceptions(self, rule):
records = super()._detect_exceptions(rule)
return records.mapped("contract_id")
22 changes: 22 additions & 0 deletions contract_exception/models/exception_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Foodles (https://www.foodles.co/).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class ExceptionRule(models.Model):
_inherit = "exception.rule"

contract_ids = fields.Many2many(
comodel_name="contract.contract", string="Contracts"
)
model = fields.Selection(
selection_add=[
("contract.contract", "Contract"),
("contract.line", "Contract line"),
],
ondelete={
"contract.contract": "cascade",
"contract.line": "cascade",
},
)
2 changes: 2 additions & 0 deletions contract_exception/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_contract_exception_confirm,contract.exception.confirm,model_contract_exception_confirm,base_exception.group_exception_rule_manager,1,1,1,1
1 change: 1 addition & 0 deletions contract_exception/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_contract
67 changes: 67 additions & 0 deletions contract_exception/tests/test_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2024 Foodles (https://www.foodles.co/).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from unittest.mock import patch

from odoo.tests import SavepointCase


class TestContract(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.customer = cls.env["res.partner"].create({"name": "Test Customer"})
cls.contract = cls.env["contract.contract"].create(
{"name": "Test Contract", "partner_id": cls.customer.id}
)

@patch("odoo.addons.contract_exception.models.contract.Contract.detect_exceptions")
@patch(
"odoo.addons.contract_exception."
"models.contract.Contract._fields_trigger_check_exception"
)
def test_contract_check_exception_with_field_that_trigger_exception(
self, mock_fields_trigger_check_exception, mock_detect_exceptions
):
mock_fields_trigger_check_exception.return_value = ["partner_id"]
self.contract._contract_check_exception({"partner_id": 1})
mock_detect_exceptions.assert_called_once()

@patch("odoo.addons.contract_exception.models.contract.Contract.detect_exceptions")
@patch(
"odoo.addons.contract_exception."
"models.contract.Contract._fields_trigger_check_exception"
)
def test_contract_check_exception_without_field_that_trigger_exception(
self, mock_fields_trigger_check_exception, mock_detect_exceptions
):
mock_fields_trigger_check_exception.return_value = []
self.contract._contract_check_exception({"partner_id": 1})
self.assertEqual(mock_detect_exceptions.call_count, 0)

def test_detect_exceptions(self):
rules = self.env["exception.rule"].create(
[
{
"name": "Test Rule",
"model": "contract.contract",
"code": "failed=True",
},
{
"name": "Test contract line Rule",
"model": "contract.line",
"code": "failed=True",
},
]
)
self.contract.contract_line_ids = [(0, 0, {"name": "Test Line"})]
exceptions = self.contract.detect_exceptions()
self.assertEqual(exceptions, rules.ids)

@patch("odoo.addons.contract_exception.models.contract.Contract.detect_exceptions")
def test_all_contracts(self, mock_detect_exceptions):
self.env["contract.contract"].test_all_contracts()
self.assertEqual(
self.env["contract.contract"].search_count([]),
mock_detect_exceptions.call_count,
)
84 changes: 84 additions & 0 deletions contract_exception/views/contract_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="action_contract_test_tree" model="ir.actions.act_window">
<field name="name">Contract Exception Rules</field>
<field name="res_model">exception.rule</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="base_exception.view_exception_rule_tree" />
<field
name="domain"
>[('model', 'in', ['contract.contract', 'contract.line'])]</field>
<field
name="context"
>{'active_test': False, 'default_model' : 'contract.contract'}</field>
</record>
<menuitem
action="action_contract_test_tree"
id="menu_contract_test"
sequence="90"
parent="account.account_management_menu"
groups="base_exception.group_exception_rule_manager"
/>
<record id="view_order_form" model="ir.ui.view">
<field name="name">contract_exception.view_contract_form</field>
<field name="model">contract.contract</field>
<field name="inherit_id" ref="contract.contract_contract_form_view" />
<field name="arch" type="xml">
<sheet position="before">
<div
class="alert alert-danger"
role="alert"
style="margin-bottom:0px;"
attrs="{'invisible': [('exceptions_summary','=',False)]}"
>
<p>
<strong>There are exceptions on this contract:</strong>
</p>
<field name="exceptions_summary" />
<button
name="action_ignore_exceptions"
type="object"
class="btn-danger"
string="Ignore Exceptions"
help="Click here to be able to confirm this contract regardless of the exceptions."
groups="base_exception.group_exception_rule_manager"
/>
</div>
</sheet>
<xpath expr="//field[@name='contract_template_id']/.." position="inside">
<field name="ignore_exception" />
</xpath>
<xpath expr="//field[@name='contract_line_ids']/tree" position="inside">
<field name="ignore_exception" invisible="1" />
</xpath>
</field>
</record>
<record id="view_contract_tree" model="ir.ui.view">
<field name="name">contract_exception.view_order_tree</field>
<field name="model">contract.contract</field>
<field name="inherit_id" ref="contract.contract_contract_tree_view" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="main_exception_id" />
</field>
</field>
</record>

<record id="contract_contract_search_view" model="ir.ui.view">
<field name="name">contract_exception.view_contracts_filter</field>
<field name="model">contract.contract</field>
<field name="inherit_id" ref="contract.contract_contract_search_view" />
<field name="arch" type="xml">
<filter name="group_by_date_end" position="after">
<separator orientation="vertical" />
<filter
icon="terp-emblem-important"
name="tofix"
string="Blocked"
domain="[('main_exception_id','!=',False)]"
/>
</filter>
</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions contract_exception/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import contract_exception_confirm, contract_manually_single_invoice
23 changes: 23 additions & 0 deletions contract_exception/wizard/contract_exception_confirm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2024 Foodles (https://www.foodles.co/).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class ContractExceptionConfirm(models.TransientModel):
_name = "contract.exception.confirm"
_description = "Contract exception wizard"
_inherit = ["exception.rule.confirm"]

related_model_id = fields.Many2one(
comodel_name="contract.contract", string="Contract"
)

date = fields.Date(required=True)

def action_confirm(self):
self.ensure_one()
if self.ignore:
self.related_model_id.ignore_exception = True
self.related_model_id.generate_invoices_manually(self.date)
return super().action_confirm()
43 changes: 43 additions & 0 deletions contract_exception/wizard/contract_exception_confirm_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_contract_exception_confirm" model="ir.ui.view">
<field name="name">Contract Exceptions</field>
<field name="model">contract.exception.confirm</field>
<field name="arch" type="xml">
<form string="Can't generate invoice due to contract exception">
<group>

<field name="exception_ids" nolabel="1" colspan="4">
<tree>
<field name="name" />
<field name="description" />
</tree>
</field>
<newline />
<field name="ignore" groups='sales_team.group_sale_manager' />
<field name="date" />
</group>
<footer>
<button
name="action_confirm"
string="Confirm"
colspan="1"
type="object"
/>
<button class="oe_link" special="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>
<record id="action_contract_exception_confirm" model="ir.actions.act_window">
<field name="name">Outstanding exceptions to manage</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">contract.exception.confirm</field>
<field name="view_mode">form</field>
<field
name="view_id"
ref="contract_exception.view_contract_exception_confirm"
/>
<field name="target">new</field>
</record>
</odoo>
18 changes: 18 additions & 0 deletions contract_exception/wizard/contract_manually_single_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2024 Foodles (https://www.foodles.co/).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from odoo import models


class ContractManuallySingleInvoice(models.TransientModel):
_inherit = "contract.manually.single.invoice"

def create_invoice(self):
if (
self.contract_id.detect_exceptions()
and not self.contract_id.ignore_exception
):
action = self.contract_id._popup_exceptions()
action.get("context").update({"default_date": self.date})
return action
return self.contract_id.generate_invoices_manually(date=self.date)
1 change: 1 addition & 0 deletions oca_dependencies.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# See https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#oca_dependencies-txt
contract https://github.com/OCA/contract dixmit:14.0-contract-improve
Loading

0 comments on commit e9f8a06

Please sign in to comment.