-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ADD] budget_management: add budget management feature
Created a budget management module to handle budgets for different periods (monthly and quarterly). Users can set a budget amount for each account. Automatically calculates expenses based on the provided amount. Shows a warning if the expense exceeds the budget amount. Added 3 types of charts: Gantt, Pivot, and Graph for better budget tracking.
- Loading branch information
Showing
14 changed files
with
512 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models, wizard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
'name': "Budget", | ||
'version': '1.0', | ||
'depends': ['base', 'account'], | ||
'author': "Hitesh Prajapati", | ||
'category': 'Budget/Budget', | ||
'license': 'LGPL-3', | ||
'application': True, | ||
'instalable': True, | ||
|
||
'data':[ | ||
'security/ir.model.access.csv', | ||
'views/account_analytic_line.xml', | ||
'wizard/budget_wizard.xml', | ||
'views/budget_budget.xml', | ||
'views/budget_line.xml', | ||
'views/budget_menu.xml', | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import budget_line, budget_budget, account_analytical_line |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from odoo import fields, models | ||
|
||
class AccountAnalyticalLine(models.Model): | ||
_inherit = 'account.analytic.line' | ||
|
||
budget_line_id = fields.Many2one('budget.line', ondelete='cascade') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
from odoo import fields, models, exceptions, api | ||
from markupsafe import Markup | ||
|
||
class BudgetCreate(models.Model): | ||
_name = 'budget.budget' | ||
_inherit = ['mail.thread', 'mail.activity.mixin'] | ||
_description = "Budget" | ||
|
||
name = fields.Char(required=True) | ||
user_id = fields.Many2one("res.users") | ||
period_start_date = fields.Date(required=True) | ||
period_end_date = fields.Date(index=True) | ||
con_color = fields.Integer() | ||
over_budget = fields.Selection( | ||
[ | ||
('warning', 'Warning'), | ||
('restriction', 'Restriction') | ||
], | ||
default="warning" | ||
) | ||
budget_line_ids = fields.One2many('budget.line', 'budget_id') | ||
state = fields.Selection( | ||
[ | ||
('draft', 'Draft'), | ||
('conformed', 'Conformed'), | ||
('revised', 'Revised'), | ||
('done', 'Done') | ||
], | ||
default='draft' | ||
) | ||
company_id = fields.Many2one('res.company') | ||
active = fields.Boolean(default=True) | ||
message_follower_ids = fields.Many2many( | ||
'res.partner', 'res_partner_followers_rel', 'res_id', 'partner_id', | ||
string='Followers', help="Partners following this record" | ||
) | ||
message_ids = fields.One2many( | ||
'mail.message', 'res_id', | ||
domain=[('model', '=', 'budget.budget'), ('model', '=', 'budget.line')], | ||
string="Messages" | ||
) | ||
is_warning = fields.Boolean(default=False, compute="_compute_warning_message") | ||
|
||
|
||
@api.depends('budget_line_ids.archived_amount', 'budget_line_ids.budget_amount') | ||
def _compute_warning_message(self): # Compute Method : Manage the warning message | ||
for record in self: | ||
is_warning = False | ||
for line in record.budget_line_ids: | ||
if line.archived_amount and line.budget_amount and line.archived_amount > line.budget_amount: | ||
is_warning = True | ||
break | ||
record.is_warning = is_warning | ||
|
||
|
||
def action_budget_line_form(self): # Action Button : for open Budget Lines | ||
action = ( | ||
self.env["ir.actions.act_window"] | ||
.with_context({"active_id": self.id}) | ||
._for_xml_id("budget_management.act_budget_lines_view") | ||
) | ||
action["display_name"] = self.name | ||
return action | ||
|
||
|
||
def action_budget_form_view(self): # Action Button : for open budget Form | ||
return { | ||
"type": "ir.actions.act_window", | ||
"res_model": "budget.budget", | ||
"view_mode": "form", | ||
"res_id": self.id | ||
} | ||
|
||
def action_to_draft(self): # Button Method : for draft | ||
self.state = 'draft' | ||
|
||
def action_to_conform(self): # Button Method : for conformed | ||
self.state = 'conformed' | ||
|
||
|
||
def action_to_revised(self): # Button Method : for revised | ||
if self.state == 'conformed': | ||
self.state = 'revised' | ||
|
||
orignal_record = self.browse(self.id) | ||
|
||
new_record = orignal_record.copy(default={ | ||
'state': 'draft', | ||
'period_start_date': self.period_start_date, | ||
'period_end_date': self.period_end_date, | ||
'name': f"Revised: {self.name}" | ||
}) | ||
|
||
for line in self.budget_line_ids: | ||
line.copy({ | ||
'budget_id': new_record.id, | ||
}) | ||
|
||
new_record.message_follower_ids = self.message_follower_ids | ||
message_body = f"Revised to: <a href='#id={new_record.id}&model=budget.budget'>{new_record.name}</a>" | ||
self.message_post(body=Markup(message_body)) | ||
|
||
message_body = f"Revised from: <a href='#id={self.id}&model=budget.budget' target='__blank'>{self.name}</a>" | ||
new_record.message_post(body=Markup(message_body)) | ||
|
||
self.active = False | ||
|
||
else: | ||
raise exceptions.ValidationError("The budget is not conformed yet...") | ||
|
||
def action_to_done(self): # Button Method : for done | ||
self.state = 'done' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from odoo import fields, models, api, exceptions | ||
|
||
class BudgetLine(models.Model): | ||
_name = 'budget.line' | ||
_description = "Budget Line Table" | ||
|
||
name = fields.Char() | ||
analytic_account_id = fields.Many2one('account.analytic.account') | ||
period_start_date = fields.Date("Start Date", related="budget_id.period_start_date", store=True) | ||
period_end_date = fields.Date("End Date", related="budget_id.period_end_date", store=True) | ||
budget_amount = fields.Float() | ||
archived_amount = fields.Float(compute="_calculate_amount") | ||
budget_id = fields.Many2one('budget.budget', ondelete="cascade") | ||
account_analytic_line_ids = fields.One2many('account.analytic.line', 'budget_line_id') | ||
company_id = fields.Many2one(related='budget_id.company_id', comodel_name='res.company') | ||
progress_percentage = fields.Float(string='Progress Percentage', compute='_compute_progress_percentage') | ||
|
||
|
||
@api.depends('archived_amount', 'budget_amount') | ||
def _compute_progress_percentage(self): # Compute Method : Percentage of the progressbar | ||
for record in self: | ||
if record.budget_amount > 0: | ||
record.progress_percentage = (record.archived_amount / record.budget_amount) * 100 | ||
else: | ||
record.progress_percentage = 0 | ||
|
||
|
||
@api.depends('analytic_account_id') | ||
def _calculate_amount(self): # Compute Method : Calculatation of Archive Amount | ||
for record in self: | ||
linked_lines = self.env["account.analytic.line"].search( | ||
[ | ||
("amount", "<", 0), | ||
("date", ">=", record.budget_id.period_start_date), | ||
("date", "<=", record.budget_id.period_end_date), | ||
("account_id", "=", record.analytic_account_id.id), | ||
] | ||
) | ||
achieved_sum = sum(linked_lines.mapped("amount")) | ||
record.write({ | ||
'archived_amount': abs(achieved_sum) | ||
}) | ||
|
||
def action_open_budget_entries(self): # Action Button : Open Form view of account.analytic.line | ||
return { | ||
"type": "ir.actions.act_window", | ||
"name": "Analytical Lines", | ||
"res_model": "account.analytic.line", | ||
"target": "current", | ||
"view_mode": "list", | ||
"res_id": self.id, | ||
"domain": [ | ||
("account_id", "=", self.analytic_account_id.id), | ||
("date", ">=", self.period_start_date), | ||
("date", "<=", self.period_end_date), | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink | ||
access_budget_budget,access_budget_budget,budget_management.model_budget_budget,base.group_user,1,1,1,1 | ||
access_budget_main,access_budget_main,budget_management.model_budget_main,base.group_user,1,1,1,1 | ||
access_budget_line,access_budget_line,budget_management.model_budget_line,base.group_user,1,1,1,1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<odoo> | ||
|
||
<record id="analytical_account_line_view" model="ir.actions.act_window"> | ||
<field name="name">acount.analytic.line</field> | ||
<field name="res_model">account.analytic.line</field> | ||
<field name="view_mode">list,form</field> | ||
</record> | ||
|
||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<odoo> | ||
|
||
<record id="budget_budget_action" model="ir.actions.act_window"> | ||
<field name="name">budget.budget</field> | ||
<field name="res_model">budget.budget</field> | ||
<field name="view_mode">kanban,form</field> | ||
<field name="help" type="html"> | ||
<p class="o_view_nocontent_smiling_face"> | ||
Define new Budget | ||
</p> | ||
<p> | ||
Use options specified in form to list Budget | ||
</p> | ||
</field> | ||
</record> | ||
|
||
<!-- Kanban View --> | ||
<record id="budget_budget_kanban_view" model="ir.ui.view"> | ||
<field name="name">budget.budget.kanban</field> | ||
<field name="model">budget.budget</field> | ||
<field name="arch" type="xml"> | ||
<kanban | ||
on_create="budget_management.budget_wizard_action" | ||
action="action_budget_line_form" type="object" | ||
quick_create_view="budget_management.budget_wizard_form_view" | ||
> | ||
<templates> | ||
<t t-name="menu"> | ||
<div class="container"> | ||
<div class="row"> | ||
<div name="card_menu_view" class="col-6"> | ||
<div role="menuitem"> | ||
<a name="action_budget_form_view" type="object">Configuration</a> | ||
<field name="con_color" widget="kanban_color_picker"/> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</t> | ||
<t t-name="card"> | ||
<div> | ||
<div> | ||
<text>Name : </text> | ||
<field name="name"></field> | ||
</div> | ||
<div> | ||
<field name="period_start_date" string="Planned Date" widget="daterange" options='{"end_date_field": "period_end_date", "always_range": "1"}' required="period_start_date or period_end_date" /> | ||
<field name="period_end_date" invisible="1" required="period_start_date"/> | ||
</div> | ||
</div> | ||
<footer> | ||
<div class="d-flex ms-auto align-items-center"> | ||
<field name="user_id" widget="many2one_avatar_user" class="me-1"/> | ||
</div> | ||
</footer> | ||
</t> | ||
</templates> | ||
</kanban> | ||
</field> | ||
</record> | ||
|
||
<!-- Form View --> | ||
<record id="budget_budget_form_view" model="ir.ui.view"> | ||
<field name="name">budget.budget.form</field> | ||
<field name="model">budget.budget</field> | ||
<field name="arch" type="xml"> | ||
<form> | ||
<div class="alert alert-warning p-2 mb-3" invisible="is_warning==False" role="alert"> | ||
<strong>Warning:</strong> achieved amount is greater than its budget amount in the budget line. | ||
</div> | ||
<header> | ||
<!-- <button name="action_to_draft" class="btn btn-primary" type="object" string="draft" invisible="state not in ['draft']" /> --> | ||
<button name="action_to_conform" class="btn btn-primary" type="object" string="conform" invisible="state in ['done', 'conformed']"/> | ||
<button name="action_to_revised" class="btn btn-primary" type="object" string="revised" invisible="state in ['draft', 'revised', 'done']"/> | ||
<button name="action_to_done" class="btn btn-primary" type="object" string="done" invisible="state in ['done','revised']"/> | ||
<field name="state" widget="statusbar" | ||
statusbar_visible="draft,conformed,revised,done" /> | ||
</header> | ||
<sheet> | ||
<group> | ||
<field name="name" readonly="state in ['revised']"/> | ||
</group> | ||
<group> | ||
<group> | ||
<field name="user_id" readonly="state in ['revised']"></field> | ||
<field name="company_id" readonly="state in ['revised']"></field> | ||
</group> | ||
<group> | ||
<field name="period_start_date" string="Planned Date" | ||
widget="daterange" | ||
options='{"end_date_field": "period_end_date", "always_range": "1"}' | ||
required="period_start_date or period_end_date" readonly="state in ['revised']"/> | ||
<field name="over_budget" readonly="state not in ['draft']"></field> | ||
</group> | ||
</group> | ||
<notebook> | ||
<page name="Budget Lines" string="Budget Lines" > | ||
<field name="budget_line_ids" > | ||
<list editable="bottom"> | ||
<field name="name" /> | ||
<field name="analytic_account_id" /> | ||
<field name="budget_amount" /> | ||
<field name="archived_amount" /> | ||
<field name="progress_percentage" widget="progressbar" /> | ||
<button type="object" name="action_open_budget_entries" string="View" icon="fa-arrow-circle-o-right" /> | ||
</list> | ||
</field> | ||
</page> | ||
</notebook> | ||
</sheet> | ||
<chatter/> | ||
</form> | ||
</field> | ||
</record> | ||
|
||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<odoo> | ||
|
||
<record id="act_budget_lines_view" model="ir.actions.act_window" > | ||
<field name="name">Budget Lines</field> | ||
<field name="res_model">budget.line</field> | ||
<field name="view_mode">list,form,pivot,graph,gantt</field> | ||
<field name="domain">[('budget_id', '=', active_id)]</field> | ||
</record> | ||
|
||
<!-- List View --> | ||
<record id="view_budget_line_tree" model="ir.ui.view" > | ||
<field name="name">budget.line.list</field> | ||
<field name="model">budget.line</field> | ||
<field name="arch" type="xml"> | ||
<list string="Budget Lines"> | ||
<field name="name" /> | ||
<field name="analytic_account_id" /> | ||
<field name="budget_amount" /> | ||
<field name="archived_amount" /> | ||
<field name="progress_percentage" widget="progressbar" /> | ||
<button type="object" name="action_open_budget_entries" string="View" icon="fa-arrow-circle-o-right" /> | ||
</list> | ||
</field> | ||
</record> | ||
|
||
<!-- Pivot View --> | ||
<record id="budget_line_view_pivot" model="ir.ui.view"> | ||
<field name="name">budget.line.view.pivot</field> | ||
<field name="model">budget.line</field> | ||
<field name="arch" type="xml"> | ||
<pivot string="Budget Pivot"> | ||
<field name="analytic_account_id" type="row"/> | ||
</pivot> | ||
</field> | ||
</record> | ||
|
||
|
||
|
||
<!-- Graph View --> | ||
<record id="budget_line_view_graph" model="ir.ui.view"> | ||
<field name="name">budget.line.view.graph</field> | ||
<field name="model">budget.line</field> | ||
<field name="arch" type="xml"> | ||
<graph string="Budget Graph" type="bar"> | ||
<field name="name" type="row"/> | ||
<field name="budget_amount" type="measure"/> | ||
</graph> | ||
</field> | ||
</record> | ||
|
||
<!-- Gantt View --> | ||
<record id="budget_budget_view_gantt" model="ir.ui.view"> | ||
<field name="name">budget.line.view.gantt</field> | ||
<field name="model">budget.line</field> | ||
<field name="arch" type="xml"> | ||
<gantt string="Budget Gantt" default_group_by="name" color="analytic_account_id" date_start="period_start_date" date_stop="period_end_date"> | ||
<field name="analytic_account_id" type="row"/> | ||
</gantt> | ||
</field> | ||
</record> | ||
|
||
</odoo> |
Oops, something went wrong.