Skip to content

Commit

Permalink
[ADD] budget_management: add budget management module
Browse files Browse the repository at this point in the history
Add budget_management module to manage the budget in the analytic account. Users
can create budgets and manage budget lines inside it. All the budget lines are
related to analytic account and analytic account lines.

The target budget for each account can be set and progress can be seen in the
budget line views. Also, users can revise the budget and a new budget can be
seen in chatter.
  • Loading branch information
nipl-odoo committed Jan 29, 2025
1 parent 8041e04 commit 33db4df
Show file tree
Hide file tree
Showing 16 changed files with 585 additions and 0 deletions.
2 changes: 2 additions & 0 deletions budget_management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
19 changes: 19 additions & 0 deletions budget_management/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "Budget Management",
"summary": "Module for managing budgets",
"description": "Module for managing budgets",
"category": "Account/ Budget Management",
"version": "1.0",
"application": True,
"installable": True,
"depends": ["base", "analytic"],
"data": [
"security/ir.model.access.csv",
"views/analytic_line_views.xml",
"views/budget_budget_line_views.xml",
"views/budget_budget_views.xml",
"views/budget_budget_menu_views.xml",
"wizard/budget_wizard_views.xml",
],
"license": "AGPL-3",
}
3 changes: 3 additions & 0 deletions budget_management/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import budget_line
from . import budget_budget
from . import analytic_line
9 changes: 9 additions & 0 deletions budget_management/models/analytic_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from odoo import fields, models


class AnalyticLine(models.Model):
_inherit = "account.analytic.line"

budget_line_id = fields.Many2one(
"budget.budget.line", "Budget Line", ondelete="cascade"
)
140 changes: 140 additions & 0 deletions budget_management/models/budget_budget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from markupsafe import Markup

from odoo import api, fields, models
from odoo.exceptions import ValidationError


class BudgetBudget(models.Model):
_name = "budget.budget"
_description = "Budget"
_inherit = ["mail.thread", "mail.activity.mixin"]

name = fields.Char(compute="_compute_name")
date_from = fields.Date(required=True)
date_to = fields.Date(required=True)
color = fields.Integer(string="Color Index")
active = fields.Boolean(default=True)
show_warning = fields.Boolean(default=False, compute="_compute_show_warning")
state = fields.Selection(
[
("draft", "Draft"),
("confirmed", "Confirmed"),
("revised", "Revised"),
("done", "Done"),
],
default="draft",
)
action_over_budget = fields.Selection(
[("warning", "Warning"), ("restrict", "Restrict")],
default="warning",
)
user_id = fields.Many2one("res.users", string="Responsible")
company_id = fields.Many2one("res.company", string="Company")
parent_id = fields.Many2one(
string="Revision Of",
comodel_name="budget.budget",
ondelete="cascade",
)
budget_line_ids = fields.One2many(
"budget.budget.line", "budget_id", string="Budget lines"
)
children_ids = fields.One2many(
string="Revisions",
comodel_name="budget.budget",
inverse_name="parent_id",
)

@api.constrains("date_from", "date_to")
def _check_dates(self):
for budget in self:
if (
budget.date_from
and budget.date_to
and budget.date_from > budget.date_to
):
raise ValidationError("The start date must be before the end date.")

# period must be unique for all active budget
@api.constrains("date_from", "date_to")
def _check_date(self):
for budget in self:
if budget.date_from and budget.date_to:
existing_budgets = self.search(
[
("date_from", "=", budget.date_to),
("date_to", "=", budget.date_from),
("active", "=", True),
("id", "!=", budget.id),
]
)
if existing_budgets:
raise ValidationError(
"Budget period must be unique for all active budgets."
)

@api.depends("budget_line_ids.achieved_amount")
def _compute_show_warning(self):
for record in self:
if record.action_over_budget == "warning" and any(
ob.achieved_amount > ob.amount for ob in record.budget_line_ids
):
record.show_warning = True
else:
record.show_warning = False

@api.depends("name", "date_from", "date_to")
def _compute_name(self):
for budget in self:
if budget.date_from and budget.date_to:
budget.name = "Budget: %s - %s " % (
budget.date_from.__format__("%d/%m/%Y"),
budget.date_to.__format__("%d/%m/%Y"),
)
else:
budget.name = "Budget: (from) - (to)"

def action_budget_confirm(self):
self.parent_id.filtered(lambda b: b.state == "confirmed").state = "revised"
for budget in self:
budget.state = "revised" if budget.children_ids else "confirmed"

def action_budget_draft(self):
self.state = "draft"

def action_budget_done(self):
self.state = "done"

def create_revised_budget(self):
revised = self.browse()
for budget in self:
budget.state = "revised"
budget.active = False
revised_budget = budget.copy(
default={
"name": budget.name,
"parent_id": budget.id,
"active": True,
}
)

revised += revised_budget
budget.message_post(
body=Markup(
"%s: <a href='#' data-oe-model='budget.budget' data-oe-id='%s'>%s</a>"
)
% (
"New revision",
revised_budget.id,
revised_budget.name,
)
)
return revised._get_records_action()

def action_open_budget_lines(self):
return {
"name": "Budget Lines",
"view_mode": "list,graph,pivot,gantt",
"res_model": "budget.budget.line",
"type": "ir.actions.act_window",
"domain": [("budget_id", "=", self.id)],
}
68 changes: 68 additions & 0 deletions budget_management/models/budget_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError


class BudgetLine(models.Model):
_name = "budget.budget.line"
_description = "Budget Budget Line"

name = fields.Char("Description", default="Budget Line")
amount = fields.Float("Budget Amount", required=True)
achieved_amount = fields.Float(
"Achieved Amount", compute="_compute_achieved_amount", store=True
)
achieved_percent = fields.Float("Achieved %", compute="_compute_achieved_percent")
date_from = fields.Date(related="budget_id.date_from")
date_to = fields.Date(related="budget_id.date_to")
sequence = fields.Integer("Sequence", default=1)
budget_id = fields.Many2one("budget.budget", required=True)
account_id = fields.Many2one("account.analytic.account", "Account")
user_id = fields.Many2one(related="budget_id.user_id")
analytic_line_ids = fields.One2many(
"account.analytic.line", "budget_line_id", string="Analytic Lines"
)

@api.constrains("achieved_amount", "amount")
def _check_restriction_on_creation(self):
for record in self:
if record.budget_id.action_over_budget == "restrict":
if record.achieved_amount > record.amount:
raise ValidationError(
"Achieved amount cannot exceed amount when 'Restriction' is selected."
)

@api.depends("amount", "achieved_amount")
def _compute_achieved_percent(self):
for line in self:
if line.account_id and line.amount:
line.achieved_percent = (line.achieved_amount) / line.amount * 100
else:
line.achieved_percent = 0

@api.depends(
"analytic_line_ids.amount",
)
def _compute_achieved_amount(self):
for record in self:
total_achieved = abs(
sum(
record.analytic_line_ids.filtered(lambda l: l.amount < 0).mapped(
"amount"
)
)
)
record.achieved_amount = total_achieved

def action_open_analytic_lines(self):
return {
"type": "ir.actions.act_window",
"name": "Analytic Lines",
"res_model": "account.analytic.line",
"view_mode": "list",
"target": "new",
"context": {
"default_account_id": self.account_id.id,
"default_date": self.budget_id.date_from,
"default_budget_line_id": self.id,
},
}
4 changes: 4 additions & 0 deletions budget_management/security/ir.model.access.csv
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
budget_management.access_budget_budget_line,access_budget_budget_line,budget_management.model_budget_budget_line,base.group_user,1,1,1,1
budget_management.access_budget_budget,access_budget_budget,budget_management.model_budget_budget,base.group_user,1,1,1,1
budget_management.access_budget_wizard,access_budget_wizard,budget_management.model_budget_wizard,base.group_user,1,1,1,1
7 changes: 7 additions & 0 deletions budget_management/static/.comments/logo.png.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<comment version="3.0">
<caption/>
<note>Piggy bank and dollar coin icon thin line for web and mobile, modern minimalistic flat design. Vector icon with dark grey outline and offset colour on light grey background.</note>
<place/>
<categories/>
</comment>
Binary file added budget_management/static/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions budget_management/views/analytic_line_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="view_account_analytic_line_tree_inherit_module_name" model="ir.ui.view">
<field name="name">account.analytic.line.view.list.inherit</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="analytic.view_account_analytic_line_tree"/>
<field name="arch" type="xml">
<xpath expr="//list" position="inside">
<field name="budget_line_id" optional="hide"></field>
</xpath>

<xpath expr="//list" position="attributes">
<attribute name="editable">bottom</attribute>
</xpath>
</field>
</record>

</odoo>
66 changes: 66 additions & 0 deletions budget_management/views/budget_budget_line_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="budget_budget_line_action" model="ir.actions.act_window">
<field name="name">Budget Lines</field>
<field name="res_model">budget.budget.line</field>
<field name="view_mode">list,graph,gantt,pivot</field>
<field name="context">[('budget_id','=',active_id)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Define new budget line
</p>
<p>
Use options specified in form to list budget line
</p>
</field>
</record>

<record id="budget_budget_line_view_list" model="ir.ui.view">
<field name="name">budget.budget.line.view.list</field>
<field name="model">budget.budget.line</field>
<field name="arch" type="xml">
<list string="Budget Line">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="account_id" optional="show"/>
<field name="amount" />
<field name="achieved_amount" decoration-danger="(achieved_amount > amount)" />
</list>
</field>
</record>

<record id="budget_budget_line_view_graph" model="ir.ui.view">
<field name="name">budget.line.graph</field>
<field name="model">budget.budget.line</field>
<field name="arch" type="xml">
<graph string="Budget Line" type="bar" stacked="true">
<field name="name" type="row"/>
<field name="achieved_amount" />
<field name="amount" type="measure"/>
</graph>
</field>
</record>

<record id="budget_budget_line_view_gantt" model="ir.ui.view">
<field name="name">budget.line.gantt.view</field>
<field name="model">budget.budget.line</field>
<field name="arch" type="xml">
<gantt default_group_by="name" progress="achieved_percent" date_start="date_from" date_stop="date_to" string="Budget Lines" color="id">
<field name="name"/>
</gantt>
</field>
</record>


<record id="budget_budget_line_view_pivot" model="ir.ui.view">
<field name="name">budget.line.pivot</field>
<field name="model">budget.budget.line</field>
<field name="arch" type="xml">
<pivot string="Budget Lines">
<field name="name" type="row"/>
</pivot>
</field>
</record>

</odoo>
7 changes: 7 additions & 0 deletions budget_management/views/budget_budget_menu_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<menuitem id="budget_management_menu" name="Budget Management" sequence="10" web_icon="budget_management,static/logo.png"/>
<menuitem id="budget_budget_menu" name="Budgets" parent="budget_management_menu" action="budget_management.budget_budget_action" sequence="10"/>

</odoo>
Loading

0 comments on commit 33db4df

Please sign in to comment.