62
37
domain = domain[:-2]
66
# get list of accounts for duplet format lines
67
def _create_account_couple_domain(self, account_destination_list, general_domain):
41
def _create_account_destination_domain(self, account_destination_list):
68
42
if len(account_destination_list) == 0:
69
return False # Just make this condition to False
44
('general_account_id', 'in', []),
45
('destination_id', 'in', [])]
70
46
elif len(account_destination_list) == 1:
72
48
('general_account_id', '=', account_destination_list[0].account_id.id),
73
49
('destination_id', '=', account_destination_list[0].destination_id.id)]
77
firstElement = self._create_account_couple_domain([account_destination_list[0]], general_domain)
78
secondElement = self._create_account_couple_domain(account_destination_list[1:], general_domain)
80
if firstElement and secondElement:
81
return ['|'] + firstElement + secondElement
86
# get list of accounts for quadruplet format lines
87
def _create_account_quadruplet_domain(self, account_quadruplet_list, funding_pool_ids=False):
88
if len(account_quadruplet_list) == 0:
90
elif len(account_quadruplet_list) == 1:
91
if account_quadruplet_list[0].funding_pool_id.id in funding_pool_ids:
92
quad_element = account_quadruplet_list[0]
95
('general_account_id', '=', quad_element.account_id.id),
96
('destination_id', '=', quad_element.account_destination_id.id),
98
('cost_center_id', '=', quad_element.cost_center_id.id),
99
('account_id', '=', quad_element.funding_pool_id.id)]
103
firstElement = self._create_account_quadruplet_domain([account_quadruplet_list[0]], funding_pool_ids)
104
secondElement = self._create_account_quadruplet_domain(account_quadruplet_list[1:], funding_pool_ids)
106
if firstElement and secondElement:
107
return ['|'] + firstElement + secondElement
51
return ['|'] + self._create_account_destination_domain([account_destination_list[0]]) + self._create_account_destination_domain(account_destination_list[1:])
112
53
def _get_number_of_childs(self, cr, uid, ids, field_name=None, arg=None, context=None):
142
82
for total_cost in total_costs.values():
143
83
result += total_cost
146
def button_delete_all_quads(self, cr, uid, ids, context=None):
147
self.write(cr, uid, ids, {'account_quadruplet_ids':[(6, 0, [])]}, context=context )
150
def button_delete_all_couples(self, cr, uid, ids, context=None):
151
self.write(cr, uid, ids, {'account_destination_ids':[(6, 0, [])]}, context=context )
154
# Get the list of accounts for both duplet and quadruplet
155
def _get_accounts_couple_and_quadruplets(self, browse_line):
156
account_destination_result = []
157
account_quadruplet_result = []
86
def _get_account_destination_ids(self, browse_line, funding_pool_account_destination_ids):
159
88
if browse_line.line_type != 'view':
160
if browse_line.is_quadruplet:
161
account_quadruplet_result = [account_quadruplet for account_quadruplet in browse_line.account_quadruplet_ids]
163
account_destination_result = [account_destination for account_destination in browse_line.account_destination_ids]
89
result = [account_destination for account_destination in browse_line.account_destination_ids if account_destination.id in funding_pool_account_destination_ids]
165
91
for child_line in browse_line.child_ids:
166
temp = self._get_accounts_couple_and_quadruplets(child_line)
167
account_destination_result += temp['account_destination_list']
168
account_quadruplet_result += temp['account_quadruplet_list']
169
return {'account_destination_list': account_destination_result,
170
'account_quadruplet_list': account_quadruplet_result}
92
result += self._get_account_destination_ids(child_line, funding_pool_account_destination_ids)
172
95
def _get_general_domain(self, cr, uid, browse_format, domain_type, context=None):
173
96
# Method to get the domain (allocated or project) of a line
175
97
date_domain = "[('document_date', '>=', '"
176
98
date_domain += browse_format.eligibility_from_date
177
99
date_domain += "'), ('document_date', '<=', '"
178
100
date_domain += browse_format.eligibility_to_date
179
101
date_domain += "')]"
182
gen_domain['date_domain'] = date_domain
184
cost_center_domain = self._create_domain('cost_center_id', browse_format.cost_center_ids)
185
gen_domain['cost_center_domain'] = cost_center_domain
188
# note: even though reporting should be from quadruplets and couples, an additional verification against funding pools
189
# and cost centers is required. This is because a user could follow this sequence -
190
# 1. add a funding pool to the contract
191
# 2. create quads based on it
192
# 3. remove the funding pool
193
# 4. The quad reporting line still refers to the removed FP.
102
# list of expense accounts in the funding pools.
103
# we take them all (funded and project), as for funded,
104
# we are sure that no allocation will be done with
105
# (funded funding pool, account in other funding pool)
106
funding_pool_account_destination_ids = []
107
for funding_pool_line in browse_format.funding_pool_ids:
108
funding_pool = funding_pool_line.funding_pool_id
109
for account_destination in funding_pool.tuple_destination_account_ids:
110
funding_pool_account_destination_ids.append(account_destination.id)
197
112
funding_pool_ids = []
198
if domain_type == 'allocated':
113
if domain_type == 'allocated':
199
114
funding_pool_ids = [funding_pool_line.funding_pool_id for funding_pool_line in browse_format.funding_pool_ids if funding_pool_line.funded]
201
116
funding_pool_ids = [funding_pool_line.funding_pool_id for funding_pool_line in browse_format.funding_pool_ids]
204
# remove 'FP' funding pool from the list if it exists.
205
fp_ids = self.pool.get('account.analytic.account').search(cr, uid, ['&',('category','=','FUNDING'),('code','=','FP')], context=None)
206
funding_pool_ids = [x for x in funding_pool_ids if x not in fp_ids]
207
funding_pool_domain = self._create_domain('account_id', funding_pool_ids)
208
gen_domain['funding_pool_domain'] = funding_pool_domain
210
gen_domain['funding_pool_ids'] = [x.id for x in funding_pool_ids]
214
def _get_analytic_domain(self, cr, uid, browse_line, domain_type, isFirst=True, context=None):
117
funding_pool_domain = self._create_domain('account_id', funding_pool_ids)
119
cost_center_domain = self._create_domain('cost_center_id', browse_format.cost_center_ids)
120
return {'date_domain': date_domain,
121
'funding_pool_domain': funding_pool_domain,
122
'cost_center_domain': cost_center_domain,
123
'funding_pool_account_destination_ids': funding_pool_account_destination_ids}
125
def _get_analytic_domain(self, cr, uid, browse_line, domain_type, context=None):
215
126
if browse_line.line_type in ('consumption', 'overhead'):
216
# No domain for this case
127
# No domain for those
219
# last domain: get only non-corrected lines.
220
non_corrected_domain = [('is_reallocated', '=', False),('is_reversal', '=', False)]
221
130
format = browse_line.format_id
222
131
if format.eligibility_from_date and format.eligibility_to_date:
223
#### DUY US-385: MOVE THIS TO OUTSIDE OF THE ALL THE LOOPS
224
132
general_domain = self._get_general_domain(cr, uid, format, domain_type, context=context)
226
133
# Account + destination domain
227
account_destination_quadruplet_ids = self._get_accounts_couple_and_quadruplets(browse_line)
228
account_couple_domain = self._create_account_couple_domain(account_destination_quadruplet_ids['account_destination_list'], False)
229
# get the criteria for accounts of quadruplet mode
230
account_quadruplet_domain = self._create_account_quadruplet_domain(account_destination_quadruplet_ids['account_quadruplet_list'], general_domain['funding_pool_ids'])
232
if not account_couple_domain and not account_quadruplet_domain:
233
return [('id', '=', '-1')]
235
accounts_criteria = ['&', '&', ] + non_corrected_domain
236
if account_couple_domain and account_quadruplet_domain:
237
accounts_criteria += ['|'] + account_couple_domain + account_quadruplet_domain
238
elif account_couple_domain:
239
accounts_criteria += account_couple_domain
240
elif account_quadruplet_domain:
241
accounts_criteria += account_quadruplet_domain
243
return accounts_criteria
134
account_destination_ids = self._get_account_destination_ids(browse_line, general_domain['funding_pool_account_destination_ids'])
135
account_domain = self._create_account_destination_domain(account_destination_ids)
136
# create the final domain
137
date_domain = eval(general_domain['date_domain'])
138
return [date_domain[0], date_domain[1]] + account_domain + [eval(general_domain['funding_pool_domain']), eval(general_domain['cost_center_domain'])]
247
def _is_overhead_present(self, cr, uid, ids, context={}):
248
for line in self.browse(cr, uid, ids, context=context):
249
if line.line_type == 'overhead':
251
elif line.line_type == 'view':
252
if self._is_overhead_present(cr, uid, [x.id for x in line.child_ids], context=context):
256
def _get_actual_line_ids(self, cr, uid, ids, context={}):
258
for line in self.browse(cr, uid, ids, context=context):
259
if line.line_type == 'view':
260
actual_line_ids += self._get_actual_line_ids(cr, uid, [x.id for x in line.child_ids], context=context)
261
elif line.line_type in ['actual', 'consumption']:
262
actual_line_ids.append(line.id)
263
return actual_line_ids
265
def _get_view_amount(self, browse_line, total_costs, retrieved_lines):
266
if browse_line.line_type == 'view':
268
for child_line in browse_line.child_ids:
269
if child_line.id not in retrieved_lines:
270
self._get_view_amount(child_line, total_costs, retrieved_lines)
271
sum += retrieved_lines[child_line.id]
272
retrieved_lines[browse_line.id] = sum
273
elif browse_line.line_type == 'overhead':
274
if browse_line.overhead_type == 'cost_percentage':
275
# percentage of all costs (sum of all 2nd-level lines, except overhead)
276
retrieved_lines[browse_line.id] = round(total_costs * browse_line.overhead_percentage / 100.0)
277
elif browse_line.overhead_type == 'grant_percentage':
278
# percentage of all costs (sum of all 2nd-level lines, except overhead)
279
retrieved_lines[browse_line.id] = round(total_costs * browse_line.overhead_percentage / (100.0 - browse_line.overhead_percentage))
140
# Dates are not set (since we are probably in a donor).
282
144
def _get_budget_amount(self, cr, uid, ids, field_name=None, arg=None, context=None):
284
146
Method to compute the allocated budget/amounts, depending on the information in the line
287
# 1st step: get the real list of actual lines to compute
289
overhead = self._is_overhead_present(cr, uid, ids, context=context)
290
if overhead and len(ids) > 0:
291
line_for_format = self.browse(cr, uid, ids[0], context=context)
292
actual_line_ids = [line.id for line in line_for_format.format_id.actual_line_ids if line.line_type in ['actual', 'consumption']]
294
actual_line_ids = self._get_actual_line_ids(cr, uid, ids, context=context)
296
# 2nd step: retrieve values for all actual lines above
297
for line in self.browse(cr, uid, actual_line_ids, context=context):
149
for line in self.browse(cr, uid, ids, context=context):
299
151
res[line.id] = 0.0
300
152
if line.line_type:
340
190
res[line.id] = line.allocated_real_value
341
191
elif field_name == 'project_real':
342
192
res[line.id] = line.project_real_value
193
elif line.line_type == 'view':
194
# Sum of all its children
196
for child_line in line.child_ids:
197
child_values = self._get_actual_amount(cr, uid, [child_line.id], field_name, context=context)
198
sum_real += child_values[child_line.id]
199
res[line.id] = sum_real
200
elif line.line_type == 'overhead':
202
if line.overhead_type == 'cost_percentage':
203
# percentage of all costs (sum of all 2nd-level lines, except overhead)
204
total_costs = self._get_total_costs(cr, uid, line, field_name, context=context)
205
res[line.id] = round(total_costs * line.overhead_percentage / 100.0)
206
elif line.overhead_type == 'grant_percentage':
207
# percentage of all costs (sum of all 2nd-level lines, except overhead)
208
total_costs = self._get_total_costs(cr, uid, line, field_name, context=context)
209
res[line.id] = round(total_costs * line.overhead_percentage / (100.0 - line.overhead_percentage))
343
210
elif line.line_type == 'actual':
344
211
# sum of analytic lines, determined by the domain
345
212
analytic_domain = []
347
213
if field_name == 'project_real':
348
analytic_domain = self._get_analytic_domain(cr, uid, line, 'project', True, context=context)
349
report_type = 'project'
214
analytic_domain = self._get_analytic_domain(cr, uid, line, 'project', context=context)
350
215
elif field_name == 'allocated_real':
351
analytic_domain = self._get_analytic_domain(cr, uid, line, 'allocated', True, context=context)
352
report_type = 'allocated'
354
analytic_domain = self.pool.get('financing.contract.contract').add_general_domain(cr, uid, analytic_domain, line.format_id, report_type, context)
216
analytic_domain = self._get_analytic_domain(cr, uid, line, 'allocated', context=context)
355
217
# selection of analytic lines
356
if 'reporting_currency' in context: # TODO Why do we only get analytic lines if reporting_currency in context
218
if 'reporting_currency' in context:
357
219
analytic_line_obj = self.pool.get('account.analytic.line')
358
220
analytic_lines = analytic_line_obj.search(cr, uid, analytic_domain ,context=context)
359
# list of analytic journal_ids which are in the engagement journals
360
exclude_journal_ids = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=','engagement')])
361
exclude_line_ids = []
362
for analytic_line in analytic_line_obj.browse(cr, uid, analytic_lines, context=None):
363
if analytic_line.journal_id.id in exclude_journal_ids:
364
exclude_line_ids.append(analytic_line.id)
365
analytic_lines = [x for x in analytic_lines if x not in exclude_line_ids]
367
222
currency_table = None
368
223
if 'currency_table_id' in context:
373
228
real_sum += self.pool.get('res.currency').compute(cr,
375
230
analytic_line.currency_id.id,
376
context['reporting_currency'],
231
context['reporting_currency'],
377
232
analytic_line.amount_currency or 0.0,
379
234
context=date_context)
380
235
# Invert the result from the lines (positive for out, negative for in)
381
236
real_sum = -real_sum
382
237
res[line.id] = real_sum
384
# 3rd step: compute all remaining lines from the retrieved results
385
total_actual_costs = 0.0
387
total_actual_costs = sum(res.values())
388
for line in self.browse(cr, uid, [id for id in ids if id not in actual_line_ids], context=context):
389
if line.id not in res:
390
self._get_view_amount(line, total_actual_costs, res)
396
242
'name': fields.char('Name', size=64, required=True),
397
243
'code': fields.char('Code', size=16, required=True),
398
244
'format_id': fields.many2one('financing.contract.format', 'Format'),
399
'is_quadruplet': fields.boolean('Input CC/FP at line level?'),
400
'account_destination_ids': many2many_sorted('account.destination.link', 'financing_contract_actual_account_destinations', 'actual_line_id', 'account_destination_id', string='Accounts/Destinations', domain=ACCOUNT_RESTRICTED_AREA['contract_reporting_lines']),
401
'account_quadruplet_ids': many2many_sorted('financing.contract.account.quadruplet', 'financing_contract_actual_account_quadruplets', 'actual_line_id', 'account_quadruplet_id', string='Accounts/Destinations/Funding Pools/Cost Centres'),
245
'account_destination_ids': many2many_sorted('account.destination.link', 'financing_contract_actual_account_destinations', 'actual_line_id', 'account_destination_id', string='Accounts/Destinations', domain=[('account_id.user_type_report_type', '=', 'expense')]),
402
246
'parent_id': fields.many2one('financing.contract.format.line', 'Parent line'),
403
247
'child_ids': fields.one2many('financing.contract.format.line', 'parent_id', 'Child lines'),
404
248
'line_type': fields.selection([('view','View'),
412
256
'allocated_real_value': fields.float('Real allocated amount (value)'),
413
257
'project_real_value': fields.float('Real project amount (value)'),
414
258
'overhead_percentage': fields.float('Overhead percentage'),
416
259
'allocated_budget': fields.function(_get_budget_amount, method=True, store=False, string="Funded - Budget", type="float", readonly=True),
417
260
'project_budget': fields.function(_get_budget_amount, method=True, store=False, string="Total project - Budget", type="float", readonly=True),
419
261
'allocated_real': fields.function(_get_actual_amount, method=True, store=False, string="Funded - Actuals", type="float", readonly=True),
420
262
'project_real': fields.function(_get_actual_amount, method=True, store=False, string="Total project - Actuals", type="float", readonly=True),
421
'quadruplet_update': fields.text('Internal Use Only'),
422
'instance_id': fields.many2one('msf.instance','Proprietary Instance'),
426
'is_quadruplet': False,
427
266
'line_type': 'actual',
428
267
'overhead_type': 'cost_percentage',
429
268
'parent_id': lambda *a: False
434
# UF-2311: Calculate the quadruplet value before writing or creating the format line
435
def calculate_quaduplet(self, vals, context):
436
if 'line_type' in vals and vals['line_type'] == 'view':
437
vals['allocated_amount'] = 0.0
438
vals['project_amount'] = 0.0
439
vals['account_destination_ids'] = [(6, 0, [])]
440
vals['account_quadruplet_ids'] = [(6, 0, [])]
441
elif 'is_quadruplet' in vals: # If the vals contains quadruplet value, then check if it is true or false
442
if vals.get('is_quadruplet', False):
443
# delete account/destinations
444
vals['account_destination_ids'] = [(6, 0, [])]
445
if context.get('sync_update_execution'):
447
if vals.get('quadruplet_update', False):
448
quadrup_str = vals['quadruplet_update']
449
quads_list = map(int, quadrup_str.split(','))
450
vals['account_quadruplet_ids'] = [(6, 0, quads_list)]
452
temp = vals['account_quadruplet_ids']
454
vals['quadruplet_update'] = str(temp[0][2]).strip('[]')
456
vals['account_quadruplet_ids'] = [(6, 0, [])]
457
vals['quadruplet_update'] = '' # delete quadruplets
459
271
def create(self, cr, uid, vals, context=None):
463
# calculate the quadruplet combination
464
self.calculate_quaduplet(vals, context)
274
# if the account is set as view, remove budget and account values
275
if 'line_type' in vals and vals['line_type'] == 'view':
276
vals['allocated_amount'] = 0.0
277
vals['project_amount'] = 0.0
278
vals['account_destination_ids'] = []
465
279
return super(financing_contract_format_line, self).create(cr, uid, vals, context=context)
467
281
def write(self, cr, uid, ids, vals, context=None):
470
284
if isinstance(ids, (int, long)):
473
# US-180: Check if it comes from the sync update
474
if context.get('sync_update_execution') and vals.get('format_id', False):
475
# US-180: and if the financing contract of the contract format does not exist, then just ignore this update
476
exist = self.pool.get('financing.contract.contract').search(cr, uid, [('format_id', '=', vals['format_id'])])
477
if not exist: # No contract found for this format line
480
# calculate the quadruplet combination
481
self.calculate_quaduplet(vals, context)
286
# if the account is set as view, remove budget and account values
287
if 'line_type' in vals and vals['line_type'] == 'view':
288
vals['allocated_amount'] = 0.0
289
vals['project_amount'] = 0.0
290
vals['account_destination_ids'] = [(6, 0, [])]
482
291
return super(financing_contract_format_line, self).write(cr, uid, ids, vals, context=context)
484
293
def copy_format_line(self, cr, uid, browse_source_line, destination_format_id, parent_id=None, context=None):
485
294
if destination_format_id:
486
295
format_line_vals = {