28
28
_name = "msf.budget"
29
29
_description = 'MSF Budget'
32
32
def _get_total_budget_amounts(self, cr, uid, ids, field_names=None, arg=None, context=None):
35
SELECT expense.budget_id, COALESCE(expense.total, 0.0) - COALESCE(income.total, 0.0) AS diff
37
SELECT budget_id, SUM(COALESCE(month1 + month2 + month3 + month4 + month5 + month6 + month7 + month8 + month9 + month10 + month11 + month12, 0.0)) AS total
38
FROM msf_budget_line AS l, account_account AS a, account_account_type AS t
40
AND l.account_id = a.id
41
AND a.user_type = t.id
42
AND t.code = 'expense'
44
AND l.line_type = 'destination'
48
SELECT budget_id, SUM(COALESCE(month1 + month2 + month3 + month4 + month5 + month6 + month7 + month8 + month9 + month10 + month11 + month12, 0.0)) AS total
49
FROM msf_budget_line AS l, account_account AS a, account_account_type AS t
51
AND l.account_id = a.id
52
AND a.user_type = t.id
55
AND l.line_type = 'destination'
57
) AS income ON expense.budget_id = income.budget_id"""
58
cr.execute(sql, (tuple(ids),tuple(ids),))
59
tmp_res = cr.fetchall()
63
res.setdefault(b_id, 0.0)
64
res.update(dict(tmp_res))
67
def _get_instance_type(self, cr, uid, ids, field_names=None, arg=None, context=None):
69
Retrieve instance type regarding cost center id and check on instances which one have this cost center as "top cost center for budget"
74
for budget in self.browse(cr, uid, ids):
75
res[budget.id] = 'project'
76
if budget.cost_center_id:
77
target_ids = self.pool.get('account.target.costcenter').search(cr, uid, [('cost_center_id', '=', budget.cost_center_id.id), ('is_top_cost_center', '=', True), ('instance_id.level', '=', 'coordo')])
79
res[budget.id] = 'coordo'
80
if not budget.cost_center_id.parent_id:
81
res[budget.id] = 'section'
84
def _search_instance_type(self, cr, uid, obj, name, args, context=None):
86
Search all budget that have a cost coster used in a top_cost_center for an instance for the given type
93
if args[0] and args[0][2]:
94
target_ids = self.pool.get('account.target.costcenter').search(cr, uid, [('is_top_cost_center', '=', True), ('instance_id.level', '=', 'coordo')])
95
coordo_ids = [x and x.cost_center_id and x.cost_center_id.id for x in self.pool.get('account.target.costcenter').browse(cr, uid, target_ids)]
96
hq_ids = self.pool.get('account.analytic.account').search(cr, uid, [('parent_id', '=', False)])
97
if isinstance(hq_ids, (int, long)):
99
if args[0][2] == 'section':
100
return [('cost_center_id', 'in', hq_ids)]
101
elif args[0][2] == 'coordo':
102
return [('cost_center_id', 'in', coordo_ids)]
103
elif args[0][2] == 'project':
104
return [('cost_center_id', 'not in', hq_ids), ('cost_center_id', 'not in', coordo_ids)]
35
for budget in self.browse(cr, uid, ids, context=context):
36
total_amounts = self.pool.get('msf.budget.line')._get_total_amounts(cr, uid, [x.id for x in budget.budget_line_ids], context=context)
39
for budget_line in budget.budget_line_ids:
40
if not budget_line.parent_id:
41
res[budget.id] = total_amounts[budget_line.id]['budget_amount']
108
47
'name': fields.char('Name', size=64, required=True),
109
48
'code': fields.char('Code', size=64, required=True),
114
53
'decision_moment_order': fields.related('decision_moment_id', 'order', string="Decision Moment Order", readonly=True, store=True, type="integer"),
115
54
'version': fields.integer('Version'),
116
55
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
56
'display_type': fields.selection([('all', 'Expenses and destinations'),
57
('expense', 'Expenses only'),
58
('view', 'Parent expenses only')], string="Display type"),
117
59
'type': fields.selection([('normal', 'Normal'), ('view', 'View')], string="Budget type"),
118
'total_budget_amount': fields.function(_get_total_budget_amounts, method=True, store=False, string="Total Budget Amount", type="float", readonly=True),
119
'instance_type': fields.function(_get_instance_type, fnct_search=_search_instance_type, method=True, store=False, string='Instance type', type='selection', selection=[('section', 'HQ'), ('coordo', 'Coordo'), ('project', 'Project')], readonly=True),
60
'total_budget_amount': fields.function(_get_total_budget_amounts, method=True, store=False, string="Total Budget Amount", type="float", readonly="True"),
123
64
'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
66
'display_type': 'all',
128
70
_order = 'decision_moment_order desc, version, code'
130
def _check_parent(self, cr, uid, vals, context=None):
132
Check budget's parent to see if it exist.
133
Create it if we're on another instance that top cost center one.
134
Note: context can contains a list of budget lines. This permit to avoid problem of budget line template time consuming.
135
We hope the copy() will take less time than the creation of an entire budget template.
140
# Prepare some values
141
top_cost_center = self.pool.get('res.users').browse(cr, uid, uid).company_id.instance_id.top_cost_center_id
142
ana_obj = self.pool.get('account.analytic.account')
143
fy_obj = self.pool.get('account.fiscalyear')
144
tool_obj = self.pool.get('msf.budget.tools')
145
# Fetch cost center info (id and parent)
146
cc_id = vals.get('cost_center_id', False)
147
cc = ana_obj.read(cr, uid, cc_id, ['parent_id'], context=context)
148
parent_id = cc.get('parent_id', False) and cc.get('parent_id')[0] or False
149
# Fetch fiscalyear info
150
fy_id = vals.get('fiscalyear_id', False)
151
fy = fy_obj.read(cr, uid, fy_id, ['code'])
152
# Fetch decision moment id
153
decision_moment_id = vals.get('decision_moment_id', False)
155
# Check that no parent cost center exists for the given values
156
if cc_id and cc_id != top_cost_center.id and parent_id:
157
parent_cost_center = ana_obj.read(cr, uid, parent_id, ['code', 'name'], context=context)
158
have_parent_budget = self.search(cr, uid, [('fiscalyear_id', '=', fy_id), ('cost_center_id', '=', parent_id), ('decision_moment_id', '=', decision_moment_id)], count=1, context=context)
159
if have_parent_budget == 0:
160
# Create budget's parent
162
'name': "Budget " + fy.get('code', '')[4:6] + " - " + parent_cost_center.get('name', ''),
163
'code': "BU" + fy.get('code')[4:6] + " - " + parent_cost_center.get('code', ''),
164
'fiscalyear_id': fy_id,
165
'cost_center_id': parent_id,
166
'decision_moment_id': decision_moment_id,
169
parent_budget_id = self.create(cr, uid, budget_vals, context=context)
170
# Create budget's line.
171
tool_obj.create_budget_lines(cr, uid, parent_budget_id, context=context)
172
# Validate this parent
173
self.write(cr, uid, [parent_budget_id], {'state': 'valid'}, context=context)
176
72
def create(self, cr, uid, vals, context=None):
178
Create a budget then check its parent.
180
73
res = super(msf_budget, self).create(cr, uid, vals, context=context)
181
# Check parent budget
182
self._check_parent(cr, uid, vals, context=context)
185
def write(self, cr, uid, ids, vals, context=None):
187
Goal is to update parent budget regarding these criteria:
188
- context is synchronization
190
- state is different from draft (validated or done)
197
res = super(msf_budget, self).write(cr, uid, ids, vals, context=context)
198
if context.get('sync_update_execution', False) and vals.get('state', False) and vals.get('state') != 'draft':
199
# Update parent budget
200
self.update_parent_budgets(cr, uid, ids, context=context)
202
budget = self.browse(cr, uid, ids, context=context)[0]
203
if budget.type == 'normal' and vals.get('state') == 'done': # do not process for view accounts
204
ala_obj = self.pool.get('account.analytic.account')
206
cc_parent_ids = ala_obj._get_parent_of(cr, uid, budget.cost_center_id.id, context=context)
207
# exclude the cc of the current budget line
208
parent_cc_ids = [x for x in cc_parent_ids if x != budget.cost_center_id.id]
209
# find all ccs which have the same parent
210
all_cc_ids = ala_obj.search(cr, uid, [('parent_id','in',parent_cc_ids)], context=context)
211
# remove parent ccs from the list
212
peer_cc_ids = [x for x in all_cc_ids if x not in parent_cc_ids]
213
# find peer budget lines based on cc
214
peer_budget_ids = self.search(cr, uid, [('cost_center_id','in',peer_cc_ids),('decision_moment_id','=',budget.decision_moment_id.id),('fiscalyear_id','=',budget.fiscalyear_id.id),'!',('id','=',budget.id)],context=context)
215
peer_budgets = self.browse(cr, uid, peer_budget_ids, context=context)
218
for peer in peer_budgets:
219
if peer.state != 'done':
222
parent_ids = self.search(cr, uid, [('cost_center_id', 'in', parent_cc_ids),('decision_moment_id','=',budget.decision_moment_id.id),('fiscalyear_id','=',budget.fiscalyear_id.id),'!',('state','=','done')],context=context)
223
self.write(cr, uid, parent_ids, {'state': 'done'},context=context)
226
def update(self, cr, uid, ids, context=None):
228
Update given budget. But only update view one.
233
if isinstance(ids, (int, long)):
235
# Prepare some values
236
ana_obj = self.pool.get('account.analytic.account')
237
line_obj = self.pool.get('msf.budget.line')
240
SUM(COALESCE(month1, 0)),
241
SUM(COALESCE(month2, 0)),
242
SUM(COALESCE(month3, 0)),
243
SUM(COALESCE(month4, 0)),
244
SUM(COALESCE(month5, 0)),
245
SUM(COALESCE(month6, 0)),
246
SUM(COALESCE(month7, 0)),
247
SUM(COALESCE(month8, 0)),
248
SUM(COALESCE(month9, 0)),
249
SUM(COALESCE(month10, 0)),
250
SUM(COALESCE(month11, 0)),
251
SUM(COALESCE(month12, 0))
254
# Filter budget to only update those that are view one
255
to_update = self.search(cr, uid, [('id', 'in', ids), ('type', '=', 'view')])
256
# Then update budget, one by one, line by line...
257
for budget in self.browse(cr, uid, to_update, context=context):
258
cost_center_id = budget.cost_center_id and budget.cost_center_id.id or False
259
if not cost_center_id:
260
raise osv.except_osv(_('Error'), _('Problem while reading Cost Center for the given budget: %s') % (budget.get('name', ''),))
261
child_cc_ids = ana_obj.search(cr, uid, [('parent_id', 'child_of', cost_center_id)])
263
# For each CC, search the last budget
264
for cc_id in child_cc_ids:
266
('cost_center_id', '=', cc_id),
267
('type', '!=', 'view'),
268
('state', '!=', 'draft'),
269
('decision_moment_id', '=', budget.decision_moment_id.id)
271
corresponding_budget_ids = self.search(cr, uid, cc_args, limit=1, order='version DESC')
272
if corresponding_budget_ids:
273
budget_ids.append(corresponding_budget_ids)
274
# Browse each budget line to update it
275
for budget_line in budget.budget_line_ids:
290
# search all linked budget lines
291
args = [('budget_id', 'in', budget_ids), ('account_id', '=', budget_line.account_id.id), ('line_type', '=', budget_line.line_type)]
292
if budget_line.destination_id:
293
args.append(('destination_id', '=', budget_line.destination_id.id))
294
child_line_ids = line_obj.search(cr, uid, args, context=context)
296
cr.execute(sql, (tuple(child_line_ids),))
298
tmp_res = cr.fetchall()
299
res = tmp_res and tmp_res[0]
301
for x in xrange(1, 13, 1):
303
line_vals.update({'month'+str(x): res[x - 1]})
306
line_obj.write(cr, uid, [budget_line.id], line_vals)
309
def update_parent_budgets(self, cr, uid, ids, context=None):
311
Search all parent budget and update them.
316
if isinstance(ids, (int, long)):
318
# We only need to update parent budgets.
319
# So we search all parent cost center (but only them, so we don't care about cost center that are linked to given budgets)
320
# Then we use these parent cost center to find budget to update (only budget lines)
321
budgets = self.read(cr, uid, ids, ['cost_center_id'])
322
cost_center_ids = [x.get('cost_center_id', False) and x.get('cost_center_id')[0] or 0 for x in budgets]
323
cc_parent_ids = self.pool.get('account.analytic.account')._get_parent_of(cr, uid, cost_center_ids, context=context)
324
parent_ids = [x for x in cc_parent_ids if x not in cost_center_ids]
325
to_update = self.search(cr, uid, [('cost_center_id', 'in', parent_ids)])
327
self.update(cr, uid, to_update, context=context)
74
# If the "parent" budget does not exist and we're not on the proprietary instance level already, create it.
75
budget = self.browse(cr, uid, res, context=context)
76
prop_instance_cost_center = self.pool.get('res.users').browse(cr, uid, uid).company_id.instance_id.cost_center_id.id
77
if budget.cost_center_id and budget.cost_center_id.id != prop_instance_cost_center and budget.cost_center_id.parent_id:
78
parent_cost_center = budget.cost_center_id.parent_id
79
parent_budget_ids = self.search(cr,
81
[('fiscalyear_id','=',budget.fiscalyear_id.id),
82
('cost_center_id','=',parent_cost_center.id),
83
('decision_moment_id','=',budget.decision_moment_id.id)])
84
if len(parent_budget_ids) == 0:
85
parent_budget_id = self.create(cr,
87
{'name': "Budget " + budget.fiscalyear_id.code[4:6] + " - " + parent_cost_center.name,
88
'code': "BU" + budget.fiscalyear_id.code[4:6] + " - " + parent_cost_center.code,
89
'fiscalyear_id': budget.fiscalyear_id.id,
90
'cost_center_id': budget.cost_center_id.parent_id.id,
91
'decision_moment_id': budget.decision_moment_id.id,
92
'type': 'view'}, context=context)
93
# Create all lines for all accounts/destinations (no budget values, those are retrieved)
94
expense_account_ids = self.pool.get('account.account').search(cr, uid, [('user_type_code', '=', 'expense'),
95
('user_type_report_type', '=', 'expense'),
96
('type', '!=', 'view')], context=context)
97
destination_obj = self.pool.get('account.destination.link')
98
destination_link_ids = destination_obj.search(cr, uid, [('account_id', 'in', expense_account_ids)], context=context)
99
account_destination_ids = [(dest.account_id.id, dest.destination_id.id)
101
in destination_obj.browse(cr, uid, destination_link_ids, context=context)]
102
for account_id, destination_id in account_destination_ids:
103
budget_line_vals = {'budget_id': parent_budget_id,
104
'account_id': account_id,
105
'destination_id': destination_id,
106
'line_type': 'destination'}
107
self.pool.get('msf.budget.line').create(cr, uid, budget_line_vals, context=context)
108
# validate this parent
109
self.write(cr, uid, [parent_budget_id], {'state': 'valid'}, context=context)
112
# Methods for display view lines (warning, dirty, but it works)
330
113
def button_display_type(self, cr, uid, ids, context=None, *args, **kwargs):
332
Just reset the budget view to give the context to the one2many_budget_lines object
336
if isinstance(ids, (int, long)):
338
# do not erase the previous context!
118
for budget in self.read(cr, uid, ids, ['display_type']):
119
display_types[budget['id']] = budget['display_type']
121
for budget_id in ids:
123
if display_types[budget_id] == 'all':
125
elif display_types[budget_id] == 'expense':
127
elif display_types[budget_id] == 'view':
129
self.write(cr, uid, [budget_id], {'display_type': result}, context=context)
133
def budget_summary_open_window(self, cr, uid, ids, context=None):
134
parent_line_id = False
135
fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid, datetime.date.today(), True, context=context)
136
cost_center_ids = self.pool.get('account.analytic.account').search(cr, uid, [('category', '=', 'OC'), ('parent_id', '=', False)], context=context)
137
if len(cost_center_ids) != 0:
138
cr.execute("SELECT id FROM msf_budget WHERE fiscalyear_id = %s \
139
AND cost_center_id = %s \
140
AND state != 'draft' \
141
ORDER BY decision_moment_order DESC, version DESC LIMIT 1",
146
budget_id = cr.fetchall()[0][0]
147
parent_line_id = self.pool.get('msf.budget.summary').create(cr,
149
{'budget_id': budget_id},
344
'name': _('Budgets'),
345
'type': 'ir.actions.act_window',
346
'res_model': 'msf.budget',
348
'view_mode': 'form,tree',
153
'type': 'ir.actions.act_window',
154
'res_model': 'msf.budget.summary',
158
'domain': [('id', '=', parent_line_id)],
354
def budget_summary_open_window(self, cr, uid, ids, context=None):
357
fiscalyear_id = self.pool.get('account.fiscalyear').find(cr, uid, datetime.date.today(), True, context=context)
358
prop_instance = self.pool.get('res.users').browse(cr, uid, uid).company_id.instance_id
359
if prop_instance.top_cost_center_id:
360
cr.execute("SELECT id FROM msf_budget WHERE fiscalyear_id = %s \
361
AND cost_center_id = %s \
362
AND state != 'draft' \
363
ORDER BY decision_moment_order DESC, version DESC LIMIT 1",
365
prop_instance.top_cost_center_id.id))
368
budget_id = cr.fetchall()[0][0]
370
if isinstance(ids, (int, long)):
375
parent_line_id = self.pool.get('msf.budget.summary').create(cr,
376
uid, {'budget_id': budget_id}, context=context)
378
context.update({'display_fp': True})
380
'type': 'ir.actions.act_window',
381
'res_model': 'msf.budget.summary',
385
'domain': [('id', '=', parent_line_id)],
390
def action_confirmed(self, cr, uid, ids, context=None):
392
At budget validation we should update all parent budgets.
393
To do this, each parent need to take all its validated children budget at the last version.
398
if isinstance(ids, (int, long)):
400
# Only validate budget that are draft!
402
for budget in self.read(cr, uid, ids, ['state']):
403
if budget.get('state', '') and budget.get('state') == 'draft':
404
to_validate.append(budget.get('id', 0))
405
# Change budget statuses. Important in order to include given budgets in their parents!
406
self.write(cr, uid, to_validate, {'state': 'valid'}, context=context)
407
# Update parent budget
408
self.update_parent_budgets(cr, uid, to_validate, context=context)
411
def unlink(self, cr, uid, ids, context=None):
413
UFTP-156: Make sure that the validated budget cannot be deleted
415
for budget in self.browse(cr, uid, ids, context=context):
416
if budget.state == 'valid':
417
raise osv.except_osv(_('Error'), _('You cannot delete the validated budget!'))
419
return super(msf_budget, self).unlink(cr, uid, budget.id, context=context)
422
163
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: