[IMP] account_budget: complete review of the module
* budgetary position nor analytic account aren't required anymore (although one of the 2 have to be given) * by extension, the computation of practical amount is not either based on the analytic lines or the account.move.line * usability improved (for eaxmple: there's a button to open the list of items on which the practical amount computation is done) * addition of a pivot and graph views of the budget lines Was PR: #22342 Was task: 31995
This commit is contained in:
committed by
qdp-odoo
parent
f53d9c369d
commit
9148960174
@@ -468,7 +468,7 @@ class AccountMoveLine(models.Model):
|
||||
analytic_line_ids = fields.One2many('account.analytic.line', 'move_id', string='Analytic lines', oldname="analytic_lines")
|
||||
tax_ids = fields.Many2many('account.tax', string='Taxes')
|
||||
tax_line_id = fields.Many2one('account.tax', string='Originator tax', ondelete='restrict')
|
||||
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account')
|
||||
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', index=True)
|
||||
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
|
||||
company_id = fields.Many2one('res.company', related='account_id.company_id', string='Company', store=True, readonly=True)
|
||||
counterpart = fields.Char("Counterpart", compute='_get_counterpart', help="Compute the counter part accounts of this journal item for this journal entry. This can be needed in reports.")
|
||||
|
||||
@@ -384,7 +384,7 @@
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="module_account_budget"/>
|
||||
<div class="text-muted">
|
||||
Compare actual revenues & costs with budgets
|
||||
Use budgets to compare actual with expected revenues and costs
|
||||
</div>
|
||||
<div id="msg_account_budget" class="content-group" attrs="{'invisible': [('module_account_budget', '=', False)]}">
|
||||
<div class="text-warning mt16 mb4">
|
||||
|
||||
@@ -2,30 +2,11 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Budgets Management',
|
||||
'name': 'Budget Management',
|
||||
'category': 'Accounting',
|
||||
'description': """
|
||||
This module allows accountants to manage analytic and crossovered budgets.
|
||||
==========================================================================
|
||||
|
||||
Once the Budgets are defined (in Invoicing/Budgets/Budgets), the Project Managers
|
||||
can set the planned amount on each Analytic Account.
|
||||
|
||||
The accountant has the possibility to see the total of amount planned for each
|
||||
Budget in order to ensure the total planned is not greater/lower than what he
|
||||
planned for this Budget. Each list of record can also be switched to a graphical
|
||||
view of it.
|
||||
|
||||
Three reports are available:
|
||||
----------------------------
|
||||
1. The first is available from a list of Budgets. It gives the spreading, for
|
||||
these Budgets, of the Analytic Accounts.
|
||||
|
||||
2. The second is a summary of the previous one, it only gives the spreading,
|
||||
for the selected Budgets, of the Analytic Accounts.
|
||||
|
||||
3. The last one is available from the Analytic Chart of Accounts. It gives
|
||||
the spreading, for the selected Analytic Accounts of Budgets.
|
||||
Use budgets to compare actual with expected revenues and costs
|
||||
--------------------------------------------------------------
|
||||
""",
|
||||
'website': 'https://www.odoo.com/page/accounting',
|
||||
'depends': ['account'],
|
||||
|
||||
@@ -2,21 +2,205 @@
|
||||
<odoo>
|
||||
<!-- Budgets -->
|
||||
<data noupdate="1">
|
||||
<!-- Optimistic -->
|
||||
<record id="crossovered_budget_budgetoptimistic0" model="crossovered.budget">
|
||||
<field eval="'Budget '+str(time.localtime(time.time())[0]+1)+': Optimistic'" name="name"/>
|
||||
<field eval="str(time.localtime(time.time())[0]+1)+'-01-01'" name="date_from"/>
|
||||
<field eval="'Budget '+str(datetime.now().year+1)+': Optimistic'" name="name"/>
|
||||
<field eval="str(datetime.now().year+1)+'-01-01'" name="date_from"/>
|
||||
<field eval=""""draft"""" name="state"/>
|
||||
<field eval="str(time.localtime(time.time())[0]+1)+'-12-31'" name="date_to"/>
|
||||
<field name="creating_user_id" ref="base.user_root"/>
|
||||
<field eval="str(datetime.now().year+1)+'-12-31'" name="date_to"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
</record>
|
||||
</data>
|
||||
<data noupdate="1">
|
||||
<record id="budget_line_analytic_admin" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-01-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-12-31'" name="date_to"/>
|
||||
<field name="planned_amount">-35000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_administratif"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait1" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-01-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-01-31'" name="date_to"/>
|
||||
<field name="planned_amount">10000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait2" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-02-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-02-28'" name="date_to"/>
|
||||
<field name="planned_amount">10000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait3" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-03-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-03-31'" name="date_to"/>
|
||||
<field name="planned_amount">12000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait4" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-04-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-04-30'" name="date_to"/>
|
||||
<field name="planned_amount">15000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait5" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-05-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-05-31'" name="date_to"/>
|
||||
<field name="planned_amount">15000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait6" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-06-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-06-30'" name="date_to"/>
|
||||
<field name="planned_amount">15000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait7" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-07-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-07-31'" name="date_to"/>
|
||||
<field name="planned_amount">13000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait8" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-08-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-08-31'" name="date_to"/>
|
||||
<field name="planned_amount">9000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait9" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-09-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-09-30'" name="date_to"/>
|
||||
<field name="planned_amount">8000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait10" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-10-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-10-31'" name="date_to"/>
|
||||
<field name="planned_amount">15000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait11" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-11-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-11-30'" name="date_to"/>
|
||||
<field name="planned_amount">15000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait12" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetoptimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-12-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-12-31'" name="date_to"/>
|
||||
<field name="planned_amount">18000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
|
||||
<!-- pessimistic-->
|
||||
<record id="crossovered_budget_budgetpessimistic0" model="crossovered.budget">
|
||||
<field eval="'Budget '+str(time.localtime(time.time())[0]+1)+': Pessimistic'" name="name"/>
|
||||
<field eval="str(time.localtime(time.time())[0]+1)+'-01-01'" name="date_from"/>
|
||||
<field eval="'Budget '+str(datetime.now().year+1)+': Pessimistic'" name="name"/>
|
||||
<field eval="str(datetime.now().year+1)+'-01-01'" name="date_from"/>
|
||||
<field eval=""""draft"""" name="state"/>
|
||||
<field eval="str(time.localtime(time.time())[0]+1)+'-12-31'" name="date_to"/>
|
||||
<field name="creating_user_id" ref="base.user_root"/>
|
||||
<field eval="str(datetime.now().year+1)+'-12-31'" name="date_to"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
</record>
|
||||
|
||||
<record id="budget_line_analytic_admin_pessim" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-01-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-12-31'" name="date_to"/>
|
||||
<field name="planned_amount">-55000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_administratif"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim1" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-01-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-01-31'" name="date_to"/>
|
||||
<field name="planned_amount">9000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim2" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-02-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-02-28'" name="date_to"/>
|
||||
<field name="planned_amount">8000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim3" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-03-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-03-31'" name="date_to"/>
|
||||
<field name="planned_amount">10000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim4" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-04-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-04-30'" name="date_to"/>
|
||||
<field name="planned_amount">14000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim5" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-05-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-05-31'" name="date_to"/>
|
||||
<field name="planned_amount">16000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim6" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-06-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-06-30'" name="date_to"/>
|
||||
<field name="planned_amount">13000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim7" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-07-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-07-31'" name="date_to"/>
|
||||
<field name="planned_amount">10000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim8" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-08-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-08-31'" name="date_to"/>
|
||||
<field name="planned_amount">8000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim9" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-09-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-09-30'" name="date_to"/>
|
||||
<field name="planned_amount">7000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim10" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-10-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-10-31'" name="date_to"/>
|
||||
<field name="planned_amount">12000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim11" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-11-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-11-30'" name="date_to"/>
|
||||
<field name="planned_amount">17000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
<record id="budget_line_analytic_agrolait_pessim12" model="crossovered.budget.lines">
|
||||
<field name="crossovered_budget_id" ref="crossovered_budget_budgetpessimistic0" />
|
||||
<field eval="str(datetime.now().year+1)+'-12-01'" name="date_from"/>
|
||||
<field eval="str(datetime.now().year+1)+'-12-31'" name="date_to"/>
|
||||
<field name="planned_amount">17000</field>
|
||||
<field name="analytic_account_id" ref="analytic.analytic_agrolait"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import ustr
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Budgets
|
||||
@@ -16,7 +15,6 @@ class AccountBudgetPost(models.Model):
|
||||
name = fields.Char('Name', required=True)
|
||||
account_ids = fields.Many2many('account.account', 'account_budget_rel', 'budget_id', 'account_id', 'Accounts',
|
||||
domain=[('deprecated', '=', False)])
|
||||
crossovered_budget_line = fields.One2many('crossovered.budget.lines', 'general_budget_id', 'Budget Lines')
|
||||
company_id = fields.Many2one('res.company', 'Company', required=True,
|
||||
default=lambda self: self.env['res.company']._company_default_get('account.budget.post'))
|
||||
|
||||
@@ -47,7 +45,7 @@ class CrossoveredBudget(models.Model):
|
||||
_inherit = ['mail.thread']
|
||||
|
||||
name = fields.Char('Budget Name', required=True, states={'done': [('readonly', True)]})
|
||||
creating_user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user)
|
||||
user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user, oldname='creating_user_id')
|
||||
date_from = fields.Date('Start Date', required=True, states={'done': [('readonly', True)]})
|
||||
date_to = fields.Date('End Date', required=True, states={'done': [('readonly', True)]})
|
||||
state = fields.Selection([
|
||||
@@ -87,87 +85,194 @@ class CrossoveredBudgetLines(models.Model):
|
||||
_name = "crossovered.budget.lines"
|
||||
_description = "Budget Line"
|
||||
|
||||
name = fields.Char(compute='_compute_line_name')
|
||||
crossovered_budget_id = fields.Many2one('crossovered.budget', 'Budget', ondelete='cascade', index=True, required=True)
|
||||
analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account')
|
||||
general_budget_id = fields.Many2one('account.budget.post', 'Budgetary Position', required=True)
|
||||
analytic_group_id = fields.Many2one('account.analytic.group', 'Analytic Group', related='analytic_account_id.group_id', readonly=True)
|
||||
general_budget_id = fields.Many2one('account.budget.post', 'Budgetary Position')
|
||||
date_from = fields.Date('Start Date', required=True)
|
||||
date_to = fields.Date('End Date', required=True)
|
||||
paid_date = fields.Date('Paid Date')
|
||||
planned_amount = fields.Float('Planned Amount', required=True, digits=0)
|
||||
practical_amount = fields.Float(compute='_compute_practical_amount', string='Practical Amount', digits=0)
|
||||
theoritical_amount = fields.Float(compute='_compute_theoritical_amount', string='Theoretical Amount', digits=0)
|
||||
percentage = fields.Float(compute='_compute_percentage', string='Achievement')
|
||||
currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True)
|
||||
planned_amount = fields.Monetary(
|
||||
'Planned Amount', required=True,
|
||||
help="Amount you plan to earn/spend. Record a positive amount if it is a revenue and a negative amount if it is a cost.")
|
||||
practical_amount = fields.Monetary(
|
||||
compute='_compute_practical_amount', string='Practical Amount', help="Amount really earned/spent.")
|
||||
theoritical_amount = fields.Monetary(
|
||||
compute='_compute_theoritical_amount', string='Theoretical Amount',
|
||||
help="Amount you are supposed to have earned/spent at this date.")
|
||||
percentage = fields.Float(
|
||||
compute='_compute_percentage', string='Achievement',
|
||||
help="Comparison between practical and theoretical amount. This measure tells you if you are below or over budget.")
|
||||
company_id = fields.Many2one(related='crossovered_budget_id.company_id', comodel_name='res.company',
|
||||
string='Company', store=True, readonly=True)
|
||||
is_above_budget = fields.Boolean(compute='_is_above_budget')
|
||||
crossovered_budget_state = fields.Selection(related='crossovered_budget_id.state', string='Budget State', store=True, readonly=True)
|
||||
|
||||
@api.model
|
||||
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
||||
# overrides the default read_group in order to compute the computed fields manually for the group
|
||||
|
||||
result = super(CrossoveredBudgetLines, self).read_group(domain, fields, groupby, offset=offset, limit=limit,
|
||||
orderby=orderby, lazy=lazy)
|
||||
fields_list = ['practical_amount', 'theoritical_amount', 'percentage']
|
||||
if any(x in fields for x in fields_list):
|
||||
for group_line in result:
|
||||
|
||||
# initialise fields to compute to 0 if they are requested
|
||||
if 'practical_amount' in fields:
|
||||
group_line['practical_amount'] = 0
|
||||
if 'theoritical_amount' in fields:
|
||||
group_line['theoritical_amount'] = 0
|
||||
if 'percentage' in fields:
|
||||
group_line['percentage'] = 0
|
||||
group_line['practical_amount'] = 0
|
||||
group_line['theoritical_amount'] = 0
|
||||
|
||||
if group_line.get('__domain'):
|
||||
all_budget_lines_that_compose_group = self.search(group_line['__domain'])
|
||||
else:
|
||||
all_budget_lines_that_compose_group = self.search([])
|
||||
for budget_line_of_group in all_budget_lines_that_compose_group:
|
||||
if 'practical_amount' in fields or 'percentage' in fields:
|
||||
group_line['practical_amount'] += budget_line_of_group.practical_amount
|
||||
|
||||
if 'theoritical_amount' in fields or 'percentage' in fields:
|
||||
group_line['theoritical_amount'] += budget_line_of_group.theoritical_amount
|
||||
|
||||
if 'percentage' in fields:
|
||||
if group_line['theoritical_amount']:
|
||||
# use a weighted average
|
||||
group_line['percentage'] = float(
|
||||
(group_line['practical_amount'] or 0.0) / group_line['theoritical_amount']) * 100
|
||||
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def _is_above_budget(self):
|
||||
for line in self:
|
||||
if line.theoritical_amount >= 0:
|
||||
line.is_above_budget = line.practical_amount > line.theoritical_amount
|
||||
else:
|
||||
line.is_above_budget = line.practical_amount < line.theoritical_amount
|
||||
|
||||
@api.multi
|
||||
def _compute_line_name(self):
|
||||
#just in case someone opens the budget line in form view
|
||||
computed_name = self.crossovered_budget_id.name
|
||||
if self.general_budget_id:
|
||||
computed_name += ' - ' + self.general_budget_id.name
|
||||
if self.analytic_account_id:
|
||||
computed_name += ' - ' + self.analytic_account_id.name
|
||||
self.name = computed_name
|
||||
|
||||
@api.multi
|
||||
def _compute_practical_amount(self):
|
||||
for line in self:
|
||||
result = 0.0
|
||||
acc_ids = line.general_budget_id.account_ids.ids
|
||||
date_to = self.env.context.get('wizard_date_to') or line.date_to
|
||||
date_from = self.env.context.get('wizard_date_from') or line.date_from
|
||||
date_to = line.date_to
|
||||
date_from = line.date_from
|
||||
if line.analytic_account_id.id:
|
||||
self.env.cr.execute("""
|
||||
SELECT SUM(amount)
|
||||
FROM account_analytic_line
|
||||
WHERE account_id=%s
|
||||
AND (date between to_date(%s,'yyyy-mm-dd') AND to_date(%s,'yyyy-mm-dd'))
|
||||
AND general_account_id=ANY(%s)""",
|
||||
(line.analytic_account_id.id, date_from, date_to, acc_ids,))
|
||||
result = self.env.cr.fetchone()[0] or 0.0
|
||||
line.practical_amount = result
|
||||
analytic_line_obj = self.env['account.analytic.line']
|
||||
domain = [('account_id', '=', line.analytic_account_id.id),
|
||||
('date', '>=', date_from),
|
||||
('date', '<=', date_to),
|
||||
]
|
||||
if acc_ids:
|
||||
domain += [('general_account_id', 'in', acc_ids)]
|
||||
|
||||
where_query = analytic_line_obj._where_calc(domain)
|
||||
analytic_line_obj._apply_ir_rules(where_query, 'read')
|
||||
from_clause, where_clause, where_clause_params = where_query.get_sql()
|
||||
select = "SELECT SUM(amount) from " + from_clause + " where " + where_clause
|
||||
|
||||
else:
|
||||
aml_obj = self.env['account.move.line']
|
||||
domain = [('account_id', 'in',
|
||||
line.general_budget_id.account_ids.ids),
|
||||
('date', '>=', date_from),
|
||||
('date', '<=', date_to)
|
||||
]
|
||||
where_query = aml_obj._where_calc(domain)
|
||||
aml_obj._apply_ir_rules(where_query, 'read')
|
||||
from_clause, where_clause, where_clause_params = where_query.get_sql()
|
||||
select = "SELECT sum(credit)-sum(debit) from " + from_clause + " where " + where_clause
|
||||
|
||||
self.env.cr.execute(select, where_clause_params)
|
||||
line.practical_amount = self.env.cr.fetchone()[0] or 0.0
|
||||
|
||||
@api.multi
|
||||
def _compute_theoritical_amount(self):
|
||||
today = fields.Datetime.now()
|
||||
# beware: 'today' variable is mocked in the python tests and thus, its implementation matter
|
||||
today = fields.Datetime.from_string(fields.Date.today())
|
||||
for line in self:
|
||||
# Used for the report
|
||||
|
||||
if self.env.context.get('wizard_date_from') and self.env.context.get('wizard_date_to'):
|
||||
date_from = fields.Datetime.from_string(self.env.context.get('wizard_date_from'))
|
||||
date_to = fields.Datetime.from_string(self.env.context.get('wizard_date_to'))
|
||||
if date_from < fields.Datetime.from_string(line.date_from):
|
||||
date_from = fields.Datetime.from_string(line.date_from)
|
||||
elif date_from > fields.Datetime.from_string(line.date_to):
|
||||
date_from = False
|
||||
|
||||
if date_to > fields.Datetime.from_string(line.date_to):
|
||||
date_to = fields.Datetime.from_string(line.date_to)
|
||||
elif date_to < fields.Datetime.from_string(line.date_from):
|
||||
date_to = False
|
||||
|
||||
theo_amt = 0.00
|
||||
if date_from and date_to:
|
||||
line_timedelta = fields.Datetime.from_string(line.date_to) - fields.Datetime.from_string(line.date_from)
|
||||
elapsed_timedelta = date_to - date_from
|
||||
if elapsed_timedelta.days > 0:
|
||||
theo_amt = (elapsed_timedelta.total_seconds() / line_timedelta.total_seconds()) * line.planned_amount
|
||||
else:
|
||||
if line.paid_date:
|
||||
if fields.Datetime.from_string(line.date_to) <= fields.Datetime.from_string(line.paid_date):
|
||||
theo_amt = 0.00
|
||||
else:
|
||||
theo_amt = line.planned_amount
|
||||
if line.paid_date:
|
||||
if fields.Datetime.to_string(today) <= line.paid_date:
|
||||
theo_amt = 0.00
|
||||
else:
|
||||
line_timedelta = fields.Datetime.from_string(line.date_to) - fields.Datetime.from_string(line.date_from)
|
||||
elapsed_timedelta = fields.Datetime.from_string(today) - (fields.Datetime.from_string(line.date_from))
|
||||
|
||||
if elapsed_timedelta.days < 0:
|
||||
# If the budget line has not started yet, theoretical amount should be zero
|
||||
theo_amt = 0.00
|
||||
elif line_timedelta.days > 0 and fields.Datetime.from_string(today) < fields.Datetime.from_string(line.date_to):
|
||||
# If today is between the budget line date_from and date_to
|
||||
theo_amt = (elapsed_timedelta.total_seconds() / line_timedelta.total_seconds()) * line.planned_amount
|
||||
else:
|
||||
theo_amt = line.planned_amount
|
||||
theo_amt = line.planned_amount
|
||||
else:
|
||||
line_timedelta = fields.Datetime.from_string(line.date_to) - fields.Datetime.from_string(
|
||||
line.date_from)
|
||||
elapsed_timedelta = today - fields.Datetime.from_string(line.date_from)
|
||||
|
||||
if elapsed_timedelta.days < 0:
|
||||
# If the budget line has not started yet, theoretical amount should be zero
|
||||
theo_amt = 0.00
|
||||
elif line_timedelta.days > 0 and today < fields.Datetime.from_string(line.date_to):
|
||||
# If today is between the budget line date_from and date_to
|
||||
theo_amt = (elapsed_timedelta.total_seconds() / line_timedelta.total_seconds()) * line.planned_amount
|
||||
else:
|
||||
theo_amt = line.planned_amount
|
||||
line.theoritical_amount = theo_amt
|
||||
|
||||
@api.multi
|
||||
def _compute_percentage(self):
|
||||
for line in self:
|
||||
if line.theoritical_amount != 0.00:
|
||||
line.percentage = float((line.practical_amount or 0.0) / line.theoritical_amount) * 100
|
||||
line.percentage = float((line.practical_amount or 0.0) / line.theoritical_amount)
|
||||
else:
|
||||
line.percentage = 0.00
|
||||
|
||||
@api.constrains('general_budget_id', 'analytic_account_id')
|
||||
def _must_have_analytical_or_budgetary_or_both(self):
|
||||
if not self.analytic_account_id and not self.general_budget_id:
|
||||
raise ValidationError(
|
||||
_("You have to enter at least a budgetary position or analytic account on a budget line."))
|
||||
|
||||
@api.multi
|
||||
def action_open_budget_entries(self):
|
||||
if self.analytic_account_id:
|
||||
# if there is an analytic account, then the analytic items are loaded
|
||||
action = self.env['ir.actions.act_window'].for_xml_id('analytic', 'account_analytic_line_action_entries')
|
||||
action['domain'] = [('account_id', '=', self.analytic_account_id.id),
|
||||
('date', '>=', self.date_from),
|
||||
('date', '<=', self.date_to)
|
||||
]
|
||||
if self.general_budget_id:
|
||||
action['domain'] += [('general_account_id', 'in', self.general_budget_id.account_ids.ids)]
|
||||
else:
|
||||
# otherwise the journal entries booked on the accounts of the budgetary postition are opened
|
||||
action = self.env['ir.actions.act_window'].for_xml_id('account', 'action_account_moves_all_a')
|
||||
action['domain'] = [('account_id', 'in',
|
||||
self.general_budget_id.account_ids.ids),
|
||||
('date', '>=', self.date_from),
|
||||
('date', '<=', self.date_to)
|
||||
]
|
||||
return action
|
||||
|
||||
@api.one
|
||||
@api.constrains('date_from', 'date_to')
|
||||
def _line_dates_between_budget_dates(self):
|
||||
budget_date_from = fields.Datetime.from_string(self.crossovered_budget_id.date_from)
|
||||
budget_date_to = fields.Datetime.from_string(self.crossovered_budget_id.date_to)
|
||||
if self.date_from:
|
||||
date_from = fields.Datetime.from_string(self.date_from)
|
||||
if date_from < budget_date_from or date_from > budget_date_to:
|
||||
raise ValidationError(_('"Start Date" of the budget line should be included in the Period of the budget'))
|
||||
|
||||
if self.date_to:
|
||||
date_to = fields.Datetime.from_string(self.date_to)
|
||||
if date_to < budget_date_from or date_to > budget_date_to:
|
||||
raise ValidationError(_('"End Date" of the budget line should be included in the Period of the budget'))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.account.tests.account_test_classes import AccountingTestCase
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class TestAccountBudgetCommon(AccountingTestCase):
|
||||
@@ -30,74 +30,74 @@ class TestAccountBudgetCommon(AccountingTestCase):
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_partners_camp_to_camp'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-01-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-01-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-01-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-01-31',
|
||||
'planned_amount': 500.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_partners_camp_to_camp'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-02-07',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-02-28',
|
||||
'date_from': str(datetime.now().year + 1) + '-02-07',
|
||||
'date_to': str(datetime.now().year + 1) + '-02-28',
|
||||
'planned_amount': 900.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_partners_camp_to_camp'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-03-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-03-15',
|
||||
'date_from': str(datetime.now().year + 1) + '-03-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-03-15',
|
||||
'planned_amount': 300.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_our_super_product'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-03-16',
|
||||
'paid_date': str(time.localtime(time.time())[0] + 1) + '-12-03',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-03-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-03-16',
|
||||
'paid_date': str(datetime.now().year + 1) + '-12-03',
|
||||
'date_to': str(datetime.now().year + 1) + '-03-31',
|
||||
'planned_amount': 375.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_our_super_product'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-05-01',
|
||||
'paid_date': str(time.localtime(time.time())[0] + 1) + '-12-03',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-05-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-05-01',
|
||||
'paid_date': str(datetime.now().year + 1) + '-12-03',
|
||||
'date_to': str(datetime.now().year + 1) + '-05-31',
|
||||
'planned_amount': 375.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-07-16',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-07-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-07-16',
|
||||
'date_to': str(datetime.now().year + 1) + '-07-31',
|
||||
'planned_amount': 20000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-02-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-02-28',
|
||||
'date_from': str(datetime.now().year + 1) + '-02-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-02-28',
|
||||
'planned_amount': 20000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-09-16',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-09-30',
|
||||
'date_from': str(datetime.now().year + 1) + '-09-16',
|
||||
'date_to': str(datetime.now().year + 1) + '-09-30',
|
||||
'planned_amount': 10000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_sales0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-10-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-12-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-10-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-12-31',
|
||||
'planned_amount': 10000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
@@ -122,64 +122,64 @@ class TestAccountBudgetCommon(AccountingTestCase):
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_partners_camp_to_camp'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-01-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-01-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-01-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-01-31',
|
||||
'planned_amount': -500.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_partners_camp_to_camp'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-02-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-02-28',
|
||||
'date_from': str(datetime.now().year + 1) + '-02-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-02-28',
|
||||
'planned_amount': -250.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_our_super_product'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-04-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-04-30',
|
||||
'date_from': str(datetime.now().year + 1) + '-04-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-04-30',
|
||||
'planned_amount': -150.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-06-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-06-15',
|
||||
'date_from': str(datetime.now().year + 1) + '-06-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-06-15',
|
||||
'planned_amount': -7500.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-06-16',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-06-30',
|
||||
'date_from': str(datetime.now().year + 1) + '-06-16',
|
||||
'date_to': str(datetime.now().year + 1) + '-06-30',
|
||||
'planned_amount': -5000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-07-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-07-15',
|
||||
'date_from': str(datetime.now().year + 1) + '-07-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-07-15',
|
||||
'planned_amount': -2000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-08-16',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-08-31',
|
||||
'date_from': str(datetime.now().year + 1) + '-08-16',
|
||||
'date_to': str(datetime.now().year + 1) + '-08-31',
|
||||
'planned_amount': -3000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetpessimistic0'),
|
||||
})
|
||||
self.env['crossovered.budget.lines'].create({
|
||||
'analytic_account_id': self.ref('analytic.analytic_seagate_p2'),
|
||||
'general_budget_id': self.account_budget_post_purchase0.id,
|
||||
'date_from': str(time.localtime(time.time())[0] + 1) + '-09-01',
|
||||
'date_to': str(time.localtime(time.time())[0] + 1) + '-09-15',
|
||||
'date_from': str(datetime.now().year + 1) + '-09-01',
|
||||
'date_to': str(datetime.now().year + 1) + '-09-15',
|
||||
'planned_amount': -1000.0,
|
||||
'crossovered_budget_id': self.ref('account_budget.crossovered_budget_budgetoptimistic0'),
|
||||
})
|
||||
|
||||
@@ -7,9 +7,13 @@ try:
|
||||
except ImportError:
|
||||
from mock import patch
|
||||
|
||||
from odoo.fields import Datetime
|
||||
from odoo.fields import Datetime, Date
|
||||
from odoo.tests import tagged
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
# ---------------------------------------------------------
|
||||
# Tests
|
||||
# ---------------------------------------------------------
|
||||
@@ -17,12 +21,7 @@ from odoo.tests import tagged
|
||||
class TestTheoreticalAmount(TestAccountBudgetCommon):
|
||||
def setUp(self):
|
||||
super(TestTheoreticalAmount, self).setUp()
|
||||
crossovered_budget = self.env['crossovered.budget'].create({
|
||||
'name': 'test budget name',
|
||||
'date_from': '2014-01-01',
|
||||
'date_to': '2014-12-31',
|
||||
})
|
||||
crossovered_budget_line_obj = self.env['crossovered.budget.lines']
|
||||
#create the budgetary position
|
||||
user_type_id = self.ref('account.data_account_type_revenue')
|
||||
tag_id = self.ref('account.account_tag_operating')
|
||||
account_rev = self.env['account.account'].create({
|
||||
@@ -35,76 +34,66 @@ class TestTheoreticalAmount(TestAccountBudgetCommon):
|
||||
'name': 'Sales',
|
||||
'account_ids': [(4, account_rev.id, 0)],
|
||||
})
|
||||
#create the budget and budget lines
|
||||
first_january = datetime.now().replace(day=1, month=1)
|
||||
self.last_day_of_budget = first_january + timedelta(days=364) # will be 30th of December or 31th in case of leap year
|
||||
|
||||
crossovered_budget = self.env['crossovered.budget'].create({
|
||||
'name': 'test budget name',
|
||||
'date_from': str(first_january.date()),
|
||||
'date_to': str(self.last_day_of_budget.date()),
|
||||
})
|
||||
crossovered_budget_line_obj = self.env['crossovered.budget.lines']
|
||||
self.line = crossovered_budget_line_obj.create({
|
||||
'crossovered_budget_id': crossovered_budget.id,
|
||||
'general_budget_id': buget_post.id,
|
||||
'date_from': '2014-01-01',
|
||||
'date_to': '2014-12-31',
|
||||
'date_from': str(first_january.date()),
|
||||
'date_to': str(self.last_day_of_budget.date()),
|
||||
'planned_amount': -364,
|
||||
})
|
||||
self.paid_date_line = crossovered_budget_line_obj.create({
|
||||
'crossovered_budget_id': crossovered_budget.id,
|
||||
'general_budget_id': buget_post.id,
|
||||
'date_from': str(first_january.date()),
|
||||
'date_to': str(self.last_day_of_budget.date()),
|
||||
'planned_amount': -364,
|
||||
'paid_date': str(datetime.now().year) + '-09-09',
|
||||
})
|
||||
|
||||
self.patcher = patch('odoo.addons.account_budget.models.account_budget.fields.Datetime', wraps=Datetime)
|
||||
self.mock_datetime = self.patcher.start()
|
||||
self.patcher = patch('odoo.addons.account_budget.models.account_budget.fields.Date', wraps=Date)
|
||||
self.mock_date = self.patcher.start()
|
||||
|
||||
def test_01(self):
|
||||
"""Start"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-01-01 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, 0)
|
||||
def test_theoritical_amount_without_paid_date(self):
|
||||
test_list = [
|
||||
(str(datetime.now().year) + '-01-01', 0),
|
||||
(str(datetime.now().year) + '-01-02', -1),
|
||||
(str(datetime.now().year) + '-01-03', -2),
|
||||
(str(datetime.now().year) + '-01-11', -10),
|
||||
(str(datetime.now().year) + '-02-20', -50),
|
||||
(str(self.last_day_of_budget.date()), -364),
|
||||
]
|
||||
for date, expected_amount in test_list:
|
||||
_logger.info("Checking theoritical amount for the date: " + date)
|
||||
self.mock_date.today.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, expected_amount)
|
||||
#invalidate the cache of the budget lines to recompute the theoritical amount at next iteration
|
||||
self.line.invalidate_cache()
|
||||
|
||||
def test_02(self):
|
||||
"""After 24 hours"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-01-02 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -1)
|
||||
|
||||
def test_03(self):
|
||||
"""After 36 hours"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-01-02 12:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -1.5)
|
||||
|
||||
def test_04(self):
|
||||
"""After 48 hours"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-01-03 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -2)
|
||||
|
||||
def test_05(self):
|
||||
"""After 10 days"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-01-11 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -10)
|
||||
|
||||
def test_06(self):
|
||||
"""After 50 days"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-02-20 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -50)
|
||||
|
||||
def test_07(self):
|
||||
"""After 182 days, exactly half of the budget line"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-07-02 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -182)
|
||||
|
||||
def test_08(self):
|
||||
"""After 308 days at noon"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-11-05 12:00:00')) # remember, remember
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -308.5)
|
||||
|
||||
def test_09(self):
|
||||
"""One day before"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-12-30 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -363)
|
||||
|
||||
def test_10(self):
|
||||
"""At last"""
|
||||
date = Datetime.to_string(Datetime.from_string('2014-12-31 00:00:00'))
|
||||
self.mock_datetime.now.return_value = date
|
||||
self.assertAlmostEqual(self.line.theoritical_amount, -364)
|
||||
def test_theoritical_amount_with_paid_date(self):
|
||||
test_list = [
|
||||
(str(datetime.now().year) + '-01-01', 0),
|
||||
(str(datetime.now().year) + '-01-02', 0),
|
||||
(str(datetime.now().year) + '-09-08', 0),
|
||||
(str(datetime.now().year) + '-09-09', -364),
|
||||
(str(datetime.now().year) + '-09-10', -364),
|
||||
(str(self.last_day_of_budget.date()), -364),
|
||||
]
|
||||
for date, expected_amount in test_list:
|
||||
_logger.info("Checking theoritical amount for the date: " + date)
|
||||
self.mock_date.today.return_value = date
|
||||
self.assertAlmostEqual(self.paid_date_line.theoritical_amount, expected_amount)
|
||||
#invalidate the cache of the budget lines to recompute the theoritical amount at next iteration
|
||||
self.paid_date_line.invalidate_cache()
|
||||
|
||||
def tearDown(self):
|
||||
self.patcher.stop()
|
||||
|
||||
@@ -1,310 +1,364 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!--
|
||||
Budgets
|
||||
-->
|
||||
<record id="view_budget_post_search" model="ir.ui.view">
|
||||
<field name="name">account.budget.post.search</field>
|
||||
<field name="model">account.budget.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Budgetary Position">
|
||||
<field name="name" filter_domain="[('name','ilike',self)]" string="Budgetary Position"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<!--
|
||||
Budgets
|
||||
-->
|
||||
<record id="view_budget_post_search" model="ir.ui.view">
|
||||
<field name="name">account.budget.post.search</field>
|
||||
<field name="model">account.budget.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Budgetary Position">
|
||||
<field name="name" filter_domain="[('name','ilike',self)]" string="Budgetary Position"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_budget_post_tree" model="ir.ui.view">
|
||||
<field name="name">account.budget.post.tree</field>
|
||||
<field name="model">account.budget.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Budgetary Position">
|
||||
<record id="view_budget_post_tree" model="ir.ui.view">
|
||||
<field name="name">account.budget.post.tree</field>
|
||||
<field name="model">account.budget.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Budgetary Position">
|
||||
<field name="name"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="open_budget_post_form" model="ir.actions.act_window">
|
||||
<field name="name">Budgetary Positions</field>
|
||||
<field name="res_model">account.budget.post</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_id" ref="view_budget_post_tree"/>
|
||||
<field name="search_view_id" ref="view_budget_post_search"/>
|
||||
</record>
|
||||
<menuitem action="open_budget_post_form" id="menu_budget_post_form"
|
||||
parent="account.account_management_menu" sequence="5"/>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_budget_post_form">
|
||||
<field name="name">account.budget.post.form</field>
|
||||
<field name="model">account.budget.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Budgetary Position">
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Accounts">
|
||||
<field name="account_ids">
|
||||
<tree>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="open_budget_post_form" model="ir.actions.act_window">
|
||||
<field name="name">Budgetary Positions</field>
|
||||
<field name="res_model">account.budget.post</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_id" ref="view_budget_post_tree"/>
|
||||
<field name="search_view_id" ref="view_budget_post_search"/>
|
||||
</record>
|
||||
<menuitem action="open_budget_post_form" id="menu_budget_post_form" parent="account.account_management_menu" sequence="5"/>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_budget_post_form">
|
||||
<field name="name">account.budget.post.form</field>
|
||||
<field name="model">account.budget.post</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Budgetary Position">
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
<record model="ir.ui.view" id="crossovered_budget_view_form">
|
||||
<field name="name">crossovered.budget.view.form</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Budget">
|
||||
<header>
|
||||
<button string="Confirm" name="action_budget_confirm" states="draft" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button string="Approve" name="action_budget_validate" states="confirm" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button string="Done" name="action_budget_done" states="validate" type="object"
|
||||
class="oe_highlight"/>
|
||||
<button string="Reset to Draft" name="action_budget_draft" states="cancel" type="object"/>
|
||||
<button string="Cancel Budget" name="action_budget_cancel" states="confirm,validate" type="object"/>
|
||||
<field name="state" widget="statusbar" />
|
||||
</header>
|
||||
<sheet string="Budget">
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" attrs="{'readonly':[('state','!=','draft')]}" placeholder="Budget Name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="user_id" attrs="{'readonly':[('state','!=','draft')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<label for="date_from" string="Period"/>
|
||||
<div>
|
||||
<field name="date_from" class="oe_inline"
|
||||
attrs="{'readonly':[('state','!=','draft')]}"/>
|
||||
-
|
||||
<field name="date_to" class="oe_inline" attrs="{'readonly':[('state','!=','draft')]}"
|
||||
nolabel="1"/>
|
||||
</div>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Accounts">
|
||||
<field name="account_ids">
|
||||
<tree>
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<page string="Budget Lines">
|
||||
<field name="crossovered_budget_line"
|
||||
context="{'default_date_from': date_from,'default_date_to': date_to}" colspan="4"
|
||||
nolabel="1" attrs="{'readonly':[('state','!=','draft')]}">
|
||||
<tree string="Budget Lines" decoration-success="is_above_budget and planned_amount > 0" decoration-danger="is_above_budget and planned_amount < 0" editable="bottom">
|
||||
<field name="general_budget_id"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="paid_date" groups="base.group_no_one"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="planned_amount" sum="Planned Amount"/>
|
||||
<field name="practical_amount" sum="Practical Amount"/>
|
||||
<field name="theoritical_amount" sum="Theoretical Amount"/>
|
||||
<field name="percentage" widget="percentage" />
|
||||
<button type="object" name="action_open_budget_entries" string="Entries..."
|
||||
icon="fa-arrow-circle-o-right"/>
|
||||
<field name="is_above_budget" invisible="1"/>
|
||||
</tree>
|
||||
<form string="Budget Lines">
|
||||
<group>
|
||||
<group>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="general_budget_id"/>
|
||||
<field name="planned_amount"/>
|
||||
<field name="analytic_account_id"
|
||||
groups="analytic.group_analytic_accounting"/>
|
||||
</group>
|
||||
<group>
|
||||
<label for="date_from" string="Period"/>
|
||||
<div>
|
||||
<field name="date_from" class="oe_inline"/>
|
||||
-
|
||||
<field name="date_to" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="paid_date" groups="base.group_no_one"/>
|
||||
<field name="company_id" options="{'no_create': True}"
|
||||
groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="crossovered_budget_view_form">
|
||||
<field name="name">crossovered.budget.view.form</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Budget">
|
||||
<header>
|
||||
<button string="Confirm" name="action_budget_confirm" states="draft" type="object" class="oe_highlight"/>
|
||||
<button string="Approve" name="action_budget_validate" states="confirm" type="object" class="oe_highlight"/>
|
||||
<button string="Done" name="action_budget_done" states="validate" type="object" class="oe_highlight"/>
|
||||
<button string="Reset to Draft" name="action_budget_draft" states="cancel" type="object" />
|
||||
<button string="Cancel Budget" name="action_budget_cancel" states="confirm,validate" type="object"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet string="Budget">
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" attrs="{'readonly':[('state','!=','draft')]}" placeholder="Budget Name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="creating_user_id" attrs="{'readonly':[('state','!=','draft')]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<label for="date_from" string="Period"/>
|
||||
<div>
|
||||
<field name="date_from" class="oe_inline" attrs="{'readonly':[('state','!=','draft')]}"/> -
|
||||
<field name="date_to" class="oe_inline" attrs="{'readonly':[('state','!=','draft')]}" nolabel="1"/>
|
||||
<record model="ir.ui.view" id="crossovered_budget_view_tree">
|
||||
<field name="name">crossovered.budget.view.tree</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-info="state == 'draft'" decoration-muted="state in ('done','cancel')" string="Budget">
|
||||
<field name="name" colspan="1"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
<field name="user_id"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_crossovered_budget_kanban" model="ir.ui.view">
|
||||
<field name="name">crossovered.budget.kanban</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="name"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="user_id"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="row mb4">
|
||||
<div class="col-xs-8">
|
||||
<strong>
|
||||
<field name="name"/>
|
||||
</strong>
|
||||
</div>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Budget Lines">
|
||||
<field name="crossovered_budget_line" context="{'default_date_from': date_from,'default_date_to': date_to}" colspan="4" nolabel="1" attrs="{'readonly':[('state','!=','draft')]}">
|
||||
<tree string="Budget Lines">
|
||||
<field name="general_budget_id"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" required="1"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="paid_date" groups="base.group_no_one"/>
|
||||
<field name="planned_amount" sum="Planned Amount" widget="monetary"/>
|
||||
<field name="practical_amount" sum="Practical Amount" widget="monetary"/>
|
||||
<field name="theoritical_amount" sum="Theoretical Amount" widget="monetary"/>
|
||||
<field name="percentage"/>
|
||||
</tree>
|
||||
<form string="Budget Lines">
|
||||
<group>
|
||||
<group>
|
||||
<field name="general_budget_id"/>
|
||||
<field name="planned_amount" widget="monetary"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" required="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<label for="date_from" string="Period"/>
|
||||
<div>
|
||||
<field name="date_from" class="oe_inline"/> -
|
||||
<field name="date_to" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="paid_date" groups="base.group_no_one"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="crossovered_budget_view_tree">
|
||||
<field name="name">crossovered.budget.view.tree</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-info="state == 'draft'" decoration-muted="state in ('done','cancel')" string="Budget">
|
||||
<field name="name" colspan="1"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
<field name="creating_user_id"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_crossovered_budget_kanban" model="ir.ui.view">
|
||||
<field name="name">crossovered.budget.kanban</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile">
|
||||
<field name="name"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="creating_user_id"/>
|
||||
<field name="state"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="row mb4">
|
||||
<div class="col-xs-8">
|
||||
<strong><field name="name"/></strong>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<span class="pull-right">
|
||||
<field name="state" widget="kanban_label_selection" options="{'classes': {'draft': 'default', 'done': 'success'}}"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-10">
|
||||
<i class="fa fa-clock-o"/> <t t-esc="record.date_from.value"/>-<t t-esc="record.date_to.value"/>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<span class="pull-right">
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.creating_user_id.raw_value)" t-att-title="record.creating_user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<span class="pull-right">
|
||||
<field name="state" widget="kanban_label_selection"
|
||||
options="{'classes': {'draft': 'default', 'done': 'success'}}"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<div class="row">
|
||||
<div class="col-xs-10">
|
||||
<i class="fa fa-clock-o"/>
|
||||
<t t-esc="record.date_from.value"/>-
|
||||
<t t-esc="record.date_to.value"/>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<span class="pull-right">
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)"
|
||||
t-att-title="record.user_id.value" width="24" height="24"
|
||||
class="oe_kanban_avatar pull-right"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_crossovered_budget_search" model="ir.ui.view">
|
||||
<field name="name">crossovered.budget.search</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Budget">
|
||||
<field name="name" filter_domain="[('name','ilike',self)]" string="Budget"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]" help="Draft Budgets"/>
|
||||
<filter string="To Approve" name="toapprove" domain="[('state','=','confirm')]" help="To Approve Budgets" />
|
||||
<field name="state"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_crossovered_budget_search" model="ir.ui.view">
|
||||
<field name="name">crossovered.budget.search</field>
|
||||
<field name="model">crossovered.budget</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Budget">
|
||||
<field name="name" filter_domain="[('name','ilike',self)]" string="Budget"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]" help="Draft Budgets"/>
|
||||
<filter string="To Approve" name="toapprove" domain="[('state','=','confirm')]"
|
||||
help="To Approve Budgets"/>
|
||||
<field name="state"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="act_crossovered_budget_view">
|
||||
<field name="name">Budgets</field>
|
||||
<field name="res_model">crossovered.budget</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_id" ref="crossovered_budget_view_tree"/>
|
||||
<field name="search_view_id" ref="view_crossovered_budget_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new budget
|
||||
</p><p>
|
||||
A budget is a forecast of your company's income and/or expenses
|
||||
expected for a period in the future. A budget is defined on some
|
||||
financial accounts and/or analytic accounts (that may represent
|
||||
projects, departments, categories of products, etc.)
|
||||
</p><p>
|
||||
By keeping track of where your money goes, you may be less
|
||||
likely to overspend, and more likely to meet your financial
|
||||
goals. Forecast a budget by detailing the expected revenue per
|
||||
analytic account and monitor its evolution based on the actuals
|
||||
realised during that period.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem parent="account.menu_finance_entries_management"
|
||||
id="menu_act_crossovered_budget_view"
|
||||
name="Budgets"
|
||||
action="act_crossovered_budget_view" sequence="60"
|
||||
groups="account.group_account_user"/>
|
||||
|
||||
<record id="view_crossovered_budget_line_search" model="ir.ui.view">
|
||||
<field name="name">account.budget.line.search</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Budget Lines">
|
||||
<field name="analytic_account_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_crossovered_budget_line_tree">
|
||||
<field name="name">crossovered.budget.line.tree</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Budget Lines">
|
||||
<field name="crossovered_budget_id" invisible="1"/>
|
||||
<field name="general_budget_id"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="paid_date" groups="base.group_no_one"/>
|
||||
<field name="planned_amount" widget="monetary"/>
|
||||
<field name="practical_amount" widget="monetary"/>
|
||||
<field name="theoritical_amount" widget="monetary"/>
|
||||
<field name="percentage"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_crossovered_budget_line_form">
|
||||
<field name="name">crossovered.budget.line.form</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Budget Lines">
|
||||
<sheet>
|
||||
<group col="4">
|
||||
<field name="crossovered_budget_id"/>
|
||||
<field name="analytic_account_id"/>
|
||||
<field name="general_budget_id"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="paid_date"/>
|
||||
<field name="planned_amount" widget="monetary"/>
|
||||
<field name="practical_amount" widget="monetary"/>
|
||||
<field name="theoritical_amount" widget="monetary"/>
|
||||
<field name="percentage"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="act_crossovered_budget_view">
|
||||
<field name="name">Budgets</field>
|
||||
<field name="res_model">crossovered.budget</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_id" ref="crossovered_budget_view_tree"/>
|
||||
<field name="search_view_id" ref="view_crossovered_budget_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Click to create a new budget.
|
||||
</p>
|
||||
<p>
|
||||
Use budgets to compare actual with expected revenues and costs
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem parent="account.menu_finance_entries_management"
|
||||
id="menu_act_crossovered_budget_view"
|
||||
name="Budgets"
|
||||
action="act_crossovered_budget_view" sequence="60"
|
||||
groups="account.group_account_user"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="act_crossovered_budget_lines_view">
|
||||
<field name="name">Budgets</field>
|
||||
<field name="res_model">crossovered.budget.lines</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_crossovered_budget_line_tree"/>
|
||||
</record>
|
||||
<record id="view_crossovered_budget_line_search" model="ir.ui.view">
|
||||
<field name="name">account.budget.line.search</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Budget Lines">
|
||||
<field name="analytic_account_id"/>
|
||||
<field name="crossovered_budget_id"/>
|
||||
<filter name="filter_not_cancelled" string="Not Cancelled" domain="[('crossovered_budget_state','!=','cancel')]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="group_crossevered_budgdet_id" string="Budgets" domain="[]" context="{'group_by':'crossovered_budget_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
|
||||
<menuitem parent="account.account_reports_management_menu"
|
||||
id="menu_act_crossovered_budget_lines_view"
|
||||
action="act_crossovered_budget_lines_view" sequence="20"/>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Shortcuts -->
|
||||
<act_window name="Budget Items"
|
||||
context="{'search_default_analytic_account_id': [active_id], 'default_analytic_account_id': active_id}"
|
||||
res_model="crossovered.budget.lines"
|
||||
src_model="account.analytic.account"
|
||||
id="act_account_analytic_account_cb_lines"/>
|
||||
<record model="ir.ui.view" id="view_crossovered_budget_line_tree">
|
||||
<field name="name">crossovered.budget.line.tree</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Budget Lines" create="0">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="crossovered_budget_id" invisible="1"/>
|
||||
<field name="general_budget_id" />
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" />
|
||||
<field name="date_from" />
|
||||
<field name="date_to" />
|
||||
<field name="paid_date" groups="base.group_no_one" />
|
||||
<field name="planned_amount"/>
|
||||
<field name="practical_amount"/>
|
||||
<field name="theoritical_amount"/>
|
||||
<field name="percentage" widget="percentage"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_crossovered_budget_line_form">
|
||||
<field name="name">crossovered.budget.line.form</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Budget Lines">
|
||||
<sheet>
|
||||
<group col="4">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="crossovered_budget_state" invisible="1"/>
|
||||
<field name="crossovered_budget_id" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="analytic_account_id" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="general_budget_id" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="date_from" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="date_to" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="paid_date" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="planned_amount" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="practical_amount" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="theoritical_amount" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="percentage" widget="percentage" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company" attrs="{'readonly':[('crossovered_budget_state','!=','draft')]}"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_crossovered_budget_line_pivot">
|
||||
<field name="name">crossovered.budget.line.pivot</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Budget Lines">
|
||||
<field name="crossovered_budget_id" type="row"/>
|
||||
<field name="planned_amount" type="measure" string="Planned amount"/>
|
||||
<field name="theoritical_amount" type="measure" string="Theoritical amount"/>
|
||||
<field name="practical_amount" type="measure" string="Practical amount"/>
|
||||
<field name="percentage" type="measure" widget="percentage"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_crossovered_budget_line_graph">
|
||||
<field name="name">crossovered.budget.line.graph</field>
|
||||
<field name="model">crossovered.budget.lines</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Budget Lines">
|
||||
<field name="crossovered_budget_id" type="row"/>
|
||||
<field name="planned_amount" type="measure" string="Planned amount"/>
|
||||
<field name="theoritical_amount" type="measure" string="Theoritical amount"/>
|
||||
<field name="practical_amount" type="measure" string="Practical amount"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="act_crossovered_budget_lines_view">
|
||||
<field name="name">Budgets Analysis</field>
|
||||
<field name="res_model">crossovered.budget.lines</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,pivot,graph</field>
|
||||
<field name="view_id" eval="False"/>
|
||||
<field name="context">{'search_default_group_crossevered_budgdet_id': True, 'search_default_filter_not_cancelled':True}</field>
|
||||
</record>
|
||||
|
||||
<menuitem parent="account.account_reports_management_menu"
|
||||
id="menu_act_crossovered_budget_lines_view"
|
||||
action="act_crossovered_budget_lines_view" sequence="20"
|
||||
groups="account.group_account_user"/>
|
||||
|
||||
<!-- Shortcuts -->
|
||||
<act_window name="Budget Items"
|
||||
context="{'search_default_analytic_account_id': [active_id], 'default_analytic_account_id': active_id}"
|
||||
res_model="crossovered.budget.lines"
|
||||
src_model="account.analytic.account"
|
||||
id="act_account_analytic_account_cb_lines"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user