~unifield-team/unifield-wm/us-671-homere

« back to all changes in this revision

Viewing changes to analytic_distribution/analytic_distribution.py

  • Committer: chloups208
  • Date: 2012-11-21 11:15:15 UTC
  • mto: This revision was merged to the branch mainline in revision 1340.
  • Revision ID: chloups208@chloups208-laptop-20121121111515-myqv282h6xmgh053
utp-171 modification of fields po, po line, product, so, so line

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
#
20
20
##############################################################################
21
21
 
22
 
from osv import osv
23
 
import netsvc
24
 
from tools.translate import _
25
 
 
26
 
class analytic_distribution(osv.osv):
27
 
    _name = 'analytic.distribution'
28
 
    _inherit = 'analytic.distribution'
29
 
 
30
 
    def _get_distribution_state(self, cr, uid, distrib_id, parent_id, account_id, context=None):
 
22
from osv import fields, osv
 
23
import decimal_precision as dp
 
24
from tools.misc import flatten
 
25
from time import strftime
 
26
 
 
27
class analytic_distribution1(osv.osv):
 
28
    _name = "analytic.distribution"
 
29
 
 
30
    _columns = {
 
31
        'analytic_lines': fields.one2many('account.analytic.line', 'distribution_id', 'Analytic Lines'),
 
32
        'invoice_ids': fields.one2many('account.invoice', 'analytic_distribution_id', string="Invoices"),
 
33
        'invoice_line_ids': fields.one2many('account.invoice.line', 'analytic_distribution_id', string="Invoice Lines"),
 
34
        'register_line_ids': fields.one2many('account.bank.statement.line', 'analytic_distribution_id', string="Register Lines"),
 
35
        'move_line_ids': fields.one2many('account.move.line', 'analytic_distribution_id', string="Move Lines"),
 
36
        'commitment_ids': fields.one2many('account.commitment', 'analytic_distribution_id', string="Commitments voucher"),
 
37
        'commitment_line_ids': fields.one2many('account.commitment.line', 'analytic_distribution_id', string="Commitment voucher lines"),
 
38
    }
 
39
 
 
40
    def copy(self, cr, uid, id, default=None, context=None):
 
41
        """
 
42
        Copy an analytic distribution without the one2many links
 
43
        """
 
44
        if default is None:
 
45
            default = {}
 
46
        default.update({
 
47
            'analytic_lines': False,
 
48
            'invoice_ids': False,
 
49
            'invoice_line_ids': False,
 
50
            'register_line_ids': False,
 
51
            'move_line_ids': False,
 
52
            'commitment_ids': False,
 
53
            'commitment_line_ids': False,
 
54
        })
 
55
        return super(osv.osv, self).copy(cr, uid, id, default, context=context)
 
56
 
 
57
    def _get_distribution_state(self, cr, uid, id, parent_id, account_id, context=None):
31
58
        """
32
59
        Return distribution state
33
60
        """
34
61
        if context is None:
35
62
            context = {}
36
 
        # Have an analytic distribution on another account than analytic-a-holic account make no sense. So their analytic distribution is valid
37
 
        logger = netsvc.Logger()
 
63
        # Have an analytic distribution on another account than expense account make no sense. So their analytic distribution is valid
38
64
        if account_id:
39
 
            account =  self.pool.get('account.account').read(cr, uid, account_id, ['is_analytic_addicted'])
40
 
            if account and not account.get('is_analytic_addicted', False):
 
65
            account =  self.pool.get('account.account').browse(cr, uid, account_id)
 
66
            if account and account.user_type and account.user_type.code != 'expense':
41
67
                return 'valid'
42
 
        if not distrib_id:
 
68
        if not id:
43
69
            if parent_id:
44
70
                return self._get_distribution_state(cr, uid, parent_id, False, account_id, context)
45
 
            logger.notifyChannel("analytic distribution", netsvc.LOG_WARNING, _("%s: NONE!") % (distrib_id or ''))
46
71
            return 'none'
47
 
        distrib = self.browse(cr, uid, distrib_id)
 
72
        distrib = self.browse(cr, uid, id)
48
73
        # Search MSF Private Fund element, because it's valid with all accounts
49
74
        try:
50
 
            fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
 
75
            fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 
51
76
            'analytic_account_msf_private_funds')[1]
52
77
        except ValueError:
53
78
            fp_id = 0
54
 
        account = self.pool.get('account.account').read(cr, uid, account_id, ['destination_ids'])
 
79
        account = self.pool.get('account.account').browse(cr, uid, account_id)
55
80
        # Check Cost Center lines with destination/account link
56
81
        for cc_line in distrib.cost_center_lines:
57
 
            if cc_line.destination_id.id not in account.get('destination_ids', []):
58
 
                logger.notifyChannel("analytic distribution", netsvc.LOG_WARNING, _("%s: Error, destination not compatible with G/L account in CC lines") % (distrib_id or ''))
 
82
            if cc_line.destination_id.id not in [x.id for x in account.destination_ids]:
59
83
                return 'invalid'
60
84
        # Check Funding pool lines regarding:
61
85
        # - destination / account
62
86
        # - If analytic account is MSF Private funds
63
87
        # - Cost center and funding pool compatibility
64
88
        for fp_line in distrib.funding_pool_lines:
65
 
            if fp_line.destination_id.id not in account.get('destination_ids', []):
66
 
                logger.notifyChannel("analytic distribution", netsvc.LOG_WARNING, _("%s: Error, destination not compatible with G/L account for FP lines") % (distrib_id or ''))
 
89
            if fp_line.destination_id.id not in [x.id for x in account.destination_ids]:
67
90
                return 'invalid'
68
91
            # If fp_line is MSF Private Fund, all is ok
69
92
            if fp_line.analytic_id.id == fp_id:
70
93
                continue
71
94
            if (account_id, fp_line.destination_id.id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.analytic_id.tuple_destination_account_ids]:
72
 
                logger.notifyChannel("analytic distribution", netsvc.LOG_WARNING, _("%s: Error, account/destination tuple not compatible with given FP analytic account") % (distrib_id or ''))
73
95
                return 'invalid'
74
96
            if fp_line.cost_center_id.id not in [x.id for x in fp_line.analytic_id.cost_center_ids]:
75
 
                logger.notifyChannel("analytic distribution", netsvc.LOG_WARNING, _("%s: Error, CC is not compatible with given FP analytic account") % (distrib_id or ''))
76
97
                return 'invalid'
77
98
        return 'valid'
78
99
 
79
 
    def analytic_state_from_info(self, cr, uid, account_id, destination_id, cost_center_id, analytic_id, context=None):
80
 
        """
81
 
        Give analytic state from the given information.
82
 
        Return result and some info if needed.
83
 
        """
84
 
        # Checks
85
 
        if context is None:
86
 
            context = {}
87
 
        # Prepare some values
88
 
        res = 'valid'
89
 
        info = ''
90
 
        ana_obj = self.pool.get('account.analytic.account')
91
 
        account = self.pool.get('account.account').browse(cr, uid, account_id, context=context)
92
 
        fp = ana_obj.browse(cr, uid, analytic_id, context=context)
93
 
        try:
94
 
            fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
95
 
        except ValueError:
96
 
            fp_id = 0
97
 
        is_private_fund = False
98
 
        if analytic_id == fp_id:
99
 
            is_private_fund = True
100
 
        # DISTRIBUTION VERIFICATION
101
 
        # Check account user_type
102
 
        if account.user_type_code != 'expense':
103
 
            return res, _('Not an expense account')
104
 
        # Check that destination is compatible with account
105
 
        if destination_id not in [x.id for x in account.destination_ids]:
106
 
            return 'invalid', _('Destination not compatible with account')
107
 
        if not is_private_fund:
108
 
            # Check that cost center is compatible with FP (except if FP is MSF Private Fund)
109
 
            if cost_center_id not in [x.id for x in fp.cost_center_ids]:
110
 
                return 'invalid', _('Cost Center not compatible with FP')
111
 
            # Check that tuple account/destination is compatible with FP (except if FP is MSF Private Fund):
112
 
            if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp.tuple_destination_account_ids]:
113
 
                return 'invalid', _('account/destination tuple not compatible with given FP analytic account')
114
 
        return res, info
 
100
analytic_distribution1()
 
101
 
 
102
class distribution_line(osv.osv):
 
103
    _name = "distribution.line"
 
104
 
 
105
    _columns = {
 
106
        'name': fields.char('Name', size=64),
 
107
        "distribution_id": fields.many2one('analytic.distribution', 'Associated Analytic Distribution', ondelete='cascade'),
 
108
        "analytic_id": fields.many2one('account.analytic.account', 'Analytical Account'),
 
109
        "amount": fields.float('Amount', digits_compute=dp.get_precision('Account')),
 
110
        "percentage": fields.float('Percentage', digits=(16,4)),
 
111
        "currency_id": fields.many2one('res.currency', 'Currency', required=True),
 
112
        "date": fields.date(string="Date"),
 
113
        "source_date": fields.date(string="Source Date", help="This date is for source_date for analytic lines"),
 
114
    }
 
115
 
 
116
    _defaults ={
 
117
        'name': 'Distribution Line',
 
118
        'date': lambda *a: strftime('%Y-%m-%d'),
 
119
        'source_date': lambda *a: strftime('%Y-%m-%d'),
 
120
    }
 
121
 
 
122
    def create_analytic_lines(self, cr, uid, ids, move_line_id, date, document_date, source_date=False, name=False, context=None):
 
123
        '''
 
124
        Creates an analytic lines from a distribution line and an account.move.line
 
125
        '''
 
126
        if isinstance(ids, (int, long)):
 
127
            ids = [ids]
 
128
 
 
129
        ret = {}
 
130
        move_line = self.pool.get('account.move.line').browse(cr, uid, move_line_id)
 
131
        company_currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id
 
132
 
 
133
        for line in self.browse(cr, uid, ids):
 
134
            amount_cur = (move_line.credit_currency - move_line.debit_currency) * line.percentage / 100
 
135
            ctx = {'date': source_date or date}
 
136
            amount = self.pool.get('res.currency').compute(cr, uid, move_line.currency_id.id, company_currency_id, amount_cur, round=False, context=ctx)
 
137
            vals = {
 
138
                'account_id': line.analytic_id.id,
 
139
                'amount_currency': amount_cur,
 
140
                'amount': amount,
 
141
                'currency_id': move_line.currency_id.id,
 
142
                'general_account_id': move_line.account_id.id,
 
143
                'date': date,
 
144
                'source_date': source_date,
 
145
                'document_date': document_date,
 
146
                'journal_id': move_line.journal_id and move_line.journal_id.analytic_journal_id and move_line.journal_id.analytic_journal_id.id or False,
 
147
                'move_id': move_line.id,
 
148
                'name': name or move_line.name,
 
149
                'distrib_id': line.distribution_id.id,
 
150
                'distrib_line_id': '%s,%s'%(self._name, line.id),
 
151
            }
 
152
            if self._name == 'funding.pool.distribution.line':
 
153
                vals.update({
 
154
                    'destination_id': line.destination_id and line.destination_id.id or False,
 
155
                    'cost_center_id': line.cost_center_id and line.cost_center_id.id or False,
 
156
                })
 
157
            ret[line.id] = self.pool.get('account.analytic.line').create(cr, uid, vals)
 
158
 
 
159
        return ret
 
160
 
 
161
        
 
162
distribution_line()
 
163
 
 
164
class cost_center_distribution_line(osv.osv):
 
165
    _name = "cost.center.distribution.line"
 
166
    _inherit = "distribution.line"
 
167
    _columns = {
 
168
        "destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=True),
 
169
    }
 
170
    
 
171
cost_center_distribution_line()
 
172
 
 
173
class funding_pool_distribution_line(osv.osv):
 
174
    _name = "funding.pool.distribution.line"
 
175
    _inherit = "distribution.line"
 
176
    _columns = {
 
177
        "cost_center_id": fields.many2one('account.analytic.account', 'Cost Center Account', required=True),
 
178
        "destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=True),
 
179
    }
 
180
    
 
181
funding_pool_distribution_line()
 
182
 
 
183
class free_1_distribution_line(osv.osv):
 
184
    _name = "free.1.distribution.line"
 
185
    _inherit = "distribution.line"
 
186
    _columns = {
 
187
        "destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=False),
 
188
    }
 
189
    
 
190
free_1_distribution_line()
 
191
 
 
192
class free_2_distribution_line(osv.osv):
 
193
    _name = "free.2.distribution.line"
 
194
    _inherit = "distribution.line"
 
195
    _columns = {
 
196
        "destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=False),
 
197
    }
 
198
    
 
199
free_2_distribution_line()
 
200
 
 
201
class analytic_distribution(osv.osv):
 
202
    _name = 'analytic.distribution'
 
203
    _inherit = "analytic.distribution"
 
204
 
 
205
    def _get_lines_count(self, cr, uid, ids, name=False, args=False, context=None):
 
206
        """
 
207
        Get count of each analytic distribution lines type.
 
208
        Example: with an analytic distribution with 2 cost center, 3 funding pool and 1 Free 1:
 
209
        2 CC; 3 FP; 1 F1; 0 F2; 
 
210
        (Number of chars: 20 chars + 4 x some lines number)
 
211
        """
 
212
        # Some verifications
 
213
        if not context:
 
214
            context = {}
 
215
        if isinstance(ids, (int, long)):
 
216
            ids = [ids]
 
217
        # Prepare some values
 
218
        res = {}
 
219
        if not ids:
 
220
            return res
 
221
        # Browse given invoices
 
222
        for distrib in self.browse(cr, uid, ids, context=context):
 
223
            txt = ''
 
224
            txt += str(len(distrib.cost_center_lines) or '0') + ' CC; '
 
225
            txt += str(len(distrib.funding_pool_lines) or '0') + ' FP; '
 
226
            txt += str(len(distrib.free_1_lines) or '0') + ' F1; '
 
227
            txt += str(len(distrib.free_2_lines) or '0') + ' F2'
 
228
            if not txt:
 
229
                txt = ''
 
230
            res[distrib.id] = txt
 
231
        return res
 
232
 
 
233
    _columns = {
 
234
        'cost_center_lines': fields.one2many('cost.center.distribution.line', 'distribution_id', 'Cost Center Distribution'),
 
235
        'funding_pool_lines': fields.one2many('funding.pool.distribution.line', 'distribution_id', 'Funding Pool Distribution'),
 
236
        'free_1_lines': fields.one2many('free.1.distribution.line', 'distribution_id', 'Free 1 Distribution'),
 
237
        'free_2_lines': fields.one2many('free.2.distribution.line', 'distribution_id', 'Free 2 Distribution'),
 
238
        'name': fields.function(_get_lines_count, method=True, type='char', size=256, string="Name", readonly=True, store=False),
 
239
    }
 
240
 
 
241
    def update_distribution_line_amount(self, cr, uid, ids, amount=False, context=None):
 
242
        """
 
243
        Update amount on distribution lines for given distribution (ids)
 
244
        """
 
245
        # Some verifications
 
246
        if not context:
 
247
            context = {}
 
248
        if isinstance(ids, (int, long)):
 
249
            ids = [ids]
 
250
        if not amount:
 
251
            return False
 
252
        # Process distributions
 
253
        for distrib_id in ids:
 
254
            for dl_name in ['cost.center.distribution.line', 'funding.pool.distribution.line', 'free.1.distribution.line', 'free.2.distribution.line']:
 
255
                dl_obj = self.pool.get(dl_name)
 
256
                dl_ids = dl_obj.search(cr, uid, [('distribution_id', '=', distrib_id)], context=context)
 
257
                for dl in dl_obj.browse(cr, uid, dl_ids, context=context):
 
258
                    dl_vals = {
 
259
                        'amount': round(dl.percentage * amount) / 100.0,
 
260
                    }
 
261
                    dl_obj.write(cr, uid, [dl.id], dl_vals, context=context)
 
262
        return True
 
263
 
 
264
    def update_distribution_line_account(self, cr, uid, line_ids, account_id, context=None):
 
265
        """
 
266
        Update account on distribution line
 
267
        """
 
268
        # Some verifications
 
269
        if not context:
 
270
            context = {}
 
271
        if isinstance(line_ids, (int, long)):
 
272
            line_ids = [line_ids]
 
273
        if not account_id:
 
274
            return False
 
275
        # Prepare some values
 
276
        account = self.pool.get('account.analytic.account').browse(cr, uid, [account_id], context=context)[0]
 
277
 
 
278
        if account.category == 'OC':
 
279
            vals = {'cost_center_id': account_id}
 
280
        else:
 
281
            vals = {'analytic_id': account_id}
 
282
        return self.pool.get('funding.pool.distribution.line').write(cr, uid, line_ids, vals)
 
283
 
 
284
    def create_funding_pool_lines(self, cr, uid, ids, account_id=False, context=None):
 
285
        """
 
286
        Create funding pool lines regarding cost_center_lines from analytic distribution.
 
287
        If funding_pool_lines exists, then nothing appends.
 
288
        By default, add funding_pool_lines with MSF Private Fund element (written in an OpenERP demo file).
 
289
        For destination axis, get those from account_id default configuration (default_destination_id).
 
290
        """
 
291
        # Some verifications
 
292
        if not context:
 
293
            context = {}
 
294
        if isinstance(ids, (int, long)):
 
295
            ids = [ids]
 
296
        # Prepare some values
 
297
        res = {}
 
298
        # Browse distributions
 
299
        for distrib in self.browse(cr, uid, ids, context=context):
 
300
            if distrib.funding_pool_lines:
 
301
                res[distrib.id] = False
 
302
                continue
 
303
            # Browse cost center lines
 
304
            for line in distrib.cost_center_lines:
 
305
                # Search MSF Private Fund
 
306
                try:
 
307
                    pf_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 
 
308
                    'analytic_account_msf_private_funds')[1]
 
309
                except ValueError:
 
310
                    pf_id = 0
 
311
                if pf_id:
 
312
                    vals = {
 
313
                        'analytic_id': pf_id,
 
314
                        'amount': line.amount or 0.0,
 
315
                        'percentage': line.percentage or 0.0,
 
316
                        'currency_id': line.currency_id and line.currency_id.id or False,
 
317
                        'distribution_id': distrib.id or False,
 
318
                        'cost_center_id': line.analytic_id and line.analytic_id.id or False,
 
319
                        'destination_id': line.destination_id and line.destination_id.id or False,
 
320
                    }
 
321
                    # Search default destination if no one given
 
322
                    if account_id and not vals.get('destination_id'):
 
323
                        account = self.pool.get('account.account').browse(cr, uid, account_id)
 
324
                        if account and account.user_type and account.user_type.code == 'expense':
 
325
                            vals.update({'destination_id': account.default_destination_id and account.default_destination_id.id or False})
 
326
                    new_pf_line_id = self.pool.get('funding.pool.distribution.line').create(cr, uid, vals, context=context)
 
327
            res[distrib.id] = True
 
328
        return res
 
329
 
 
330
    def create_analytic_lines(self, cr, uid, ids, name, date, amount, journal_id, currency_id, document_date=False, ref=False, source_date=False, general_account_id=False, \
 
331
        move_id=False, invoice_line_id=False, commitment_line_id=False, context=None):
 
332
        """
 
333
        Create analytic lines from given elements:
 
334
         - date
 
335
         - name
 
336
         - amount
 
337
         - journal_id (analytic_journal_id)
 
338
         - currency_id
 
339
         - ref (optional)
 
340
         - source_date (optional)
 
341
         - general_account_id (optional)
 
342
         - move_id (optional)
 
343
         - invoice_line_id (optional)
 
344
         - commitment_line_id (optional)
 
345
        Return all created ids, otherwise return false (or [])
 
346
        """
 
347
        # Some verifications
 
348
        if not context:
 
349
            context = {}
 
350
        if isinstance(ids, (int, long)):
 
351
            ids = [ids]
 
352
        if not name or not date or not amount or not journal_id or not currency_id:
 
353
            return False
 
354
        if not document_date:
 
355
            document_date = date
 
356
        # Prepare some values
 
357
        res = []
 
358
        vals = {
 
359
            'name': name,
 
360
            'date': source_date or date,
 
361
            'document_date': document_date,
 
362
            'ref': ref or '',
 
363
            'journal_id': journal_id,
 
364
            'general_account_id': general_account_id or False,
 
365
            'move_id': move_id or False,
 
366
            'invoice_line_id': invoice_line_id or False,
 
367
            'user_id': uid,
 
368
            'currency_id': currency_id,
 
369
            'source_date': source_date or False,
 
370
            'commitment_line_id': commitment_line_id or False,
 
371
        }
 
372
        company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
 
373
        # Browse distribution(s)
 
374
        for distrib in self.browse(cr, uid, ids, context=context):
 
375
            vals.update({'distribution_id': distrib.id,})
 
376
            # create lines
 
377
            for distrib_lines in [distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]:
 
378
                for distrib_line in distrib_lines:
 
379
                    context.update({'date': source_date or date}) # for amount computing
 
380
                    anal_amount = (distrib_line.percentage * amount) / 100
 
381
                    vals.update({
 
382
                        'amount': -1 * self.pool.get('res.currency').compute(cr, uid, currency_id, company_currency, 
 
383
                            anal_amount, round=False, context=context),
 
384
                        'amount_currency': -1 * anal_amount,
 
385
                        'account_id': distrib_line.analytic_id.id,
 
386
                        'cost_center_id': False,
 
387
                        'destination_id': False,
 
388
                        'distrib_line_id': '%s,%s'%(distrib_line._name, distrib_line.id),
 
389
                    })
 
390
                    # Update values if we come from a funding pool
 
391
                    if distrib_line._name == 'funding.pool.distribution.line':
 
392
                        vals.update({'cost_center_id': distrib_line.cost_center_id and distrib_line.cost_center_id.id or False, 
 
393
                            'destination_id': distrib_line.destination_id and distrib_line.destination_id.id or False,})
 
394
                    # create analytic line
 
395
                    al_id = self.pool.get('account.analytic.line').create(cr, uid, vals, context=context)
 
396
                    res.append(al_id)
 
397
        return res
115
398
 
116
399
analytic_distribution()
117
400
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: