~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to financing_contract/format_line.py

  • Committer: Quentin THEURET
  • Date: 2011-12-12 08:02:59 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: qt@tempo-consulting.fr-20111212080259-oul1f0g37hcpubyc
UF-641 [ADD] Added the empty purchase_followup module

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
##############################################################################
3
 
#
4
 
#    OpenERP, Open Source Management Solution
5
 
#    Copyright (C) 2011 MSF, TeMPO Consulting.
6
 
#
7
 
#    This program is free software: you can redistribute it and/or modify
8
 
#    it under the terms of the GNU Affero General Public License as
9
 
#    published by the Free Software Foundation, either version 3 of the
10
 
#    License, or (at your option) any later version.
11
 
#
12
 
#    This program is distributed in the hope that it will be useful,
13
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
#    GNU Affero General Public License for more details.
16
 
#
17
 
#    You should have received a copy of the GNU Affero General Public License
18
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 
#
20
 
##############################################################################
21
 
 
22
 
from osv import fields, osv
23
 
import pooler
24
 
from analytic_distribution.destination_tools import many2many_sorted
25
 
from account_override import ACCOUNT_RESTRICTED_AREA
26
 
 
27
 
 
28
 
class financing_contract_format_line(osv.osv):
29
 
    
30
 
    _name = "financing.contract.format.line"
31
 
    
32
 
    def _create_domain(self, header, element_list):
33
 
        domain = "('" + header + "', 'in', ["
34
 
        if len(element_list) > 0:
35
 
            for element in element_list:
36
 
                domain += str(element.id)
37
 
                domain += ', '
38
 
            domain = domain[:-2]
39
 
        domain += "])"
40
 
        return domain
41
 
    
42
 
    def _create_account_destination_domain(self, account_destination_list, general_domain):
43
 
        if len(account_destination_list) == 0:
44
 
            return ['&',
45
 
                    '&',
46
 
                    ('general_account_id', 'in', []),
47
 
                    ('destination_id', 'in', []),
48
 
                    '&',
49
 
                    eval(general_domain['cost_center_domain']),
50
 
                    eval(general_domain['funding_pool_domain'])]
51
 
        elif len(account_destination_list) == 1:
52
 
            return ['&',
53
 
                    '&',
54
 
                    ('general_account_id', '=', account_destination_list[0].account_id.id),
55
 
                    ('destination_id', '=', account_destination_list[0].destination_id.id),
56
 
                    '&',
57
 
                    eval(general_domain['cost_center_domain']),
58
 
                    eval(general_domain['funding_pool_domain'])]
59
 
        else:
60
 
            return ['|'] \
61
 
                 + self._create_account_destination_domain([account_destination_list[0]], general_domain) \
62
 
                 + self._create_account_destination_domain(account_destination_list[1:], general_domain)
63
 
    
64
 
    def _create_account_quadruplet_domain(self, account_quadruplet_list, funding_pool_ids):
65
 
        if len(account_quadruplet_list) == 0:
66
 
            return ['&',
67
 
                    '&',
68
 
                    ('general_account_id', '=', False),
69
 
                    ('destination_id', '=', False),
70
 
                    '&',
71
 
                    ('cost_center_id', '=', False),
72
 
                    ('account_id', '=', False)]
73
 
        elif len(account_quadruplet_list) == 1:
74
 
            if account_quadruplet_list[0].funding_pool_id.id in funding_pool_ids:
75
 
                return ['&',
76
 
                        '&',
77
 
                        ('general_account_id', '=', account_quadruplet_list[0].account_destination_id.account_id.id),
78
 
                        ('destination_id', '=', account_quadruplet_list[0].account_destination_id.destination_id.id),
79
 
                        '&',
80
 
                        ('cost_center_id', '=', account_quadruplet_list[0].cost_center_id.id),
81
 
                        ('account_id', '=', account_quadruplet_list[0].funding_pool_id.id)]
82
 
            else:
83
 
                return ['&',
84
 
                        '&',
85
 
                        ('general_account_id', '=', False),
86
 
                        ('destination_id', '=', False),
87
 
                        '&',
88
 
                        ('cost_center_id', '=', False),
89
 
                        ('account_id', '=', False)]
90
 
        else:
91
 
            return ['|'] \
92
 
                 + self._create_account_quadruplet_domain([account_quadruplet_list[0]], funding_pool_ids) \
93
 
                 + self._create_account_quadruplet_domain(account_quadruplet_list[1:], funding_pool_ids)
94
 
 
95
 
    def _get_number_of_childs(self, cr, uid, ids, field_name=None, arg=None, context=None):
96
 
        # Verifications
97
 
        if not context:
98
 
            context = {}
99
 
        if isinstance(ids, (int, long)):
100
 
            ids = [ids]
101
 
        # Prepare some values
102
 
        res = {}
103
 
        for line in self.browse(cr, uid, ids, context=context):
104
 
            res[line.id] = line.child_ids and len(line.child_ids) or 0
105
 
        return res
106
 
    
107
 
    def _get_parent_ids(self, cr, uid, ids, context=None):
108
 
        res = []
109
 
        for line in self.browse(cr, uid, ids, context=context):
110
 
            if line.parent_id:
111
 
                res.append(line.parent_id.id)
112
 
        return res
113
 
    
114
 
    def _get_total_costs(self, cr, uid, browse_overhead_line, field_name=None, context=None):
115
 
        # since this is only called from the overhead line, the domain is
116
 
        # "all children, except the overhead lines"
117
 
        result = 0.0
118
 
        total_line_ids = [line.id for line in browse_overhead_line.format_id.actual_line_ids if line.line_type in ['actual', 'consumption']]
119
 
        total_costs = {}
120
 
        if field_name and field_name in ('allocated_budget', 'project_budget'):
121
 
            total_costs = self._get_budget_amount(cr, uid, total_line_ids, field_name, context=context)
122
 
        else:
123
 
            total_costs = self._get_actual_amount(cr, uid, total_line_ids, field_name, context=context)
124
 
        for total_cost in total_costs.values():
125
 
            result += total_cost
126
 
        return result
127
 
    
128
 
    def _get_account_destination_quadruplets(self, browse_line):
129
 
        account_destination_result = []
130
 
        account_quadruplet_result = []
131
 
        if browse_line.line_type != 'view':
132
 
            if browse_line.is_quadruplet:
133
 
                account_quadruplet_result = [account_quadruplet for account_quadruplet in browse_line.account_quadruplet_ids]
134
 
            else:
135
 
                account_destination_result = [account_destination for account_destination in browse_line.account_destination_ids]
136
 
        else:
137
 
            for child_line in browse_line.child_ids:
138
 
                temp = self._get_account_destination_quadruplets(child_line)
139
 
                account_destination_result += temp['account_destination_list']
140
 
                account_quadruplet_result += temp['account_quadruplet_list']
141
 
        return {'account_destination_list': account_destination_result,
142
 
                'account_quadruplet_list': account_quadruplet_result}
143
 
    
144
 
    def _get_general_domain(self, cr, uid, browse_format, domain_type, context=None):
145
 
        # Method to get the domain (allocated or project) of a line
146
 
        date_domain = "[('document_date', '>=', '"
147
 
        date_domain += browse_format.eligibility_from_date
148
 
        date_domain += "'), ('document_date', '<=', '"
149
 
        date_domain += browse_format.eligibility_to_date
150
 
        date_domain += "')]"
151
 
        # Funding pools
152
 
        funding_pool_ids = []
153
 
        if domain_type == 'allocated': 
154
 
            funding_pool_ids = [funding_pool_line.funding_pool_id for funding_pool_line in browse_format.funding_pool_ids if funding_pool_line.funded]
155
 
        else:
156
 
            funding_pool_ids = [funding_pool_line.funding_pool_id for funding_pool_line in browse_format.funding_pool_ids]
157
 
        funding_pool_domain = self._create_domain('account_id', funding_pool_ids)
158
 
        # Cost centers
159
 
        cost_center_domain = self._create_domain('cost_center_id', browse_format.cost_center_ids)
160
 
        return {'date_domain': date_domain,
161
 
                'funding_pool_domain': funding_pool_domain,
162
 
                'cost_center_domain': cost_center_domain,
163
 
                'funding_pool_ids': [x.id for x in funding_pool_ids]}
164
 
    
165
 
    def _get_analytic_domain(self, cr, uid, browse_line, domain_type, context=None):
166
 
        if browse_line.line_type in ('consumption', 'overhead'):
167
 
            # No domain for those
168
 
            return False
169
 
        else:
170
 
            # last domain: get only non-corrected lines.
171
 
            non_corrected_domain = [('is_reallocated', '=', False),
172
 
                                    ('is_reversal', '=', False)]
173
 
            format = browse_line.format_id
174
 
            if format.eligibility_from_date and format.eligibility_to_date:
175
 
                general_domain = self._get_general_domain(cr, uid, format, domain_type, context=context)
176
 
                # Account + destination domain
177
 
                account_destination_quadruplet_ids = self._get_account_destination_quadruplets(browse_line)
178
 
                account_destination_domain = self._create_account_destination_domain(account_destination_quadruplet_ids['account_destination_list'], general_domain)
179
 
                account_quadruplet_domain = self._create_account_quadruplet_domain(account_destination_quadruplet_ids['account_quadruplet_list'], general_domain['funding_pool_ids'])
180
 
                # create the final domain
181
 
                date_domain = eval(general_domain['date_domain'])
182
 
                return [date_domain[0], date_domain[1]]  + non_corrected_domain + ['|'] + account_destination_domain + account_quadruplet_domain
183
 
            else:
184
 
                # Dates are not set (since we are probably in a donor).
185
 
                # Return False
186
 
                return False
187
 
    
188
 
    def _is_overhead_present(self, cr, uid, ids, context={}):
189
 
        for line in self.browse(cr, uid, ids, context=context):
190
 
            if line.line_type == 'overhead':
191
 
                return True
192
 
            elif line.line_type == 'view':
193
 
                if self._is_overhead_present(cr, uid, [x.id for x in line.child_ids], context=context):
194
 
                    return True
195
 
        return False
196
 
    
197
 
    def _get_actual_line_ids(self, cr, uid, ids, context={}):
198
 
        actual_line_ids = []
199
 
        for line in self.browse(cr, uid, ids, context=context):
200
 
            if line.line_type == 'view':
201
 
                actual_line_ids += self._get_actual_line_ids(cr, uid, [x.id for x in line.child_ids], context=context)
202
 
            elif line.line_type in ['actual', 'consumption']:
203
 
                actual_line_ids.append(line.id)
204
 
        return actual_line_ids
205
 
    
206
 
    def _get_view_amount(self, browse_line, total_costs, retrieved_lines):
207
 
        if browse_line.line_type == 'view':
208
 
            sum = 0.0
209
 
            for child_line in browse_line.child_ids:
210
 
                if child_line.id not in retrieved_lines:
211
 
                    self._get_view_amount(child_line, total_costs, retrieved_lines)
212
 
                sum += retrieved_lines[child_line.id]
213
 
            retrieved_lines[browse_line.id] = sum
214
 
        elif browse_line.line_type == 'overhead':
215
 
            if browse_line.overhead_type == 'cost_percentage':
216
 
                # percentage of all costs (sum of all 2nd-level lines, except overhead)
217
 
                retrieved_lines[browse_line.id] = round(total_costs * browse_line.overhead_percentage / 100.0)
218
 
            elif browse_line.overhead_type == 'grant_percentage':
219
 
                # percentage of all costs (sum of all 2nd-level lines, except overhead)
220
 
                retrieved_lines[browse_line.id] = round(total_costs * browse_line.overhead_percentage / (100.0 - browse_line.overhead_percentage))
221
 
        
222
 
 
223
 
    def _get_budget_amount(self, cr, uid, ids, field_name=None, arg=None, context=None):
224
 
        """
225
 
            Method to compute the allocated budget/amounts, depending on the information in the line
226
 
        """
227
 
        res = {}
228
 
        # 1st step: get the real list of actual lines to compute
229
 
        actual_line_ids = []
230
 
        overhead = self._is_overhead_present(cr, uid, ids, context=context)
231
 
        if overhead and len(ids) > 0:
232
 
            line_for_format = self.browse(cr, uid, ids[0], context=context)
233
 
            actual_line_ids = [line.id for line in line_for_format.format_id.actual_line_ids if line.line_type in ['actual', 'consumption']]
234
 
        else:
235
 
            actual_line_ids = self._get_actual_line_ids(cr, uid, ids, context=context)
236
 
        
237
 
        # 2nd step: retrieve values for all actual lines above
238
 
        for line in self.browse(cr, uid, actual_line_ids, context=context):
239
 
            # default value
240
 
            res[line.id] = 0.0
241
 
            if line.line_type:
242
 
                if line.line_type in ('actual', 'consumption'):
243
 
                    # Value is set by the user, just return it
244
 
                    if field_name == 'allocated_budget':
245
 
                        res[line.id] = line.allocated_budget_value
246
 
                    elif field_name == 'project_budget':
247
 
                        res[line.id] = line.project_budget_value
248
 
                        
249
 
        # 3rd step: compute all remaining lines from the retrieved results
250
 
        total_budget_costs = 0.0
251
 
        if overhead:
252
 
            total_budget_costs = sum(res.values())
253
 
        for line in self.browse(cr, uid, [id for id in ids if id not in actual_line_ids], context=context):
254
 
            if line.id not in res:
255
 
                self._get_view_amount(line, total_budget_costs, res)
256
 
        
257
 
        return res
258
 
 
259
 
    def _get_actual_amount(self, cr, uid, ids, field_name=None, arg=None, context=None):
260
 
        """
261
 
            Method to compute the allocated budget/amounts, depending on the information in the line
262
 
        """
263
 
        res = {}
264
 
        # 1st step: get the real list of actual lines to compute
265
 
        actual_line_ids = []
266
 
        overhead = self._is_overhead_present(cr, uid, ids, context=context)
267
 
        if overhead and len(ids) > 0:
268
 
            line_for_format = self.browse(cr, uid, ids[0], context=context)
269
 
            actual_line_ids = [line.id for line in line_for_format.format_id.actual_line_ids if line.line_type in ['actual', 'consumption']]
270
 
        else:
271
 
            actual_line_ids = self._get_actual_line_ids(cr, uid, ids, context=context)
272
 
        
273
 
        # 2nd step: retrieve values for all actual lines above
274
 
        for line in self.browse(cr, uid, actual_line_ids, context=context):
275
 
            # default value
276
 
            res[line.id] = 0.0
277
 
            if line.line_type:
278
 
                if line.line_type == 'consumption':
279
 
                    # Value is set by the user, just return it
280
 
                    if field_name == 'allocated_real':
281
 
                        res[line.id] = line.allocated_real_value
282
 
                    elif field_name == 'project_real':
283
 
                        res[line.id] = line.project_real_value
284
 
                elif line.line_type == 'actual':
285
 
                    # sum of analytic lines, determined by the domain
286
 
                    analytic_domain = []
287
 
                    if field_name == 'project_real':
288
 
                        analytic_domain = self._get_analytic_domain(cr, uid, line, 'project', context=context)
289
 
                    elif field_name == 'allocated_real':
290
 
                        analytic_domain = self._get_analytic_domain(cr, uid, line, 'allocated', context=context)
291
 
                    # selection of analytic lines
292
 
                    if 'reporting_currency' in context:
293
 
                        analytic_line_obj = self.pool.get('account.analytic.line')
294
 
                        analytic_lines = analytic_line_obj.search(cr, uid, analytic_domain ,context=context)
295
 
                        real_sum = 0.0
296
 
                        currency_table = None
297
 
                        if 'currency_table_id' in context:
298
 
                            currency_table = context['currency_table_id']
299
 
                        for analytic_line in analytic_line_obj.browse(cr, uid, analytic_lines, context=context):
300
 
                            date_context = {'date': analytic_line.document_date,
301
 
                                            'currency_table_id': currency_table}
302
 
                            real_sum += self.pool.get('res.currency').compute(cr,
303
 
                                                                              uid,
304
 
                                                                              analytic_line.currency_id.id,
305
 
                                                                              context['reporting_currency'], 
306
 
                                                                              analytic_line.amount_currency or 0.0,
307
 
                                                                              round=False,
308
 
                                                                              context=date_context)
309
 
                        # Invert the result from the lines (positive for out, negative for in)
310
 
                        real_sum = -real_sum
311
 
                        res[line.id] = real_sum
312
 
                        
313
 
        # 3rd step: compute all remaining lines from the retrieved results
314
 
        total_actual_costs = 0.0
315
 
        if overhead:
316
 
            total_actual_costs = sum(res.values())
317
 
        for line in self.browse(cr, uid, [id for id in ids if id not in actual_line_ids], context=context):
318
 
            if line.id not in res:
319
 
                self._get_view_amount(line, total_actual_costs, res)
320
 
        
321
 
        return res
322
 
 
323
 
 
324
 
    _columns = {
325
 
        'name': fields.char('Name', size=64, required=True),
326
 
        'code': fields.char('Code', size=16, required=True),
327
 
        'format_id': fields.many2one('financing.contract.format', 'Format'),
328
 
        'is_quadruplet': fields.boolean('Input CC/FP at line level?'),
329
 
        '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']),
330
 
        'parent_id': fields.many2one('financing.contract.format.line', 'Parent line'),
331
 
        'child_ids': fields.one2many('financing.contract.format.line', 'parent_id', 'Child lines'),
332
 
        'line_type': fields.selection([('view','View'),
333
 
                                       ('actual','Actual'),
334
 
                                       ('consumption','Consumption'),
335
 
                                       ('overhead','Overhead')], 'Line type', required=True),
336
 
        'overhead_type': fields.selection([('cost_percentage','Percentage of direct costs'),
337
 
                                           ('grant_percentage','Percentage of grant')], 'Overhead calculation mode'),
338
 
        'allocated_budget_value': fields.float('Budget allocated amount (value)'),
339
 
        'project_budget_value': fields.float('Budget project amount (value)'),
340
 
        'allocated_real_value': fields.float('Real allocated amount (value)'),
341
 
        'project_real_value': fields.float('Real project amount (value)'),
342
 
        'overhead_percentage': fields.float('Overhead percentage'),
343
 
 
344
 
        'allocated_budget': fields.function(_get_budget_amount, method=True, store=False, string="Funded - Budget", type="float", readonly=True),
345
 
        'project_budget': fields.function(_get_budget_amount, method=True, store=False, string="Total project - Budget", type="float", readonly=True),
346
 
 
347
 
        'allocated_real': fields.function(_get_actual_amount, method=True, store=False, string="Funded - Actuals", type="float", readonly=True),
348
 
        'project_real': fields.function(_get_actual_amount, method=True, store=False, string="Total project - Actuals", type="float", readonly=True),
349
 
 
350
 
    }
351
 
    
352
 
    _defaults = {
353
 
        'is_quadruplet': False,
354
 
        'line_type': 'actual',
355
 
        'overhead_type': 'cost_percentage',
356
 
        'parent_id': lambda *a: False
357
 
    }
358
 
 
359
 
    _order = 'code asc'
360
 
 
361
 
    def create(self, cr, uid, vals, context=None):
362
 
        if not context:
363
 
            context = {}
364
 
        # if the account is set as view, remove budget and account values
365
 
        if 'line_type' in vals and vals['line_type'] == 'view':
366
 
            vals['allocated_amount'] = 0.0
367
 
            vals['project_amount'] = 0.0
368
 
            vals['account_destination_ids'] = []
369
 
            vals['account_quadruplet_ids'] = []
370
 
        elif 'is_quadruplet' in vals:
371
 
            if vals['is_quadruplet']:
372
 
                # delete account/destinations
373
 
                vals['account_destination_ids'] = []
374
 
            else:
375
 
                # delete quadruplets
376
 
                vals['account_quadruplet_ids'] = []
377
 
        return super(financing_contract_format_line, self).create(cr, uid, vals, context=context)
378
 
    
379
 
    def write(self, cr, uid, ids, vals, context=None):
380
 
        if not context:
381
 
            context = {}
382
 
        if isinstance(ids, (int, long)):
383
 
            ids = [ids]
384
 
        # if the account is set as view, remove budget and account values
385
 
        if 'line_type' in vals and vals['line_type'] == 'view':
386
 
            vals['allocated_amount'] = 0.0
387
 
            vals['project_amount'] = 0.0
388
 
            vals['account_destination_ids'] = [(6, 0, [])]
389
 
            vals['account_quadruplet_ids'] = [(6, 0, [])]
390
 
        elif 'is_quadruplet' in vals:
391
 
            if vals['is_quadruplet']:
392
 
                # delete previous account/destinations
393
 
                vals['account_destination_ids'] = [(6, 0, [])]
394
 
            else:
395
 
                # delete previous quadruplets
396
 
                vals['account_quadruplet_ids'] = [(6, 0, [])]
397
 
        return super(financing_contract_format_line, self).write(cr, uid, ids, vals, context=context)
398
 
    
399
 
    def copy_format_line(self, cr, uid, browse_source_line, destination_format_id, parent_id=None, context=None):
400
 
        if destination_format_id:
401
 
            format_line_vals = {
402
 
                'name': browse_source_line.name,
403
 
                'code': browse_source_line.code,
404
 
                'format_id': destination_format_id,
405
 
                'parent_id': parent_id,
406
 
                'line_type': browse_source_line.line_type,
407
 
                'account_quadruplet_ids': [(6, 0, [])],
408
 
            }
409
 
            account_destination_ids = [account_destination.id for account_destination in browse_source_line.account_destination_ids]
410
 
            format_line_vals['account_destination_ids'] = [(6, 0, account_destination_ids)]
411
 
            parent_line_id = self.pool.get('financing.contract.format.line').create(cr, uid, format_line_vals, context=context)
412
 
            for child_line in browse_source_line.child_ids:
413
 
                self.copy_format_line(cr, uid, child_line, destination_format_id, parent_line_id, context=context)
414
 
        return
415
 
            
416
 
financing_contract_format_line()
417
 
 
418
 
class financing_contract_format(osv.osv):
419
 
    
420
 
    _name = "financing.contract.format"
421
 
    _inherit = "financing.contract.format"
422
 
    
423
 
    _columns = {
424
 
        'actual_line_ids': fields.one2many('financing.contract.format.line', 'format_id', 'Actual lines')
425
 
    }
426
 
    
427
 
    def copy_format_lines(self, cr, uid, source_id, destination_id, context=None):
428
 
        # remove all old report lines
429
 
        destination_obj = self.browse(cr, uid, destination_id, context=context)
430
 
        for to_remove_line in destination_obj.actual_line_ids:
431
 
            self.pool.get('financing.contract.format.line').unlink(cr, uid, to_remove_line.id, context=context)
432
 
        source_obj = self.browse(cr, uid, source_id, context=context)
433
 
        # Method to copy a format
434
 
        # copy format lines
435
 
        for source_line in source_obj.actual_line_ids:
436
 
            if not source_line.parent_id:
437
 
                self.pool.get('financing.contract.format.line').copy_format_line(cr,
438
 
                                                                                 uid,
439
 
                                                                                 source_line,
440
 
                                                                                 destination_id,
441
 
                                                                                 parent_id=None,
442
 
                                                                                 context=context)
443
 
        return
444
 
        
445
 
financing_contract_format()
446
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: