diff --git a/budget_management/__manifest__.py b/budget_management/__manifest__.py
index f3dbd3571a..611d9557d2 100644
--- a/budget_management/__manifest__.py
+++ b/budget_management/__manifest__.py
@@ -1,7 +1,8 @@
{
"name": "Budget Management",
+ "category": "Budget",
"version": "1.0",
- "depends": ["base", "account"],
+ "depends": ["base", "account", "accountant"],
"author": "djip-odoo",
"description": """
Part of technical training
@@ -10,11 +11,9 @@
"data": [
"security/ir.model.access.csv",
"views/budget_line_views.xml",
- "wizards/actions_wizard.xml",
"views/actions_menu_and_button.xml",
"views/menu_views.xml",
"views/budget_views.xml",
- 'views/account_analytic_line_view.xml',
"wizards/wizard_add_budgets_view.xml",
],
"application": True,
diff --git a/budget_management/models/account_analytic_line.py b/budget_management/models/account_analytic_line.py
index c0dcc7c479..57fb99c146 100644
--- a/budget_management/models/account_analytic_line.py
+++ b/budget_management/models/account_analytic_line.py
@@ -4,42 +4,79 @@
class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"
-
- budget_line_id = fields.Many2one(comodel_name="budget.management.budget.lines")
-
+
@api.model_create_multi
def create(self, vals_list):
# print("* " * 100)
# print(vals_list)
# print("* " * 100)
+
for vals in vals_list:
- budget_line = self.env["budget.management.budget.lines"].browse(
- vals.get("budget_line_id")
- )
- budget = budget_line.budget_id
- if budget.on_over_budget == "restriction":
- if sum(budget_line.analytic_line_ids.mapped("amount"))+vals.get("amount") > budget_line.budget_amount:
- raise ValidationError(
- "You cannot create a budget line because it exceeds the allowed budget!"
- )
+ entry_date = vals.get("date")
+ if not entry_date:
+ raise ValidationError("The date field is required to determine the appropriate budget.")
+
+ budget_line = self.env["budget.management.budget.lines"].search([
+ ("budget_id.date_start", "<=", entry_date),
+ ("budget_id.date_end", ">=", entry_date),
+ ("budget_id.active", "=", True),
+ ("analytic_account_id", "=", vals.get("account_id"))
+ ], limit=1)
+
+ if budget_line:
+ budget = budget_line.budget_id
+
+ analytic_account_lines = self.env["account.analytic.line"].search_read(
+ [
+ ("account_id", "=", vals.get("account_id")),
+ ("date", ">=", budget.date_start),
+ ("date", "<=", budget.date_end),
+ ("amount", "<", 0),
+ ],
+ fields=["amount"],
+ )
+ # print(list(line.get("amount") for line in analytic_account_lines))
+ achieved = (sum(line.get("amount") for line in analytic_account_lines))
+ # print(budget.on_over_budget, abs(achieved + vals.get("amount")), budget_line.budget_amount)
+
+ if budget.on_over_budget == "restriction":
+ if abs(achieved + vals.get("amount")) > budget_line.budget_amount:
+ raise ValidationError(
+ "You cannot create a budget line because it exceeds the allowed budget!"
+ )
return super(AccountAnalyticLine, self).create(vals_list)
def write(self, vals):
- # print("* " * 100)
- # print(vals)
- # print("* " * 100)
- if "amount" in vals:
+ if "date" in vals or "amount" in vals or "account_id" in vals:
for record in self:
- old_amount = record.amount
- new_amount = vals.get("amount")
- print(old_amount,new_amount)
- total_amount = sum(record.budget_line_id.analytic_line_ids.mapped("amount")) + new_amount - old_amount
+ entry_date = vals.get("date", record.date)
+
+ budget_line = self.env["budget.management.budget.lines"].search([
+ ("budget_id.date_start", "<=", entry_date),
+ ("budget_id.date_end", ">=", entry_date),
+ ("budget_id.active", "=", True),
+ ("analytic_account_id", "=", vals.get("account_id", record.account_id.id))
+ ], limit=1)
+
+ if budget_line:
+ budget = budget_line.budget_id
+
+ analytic_account_lines = self.env["account.analytic.line"].search_read(
+ [
+ ("account_id", "=", vals.get("account_id", record.account_id.id)),
+ ("date", ">=", budget.date_start),
+ ("date", "<=", budget.date_end),
+ ("amount", "<", 0),
+ ],
+ fields=["amount"],
+ )
+ achieved = (sum(line.get("amount") for line in analytic_account_lines))
+
+ new_amount = vals.get("amount", record.amount)
+ if budget.on_over_budget == "restriction":
+ if abs(achieved - record.amount + new_amount) > budget_line.budget_amount:
+ raise ValidationError(
+ "You cannot modify the budget line because it exceeds the allowed budget!"
+ )
- budget_line = record.budget_line_id
- budget = budget_line.budget_id
- if budget.on_over_budget == "restriction" and total_amount > budget_line.budget_amount:
- raise ValidationError(
- "You cannot update this budget line because it exceeds the allowed budget!"
- )
-
return super(AccountAnalyticLine, self).write(vals)
diff --git a/budget_management/models/budget.py b/budget_management/models/budget.py
index 82de9c443d..cd8e8db07c 100644
--- a/budget_management/models/budget.py
+++ b/budget_management/models/budget.py
@@ -1,5 +1,6 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError, UserError
+from markupsafe import escape, Markup
class Budget(models.Model):
@@ -35,6 +36,7 @@ class Budget(models.Model):
comodel_name="res.users", # Assuming you want a link to Odoo users
tracking=True,
readonly=True,
+ string="Revised by"
)
date_start = fields.Date(string="Start Date", required=True)
date_end = fields.Date(string="Expiration Date", required=True, index=True)
@@ -46,7 +48,7 @@ class Budget(models.Model):
budget_line_ids = fields.One2many(
comodel_name="budget.management.budget.lines", inverse_name="budget_id"
)
- warnings = fields.Text(readonly=True)
+ warnings = fields.Text(compute="_check_over_budget")
currency_id = fields.Many2one(
comodel_name="res.currency",
string="Currency",
@@ -58,9 +60,15 @@ class Budget(models.Model):
def _compute_budget_name(self):
for record in self:
if record.date_start and record.date_end:
- start_date = record.date_start.strftime("%Y-%m")
- end_date = record.date_end.strftime("%Y-%m")
- record.name = f"Budget {start_date} to {end_date}"
+ start_date = record.date_start
+ end_date = record.date_end
+ if (
+ start_date.year == end_date.year
+ and start_date.month == end_date.month
+ ):
+ record.name = f"Budget - {start_date.strftime('%B %Y')}"
+ else:
+ record.name = f"Budget - {start_date.strftime('%d %B, %Y')} to {end_date.strftime('%d %B, %Y')}"
else:
record.name = "Unknown Budget"
@@ -78,13 +86,37 @@ def onclick_revise(self):
for record in self:
if record.state != "confirmed":
raise UserError("Only confirmed budgets can be revised.")
- if record.state in ["confirmed"]:
+
+ if record.state == "confirmed":
record.revision_id = self.env.user
record.state = "revised"
- # new_budget = record.copy({"revision_id": self.id, "state": "draft"})
- # record.message_post(
- # body=f'Revised into {new_budget.name}.'
- # )
+ record.active = False
+
+ new_budget = record.copy(
+ {"revision_id": None, "state": "draft", "active": True}
+ )
+
+ for budget_line in record.budget_line_ids:
+ self.env["budget.management.budget.lines"].create(
+ {
+ "budget_id": new_budget.id,
+ "name": budget_line.name,
+ "budget_amount": budget_line.budget_amount,
+ "achieved_amount": budget_line.achieved_amount,
+ "achieved_percentage": budget_line.achieved_percentage,
+ "analytic_account_id": budget_line.analytic_account_id.id,
+ "currency_id": budget_line.currency_id.id,
+ }
+ )
+
+ action = self.env.ref(
+ "budget_management.action_budget_management_menu_budget"
+ )
+ record.message_post(
+ body=Markup(
+ f'{new_budget.name}.'
+ )
+ )
def onclick_done(self):
for record in self:
@@ -102,7 +134,25 @@ def _check_period_overlap(self):
("company_id", "=", record.company_id.id),
]
)
- if overlapping_budgets:
+ overlapping_non_revised = False
+
+ for budget in overlapping_budgets:
+ if budget.state != "revised":
+ overlapping_non_revised = True
+ break
+
+ if overlapping_non_revised:
raise ValidationError(
- "Cannot create overlapping budgets for the same period and company."
+ "Cannot create overlapping budgets for the same period and company. If not displayed your selected period budget! please check archived budgets"
)
+
+ @api.depends("budget_line_ids.over_budget")
+ def _check_over_budget(self):
+ for record in self:
+ if (
+ record.on_over_budget == "warning"
+ and any(ob > 0 for ob in record.budget_line_ids.mapped("over_budget")) > 0
+ ):
+ record.warnings = "Achieved amount exceeds the budget!"
+ else:
+ record.warnings = False
diff --git a/budget_management/models/budget_line.py b/budget_management/models/budget_line.py
index 99b3356000..8047c360c8 100644
--- a/budget_management/models/budget_line.py
+++ b/budget_management/models/budget_line.py
@@ -10,7 +10,7 @@ class BudgetLine(models.Model):
budget_id = fields.Many2one(
comodel_name="budget.budget", string="Budget", required=True
)
- state = fields.Selection(related="budget_id.state")
+ state = fields.Selection(related="budget_id.state", readonly=True)
budget_amount = fields.Monetary(
string="Budget Amount",
default=0.0,
@@ -19,30 +19,21 @@ class BudgetLine(models.Model):
)
achieved_amount = fields.Monetary(
string="Achieved Amount",
- default=0.0,
compute="_compute_achieved_amount",
- store=True,
currency_field="currency_id",
)
achieved_percentage = fields.Float(
string="Achieved (%)",
compute="_compute_achieved_amount",
- store=True,
readonly=True,
help="Percentage of the budget achieved based on analytic lines.",
)
analytic_account_id = fields.Many2one(
"account.analytic.account", string="Analytic Account", required=True
)
- analytic_line_ids = fields.One2many(
- comodel_name="account.analytic.line",
- inverse_name="budget_line_id",
- string="Analytic Lines",
- )
over_budget = fields.Monetary(
string="Over Budget",
compute="_compute_achieved_amount",
- store=True,
help="The amount by which the budget line exceeds its allocated budget.",
currency_field="currency_id",
)
@@ -53,10 +44,27 @@ class BudgetLine(models.Model):
readonly=True,
)
- @api.depends("analytic_line_ids.amount")
+ @api.depends("budget_amount")
def _compute_achieved_amount(self):
for record in self:
- record.achieved_amount = sum(record.analytic_line_ids.mapped("amount"))
+ if not record.analytic_account_id:
+ record.achieved_amount = 0.0
+ record.achieved_percentage = 0.0
+ record.over_budget = 0.0
+ continue
+ analytic_account_lines = self.env["account.analytic.line"].search_read(
+ [
+ ("auto_account_id", "=", record.analytic_account_id.id),
+ ("date", ">=", record.budget_id.date_start),
+ ("date", "<=", record.budget_id.date_end),
+ ("amount", "<", 0),
+ ],
+ fields=["amount"],
+ )
+
+ achieved = sum(line.get("amount") for line in analytic_account_lines)
+
+ record.achieved_amount = abs(achieved)
record.achieved_percentage = (
(record.achieved_amount / record.budget_amount) * 100
if record.budget_amount > 0
@@ -64,13 +72,13 @@ def _compute_achieved_amount(self):
)
record.over_budget = max(0.0, record.achieved_amount - record.budget_amount)
- if (
- record.budget_id.on_over_budget == "warning"
- and record.achieved_amount > record.budget_amount
- ):
- record.budget_id.warnings = "Achived amount is more than your budget!"
- else:
- record.budget_id.warnings = False
+ # if (
+ # record.budget_id.on_over_budget == "warning"
+ # and any(record.over_budget for record in self) > 0
+ # ):
+ # record.budget_id.warnings = "Achieved amount exceeds the budget!"
+ # else:
+ # record.budget_id.warnings = False
@api.constrains("budget_amount")
def _check_budget_amount(self):
@@ -78,25 +86,48 @@ def _check_budget_amount(self):
if record.budget_amount < 0:
raise ValidationError("Budget amount cannot be negative.")
-
@api.model_create_multi
def create(self, vals_list):
active_budget = None
if self.env.context.get("active_id"):
- active_budget = self.env["budget.budget"].browse(self.env.context.get("active_id"))
- if active_budget.state != "draft":
- raise UserError("Budget lines can only be created when the state is 'draft'.")
+ active_budget = self.env["budget.budget"].browse(
+ self.env.context["active_id"]
+ )
else:
for vals in vals_list:
- budget_id = vals.get("budget_id")
- if budget_id:
- active_budget = self.env["budget.budget"].browse(budget_id)
+ if vals.get("budget_id"):
+ active_budget = self.env["budget.budget"].browse(vals["budget_id"])
break
-
- if not active_budget:
- raise UserError("No budget found in context or record.")
- if active_budget.state != "draft":
- raise UserError("Budget lines can only be created when the state is 'draft'.")
+ if not active_budget or active_budget.state != "draft":
+ raise UserError(
+ "Budget lines can only be created when the budget is in 'draft' state."
+ )
+
+ return super(BudgetLine, self).create(vals_list)
+
+ def open_analytic_lines_action(self):
+ if not self.budget_id:
+ raise UserError("No budget linked to this budget line.")
+
+ budget_start_date = self.budget_id.date_start
+ budget_end_date = self.budget_id.date_end
- return super(BudgetLine, self).create(vals_list)
\ No newline at end of file
+ return {
+ "type": "ir.actions.act_window",
+ "name": "Analytic Lines",
+ "res_model": "account.analytic.line",
+ "view_mode": "list",
+ "target": "current",
+ "context": {
+ "default_account_id": self.analytic_account_id.id,
+ "budget_start_date": budget_start_date,
+ "budget_end_date": budget_end_date,
+ },
+ "domain": [
+ ("account_id", "=", self.analytic_account_id.id),
+ ("date", ">=", budget_start_date),
+ ("date", "<=", budget_end_date),
+ ("amount", "<", 0),
+ ],
+ }
diff --git a/budget_management/views/account_analytic_line_view.xml b/budget_management/views/account_analytic_line_view.xml
deleted file mode 100644
index fe40f473ba..0000000000
--- a/budget_management/views/account_analytic_line_view.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
- account.analytic.line.list
- account.analytic.line
-
-
-
-
- bottom
-
-
-
-
-
-
-
diff --git a/budget_management/views/actions_menu_and_button.xml b/budget_management/views/actions_menu_and_button.xml
index 33d50110ac..60375fbc6b 100644
--- a/budget_management/views/actions_menu_and_button.xml
+++ b/budget_management/views/actions_menu_and_button.xml
@@ -2,7 +2,7 @@
-
-
- Analytic Lines
- account.analytic.line
- list
- {'default_budget_line_id': active_id}
- [('budget_line_id', '=', context.get('default_budget_line_id'))]
-
diff --git a/budget_management/views/budget_line_views.xml b/budget_management/views/budget_line_views.xml
index 793cfc3f16..65af754809 100644
--- a/budget_management/views/budget_line_views.xml
+++ b/budget_management/views/budget_line_views.xml
@@ -6,11 +6,10 @@
-
@@ -18,18 +17,6 @@
-
- budget.management.budget.lines.graph
- budget.management.budget.lines
-
-
-
-
-
-
-
-
-
budget.management.budget.lines.pivot
@@ -57,4 +44,22 @@
-->
-
\ No newline at end of file
+
+
+
+
diff --git a/budget_management/views/budget_views.xml b/budget_management/views/budget_views.xml
index ea89e50a79..06573ae9fc 100644
--- a/budget_management/views/budget_views.xml
+++ b/budget_management/views/budget_views.xml
@@ -79,7 +79,7 @@
@@ -130,6 +130,8 @@
create="state in ['draft']"
>
+
-
@@ -150,4 +152,4 @@
-
\ No newline at end of file
+
diff --git a/budget_management/wizards/actions_wizard.xml b/budget_management/wizards/actions_wizard.xml
deleted file mode 100644
index 56d2f04b3b..0000000000
--- a/budget_management/wizards/actions_wizard.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/budget_management/wizards/add_budget_wizard.py b/budget_management/wizards/add_budget_wizard.py
index a8a6e8bcfc..b35f2eab18 100644
--- a/budget_management/wizards/add_budget_wizard.py
+++ b/budget_management/wizards/add_budget_wizard.py
@@ -1,10 +1,11 @@
-from odoo import models, fields
+from odoo import models, fields, Command
from datetime import timedelta
from dateutil.relativedelta import relativedelta
class AddBudgetWizard(models.TransientModel):
_name = "add.budget.wizard"
+ _description = " "
date_start = fields.Date(required=True, string="Start Date")
date_end = fields.Date(
@@ -20,16 +21,14 @@ class AddBudgetWizard(models.TransientModel):
)
analytic_account_ids = fields.Many2many(
- "account.analytic.account", string="Analytic Account"
+ "account.analytic.account", string="Analytic Account", required=True
)
- # analytic_account = fields.Many2many(comodel_name="account.analytic.account")
def action_add_budget(self):
"""Creates budget records based on the selected periods."""
if self.date_start >= self.date_end:
raise ValueError("Start Date must be before Expiration Date.")
- # Calculate the periods and create budgets
current_date = self.date_start
budget_entries = []
@@ -43,16 +42,28 @@ def action_add_budget(self):
budget_entries.append(
{
- "name": f"Budget {current_date.strftime('%Y-%m')} to {end_date.strftime('%Y-%m')}",
+ "name": (
+ f"Budget - {current_date.strftime('%B-%Y')}"
+ if self.periods == "monthly"
+ else f"Budget - {current_date.strftime('%d %B, %Y')} to {end_date.strftime('%d %B, %Y')}"
+ ),
"date_start": current_date,
"date_end": end_date,
- # "budget_line_ids": self.analytic_account_ids,
+ "budget_line_ids": [
+ Command.create(
+ {"name": "budget line", "analytic_account_id": account.id}
+ )
+ for account in self.analytic_account_ids
+ ],
}
)
current_date = next_date
- print(budget_entries)
+
self.env["budget.budget"].create(budget_entries)
- # Return a window close action
- return {"type": "ir.actions.act_window_close"}
+ return {
+ "type": "ir.actions.client",
+ "tag": "reload",
+ "message": "Budgets have been successfully created.",
+ }