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

« back to all changes in this revision

Viewing changes to analytic_distribution/account_commitment.py

  • Committer: jf
  • Date: 2012-06-13 12:43:21 UTC
  • mfrom: (827.5.11 uf-635)
  • Revision ID: jf@tempo4-20120613124321-2b8cwgl86gyy2tb7
UF-635 [DEV] Documents workflow: Graphic representation
lp:~unifield-team/unifield-wm/uf-635 revno 838

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: utf-8 -*-
 
3
##############################################################################
 
4
#
 
5
#    OpenERP, Open Source Management Solution
 
6
#    Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved
 
7
#    Developer: Olivier DOSSMANN
 
8
#
 
9
#    This program is free software: you can redistribute it and/or modify
 
10
#    it under the terms of the GNU Affero General Public License as
 
11
#    published by the Free Software Foundation, either version 3 of the
 
12
#    License, or (at your option) any later version.
 
13
#
 
14
#    This program is distributed in the hope that it will be useful,
 
15
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
#    GNU Affero General Public License for more details.
 
18
#
 
19
#    You should have received a copy of the GNU Affero General Public License
 
20
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
#
 
22
##############################################################################
 
23
 
 
24
from osv import osv
 
25
from osv import fields
 
26
from tools.translate import _
 
27
from time import strftime
 
28
import decimal_precision as dp
 
29
from account_tools import get_period_from_date
 
30
from tools.misc import flatten
 
31
 
 
32
class account_commitment(osv.osv):
 
33
    _name = 'account.commitment'
 
34
    _description = "Account Commitment Voucher"
 
35
    _order = "id desc"
 
36
 
 
37
    def _get_total(self, cr, uid, ids, name, args, context=None):
 
38
        """
 
39
        Give total of given commitments
 
40
        """
 
41
        # Some verifications
 
42
        if not context:
 
43
            context = {}
 
44
        if isinstance(ids, (int, long)):
 
45
            ids = [ids]
 
46
        # Prepare some values
 
47
        res = {}
 
48
        # Browse commitments
 
49
        for co in self.browse(cr, uid, ids, context=context):
 
50
            res[co.id] = 0.0
 
51
            for line in co.line_ids:
 
52
                res[co.id] += line.amount
 
53
        return res
 
54
 
 
55
    _columns = {
 
56
        'journal_id': fields.many2one('account.analytic.journal', string="Journal", readonly=True, required=True),
 
57
        'name': fields.char(string="Number", size=64, readonly=True, required=True),
 
58
        'currency_id': fields.many2one('res.currency', string="Currency", readonly=True, required=True),
 
59
        'partner_id': fields.many2one('res.partner', string="Supplier", readonly=True, required=True),
 
60
        'period_id': fields.many2one('account.period', string="Period", readonly=True, required=True),
 
61
        'state': fields.selection([('draft', 'Draft'), ('open', 'Validated'), ('done', 'Done')], readonly=True, string="State", required=True),
 
62
        'date': fields.date(string="Commitment Date", readonly=True, required=True, states={'draft': [('readonly', False)], 'open': [('readonly', False)]}),
 
63
        'line_ids': fields.one2many('account.commitment.line', 'commit_id', string="Commitment Voucher Lines"),
 
64
        'total': fields.function(_get_total, type='float', method=True, digits_compute=dp.get_precision('Account'), readonly=True, string="Total"),
 
65
        'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"),
 
66
        'type': fields.selection([('manual', 'Manual'), ('external', 'Automatic - External supplier'), ('esc', 'Automatic - ESC supplier')], string="Type", readonly=True),
 
67
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
 
68
        'notes': fields.text(string="Comment"),
 
69
    }
 
70
 
 
71
    _defaults = {
 
72
        'name': lambda s, cr, uid, c: s.pool.get('ir.sequence').get(cr, uid, 'account.commitment') or '',
 
73
        'state': lambda *a: 'draft',
 
74
        'date': lambda *a: strftime('%Y-%m-%d'),
 
75
        'type': lambda *a: 'manual',
 
76
        'from_yml_test': lambda *a: False,
 
77
        'journal_id': lambda s, cr, uid, c: s.pool.get('account.analytic.journal').search(cr, uid, [('type', '=', 'engagement')], limit=1, context=c)[0]
 
78
    }
 
79
 
 
80
    def create(self, cr, uid, vals, context=None):
 
81
        """
 
82
        Update period_id regarding date.
 
83
        """
 
84
        # Some verifications
 
85
        if not context:
 
86
            context = {}
 
87
        if not 'period_id' in vals:
 
88
            period_ids = get_period_from_date(self, cr, uid, vals.get('date', strftime('%Y-%m-%d')), context=context)
 
89
            vals.update({'period_id': period_ids and period_ids[0]})
 
90
        return super(account_commitment, self).create(cr, uid, vals, context=context)
 
91
 
 
92
    def write(self, cr, uid, ids, vals, context=None):
 
93
        """
 
94
        Update analytic lines date if date in vals for validated commitment voucher.
 
95
        """
 
96
        # Some verifications
 
97
        if not context:
 
98
            context = {}
 
99
        if isinstance(ids, (int, long)):
 
100
            ids = [ids]
 
101
        # Browse elements if 'date' in vals
 
102
        if vals.get('date', False):
 
103
            date = vals.get('date')
 
104
            period_ids = get_period_from_date(self, cr, uid, date, context=context)
 
105
            vals.update({'period_id': period_ids and period_ids[0]})
 
106
            for c in self.browse(cr, uid, ids, context=context):
 
107
                if c.state == 'open':
 
108
                    for cl in c.line_ids:
 
109
                        # Verify that date is compatible with all analytic account from distribution
 
110
                        if cl.analytic_distribution_id:
 
111
                            distrib = cl.analytic_distribution_id
 
112
                        elif cl.commit_id and cl.commit_id.analytic_distribution_id:
 
113
                            distrib = cl.commit_id.analytic_distribution_id
 
114
                        else:
 
115
                            raise osv.except_osv(_('Warning'), _('No analytic distribution found for %s %s') % (cl.account_id.code, cl.initial_amount))
 
116
                        for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]:
 
117
                            for distrib_line in distrib_lines:
 
118
                                if (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start) or (distrib_line.analytic_id.date and date > distrib_line.analytic_id.date):
 
119
                                    raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') % (distrib_line.analytic_id.name,))
 
120
                        self.pool.get('account.analytic.line').write(cr, uid, [x.id for x in cl.analytic_lines], {'date': date, 'source_date': date}, context=context)
 
121
        # Default behaviour
 
122
        res = super(account_commitment, self).write(cr, uid, ids, vals, context=context)
 
123
        return res
 
124
 
 
125
    def copy(self, cr, uid, id, default=None, context=None):
 
126
        """
 
127
        Copy analytic_distribution
 
128
        """
 
129
        # Some verifications
 
130
        if not context:
 
131
            context = {}
 
132
        if not default:
 
133
            default = {}
 
134
        # Update default values
 
135
        default.update({
 
136
            'name': self.pool.get('ir.sequence').get(cr, uid, 'account.commitment'),
 
137
            'state': 'draft',
 
138
        })
 
139
        # Default method
 
140
        res = super(account_commitment, self).copy(cr, uid, id, default, context)
 
141
        # Update analytic distribution
 
142
        if res:
 
143
            c = self.browse(cr, uid, res, context=context)
 
144
        if res and c.analytic_distribution_id:
 
145
            new_distrib_id = self.pool.get('analytic.distribution').copy(cr, uid, c.analytic_distribution_id.id, {}, context=context)
 
146
            if new_distrib_id:
 
147
                self.write(cr, uid, [res], {'analytic_distribution_id': new_distrib_id}, context=context)
 
148
        return res
 
149
 
 
150
    def unlink(self, cr, uid, ids, context=None):
 
151
        """
 
152
        Only delete "done" state commitments
 
153
        """
 
154
        # Some verifications
 
155
        if not context:
 
156
            context = {}
 
157
        if isinstance(ids, (int, long)):
 
158
            ids = [ids]
 
159
        new_ids = []
 
160
        # Check that elements are in done state
 
161
        for co in self.browse(cr, uid, ids):
 
162
            if co.state == 'done':
 
163
                new_ids.append(co.id)
 
164
        # Give user a message if no done commitments found
 
165
        if not new_ids:
 
166
            raise osv.except_osv(_('Warning'), _('You can only delete done commitments!'))
 
167
        return super(account_commitment, self).unlink(cr, uid, new_ids, context)
 
168
 
 
169
    def button_analytic_distribution(self, cr, uid, ids, context=None):
 
170
        """
 
171
        Launch analytic distribution wizard on a commitment
 
172
        """
 
173
        # Some verifications
 
174
        if not context:
 
175
            context = {}
 
176
        if isinstance(ids, (int, long)):
 
177
            ids = [ids]
 
178
        # Prepare some values
 
179
        commitment = self.browse(cr, uid, ids[0], context=context)
 
180
        amount = commitment.total or 0.0
 
181
        # Search elements for currency
 
182
        company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
 
183
        currency = commitment.currency_id and commitment.currency_id.id or company_currency
 
184
        # Get analytic_distribution_id
 
185
        distrib_id = commitment.analytic_distribution_id and commitment.analytic_distribution_id.id
 
186
        # Prepare values for wizard
 
187
        vals = {
 
188
            'total_amount': amount,
 
189
            'commitment_id': commitment.id,
 
190
            'currency_id': currency or False,
 
191
            'state': 'dispatch',
 
192
        }
 
193
        if distrib_id:
 
194
            vals.update({'distribution_id': distrib_id,})
 
195
        # Create the wizard
 
196
        wiz_obj = self.pool.get('analytic.distribution.wizard')
 
197
        wiz_id = wiz_obj.create(cr, uid, vals, context=context)
 
198
        # Update some context values
 
199
        context.update({
 
200
            'active_id': ids[0],
 
201
            'active_ids': ids,
 
202
        })
 
203
        # Open it!
 
204
        return {
 
205
                'name': 'Global analytic distribution',
 
206
                'type': 'ir.actions.act_window',
 
207
                'res_model': 'analytic.distribution.wizard',
 
208
                'view_type': 'form',
 
209
                'view_mode': 'form',
 
210
                'target': 'new',
 
211
                'res_id': [wiz_id],
 
212
                'context': context,
 
213
        }
 
214
 
 
215
    def button_compute(self, cr, uid, ids, context=None):
 
216
        """
 
217
        Compute commitment voucher total.
 
218
        """
 
219
        # trick to refresh view and update total amount
 
220
        return self.write(cr, uid, ids, [], context=context)
 
221
 
 
222
    def get_engagement_lines(self, cr, uid, ids, context=None):
 
223
        """
 
224
        Return all engagement lines from given commitments (in context)
 
225
        """
 
226
        # Some verifications
 
227
        if not context:
 
228
            context = {}
 
229
        if context.get('active_ids', False):
 
230
            ids = context.get('active_ids')
 
231
        if isinstance(ids, (int, long)):
 
232
            ids = [ids]
 
233
        # Prepare some values
 
234
        valid_ids = []
 
235
        # Search valid ids
 
236
        for co in self.browse(cr, uid, ids):
 
237
            for line in co.line_ids:
 
238
                if line.analytic_lines:
 
239
                    valid_ids.append([x.id for x in line.analytic_lines])
 
240
        valid_ids = flatten(valid_ids)
 
241
        domain = [('id', 'in', valid_ids), ('account_id.category', '=', 'FUNDING')]
 
242
        # Permit to only display engagement lines
 
243
        context.update({'search_default_engagements': 1, 'display_fp': True})
 
244
        return {
 
245
            'name': 'Analytic Entries',
 
246
            'type': 'ir.actions.act_window',
 
247
            'res_model': 'account.analytic.line',
 
248
            'view_type': 'form',
 
249
            'view_mode': 'tree,form',
 
250
            'context': context,
 
251
            'domain': domain,
 
252
            'target': 'current',
 
253
        }
 
254
 
 
255
    def onchange_date(self, cr, uid, ids, date, period_id=False, context=None):
 
256
        """
 
257
        Update period regarding given date
 
258
        """
 
259
        # Some verifications
 
260
        if not context:
 
261
            context = {}
 
262
        if not date:
 
263
            return False
 
264
        # Prepare some values
 
265
        vals = {}
 
266
        periods = get_period_from_date(self, cr, uid, date, context=context)
 
267
        if periods:
 
268
            vals['period_id'] = periods[0]
 
269
        return {'value': vals}
 
270
 
 
271
    def create_analytic_lines(self, cr, uid, ids, context=None):
 
272
        """
 
273
        Create analytic line for given commitment voucher.
 
274
        """
 
275
        # Some verifications
 
276
        if not context:
 
277
            context = {}
 
278
        if isinstance(ids, (int, long)):
 
279
            ids = [ids]
 
280
        # Browse commitments
 
281
        for c in self.browse(cr, uid, ids, context=context):
 
282
            for cl in c.line_ids:
 
283
                # Continue if we come from yaml tests
 
284
                if c.from_yml_test or cl.from_yml_test:
 
285
                    continue
 
286
                # Verify that analytic distribution is present
 
287
                if cl.analytic_distribution_state != 'valid':
 
288
                    raise osv.except_osv(_('Error'), _('Analytic distribution is not valid for account "%s %s".') % 
 
289
                        (cl.account_id and cl.account_id.code, cl.account_id and cl.account_id.name))
 
290
                # Take analytic distribution either from line or from commitment voucher
 
291
                distrib_id = cl.analytic_distribution_id and cl.analytic_distribution_id.id or c.analytic_distribution_id and c.analytic_distribution_id.id or False
 
292
                if not distrib_id:
 
293
                    raise osv.except_osv(_('Error'), _('No analytic distribution found!'))
 
294
                # Search if analytic lines exists for this commitment voucher line
 
295
                al_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cl.id)], context=context)
 
296
                if not al_ids:
 
297
                    # Create engagement journal lines
 
298
                    self.pool.get('analytic.distribution').create_analytic_lines(cr, uid, [distrib_id], c.name, c.date, 
 
299
                        cl.amount, c.journal_id and c.journal_id.id, c.currency_id and c.currency_id.id, c.purchase_id and c.purchase_id.name or False, 
 
300
                        c.date, cl.account_id and cl.account_id.id or False, False, False, cl.id, context=context)
 
301
        return True
 
302
 
 
303
    def action_commitment_open(self, cr, uid, ids, context=None):
 
304
        """
 
305
        To do when we validate a commitment.
 
306
        """
 
307
        # Some verifications
 
308
        if not context:
 
309
            context = {}
 
310
        if isinstance(ids, (int, long)):
 
311
            ids = [ids]
 
312
        # Browse commitments and create analytic lines
 
313
        self.create_analytic_lines(cr, uid, ids, context=context)
 
314
        # Validate commitment voucher
 
315
        return self.write(cr, uid, ids, {'state': 'open'}, context=context)
 
316
 
 
317
    def action_commitment_done(self, cr, uid, ids, context=None):
 
318
        """
 
319
        To do when a commitment is done.
 
320
        """
 
321
        # Some verifications
 
322
        if not context:
 
323
            context = {}
 
324
        if isinstance(ids, (int, long)):
 
325
            ids = [ids]
 
326
        # Browse commitments
 
327
        for c in self.browse(cr, uid, ids, context=context):
 
328
            # Search analytic lines that have commitment line ids
 
329
            search_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', 'in', [x.id for x in c.line_ids])], context=context)
 
330
            # Delete them
 
331
            res = self.pool.get('account.analytic.line').unlink(cr, uid, search_ids, context=context)
 
332
            # And finally update commitment voucher state and lines amount
 
333
            if not res:
 
334
                raise osv.except_osv(_('Error'), _('An error occured on engagement lines deletion.'))
 
335
            self.pool.get('account.commitment.line').write(cr, uid, [x.id for x in c.line_ids], {'amount': 0}, context=context)
 
336
            self.write(cr, uid, [c.id], {'state':'done'}, context=context)
 
337
        return True
 
338
 
 
339
account_commitment()
 
340
 
 
341
class account_commitment_line(osv.osv):
 
342
    _name = 'account.commitment.line'
 
343
    _description = "Account Commitment Voucher Line"
 
344
    _order = "id desc"
 
345
 
 
346
    def _get_distribution_state(self, cr, uid, ids, name, args, context=None):
 
347
        """
 
348
        Get state of distribution:
 
349
         - if compatible with the commitment voucher line, then "valid"
 
350
         - if no distribution, take a tour of commitment voucher distribution, if compatible, then "valid"
 
351
         - if no distribution on commitment voucher line and commitment voucher, then "none"
 
352
         - all other case are "invalid"
 
353
        """
 
354
        # Some verifications
 
355
        if not context:
 
356
            context = {}
 
357
        if isinstance(ids, (int, long)):
 
358
            ids = [ids]
 
359
        # Prepare some values
 
360
        res = {}
 
361
        # Browse all given lines
 
362
        for line in self.browse(cr, uid, ids, context=context):
 
363
            if line.from_yml_test:
 
364
                res[line.id] = 'valid'
 
365
            else:
 
366
                res[line.id] = self.pool.get('analytic.distribution')._get_distribution_state(cr, uid, line.analytic_distribution_id.id, line.commit_id.analytic_distribution_id.id, line.account_id.id) 
 
367
        return res
 
368
 
 
369
    def _have_analytic_distribution_from_header(self, cr, uid, ids, name, arg, context=None):
 
370
        """
 
371
        If Commitment have an analytic distribution, return False, else return True
 
372
        """
 
373
        # Some verifications
 
374
        if not context:
 
375
            context = {}
 
376
        if isinstance(ids, (int, long)):
 
377
            ids = [ids]
 
378
        res = {}
 
379
        for co in self.browse(cr, uid, ids, context=context):
 
380
            res[co.id] = True
 
381
            if co.analytic_distribution_id:
 
382
                res[co.id] = False
 
383
        return res
 
384
 
 
385
    _columns = {
 
386
        'account_id': fields.many2one('account.account', string="Account", required=True),
 
387
        'amount': fields.float(string="Amount left", digits_compute=dp.get_precision('Account'), required=False),
 
388
        'initial_amount': fields.float(string="Initial amount", digits_compute=dp.get_precision('Account'), required=True),
 
389
        'commit_id': fields.many2one('account.commitment', string="Commitment Voucher", on_delete="cascade"),
 
390
        'analytic_distribution_id': fields.many2one('analytic.distribution', string="Analytic distribution"),
 
391
        'analytic_distribution_state': fields.function(_get_distribution_state, method=True, type='selection', 
 
392
            selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], 
 
393
            string="Distribution state", help="Informs from distribution state among 'none', 'valid', 'invalid."),
 
394
        'have_analytic_distribution_from_header': fields.function(_have_analytic_distribution_from_header, method=True, type='boolean', 
 
395
            string='Header Distrib.?'),
 
396
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
 
397
        'analytic_lines': fields.one2many('account.analytic.line', 'commitment_line_id', string="Analytic Lines"),
 
398
        'first': fields.boolean(string="Is not created?", help="Useful for onchange method for views. Should be False after line creation.", 
 
399
            readonly=True),
 
400
    }
 
401
 
 
402
    _defaults = {
 
403
        'initial_amount': lambda *a: 0.0,
 
404
        'amount': lambda *a: 0.0,
 
405
        'from_yml_test': lambda *a: False,
 
406
        'first': lambda *a: True,
 
407
    }
 
408
 
 
409
    def onchange_initial_amount(self, cr, uid, ids, first, amount):
 
410
        """
 
411
        """
 
412
        # Prepare some values
 
413
        res = {}
 
414
        # Some verification
 
415
        if first and amount:
 
416
            res['value'] = {'amount': amount}
 
417
        return res
 
418
 
 
419
    def update_analytic_lines(self, cr, uid, ids, amount, account_id=False, context=None):
 
420
        """
 
421
        Update analytic lines from given commitment lines with an ugly method: delete all analytic lines and recreate them…
 
422
        """
 
423
        # Some verifications
 
424
        if not context:
 
425
            context = {}
 
426
        if isinstance(ids, (int, long)):
 
427
            ids = [ids]
 
428
        # Prepare some values
 
429
        company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
 
430
        for cl in self.browse(cr, uid, ids, context=context):
 
431
            # Browse distribution
 
432
            distrib_id = cl.analytic_distribution_id and cl.analytic_distribution_id.id or False
 
433
            if not distrib_id:
 
434
                distrib_id = cl.commit_id and cl.commit_id.analytic_distribution_id and cl.commit_id.analytic_distribution_id.id or False
 
435
            if distrib_id:
 
436
                analytic_line_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cl.id)], context=context)
 
437
                self.pool.get('account.analytic.line').unlink(cr, uid, analytic_line_ids, context=context)
 
438
                ref = cl.commit_id and cl.commit_id.purchase_id and cl.commit_id.purchase_id.name or ''
 
439
                self.pool.get('analytic.distribution').create_analytic_lines(cr, uid, [distrib_id], cl.commit_id and cl.commit_id.name or 'Commitment voucher line', cl.commit_id.date, amount, 
 
440
                    cl.commit_id.journal_id.id, cl.commit_id.currency_id.id, ref, cl.commit_id.date, account_id or cl.account_id.id, move_id=False, invoice_line_id=False, 
 
441
                    commitment_line_id=cl.id, context=context)
 
442
        return True
 
443
 
 
444
    def create(self, cr, uid, vals, context=None):
 
445
        """
 
446
        Verify that given account_id (in vals) is not 'view'.
 
447
        Update initial amount with those given by 'amount' field.
 
448
        Verify amount sign.
 
449
        """
 
450
        # Some verifications
 
451
        if not context:
 
452
            context = {}
 
453
        # Change 'first' value to False (In order view correctly displayed)
 
454
        if not 'first' in vals:
 
455
            vals.update({'first': False})
 
456
        # Copy initial_amount to amount
 
457
        vals.update({'amount': vals.get('initial_amount', 0.0)})
 
458
        if 'account_id' in vals:
 
459
            account_id = vals.get('account_id')
 
460
            account = self.pool.get('account.account').browse(cr, uid, [account_id], context=context)[0]
 
461
            if account.type in ['view']:
 
462
                raise osv.except_osv(_('Error'), _("You cannot create a commitment voucher line on a 'view' account type!"))
 
463
        # Verify amount validity
 
464
        if 'amount' in vals and vals.get('amount', 0.0) < 0.0:
 
465
            raise osv.except_osv(_('Warning'), _('Amount Left should be equal or superior to 0!'))
 
466
        if 'initial_amount' in vals and vals.get('initial_amount', 0.0) <= 0.0:
 
467
            raise osv.except_osv(_('Warning'), _('Initial Amount should be superior to 0!'))
 
468
        if 'initial_amount' in vals and 'amount' in vals:
 
469
            if vals.get('initial_amount') < vals.get('amount'):
 
470
                raise osv.except_osv(_('Warning'), _('Initial Amount should be superior to Amount Left'))
 
471
        res = super(account_commitment_line, self).create(cr, uid, vals, context={})
 
472
        if res:
 
473
            for cl in self.browse(cr, uid, [res], context=context):
 
474
                if 'amount' in vals and cl.commit_id and cl.commit_id.state and cl.commit_id.state == 'open':
 
475
                    self.update_analytic_lines(cr, uid, [cl.id], vals.get('amount'), context=context)
 
476
        return res
 
477
 
 
478
    def write(self, cr, uid, ids, vals, context=None):
 
479
        """
 
480
        Verify that given account_id is not 'view'.
 
481
        Update initial_amount if amount in vals and type is 'manual' and state is 'draft'.
 
482
        Update analytic distribution if amount in vals.
 
483
        Verify amount sign.
 
484
        """
 
485
        # Some verifications
 
486
        if not context:
 
487
            context = {}
 
488
        if 'account_id' in vals:
 
489
            account_id = vals.get('account_id')
 
490
            account = self.pool.get('account.account').browse(cr, uid, [account_id], context=context)[0]
 
491
            if account.type in ['view']:
 
492
                raise osv.except_osv(_('Error'), _("You cannot write a commitment voucher line on a 'view' account type!"))
 
493
        # Verify amount validity
 
494
        if 'amount' in vals and vals.get('amount', 0.0) < 0.0:
 
495
            raise osv.except_osv(_('Warning'), _('Amount Left should be equal or superior to 0!'))
 
496
        if 'initial_amount' in vals and vals.get('initial_amount', 0.0) <= 0.0:
 
497
            raise osv.except_osv(_('Warning'), _('Initial Amount should be superior to 0!'))
 
498
        # Update analytic distribution if needed and initial_amount
 
499
        for line in self.browse(cr, uid, ids, context=context):
 
500
            # verify that initial amount is superior to amount left
 
501
            message = _('Initial Amount should be superior to Amount Left')
 
502
            if 'amount' in vals and 'initial_amount' in vals:
 
503
                if vals.get('initial_amount') < vals.get('amount'):
 
504
                    raise osv.except_osv(_('Warning'), message)
 
505
            elif 'amount' in vals:
 
506
                if line.initial_amount < vals.get('amount'):
 
507
                    raise osv.except_osv(_('Warning'), message)
 
508
            elif 'initial_amount' in vals:
 
509
                if vals.get('initial_amount') < line.amount:
 
510
                    raise osv.except_osv(_('Warning'), message)
 
511
            # verify analytic distribution only on 'open' commitments
 
512
            if line.commit_id and line.commit_id.state and line.commit_id.state == 'open':
 
513
                # Search distribution
 
514
                distrib_id = line.analytic_distribution_id and line.analytic_distribution_id.id or False
 
515
                if not distrib_id:
 
516
                    distrib_id = line.commit_id.analytic_distribution_id and line.commit_id.analytic_distribution_id.id or False
 
517
                # Verify amount
 
518
                if 'amount' in vals and vals.get('amount', 0.0) == '0.0':
 
519
                    # delete analytic lines that are null
 
520
                    if distrib_id:
 
521
                        distrib = self.pool.get('analytic.distribution').browse(cr, uid, [distrib_id], context=context)[0]
 
522
                        if distrib and distrib.analytic_lines:
 
523
                            self.pool.get('account.analytic.line').unlink(cr, uid, [x.id for x in distrib.analytic_lines], context=context)
 
524
                elif 'amount' in vals:
 
525
                    # Verify expense account
 
526
                    account_id = False
 
527
                    if 'account_id' in vals and vals.get('account_id', False) and line.account_id.id != vals.get('account_id'):
 
528
                        account_id = vals.get('account_id')
 
529
                    # Update analytic lines
 
530
                    if distrib_id:
 
531
                        self.update_analytic_lines(cr, uid, [line.id], vals.get('amount'), account_id, context=context)
 
532
        return super(account_commitment_line, self).write(cr, uid, ids, vals, context={})
 
533
 
 
534
    def button_analytic_distribution(self, cr, uid, ids, context=None):
 
535
        """
 
536
        Launch analytic distribution wizard on a commitment voucher line
 
537
        """
 
538
        # Some verifications
 
539
        if not context:
 
540
            context = {}
 
541
        if isinstance(ids, (int, long)):
 
542
            ids = [ids]
 
543
        if not ids:
 
544
            raise osv.except_osv(_('Error'), _('No invoice line given. Please save your commitment voucher line before.'))
 
545
        # Prepare some values
 
546
        commitment_voucher_line = self.browse(cr, uid, ids[0], context=context)
 
547
        distrib_id = False
 
548
        amount = commitment_voucher_line.amount or 0.0
 
549
        # Search elements for currency
 
550
        company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
 
551
        currency = commitment_voucher_line.commit_id.currency_id and commitment_voucher_line.commit_id.currency_id.id or company_currency
 
552
        # Get analytic distribution id from this line
 
553
        distrib_id = commitment_voucher_line and commitment_voucher_line.analytic_distribution_id and commitment_voucher_line.analytic_distribution_id.id or False
 
554
        # Prepare values for wizard
 
555
        vals = {
 
556
            'total_amount': amount,
 
557
            'commitment_line_id': commitment_voucher_line.id,
 
558
            'currency_id': currency or False,
 
559
            'state': 'dispatch',
 
560
            'account_id': commitment_voucher_line.account_id and commitment_voucher_line.account_id.id or False,
 
561
        }
 
562
        if distrib_id:
 
563
            vals.update({'distribution_id': distrib_id,})
 
564
        # Create the wizard
 
565
        wiz_obj = self.pool.get('analytic.distribution.wizard')
 
566
        wiz_id = wiz_obj.create(cr, uid, vals, context=context)
 
567
        # Update some context values
 
568
        context.update({
 
569
            'active_id': ids[0],
 
570
            'active_ids': ids,
 
571
        })
 
572
        # Open it!
 
573
        return {
 
574
                'name': 'Analytic distribution',
 
575
                'type': 'ir.actions.act_window',
 
576
                'res_model': 'analytic.distribution.wizard',
 
577
                'view_type': 'form',
 
578
                'view_mode': 'form',
 
579
                'target': 'new',
 
580
                'res_id': [wiz_id],
 
581
                'context': context,
 
582
        }
 
583
 
 
584
account_commitment_line()
 
585
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: