Files
enterprise-addons/project_account_budget/models/project_project.py
T

175 lines
8.1 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
import json
from odoo import fields, models, _
from odoo.fields import Domain
class ProjectProject(models.Model):
_inherit = "project.project"
total_budget_amount = fields.Monetary('Total planned amount', compute='_compute_budget', default=0, export_string_translation=False)
total_budget_progress = fields.Float("Budget Spent", compute="_compute_budget", export_string_translation=False)
def _get_budget_analytic_account_domain(self):
return Domain.OR([
[(account.plan_id._column_name(), '=', account.id)] for account in self.account_id
])
def _compute_budget(self):
budget_items = self.env['budget.line'].sudo()._read_group(
Domain.AND([
self._get_budget_analytic_account_domain(),
[('budget_analytic_id.state', 'in', ['confirmed', 'done'])],
]),
groupby=['account_id', 'budget_analytic_id'],
aggregates=['budget_amount:sum', 'achieved_amount:sum'],
)
budget_items_by_account_analytic = defaultdict(lambda: {
'budget_amount': 0,
'budget_amount_for_progress': 0,
'achieved_amount_for_progress': 0,
})
for analytic_account, budget_analytic_id, budget_amount_sum, achieved_amount_sum in budget_items:
type_factor = -1 if budget_analytic_id.budget_type == 'expense' else 1
budget_items_by_account_analytic[analytic_account.id]["budget_amount"] += budget_amount_sum
budget_items_by_account_analytic[analytic_account.id]["budget_amount_for_progress"] += budget_amount_sum * type_factor
budget_items_by_account_analytic[analytic_account.id]["achieved_amount_for_progress"] += achieved_amount_sum * type_factor
for project in self:
total_budget_amount = budget_items_by_account_analytic[project.account_id.id]['budget_amount']
total_budget_amount_fp = budget_items_by_account_analytic[project.account_id.id]['budget_amount_for_progress']
total_achieved_amount_fp = budget_items_by_account_analytic[project.account_id.id]['achieved_amount_for_progress']
project.total_budget_progress = total_budget_amount_fp and (total_achieved_amount_fp - total_budget_amount_fp) / abs(total_budget_amount_fp)
project.total_budget_amount = total_budget_amount
def action_view_budget_lines(self, domain=None):
self.ensure_one()
budget_lines = self.env['budget.line'].search(Domain.AND([
[(self.account_id.plan_id._column_name(), '=', self.account_id.id), ('budget_analytic_id.state', 'in', ['confirmed', 'done'])],
domain or [],
]))
return {
"type": "ir.actions.act_window",
"res_model": "budget.analytic",
"res_id": budget_lines.budget_analytic_id.id,
'context': {'create': False},
"name": _("Budget Items"),
'view_mode': 'form',
}
# ----------------------------
# Project Updates
# ----------------------------
def get_panel_data(self):
panel_data = super().get_panel_data()
panel_data['account_id'] = self.account_id.id
panel_data['budget_items'] = self._get_budget_items()
panel_data['show_budget_items'] = bool(self.account_id)
return panel_data
def get_budget_items(self):
self.ensure_one()
if self.account_id and self.env.user.has_group('project.group_project_user'):
return self._get_budget_items(True)
return {}
def _get_budget_items_domain(self):
self.ensure_one()
return [
(self.account_id.plan_id._column_name(), '=', self.account_id.id),
('budget_analytic_id', '!=', False),
('budget_analytic_id.state', 'in', ['confirmed', 'done']),
]
def _get_budget_items(self, with_action=True):
self.ensure_one()
if not self.account_id:
return
budget_items_domain = self._get_budget_items_domain()
budget_lines = self.env['budget.line'].sudo()._read_group(
budget_items_domain,
['budget_analytic_id', 'company_id'],
['budget_amount:sum', 'achieved_amount:sum', 'id:array_agg'],
)
has_company_access = False
for line in budget_lines:
if line[1].id in self.env.context.get('allowed_company_ids', []):
has_company_access = True
break
total_allocated = total_spent = 0.0
total_allocated_for_progress = total_spent_for_progress = 0.0
can_see_budget_items = with_action and has_company_access and (
self.env.user.has_group('account.group_account_readonly')
or self.env.user.has_group('analytic.group_analytic_accounting')
)
budget_data_per_budget = defaultdict(
lambda: {
'allocated': 0,
'spent': 0,
'budget_type': False,
**({
'ids': [],
'budgets': [],
} if can_see_budget_items else {})
}
)
for budget_analytic, _dummy, allocated, spent, ids in budget_lines:
budget_data = budget_data_per_budget[budget_analytic]
budget_data['id'] = budget_analytic.id
budget_data['name'] = budget_analytic.display_name
budget_data['allocated'] += allocated
budget_data['spent'] += spent
budget_data['budget_type'] = budget_analytic.budget_type
total_allocated += -allocated if budget_analytic.budget_type == 'expense' else allocated
total_spent += -spent if budget_analytic.budget_type == 'expense' else spent
total_allocated_for_progress += allocated * -1 if budget_analytic.budget_type == 'expense' else allocated
total_spent_for_progress += spent * -1 if budget_analytic.budget_type == 'expense' else spent
if can_see_budget_items:
budget_item = {
'id': budget_analytic.id,
'name': budget_analytic.display_name,
'allocated': allocated,
'spent': spent,
'budget_type': budget_analytic.budget_type,
'progress': allocated and (spent - allocated) / abs(allocated) * (-1 if budget_analytic.budget_type == 'expense' else 1),
}
budget_data['budgets'].append(budget_item)
budget_data['ids'] += ids
else:
budget_data['budgets'] = []
for budget_data in budget_data_per_budget.values():
budget_data['progress'] = budget_data['allocated'] and (budget_data['spent'] - budget_data['allocated']) / abs(budget_data['allocated']) \
* (-1 if budget_data['budget_type'] == 'expense' else 1)
budget_data_per_budget = list(budget_data_per_budget.values())
if can_see_budget_items:
for budget_data in budget_data_per_budget:
if len(budget_data['budgets']) == 1:
budget_data['budgets'].clear()
budget_data['action'] = {
'name': 'action_view_budget_lines',
'type': 'object',
'args': json.dumps([[('id', 'in', budget_data.pop('ids'))]]),
}
can_add_budget = with_action and self.env.user.has_group('account.group_account_user')
budget_items = {
'data': budget_data_per_budget,
'total': {
'allocated': total_allocated,
'spent': total_spent,
'progress': (total_spent_for_progress - total_allocated_for_progress) / abs(total_allocated_for_progress) if total_allocated_for_progress else 0,
},
'can_add_budget': can_add_budget,
}
if can_add_budget:
budget_items['form_view_id'] = self.env.ref('project_account_budget.view_budget_analytic_form_dialog').id
budget_items['company_id'] = self.company_id.id or self.env.company.id
return budget_items