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

« back to all changes in this revision

Viewing changes to msf_accrual/msf_accrual_line.py

[UF-43] fix added noupdate to demo data

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
 
from tools.translate import _
24
 
import datetime
25
 
from dateutil.relativedelta import relativedelta
26
 
 
27
 
class msf_accrual_line(osv.osv):
28
 
    _name = 'msf.accrual.line'
29
 
    _rec_name = 'date'
30
 
    
31
 
    def onchange_period(self, cr, uid, ids, period_id, context=None):
32
 
        if period_id is False:
33
 
            return {'value': {'date': False}}
34
 
        else:
35
 
            period = self.pool.get('account.period').browse(cr, uid, period_id, context=context)
36
 
            return {'value': {'date': period.date_stop, 'document_date': period.date_stop}}
37
 
    
38
 
    def _get_functional_amount(self, cr, uid, ids, field_name, arg, context=None):
39
 
        res = {}
40
 
        for accrual_line in self.browse(cr, uid, ids, context=context):
41
 
            date_context = {'date': accrual_line.date}
42
 
            res[accrual_line.id] =  self.pool.get('res.currency').compute(cr,
43
 
                                                                          uid,
44
 
                                                                          accrual_line.currency_id.id,
45
 
                                                                          accrual_line.functional_currency_id.id, 
46
 
                                                                          accrual_line.accrual_amount or 0.0,
47
 
                                                                          round=True,
48
 
                                                                          context=date_context)
49
 
        return res
50
 
 
51
 
    def _get_entry_sequence(self, cr, uid, ids, field_name, arg, context=None):
52
 
        res = {}
53
 
        if not ids:
54
 
            return res
55
 
        for rec in self.browse(cr, uid, ids, context=context):
56
 
            es = ''
57
 
            if rec.state != 'draft' and rec.analytic_distribution_id \
58
 
                and rec.analytic_distribution_id.move_line_ids:
59
 
                    # get the NOT REV entry
60
 
                    # (same period as REV posting date is M+1)
61
 
                    move_line_br = False
62
 
                    for mv in rec.analytic_distribution_id.move_line_ids:
63
 
                        if mv.period_id.id == rec.period_id.id:
64
 
                            move_line_br = mv
65
 
                            break
66
 
                    if move_line_br:
67
 
                        es = move_line_br.move_id \
68
 
                             and move_line_br.move_id.name or ''
69
 
            res[rec.id] = es
70
 
        return res
71
 
    
72
 
    _columns = {
73
 
        'date': fields.date("Date"),
74
 
        'document_date': fields.date("Document Date", required=True),
75
 
        'period_id': fields.many2one('account.period', 'Period', required=True, domain=[('state', '=', 'draft'), ('is_system', '=', False)]),
76
 
        'description': fields.char('Description', size=64, required=True),
77
 
        'reference': fields.char('Reference', size=64),
78
 
        'expense_account_id': fields.many2one('account.account', 'Expense Account', required=True, domain=[('type', '!=', 'view'), ('user_type_code', '=', 'expense')]),
79
 
        'accrual_account_id': fields.many2one('account.account', 'Accrual Account', required=True, domain=[('type', '!=', 'view'), ('user_type_code', 'in', ['receivables', 'payables', 'debt'])]),
80
 
        'accrual_amount': fields.float('Accrual Amount', required=True),
81
 
        'currency_id': fields.many2one('res.currency', 'Currency', required=True),
82
 
        'journal_id': fields.many2one('account.journal', 'Journal', required=True),
83
 
        'third_party_type': fields.selection([
84
 
                ('', ''),
85
 
                ('res.partner', 'Partner'),
86
 
                ('hr.employee', 'Employee'),
87
 
            ], 'Third Party', required=False),
88
 
        'partner_id': fields.many2one('res.partner', 'Third Party Partner', ondelete="restrict"),
89
 
        'employee_id': fields.many2one('hr.employee', 'Third Party Employee', ondelete="restrict"),
90
 
        'analytic_distribution_id': fields.many2one('analytic.distribution', 'Analytic Distribution'),
91
 
        'functional_amount': fields.function(_get_functional_amount, method=True, store=False, string="Functional Amount", type="float", readonly="True"),
92
 
        'functional_currency_id': fields.many2one('res.currency', 'Functional Currency', required=True, readonly=True),
93
 
        'state': fields.selection([('draft', 'Draft'),
94
 
                                   ('posted', 'Posted'),
95
 
                                   ('cancel', 'Cancelled')], 'Status', required=True),
96
 
        # Field to store the third party's name for list view
97
 
        'third_party_name': fields.char('Third Party', size=64),
98
 
        'entry_sequence': fields.function(_get_entry_sequence, method=True,
99
 
            store=False, string="Number", type="char", readonly="True"),
100
 
    }
101
 
    
102
 
    _defaults = {
103
 
        'third_party_type': 'res.partner',
104
 
        'journal_id': lambda self,cr,uid,c: self.pool.get('account.journal').search(cr, uid, [('type', '=', 'accrual'),
105
 
                                                                                              ('is_current_instance', '=', True)])[0],
106
 
        'functional_currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
107
 
        'state': 'draft',
108
 
    }
109
 
 
110
 
    def _create_write_set_vals(self, cr, uid, vals, context=None):
111
 
        if 'third_party_type' in vals:
112
 
            if vals['third_party_type'] == 'hr.employee' and 'employee_id' in vals:
113
 
                employee = self.pool.get('hr.employee').browse(cr, uid, vals['employee_id'], context=context)
114
 
                vals['third_party_name'] = employee.name
115
 
            elif vals['third_party_type'] == 'res.partner' and 'partner_id' in vals:
116
 
                partner = self.pool.get('res.partner').browse(cr, uid, vals['partner_id'], context=context)
117
 
                vals['third_party_name'] = partner.name
118
 
            elif not vals['third_party_type']:
119
 
                vals['partner_id'] = False
120
 
        if 'period_id' in vals:
121
 
            period = self.pool.get('account.period').browse(cr, uid, vals['period_id'], context=context)
122
 
            vals['date'] = period.date_stop
123
 
        if 'currency_id' in vals and 'date' in vals:
124
 
            cr.execute("SELECT currency_id, name, rate FROM res_currency_rate WHERE currency_id = %s AND name <= %s ORDER BY name desc LIMIT 1" ,(vals['currency_id'], vals['date']))
125
 
            if not cr.rowcount:
126
 
                currency_name = self.pool.get('res.currency').browse(cr, uid, vals['currency_id'], context=context).name
127
 
                formatted_date = datetime.datetime.strptime(vals['date'], '%Y-%m-%d').strftime('%d/%b/%Y')
128
 
                raise osv.except_osv(_('Warning !'), _("The currency '%s' does not have any rate set for date '%s'!") % (currency_name, formatted_date))
129
 
 
130
 
    def create(self, cr, uid, vals, context=None):
131
 
        if context is None:
132
 
            context = {}
133
 
 
134
 
        if 'document_date' in vals and vals.get('period_id', False):
135
 
            # US-192 check doc date regarding post date
136
 
            # => read (as date readonly in form) to get posting date: 
137
 
            # is end of period
138
 
            posting_date = self.pool.get('account.period').read(cr, uid,
139
 
                vals['period_id'], ['date_stop', ],
140
 
                context=context)['date_stop']
141
 
            self.pool.get('finance.tools').check_document_date(cr, uid,
142
 
                vals['document_date'], posting_date, context=context)
143
 
 
144
 
        self._create_write_set_vals(cr, uid, vals, context=context)
145
 
        return super(msf_accrual_line, self).create(cr, uid, vals, context=context)
146
 
 
147
 
    def write(self, cr, uid, ids, vals, context=None):
148
 
        if context is None:
149
 
            context = {}
150
 
        if isinstance(ids, (int, long, )):
151
 
            ids = [ids]
152
 
 
153
 
        if 'document_date' in vals:
154
 
            # US-192 check doc date reagarding post date
155
 
            # => read date field (as readonly in form)
156
 
            for r in self.read(cr, uid, ids, ['date', ], context=context):
157
 
                self.pool.get('finance.tools').check_document_date(cr, uid,
158
 
                    vals['document_date'], r['date'], context=context)
159
 
 
160
 
        self._create_write_set_vals(cr, uid, vals, context=context)
161
 
        return super(msf_accrual_line, self).write(cr, uid, ids, vals, context=context)
162
 
    
163
 
    def button_cancel(self, cr, uid, ids, context=None):
164
 
        if context is None:
165
 
            context = {}
166
 
        if isinstance(ids, (int, long)):
167
 
            ids = [ids]
168
 
        period_obj = self.pool.get('account.period')
169
 
        move_obj = self.pool.get('account.move')
170
 
        move_line_obj = self.pool.get('account.move.line')
171
 
        for accrual_line in self.browse(cr, uid, ids, context=context):
172
 
            # check for periods, distribution, etc.
173
 
            if accrual_line.state != 'posted':
174
 
                raise osv.except_osv(_('Warning !'), _("The line '%s' is already posted!") % accrual_line.description)
175
 
 
176
 
            # US-770/1
177
 
            if accrual_line.period_id.state not in ('draft', 'field-closed'):
178
 
                raise osv.except_osv(_('Warning !'), _("The period '%s' is not open!" % accrual_line.period_id.name))
179
 
 
180
 
            move_date = accrual_line.period_id.date_stop
181
 
            reversal_move_date = (datetime.datetime.strptime(move_date, '%Y-%m-%d') + relativedelta(days=1)).strftime('%Y-%m-%d')
182
 
            # check if periods are open
183
 
            reversal_period_ids = period_obj.find(cr, uid, reversal_move_date, context=context)
184
 
            reversal_period_id = reversal_period_ids[0]
185
 
            # Create moves
186
 
            move_vals = {
187
 
                'ref': accrual_line.reference,
188
 
                'period_id': accrual_line.period_id.id,
189
 
                'journal_id': accrual_line.journal_id.id,
190
 
                'date': move_date
191
 
            }
192
 
            reversal_move_vals = {
193
 
                'ref': accrual_line.reference,
194
 
                'period_id': reversal_period_id,
195
 
                'journal_id': accrual_line.journal_id.id,
196
 
                'date': reversal_move_date
197
 
            }
198
 
            move_id = move_obj.create(cr, uid, move_vals, context=context)
199
 
            reversal_move_id = move_obj.create(cr, uid, reversal_move_vals, context=context)
200
 
 
201
 
            cancel_description = "CANCEL - " + accrual_line.description
202
 
            reverse_cancel_description = "CANCEL - REV - " + accrual_line.description
203
 
 
204
 
            # Create move lines
205
 
            booking_field = accrual_line.accrual_amount > 0 and 'debit_currency' or 'credit_currency'  # reverse of initial entry
206
 
            accrual_move_line_vals = {
207
 
                'accrual': True,
208
 
                'move_id': move_id,
209
 
                'date': move_date,
210
 
                'document_date': accrual_line.document_date,
211
 
                'journal_id': accrual_line.journal_id.id,
212
 
                'period_id': accrual_line.period_id.id,
213
 
                'reference': accrual_line.reference,
214
 
                'name': cancel_description,
215
 
                'account_id': accrual_line.accrual_account_id.id,
216
 
                'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
217
 
                'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
218
 
                booking_field: abs(accrual_line.accrual_amount),
219
 
                'currency_id': accrual_line.currency_id.id,
220
 
            }
221
 
            booking_field = accrual_line.accrual_amount > 0 and 'credit_currency' or 'debit_currency'
222
 
            expense_move_line_vals = {
223
 
                'accrual': True,
224
 
                'move_id': move_id,
225
 
                'date': move_date,
226
 
                'document_date': accrual_line.document_date,
227
 
                'journal_id': accrual_line.journal_id.id,
228
 
                'period_id': accrual_line.period_id.id,
229
 
                'reference': accrual_line.reference,
230
 
                'name': cancel_description,
231
 
                'account_id': accrual_line.expense_account_id.id,
232
 
                'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
233
 
                'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
234
 
                booking_field: abs(accrual_line.accrual_amount),
235
 
                'currency_id': accrual_line.currency_id.id,
236
 
                'analytic_distribution_id': accrual_line.analytic_distribution_id.id,
237
 
            }
238
 
 
239
 
            # and their reversal (source_date to keep the old change rate)
240
 
            booking_field = accrual_line.accrual_amount > 0 and 'credit_currency' or 'debit_currency'
241
 
            reversal_accrual_move_line_vals = {
242
 
                'accrual': True,
243
 
                'move_id': reversal_move_id,
244
 
                'date': reversal_move_date,
245
 
                'document_date': reversal_move_date,
246
 
                'source_date': move_date,
247
 
                'journal_id': accrual_line.journal_id.id,
248
 
                'period_id': reversal_period_id,
249
 
                'reference': accrual_line.reference,
250
 
                'name': reverse_cancel_description,
251
 
                'account_id': accrual_line.accrual_account_id.id,
252
 
                'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
253
 
                'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
254
 
                booking_field: abs(accrual_line.accrual_amount),
255
 
                'currency_id': accrual_line.currency_id.id,
256
 
            }
257
 
            booking_field = accrual_line.accrual_amount > 0 and 'debit_currency' or 'credit_currency'
258
 
            reversal_expense_move_line_vals = {
259
 
                'accrual': True,
260
 
                'move_id': reversal_move_id,
261
 
                'date': reversal_move_date,
262
 
                'document_date': reversal_move_date,
263
 
                'source_date': move_date,
264
 
                'journal_id': accrual_line.journal_id.id,
265
 
                'period_id': reversal_period_id,
266
 
                'reference': accrual_line.reference,
267
 
                'name': reverse_cancel_description,
268
 
                'account_id': accrual_line.expense_account_id.id,
269
 
                'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
270
 
                'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
271
 
                booking_field: abs(accrual_line.accrual_amount),
272
 
                'currency_id': accrual_line.currency_id.id,
273
 
                'analytic_distribution_id': accrual_line.analytic_distribution_id.id,
274
 
            }
275
 
 
276
 
            accrual_move_line_id = move_line_obj.create(cr, uid, accrual_move_line_vals, context=context)
277
 
            expense_move_line_id = move_line_obj.create(cr, uid, expense_move_line_vals, context=context)
278
 
            reversal_accrual_move_line_id = move_line_obj.create(cr, uid, reversal_accrual_move_line_vals, context=context)
279
 
            reversal_expense_move_line_id = move_line_obj.create(cr, uid, reversal_expense_move_line_vals, context=context)
280
 
 
281
 
            # Post the moves
282
 
            move_obj.post(cr, uid, [move_id, reversal_move_id], context=context)
283
 
            # Reconcile the accrual move line with its reversal
284
 
            move_line_obj.reconcile_partial(cr, uid, [accrual_move_line_id, reversal_accrual_move_line_id], context=context)
285
 
            # validate the accrual line
286
 
            self.write(cr, uid, [accrual_line.id], {'state': 'cancel'}, context=context)
287
 
        return True
288
 
    
289
 
    def button_duplicate(self, cr, uid, ids, context=None):
290
 
        """
291
 
        Copy given lines and delete all links
292
 
        """
293
 
        # Some verifications
294
 
        if not context:
295
 
            context = {}
296
 
        if isinstance(ids, (int, long)):
297
 
            ids = [ids]
298
 
        # Browse lines
299
 
        for line in self.browse(cr, uid, ids, context=context):
300
 
            default_vals = ({
301
 
                'description': '(copy) ' + line.description,
302
 
            })
303
 
            if line.analytic_distribution_id:
304
 
                # the distribution must be copied, not just the id
305
 
                new_distrib_id = self.pool.get('analytic.distribution').copy(cr, uid, line.analytic_distribution_id.id, {}, context=context)
306
 
                if new_distrib_id:
307
 
                    default_vals.update({'analytic_distribution_id': new_distrib_id})
308
 
            self.copy(cr, uid, line.id, default_vals, context=context)
309
 
        return True
310
 
    
311
 
    def button_analytic_distribution(self, cr, uid, ids, context=None):
312
 
        """
313
 
        Launch analytic distribution wizard on an invoice line
314
 
        """
315
 
        # Some verifications
316
 
        if not context:
317
 
            context = {}
318
 
        if isinstance(ids, (int, long)):
319
 
            ids = [ids]
320
 
        # Prepare some values
321
 
        accrual_line = self.browse(cr, uid, ids[0], context=context)
322
 
        # Search elements for currency
323
 
        currency = accrual_line.currency_id and accrual_line.currency_id.id
324
 
        # Get analytic distribution id from this line
325
 
        distrib_id = accrual_line and accrual_line.analytic_distribution_id and accrual_line.analytic_distribution_id.id or False
326
 
        # Prepare values for wizard
327
 
        vals = {
328
 
            'total_amount': accrual_line.accrual_amount or 0.0,
329
 
            'accrual_line_id': accrual_line.id,
330
 
            'currency_id': currency or False,
331
 
            'state': 'dispatch',
332
 
            'account_id': accrual_line.expense_account_id and accrual_line.expense_account_id.id or False,
333
 
            'posting_date': accrual_line.date,
334
 
            'document_date': accrual_line.document_date,
335
 
        }
336
 
        if distrib_id:
337
 
            vals.update({'distribution_id': distrib_id,})
338
 
        # Create the wizard
339
 
        wiz_obj = self.pool.get('analytic.distribution.wizard')
340
 
        wiz_id = wiz_obj.create(cr, uid, vals, context=context)
341
 
        # Update some context values
342
 
        context.update({
343
 
            'active_id': ids[0],
344
 
            'active_ids': ids,
345
 
            'from_accrual_line': True
346
 
        })
347
 
        # Open it!
348
 
        return {
349
 
                'name': _('Analytic distribution'),
350
 
                'type': 'ir.actions.act_window',
351
 
                'res_model': 'analytic.distribution.wizard',
352
 
                'view_type': 'form',
353
 
                'view_mode': 'form',
354
 
                'target': 'new',
355
 
                'res_id': [wiz_id],
356
 
                'context': context,
357
 
        }
358
 
 
359
 
    def button_delete(self, cr, uid, ids, context=None):
360
 
        return self.unlink(cr, uid, ids, context=context)
361
 
 
362
 
    def unlink(self, cr, uid, ids, context=None):
363
 
        if not ids:
364
 
            return
365
 
        for rec in self.browse(cr, uid, ids, context=context):
366
 
            if rec.state != 'draft':
367
 
                raise osv.except_osv(_('Warning'),
368
 
                    _('You can only delete draft accruals'))
369
 
        return super(msf_accrual_line, self).unlink(cr, uid, ids,
370
 
            context=context)
371
 
    
372
 
msf_accrual_line()
373
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: