Skip to content

Commit 3402b37

Browse files
committed
[ADD] purchase_budget_oca_validation
1 parent b6c8d9a commit 3402b37

15 files changed

Lines changed: 464 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
==========================
2+
Purchase Budget Validation
3+
==========================
4+
5+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
6+
:target: https://odoo-community.org/page/development-status
7+
:alt: Beta
8+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
9+
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
10+
:alt: License: LGPL-3
11+
12+
|badge1| |badge2|
13+
14+
This module checks the purchase order lines amount against their related budgets and
15+
prevents you from going over budget.
16+
17+
**Table of contents**
18+
19+
.. contents::
20+
:local:
21+
22+
Configuration
23+
=============
24+
25+
* Create a budget with either a budgetary position, an analytic account or analytic tag
26+
* Confirm it
27+
28+
Usage
29+
=====
30+
31+
* Create a purchase order and select a product, analytic account or tag that matches one of your budget lines.
32+
* Check for message in the chatter if you are over a budget.
33+
34+
Bug Tracker
35+
===========
36+
37+
Bugs are tracked on `GitHub Issues <https://github.com/ursais/14.0/issues>`_.
38+
In case of trouble, please check there if your issue has already been reported.
39+
If you spotted it first, help us smashing it by providing a detailed and welcomed
40+
`feedback <https://github.com/ursais/14.0/issues/new?body=module:%20purchase_budget_validation%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
41+
42+
Credits
43+
=======
44+
45+
Authors
46+
~~~~~~~
47+
48+
* Open Source Integrators
49+
* Serpent Consulting Services
50+
51+
Contributors
52+
~~~~~~~~~~~~
53+
54+
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
55+
* Hardik Suthar <hsuthar@opensourceintegrators.com>
56+
57+
Other credits
58+
~~~~~~~~~~~~~
59+
60+
The development of this module has been financially supported by:
61+
62+
* Casai
63+
64+
Maintainers
65+
~~~~~~~~~~~
66+
67+
.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px
68+
:target: https://github.com/max3903
69+
:alt: max3903
70+
71+
|maintainer-max3903|
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
from . import models
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
{
4+
"name": "Purchase Budget OCA Validation",
5+
"version": "14.0.1.0.0",
6+
"author": "Open Source Integrators, Odoo Community Association (OCA)",
7+
"summary": "Check purchase orders against budgets",
8+
"license": "AGPL-3",
9+
"category": "Purchase",
10+
"maintainer": "Open Source Integrators",
11+
"website": "https://github.com/OCA/account-budgeting",
12+
"depends": ["purchase", "account_budget_oca_analytic_tag"],
13+
"installable": True,
14+
"maintainers": ["max3903"],
15+
"development_status": "Beta",
16+
"data": [
17+
"views/crossovered_budget_lines_view.xml",
18+
"views/purchase_order_view.xml",
19+
],
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
from . import crossovered_budget
4+
from . import crossovered_budget_lines
5+
from . import purchase_order
6+
from . import purchase_order_line
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
from odoo import fields, models
4+
5+
6+
class CrossoveredBudget(models.Model):
7+
_inherit = "crossovered.budget"
8+
9+
amount_include_tax = fields.Boolean(
10+
"Include Taxes in Amount", default=False, copy=False
11+
)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
from odoo import fields, models
4+
5+
6+
class CrossoveredBudgetLines(models.Model):
7+
_inherit = "crossovered.budget.lines"
8+
9+
committed_amount = fields.Float(
10+
compute="_compute_committ_uncommitt_amount", string="Committed Amount"
11+
)
12+
uncommitted_amount = fields.Float(
13+
compute="_compute_committ_uncommitt_amount", string="Uncommitted Amount"
14+
)
15+
over_budget = fields.Boolean(compute="_compute_over_budget", string="Over Budget")
16+
17+
def _compute_committ_uncommitt_amount(self):
18+
purchase_line_obj = self.env["purchase.order.line"]
19+
for budget_line in self:
20+
commit_domain = [
21+
("date_order", ">=", budget_line.date_from),
22+
("date_order", "<=", budget_line.date_to),
23+
("order_id.state", "in", ("purchase", "done")),
24+
"|",
25+
("account_analytic_id", "=", budget_line.analytic_account_id.id),
26+
("analytic_tag_ids", "in", budget_line.analytic_tag_id.ids),
27+
"|",
28+
(
29+
"product_id.property_account_expense_id",
30+
"in",
31+
budget_line.general_budget_id.account_ids.ids,
32+
),
33+
(
34+
"product_id.categ_id.property_account_expense_categ_id",
35+
"in",
36+
budget_line.general_budget_id.account_ids.ids,
37+
),
38+
]
39+
po_lines_commimt = purchase_line_obj.search(commit_domain)
40+
if budget_line.crossovered_budget_id.amount_include_tax:
41+
budget_line.committed_amount = -sum(
42+
[
43+
line.price_unit * (line.product_qty - line.qty_invoiced)
44+
+ line.price_tax
45+
for line in po_lines_commimt
46+
]
47+
)
48+
else:
49+
budget_line.committed_amount = -sum(
50+
[
51+
line.price_unit * (line.product_qty - line.qty_invoiced)
52+
for line in po_lines_commimt
53+
]
54+
)
55+
uncommit_domain = [
56+
("date_order", ">=", budget_line.date_from),
57+
("date_order", "<=", budget_line.date_to),
58+
("order_id.state", "not in", ("purchase", "done", "cancel")),
59+
"|",
60+
("account_analytic_id", "=", budget_line.analytic_account_id.id),
61+
("analytic_tag_ids", "in", budget_line.analytic_tag_id.ids),
62+
"|",
63+
(
64+
"product_id.property_account_expense_id",
65+
"in",
66+
budget_line.general_budget_id.account_ids.ids,
67+
),
68+
(
69+
"product_id.categ_id.property_account_expense_categ_id",
70+
"in",
71+
budget_line.general_budget_id.account_ids.ids,
72+
),
73+
]
74+
po_lines_uncommit = purchase_line_obj.search(uncommit_domain)
75+
if budget_line.crossovered_budget_id.amount_include_tax:
76+
budget_line.uncommitted_amount = -sum(
77+
[line.price_subtotal + line.price_tax for line in po_lines_uncommit]
78+
)
79+
else:
80+
budget_line.uncommitted_amount = -sum(
81+
[line.price_subtotal for line in po_lines_uncommit]
82+
)
83+
84+
def _compute_over_budget(self):
85+
for rec in self:
86+
rec.over_budget = False
87+
if (
88+
abs(rec.practical_amount)
89+
+ abs(rec.committed_amount)
90+
+ abs(rec.uncommitted_amount)
91+
) > abs(rec.planned_amount):
92+
rec.over_budget = True
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
from odoo import _, api, fields, models
5+
from odoo.exceptions import ValidationError
6+
7+
8+
class PurchaseOrder(models.Model):
9+
_inherit = "purchase.order"
10+
11+
over_budget = fields.Boolean(
12+
compute="_compute_over_budget_po", string="Over Budget"
13+
)
14+
15+
def _compute_over_budget_po(self):
16+
for rec in self:
17+
rec.over_budget = rec.check_budget(notify=False)
18+
19+
def check_budget(self, notify=False):
20+
message = ""
21+
users = self.env["res.users"]
22+
over_budget = False
23+
for po_line in self.order_line:
24+
budget_lines = po_line._get_budget_lines()
25+
for b_line in budget_lines:
26+
if b_line.over_budget:
27+
users += b_line.crossovered_budget_id.creating_user_id
28+
message += _(
29+
"This order is over budget %s / %s (%s):"
30+
"<br/>%s + %s + %s = %s > %s.<br/>"
31+
% (
32+
b_line.crossovered_budget_id.name,
33+
b_line.general_budget_id.name,
34+
b_line.crossovered_budget_id.creating_user_id.name,
35+
abs(b_line.uncommitted_amount),
36+
abs(b_line.committed_amount),
37+
abs(b_line.practical_amount),
38+
abs(b_line.practical_amount)
39+
+ abs(b_line.committed_amount)
40+
+ abs(b_line.uncommitted_amount),
41+
abs(b_line.planned_amount),
42+
)
43+
)
44+
if message:
45+
over_budget = True
46+
if notify:
47+
self.message_subscribe(partner_ids=users.partner_id.ids)
48+
self.message_post(body=message)
49+
return over_budget
50+
51+
def button_confirm(self):
52+
for rec in self:
53+
if rec.over_budget:
54+
raise ValidationError(
55+
_(
56+
"You cannot confirm the purchase order "
57+
"as one budget would go over the planned amount. "
58+
"Please check the chatter for more details."
59+
)
60+
)
61+
else:
62+
super(PurchaseOrder, rec).button_confirm()
63+
64+
@api.model
65+
def create(self, vals):
66+
res = super().create(vals)
67+
res.check_budget(notify=True)
68+
return res
69+
70+
def write(self, vals):
71+
res = super().write(vals)
72+
for po in self:
73+
po.check_budget(notify=True)
74+
return res
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright (c) 2022 Open Source Integrators
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
from odoo import models
4+
5+
6+
class PurchaseOrderLine(models.Model):
7+
_inherit = "purchase.order.line"
8+
9+
def _get_budget_lines(self):
10+
return self.env["crossovered.budget.lines"].search(
11+
[
12+
("date_from", "<=", self.date_order),
13+
("date_to", ">=", self.date_order),
14+
("crossovered_budget_id.state", "=", "validate"),
15+
"|",
16+
("analytic_account_id", "=", self.account_analytic_id.id),
17+
("analytic_tag_id", "in", self.analytic_tag_ids.ids),
18+
"|",
19+
(
20+
"general_budget_id.account_ids",
21+
"in",
22+
self.product_id.property_account_expense_id.ids,
23+
),
24+
(
25+
"general_budget_id.account_ids",
26+
"in",
27+
self.product_id.categ_id.property_account_expense_categ_id.ids,
28+
),
29+
]
30+
)
9.23 KB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright (C) 2022 Open Source Integrators (https://www.opensourceintegrators.com)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
from . import test_purchase_budget_validation

0 commit comments

Comments
 (0)