[NEW] Add budget model funcitonnalities
User is able to generate budget model and model lines User can reset a budget and generate one using an existing model
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
from . import wizard
|
||||||
|
@@ -73,6 +73,7 @@ This module is maintained by ELABORE.
|
|||||||
# always loaded
|
# always loaded
|
||||||
"data": [
|
"data": [
|
||||||
"security/ir.model.access.csv",
|
"security/ir.model.access.csv",
|
||||||
|
"wizard/budget_forecast_model_choice.xml",
|
||||||
"views/account_analytic_account.xml",
|
"views/account_analytic_account.xml",
|
||||||
"views/account_analytic_account_categories.xml",
|
"views/account_analytic_account_categories.xml",
|
||||||
"views/account_invoice.xml",
|
"views/account_invoice.xml",
|
||||||
@@ -80,6 +81,7 @@ This module is maintained by ELABORE.
|
|||||||
"views/budget_forecast.xml",
|
"views/budget_forecast.xml",
|
||||||
"views/budget_coefficient.xml",
|
"views/budget_coefficient.xml",
|
||||||
"views/budget_coefficient_model.xml",
|
"views/budget_coefficient_model.xml",
|
||||||
|
"views/budget_forecast_model.xml",
|
||||||
"views/product_template_form.xml",
|
"views/product_template_form.xml",
|
||||||
"views/project_views.xml",
|
"views/project_views.xml",
|
||||||
"views/hr_employee.xml",
|
"views/hr_employee.xml",
|
||||||
|
@@ -4,6 +4,7 @@ from . import account_analytic_account
|
|||||||
from . import account_analytic_line
|
from . import account_analytic_line
|
||||||
from . import account_move
|
from . import account_move
|
||||||
from . import budget_forecast
|
from . import budget_forecast
|
||||||
|
from . import budget_forecast_model
|
||||||
from . import sale_order
|
from . import sale_order
|
||||||
from . import product
|
from . import product
|
||||||
from . import project
|
from . import project
|
||||||
|
@@ -16,7 +16,6 @@ class BudgetForecast(models.Model):
|
|||||||
"account.analytic.account",
|
"account.analytic.account",
|
||||||
"Analytic Account",
|
"Analytic Account",
|
||||||
required=True,
|
required=True,
|
||||||
ondelete="restrict",
|
|
||||||
index=True,
|
index=True,
|
||||||
copy=True,
|
copy=True,
|
||||||
)
|
)
|
||||||
@@ -138,25 +137,24 @@ class BudgetForecast(models.Model):
|
|||||||
parent_ids = self.mapped("parent_id")
|
parent_ids = self.mapped("parent_id")
|
||||||
if not child_unlink:
|
if not child_unlink:
|
||||||
for record in self:
|
for record in self:
|
||||||
if not record.is_summary and (
|
if record.display_type in [
|
||||||
record.display_type
|
|
||||||
in [
|
|
||||||
"line_section",
|
"line_section",
|
||||||
"line_subsection",
|
"line_subsection",
|
||||||
]
|
]:
|
||||||
):
|
if record.is_summary:
|
||||||
# find similar section/sub_section lines
|
domain = [("summary_id", "=", record.id)]
|
||||||
lines = record.env["budget.forecast"].search(
|
else:
|
||||||
[
|
domain = [
|
||||||
|
("id", "!=", record.id),
|
||||||
"|",
|
"|",
|
||||||
("summary_id", "=", record.summary_id.id),
|
("summary_id", "=", record.summary_id.id),
|
||||||
("id", "=", record.summary_id.id),
|
("id", "=", record.summary_id.id),
|
||||||
]
|
]
|
||||||
)
|
# find similar section/sub_section lines
|
||||||
|
lines = record.env["budget.forecast"].search(domain)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line.unlink(True)
|
line.unlink(True)
|
||||||
res = super(BudgetForecast, self).unlink()
|
res = super(BudgetForecast, self).unlink()
|
||||||
parent_ids.exists()._calc_plan()
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.onchange("product_id")
|
@api.onchange("product_id")
|
||||||
|
37
account_budget_forecast/models/budget_forecast_model.py
Normal file
37
account_budget_forecast/models/budget_forecast_model.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
class BudgetForecastModel(models.Model):
|
||||||
|
_name = "budget.forecast.model"
|
||||||
|
_description = _name
|
||||||
|
|
||||||
|
name = fields.Char("Name", copy=False)
|
||||||
|
|
||||||
|
|
||||||
|
class BudgetForecastModelLine(models.Model):
|
||||||
|
_name = "budget.forecast.model.line"
|
||||||
|
_description = _name
|
||||||
|
|
||||||
|
budget_model = fields.Many2one("budget.forecast.model", copy=True)
|
||||||
|
|
||||||
|
name = fields.Char("Name", copy=True)
|
||||||
|
|
||||||
|
display_type = fields.Selection(
|
||||||
|
[
|
||||||
|
("line_section", "Section"),
|
||||||
|
("line_subsection", "Sub-Section"),
|
||||||
|
],
|
||||||
|
copy=True,
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
"product.product", copy=True, domain="[('budget_level', '=', display_type)]"
|
||||||
|
)
|
||||||
|
parent_id = fields.Many2one(
|
||||||
|
"budget.forecast.model.line",
|
||||||
|
store=True,
|
||||||
|
copy=False,
|
||||||
|
)
|
||||||
|
child_ids = fields.One2many("budget.forecast.model.line", "parent_id", copy=False)
|
@@ -2,3 +2,6 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|||||||
access_budget_forecast_project_user,access_budget_forecast_project_user,model_budget_forecast,project.group_project_user,1,1,1,1
|
access_budget_forecast_project_user,access_budget_forecast_project_user,model_budget_forecast,project.group_project_user,1,1,1,1
|
||||||
access_budget_coefficient,budget_coefficient,model_budget_coefficient,base.group_user,1,1,1,1
|
access_budget_coefficient,budget_coefficient,model_budget_coefficient,base.group_user,1,1,1,1
|
||||||
access_budget_coefficient_model,budget_coefficient_model,model_budget_coefficient_model,base.group_user,1,1,1,1
|
access_budget_coefficient_model,budget_coefficient_model,model_budget_coefficient_model,base.group_user,1,1,1,1
|
||||||
|
access_budget_forecast_model,budget_forecast_model,model_budget_forecast_model,base.group_user,1,1,1,1
|
||||||
|
access_budget_forecast_model_line,budget_forecast_model_line,model_budget_forecast_model_line,base.group_user,1,1,1,1
|
||||||
|
access_budget_forecast_model_choice,budget_forecast_model_choice,model_budget_forecast_model_choice,base.group_user,1,1,1,1
|
|
@@ -17,6 +17,7 @@
|
|||||||
<button type="object" class="oe_stat_button" icon="fa-eye" name="displayActualAmounts" string="Hide Actual amounts" attrs="{'invisible' : [('display_actual_amounts', '=', False)]}">
|
<button type="object" class="oe_stat_button" icon="fa-eye" name="displayActualAmounts" string="Hide Actual amounts" attrs="{'invisible' : [('display_actual_amounts', '=', False)]}">
|
||||||
<field name="display_actual_amounts" widget="boolean_toggle" />
|
<field name="display_actual_amounts" widget="boolean_toggle" />
|
||||||
</button>
|
</button>
|
||||||
|
<button type="action" class="oe_stat_button" icon="fa-refresh" name="%(action_budget_forecast_model_choice)d" string="Reset Budget" />
|
||||||
</header>
|
</header>
|
||||||
<sheet string="Project Budget">
|
<sheet string="Project Budget">
|
||||||
<group>
|
<group>
|
||||||
|
37
account_budget_forecast/views/budget_forecast_model.xml
Normal file
37
account_budget_forecast/views/budget_forecast_model.xml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_budget_forecast_model_tree" model="ir.ui.view">
|
||||||
|
<field name="name">budget.forecast.model.tree</field>
|
||||||
|
<field name="model">budget.forecast.model</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree editable="top">
|
||||||
|
<field name="name" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_budget_forecast_model_line_tree" model="ir.ui.view">
|
||||||
|
<field name="name">budget.forecast.model.line.tree</field>
|
||||||
|
<field name="model">budget.forecast.model.line</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree editable="top">
|
||||||
|
<field name="budget_model" />
|
||||||
|
<field name="display_type" />
|
||||||
|
<field name="product_id" />
|
||||||
|
<field name="name" />
|
||||||
|
<field name="parent_id" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="act_budget_forecast_models" model="ir.actions.act_window">
|
||||||
|
<field name="name">Budget Forecast Model</field>
|
||||||
|
<field name="res_model">budget.forecast.model</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
</record>
|
||||||
|
<record id="act_budget_forecast_model_lines" model="ir.actions.act_window">
|
||||||
|
<field name="name">Budget Forecast Model Lines</field>
|
||||||
|
<field name="res_model">budget.forecast.model.line</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
@@ -5,4 +5,8 @@
|
|||||||
|
|
||||||
<menuitem id="menu_budget_forecast_coeff_models" action="act_budget_forecast_coeff_models" parent="project.menu_project_config" sequence="20" />
|
<menuitem id="menu_budget_forecast_coeff_models" action="act_budget_forecast_coeff_models" parent="project.menu_project_config" sequence="20" />
|
||||||
|
|
||||||
|
<menuitem id="menu_budget_forecast_models" action="act_budget_forecast_models" parent="project.menu_project_config" sequence="20" />
|
||||||
|
|
||||||
|
<menuitem id="menu_budget_forecast_model_lines" action="act_budget_forecast_model_lines" parent="project.menu_project_config" sequence="20" />
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
3
account_budget_forecast/wizard/__init__.py
Normal file
3
account_budget_forecast/wizard/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import budget_forecast_model_choice
|
@@ -0,0 +1,71 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from bdb import set_trace
|
||||||
|
from odoo import models, fields, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class BudgetForecastModelChoice(models.Model):
|
||||||
|
_name = "budget.forecast.model.choice"
|
||||||
|
_description = _name
|
||||||
|
|
||||||
|
budget_forecast_model = fields.Many2one("budget.forecast.model")
|
||||||
|
|
||||||
|
def create_budget_forecast_lines(self):
|
||||||
|
analytic_account = self.env["account.analytic.account"].browse(
|
||||||
|
self._context.get("active_ids")
|
||||||
|
)
|
||||||
|
# Budget line deletion must be done in a precise order to avoid trying to unlink already deleted lines.
|
||||||
|
# 1. Delete summary lines to remove all the linked section and subsection lines
|
||||||
|
for line in analytic_account.budget_forecast_ids.filtered(
|
||||||
|
lambda r: r.is_summary
|
||||||
|
):
|
||||||
|
_logger.debug("SUMMARY BUDGET LINE = %s", line.name)
|
||||||
|
line.unlink()
|
||||||
|
|
||||||
|
# 2. Delete article and note lines
|
||||||
|
for line in analytic_account.budget_forecast_ids.filtered(
|
||||||
|
lambda r: r.display_type in ["line_article", "line_note"]
|
||||||
|
):
|
||||||
|
_logger.debug("OTHER BUDGET LINE = %s", line.name)
|
||||||
|
line.unlink()
|
||||||
|
|
||||||
|
if self.budget_forecast_model:
|
||||||
|
# Create new summary lines
|
||||||
|
section_model_lines = self.env["budget.forecast.model.line"].search(
|
||||||
|
[
|
||||||
|
("budget_model", "=", self.budget_forecast_model.id),
|
||||||
|
("display_type", "=", "line_section"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
sequence = 0
|
||||||
|
for section in section_model_lines:
|
||||||
|
vals = {
|
||||||
|
"analytic_id": self.env["account.analytic.account"]
|
||||||
|
.browse(self._context.get("active_ids"))
|
||||||
|
.id,
|
||||||
|
"display_type": "line_section",
|
||||||
|
"product_id": section.product_id.id,
|
||||||
|
"description": section.name,
|
||||||
|
"is_summary": True,
|
||||||
|
"sequence": sequence,
|
||||||
|
}
|
||||||
|
section_line = self.env["budget.forecast"].create(vals)
|
||||||
|
sequence += 1
|
||||||
|
if section.child_ids:
|
||||||
|
for sub_section in section.child_ids:
|
||||||
|
vals = {
|
||||||
|
"analytic_id": self.env["account.analytic.account"]
|
||||||
|
.browse(self._context.get("active_ids"))
|
||||||
|
.id,
|
||||||
|
"display_type": "line_subsection",
|
||||||
|
"product_id": sub_section.product_id.id,
|
||||||
|
"description": sub_section.name,
|
||||||
|
"is_summary": True,
|
||||||
|
"parent_id": section_line.id,
|
||||||
|
"sequence": sequence,
|
||||||
|
}
|
||||||
|
self.env["budget.forecast"].create(vals)
|
||||||
|
sequence += 1
|
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="budget_forecast_model_wizard" model="ir.ui.view">
|
||||||
|
<field name="name">budget.forecast.model.wizard</field>
|
||||||
|
<field name="model">budget.forecast.model.choice</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Reset : choose the Budget Forecast Model">
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<span class="fa fa-info-circle" />
|
||||||
|
Caution: all current budget lines will be deleted!
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<field name="budget_forecast_model" widget="selection" placeholder="Empty budget" />
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button string="Select" name="create_budget_forecast_lines" type="object" class="btn-primary" />
|
||||||
|
<button string="Cancel" class="btn-secondary" special="cancel" />
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_budget_forecast_model_choice" model="ir.actions.act_window">
|
||||||
|
<field name="name">Reset Budget</field>
|
||||||
|
<field name="res_model">budget.forecast.model.choice</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="budget_forecast_model_wizard" />
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
Reference in New Issue
Block a user