Skip to content

Commit abbcce9

Browse files
committed
[IMP] budget_management: improved form view, added analytic line
Added restriction on add analtic line based on users selected preferece Inherited list view of account analytic line to make it editable and creatable Added action for open analytical lines on click on view button. Developed graph and pivot views for budget. Added constraints on write, create form view of budget , analytic line and budget line as per requirement. Updated float to monatry field for shwoing currency.
1 parent ee354fb commit abbcce9

13 files changed

+293
-37
lines changed

budget_management/__manifest__.py

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"views/actions_menu_and_button.xml",
1515
"views/menu_views.xml",
1616
"views/budget_views.xml",
17+
'views/account_analytic_line_view.xml',
1718
"wizards/wizard_add_budgets_view.xml",
1819
],
1920
"application": True,

budget_management/models/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from . import budget
22
from . import budget_line
3+
from . import account_analytic_line
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from odoo import models, fields, api
2+
from odoo.exceptions import ValidationError
3+
4+
5+
class AccountAnalyticLine(models.Model):
6+
_inherit = "account.analytic.line"
7+
8+
budget_line_id = fields.Many2one(comodel_name="budget.management.budget.lines")
9+
10+
@api.model_create_multi
11+
def create(self, vals_list):
12+
# print("* " * 100)
13+
# print(vals_list)
14+
# print("* " * 100)
15+
for vals in vals_list:
16+
budget_line = self.env["budget.management.budget.lines"].browse(
17+
vals.get("budget_line_id")
18+
)
19+
budget = budget_line.budget_id
20+
if budget.on_over_budget == "restriction":
21+
if sum(budget_line.analytic_line_ids.mapped("amount"))+vals.get("amount") > budget_line.budget_amount:
22+
raise ValidationError(
23+
"You cannot create a budget line because it exceeds the allowed budget!"
24+
)
25+
return super(AccountAnalyticLine, self).create(vals_list)
26+
27+
def write(self, vals):
28+
# print("* " * 100)
29+
# print(vals)
30+
# print("* " * 100)
31+
if "amount" in vals:
32+
for record in self:
33+
old_amount = record.amount
34+
new_amount = vals.get("amount")
35+
print(old_amount,new_amount)
36+
total_amount = sum(record.budget_line_id.analytic_line_ids.mapped("amount")) + new_amount - old_amount
37+
38+
budget_line = record.budget_line_id
39+
budget = budget_line.budget_id
40+
if budget.on_over_budget == "restriction" and total_amount > budget_line.budget_amount:
41+
raise ValidationError(
42+
"You cannot update this budget line because it exceeds the allowed budget!"
43+
)
44+
45+
return super(AccountAnalyticLine, self).write(vals)

budget_management/models/budget.py

+39-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
from odoo import models, fields, api
2-
from datetime import date
2+
from odoo.exceptions import ValidationError, UserError
33

44

55
class Budget(models.Model):
66
_name = "budget.budget"
77
_inherit = ["mail.thread", "mail.activity.mixin"]
8+
_description = " "
89

910
name = fields.Char(compute="_compute_budget_name", store=True, readonly=True)
1011
active = fields.Boolean(default=True)
1112
is_favorite = fields.Boolean(default=False)
13+
color = fields.Integer(string="Color Index")
1214
state = fields.Selection(
1315
selection=[
1416
("draft", "Draft"),
@@ -41,10 +43,16 @@ class Budget(models.Model):
4143
string="Company",
4244
default=lambda self: self.env.company,
4345
)
44-
4546
budget_line_ids = fields.One2many(
4647
comodel_name="budget.management.budget.lines", inverse_name="budget_id"
4748
)
49+
warnings = fields.Text(readonly=True)
50+
currency_id = fields.Many2one(
51+
comodel_name="res.currency",
52+
string="Currency",
53+
required=True,
54+
default=lambda self: self.env.company.currency_id,
55+
)
4856

4957
@api.depends("date_start", "date_end")
5058
def _compute_budget_name(self):
@@ -61,13 +69,40 @@ def onclick_reset_to_draft(self):
6169
if record.state != "draft":
6270
record.state = "draft"
6371

72+
def onclick_confirmed(self):
73+
for record in self:
74+
if record.state == "draft":
75+
record.state = "confirmed"
76+
6477
def onclick_revise(self):
6578
for record in self:
66-
if record.state in ["confirmed", "draft"]:
67-
record.revision_id = lambda self: self.env.user
79+
if record.state != "confirmed":
80+
raise UserError("Only confirmed budgets can be revised.")
81+
if record.state in ["confirmed"]:
82+
record.revision_id = self.env.user
6883
record.state = "revised"
84+
# new_budget = record.copy({"revision_id": self.id, "state": "draft"})
85+
# record.message_post(
86+
# body=f'Revised into <a href="#id={new_budget.id}&model=budget.budget">{new_budget.name}</a>.'
87+
# )
6988

7089
def onclick_done(self):
7190
for record in self:
7291
if record.state in ["confirmed", "revised"]:
7392
record.state = "done"
93+
94+
@api.constrains("date_start", "date_end")
95+
def _check_period_overlap(self):
96+
for record in self:
97+
overlapping_budgets = self.search(
98+
[
99+
("id", "!=", record.id),
100+
("date_start", "<=", record.date_start),
101+
("date_end", ">=", record.date_end),
102+
("company_id", "=", record.company_id.id),
103+
]
104+
)
105+
if overlapping_budgets:
106+
raise ValidationError(
107+
"Cannot create overlapping budgets for the same period and company."
108+
)
+93-16
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,102 @@
11
from odoo import models, fields, api
2+
from odoo.exceptions import ValidationError, UserError
3+
24

35
class BudgetLine(models.Model):
46
_name = "budget.management.budget.lines"
7+
_description = "Budget Management Budget Lines"
58

6-
name = fields.Char()
7-
budget_id = fields.Many2one(comodel_name="budget.budget", string="Budget")
8-
budget_amount = fields.Float(default=0.0)
9-
achieved_amount = fields.Float(default=0.0)
9+
name = fields.Char(string="Name")
10+
budget_id = fields.Many2one(
11+
comodel_name="budget.budget", string="Budget", required=True
12+
)
13+
state = fields.Selection(related="budget_id.state")
14+
budget_amount = fields.Monetary(
15+
string="Budget Amount",
16+
default=0.0,
17+
currency_field="currency_id",
18+
help="The total allocated budget for this budget line.",
19+
)
20+
achieved_amount = fields.Monetary(
21+
string="Achieved Amount",
22+
default=0.0,
23+
compute="_compute_achieved_amount",
24+
store=True,
25+
currency_field="currency_id",
26+
)
1027
achieved_percentage = fields.Float(
11-
default=0.0,
12-
compute="_compute_achieved_percentage",
13-
store=True
14-
)
15-
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
16-
# analytic_line_ids = fields.One2many('account.analytic.line', string='Analytic Account')
17-
18-
@api.depends("budget_amount", "achieved_amount")
19-
def _compute_achieved_percentage(self):
28+
string="Achieved (%)",
29+
compute="_compute_achieved_amount",
30+
store=True,
31+
readonly=True,
32+
help="Percentage of the budget achieved based on analytic lines.",
33+
)
34+
analytic_account_id = fields.Many2one(
35+
"account.analytic.account", string="Analytic Account", required=True
36+
)
37+
analytic_line_ids = fields.One2many(
38+
comodel_name="account.analytic.line",
39+
inverse_name="budget_line_id",
40+
string="Analytic Lines",
41+
)
42+
over_budget = fields.Monetary(
43+
string="Over Budget",
44+
compute="_compute_achieved_amount",
45+
store=True,
46+
help="The amount by which the budget line exceeds its allocated budget.",
47+
currency_field="currency_id",
48+
)
49+
currency_id = fields.Many2one(
50+
comodel_name="res.currency",
51+
related="budget_id.currency_id",
52+
string="Currency",
53+
readonly=True,
54+
)
55+
56+
@api.depends("analytic_line_ids.amount")
57+
def _compute_achieved_amount(self):
2058
for record in self:
21-
if record.budget_amount:
22-
record.achieved_percentage = (record.achieved_amount / record.budget_amount) * 100
59+
record.achieved_amount = sum(record.analytic_line_ids.mapped("amount"))
60+
record.achieved_percentage = (
61+
(record.achieved_amount / record.budget_amount) * 100
62+
if record.budget_amount > 0
63+
else 0.0
64+
)
65+
record.over_budget = max(0.0, record.achieved_amount - record.budget_amount)
66+
67+
if (
68+
record.budget_id.on_over_budget == "warning"
69+
and record.achieved_amount > record.budget_amount
70+
):
71+
record.budget_id.warnings = "Achived amount is more than your budget!"
2372
else:
24-
record.achieved_percentage = 0.0
73+
record.budget_id.warnings = False
74+
75+
@api.constrains("budget_amount")
76+
def _check_budget_amount(self):
77+
for record in self:
78+
if record.budget_amount < 0:
79+
raise ValidationError("Budget amount cannot be negative.")
80+
81+
82+
@api.model_create_multi
83+
def create(self, vals_list):
84+
active_budget = None
85+
if self.env.context.get("active_id"):
86+
active_budget = self.env["budget.budget"].browse(self.env.context.get("active_id"))
87+
if active_budget.state != "draft":
88+
raise UserError("Budget lines can only be created when the state is 'draft'.")
89+
else:
90+
for vals in vals_list:
91+
budget_id = vals.get("budget_id")
92+
if budget_id:
93+
active_budget = self.env["budget.budget"].browse(budget_id)
94+
break
95+
96+
if not active_budget:
97+
raise UserError("No budget found in context or record.")
98+
99+
if active_budget.state != "draft":
100+
raise UserError("Budget lines can only be created when the state is 'draft'.")
25101

102+
return super(BudgetLine, self).create(vals_list)
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
22
budget_management.access_budget_budget,access_budget_budget,budget_management.model_budget_budget,base.group_user,1,1,1,1
33
budget_management.access_add_budget_wizard,access_add_budget_wizard,budget_management.model_add_budget_wizard,base.group_user,1,1,1,0
4-
budget_management.access_budget_management_budget_lines,access_budget_management_budget_lines,budget_management.model_budget_management_budget_lines,base.group_user,1,1,1,1
4+
budget_management.access_budget_management_budget_lines,access_budget_management_budget_lines,budget_management.model_budget_management_budget_lines,base.group_user,1,1,1,1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<odoo>
2+
<record id="budget_mangement_account_analytic_list_view" model="ir.ui.view">
3+
<field name="name">account.analytic.line.list</field>
4+
<field name="model">account.analytic.line</field>
5+
<field name="inherit_id" ref="analytic.view_account_analytic_line_tree"/>
6+
<field name="arch" type="xml">
7+
8+
<xpath expr="//list" position="attributes">
9+
<attribute name="editable">bottom</attribute>
10+
</xpath>
11+
<xpath expr="//list" position="inside">
12+
<field name="budget_line_id"/>
13+
</xpath>
14+
</field>
15+
</record>
16+
</odoo>

budget_management/views/actions_menu_and_button.xml

+14-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<record id="action_budget_management_menu_budget" model="ir.actions.act_window">
33
<field name="name">Budgets</field>
44
<field name="res_model">budget.budget</field>
5-
<field name="view_mode">kanban,form</field>
5+
<field name="view_mode">kanban,form,list</field>
66
<field name="help" type="html">
77
<p class="o_view_nocontent_smiling_face">
88
No records available.
@@ -14,11 +14,20 @@
1414
</record>
1515

1616
<record id="action_budget_management_budget_line" model="ir.actions.act_window">
17-
<field name="name">Budgets Lines</field>
17+
<field name="name">Budget Lines</field>
1818
<field name="res_model">budget.management.budget.lines</field>
19-
<field name="view_mode">list</field>
19+
<field name="view_mode">list,graph,pivot</field>
2020
<field name="view_id" ref="budget_management.view_budget_management_budget_line_tree" />
21-
<field name="context">{'default_budget_id': active_id}</field>
21+
<field name="context">{'default_budget_id': active_id}</field>
2222
<field name="domain">[('budget_id', '=', context.get('default_budget_id'))]</field>
2323
</record>
24-
</odoo>
24+
25+
26+
<record id="action_open_account_analytic_lines_list" model="ir.actions.act_window">
27+
<field name="name">Analytic Lines</field>
28+
<field name="res_model">account.analytic.line</field>
29+
<field name="view_mode">list</field>
30+
<field name="context">{'default_budget_line_id': active_id}</field>
31+
<field name="domain">[('budget_line_id', '=', context.get('default_budget_line_id'))]</field>
32+
</record>
33+
</odoo>

budget_management/views/budget_line_views.xml

+43-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,53 @@
88
sample="1"
99
editable="bottom"
1010
>
11+
<field name="currency_id" column_invisible="True" />
1112
<field string="Budget" name="name" />
13+
<field name="budget_id" column_invisible="True" />
1214
<field string="Analytic Account" name="analytic_account_id" />
1315
<field string="Budget Amount" name="budget_amount" />
14-
<field string="Archived Amount" name="achieved_amount" />
16+
<field string="Achived Amount" name="achieved_amount" />
1517
</list>
1618
</field>
1719
</record>
20+
21+
<record id="view_budget_management_budget_line_graph" model="ir.ui.view">
22+
<field name="name">budget.management.budget.lines.graph</field>
23+
<field name="model">budget.management.budget.lines</field>
24+
<field name="arch" type="xml">
25+
<graph string="Budget Lines" type="bar" stacked="1">
26+
<field name="name" type="row" />
27+
<field name="budget_amount" type="measure" operator="+" />
28+
<field name="achieved_amount" type="measure" operator="+" />
29+
<field name="over_budget" />
30+
</graph>
31+
</field>
32+
</record>
33+
34+
<record id="view_budget_management_budget_line_pivot" model="ir.ui.view">
35+
<field name="name">budget.management.budget.lines.pivot</field>
36+
<field name="model">budget.management.budget.lines</field>
37+
<field name="arch" type="xml">
38+
<pivot string="Budget Lines">
39+
<field name="name" type="row" />
40+
</pivot>
41+
</field>
42+
</record>
43+
44+
<!-- <record id="view_budget_management_budget_line_gantt" model="ir.ui.view">
45+
<field name="name">budget.management.budget.lines.gantt</field>
46+
<field name="model">budget.management.budget.lines</field>
47+
<field name="arch" type="xml">
48+
<gantt string="Budget Lines" date_start="start_date" date_stop="end_date">
49+
<field name="name" />
50+
51+
<field name="user_id" widget="many2one_avatar_user" />
52+
53+
<field name="achieved_percentage" widget="progressbar"
54+
options="{'color_field': 'progress_color'}" />
55+
<field name="analytic_account_id" />
56+
</gantt>
57+
</field>
58+
</record> -->
59+
1860
</odoo>

0 commit comments

Comments
 (0)