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

« back to all changes in this revision

Viewing changes to msf_accrual/msf_accrual_line.py

  • Committer: Quentin THEURET
  • Date: 2016-03-04 12:15:00 UTC
  • Revision ID: qt@tempo-consulting.fr-20160304121500-u2ay8zrf83ih9fu3
US-826 [IMP] Change the way to check if products is not consistent on add multiple line wizard

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 
27
27
class msf_accrual_line(osv.osv):
28
28
    _name = 'msf.accrual.line'
 
29
    _rec_name = 'date'
29
30
    
30
31
    def onchange_period(self, cr, uid, ids, period_id, context=None):
31
32
        if period_id is False:
32
33
            return {'value': {'date': False}}
33
34
        else:
34
35
            period = self.pool.get('account.period').browse(cr, uid, period_id, context=context)
35
 
            return {'value': {'date': period.date_stop}}
 
36
            return {'value': {'date': period.date_stop, 'document_date': period.date_stop}}
36
37
    
37
38
    def _get_functional_amount(self, cr, uid, ids, field_name, arg, context=None):
38
39
        res = {}
46
47
                                                                          round=True,
47
48
                                                                          context=date_context)
48
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
49
71
    
50
72
    _columns = {
51
73
        'date': fields.date("Date"),
52
 
        'period_id': fields.many2one('account.period', 'Period', required=True, domain=[('state', '=', 'draft')]),
 
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)]),
53
76
        'description': fields.char('Description', size=64, required=True),
54
77
        'reference': fields.char('Reference', size=64),
55
78
        'expense_account_id': fields.many2one('account.account', 'Expense Account', required=True, domain=[('type', '!=', 'view'), ('user_type_code', '=', 'expense')]),
57
80
        'accrual_amount': fields.float('Accrual Amount', required=True),
58
81
        'currency_id': fields.many2one('res.currency', 'Currency', required=True),
59
82
        'journal_id': fields.many2one('account.journal', 'Journal', required=True),
60
 
        'third_party_type': fields.selection([('res.partner', 'Partner'),
61
 
                                              ('hr.employee', 'Employee')], 'Third Party', required=True),
62
 
        'partner_id': fields.many2one('res.partner', 'Third Party Partner'),
63
 
        'employee_id': fields.many2one('hr.employee', 'Third Party Employee'),
 
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"),
64
90
        'analytic_distribution_id': fields.many2one('analytic.distribution', 'Analytic Distribution'),
65
91
        'functional_amount': fields.function(_get_functional_amount, method=True, store=False, string="Functional Amount", type="float", readonly="True"),
66
92
        'functional_currency_id': fields.many2one('res.currency', 'Functional Currency', required=True, readonly=True),
69
95
                                   ('cancel', 'Cancelled')], 'Status', required=True),
70
96
        # Field to store the third party's name for list view
71
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"),
72
100
    }
73
101
    
74
102
    _defaults = {
75
103
        'third_party_type': 'res.partner',
76
 
        'journal_id': lambda self,cr,uid,c: self.pool.get('account.journal').search(cr, uid, [('code', '=', 'AC')])[0],
 
104
        'journal_id': lambda self,cr,uid,c: self.pool.get('account.journal').search(cr, uid, [('type', '=', 'accrual'),
 
105
                                                                                              ('is_current_instance', '=', True)])[0],
77
106
        'functional_currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
78
107
        'state': 'draft',
79
108
    }
80
 
    
 
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
 
81
130
    def create(self, cr, uid, vals, context=None):
82
131
        if context is None:
83
132
            context = {}
84
 
        if 'third_party_type' in vals:
85
 
            if vals['third_party_type'] == 'hr.employee' and 'employee_id' in vals:
86
 
                employee = self.pool.get('hr.employee').browse(cr, uid, vals['employee_id'], context=context)
87
 
                vals['third_party_name'] = employee.name
88
 
            elif vals['third_party_type'] == 'res.partner' and 'partner_id' in vals:
89
 
                partner = self.pool.get('res.partner').browse(cr, uid, vals['partner_id'], context=context)
90
 
                vals['third_party_name'] = partner.name
91
 
        if 'period_id' in vals:
92
 
            period = self.pool.get('account.period').browse(cr, uid, vals['period_id'], context=context)
93
 
            vals['date'] = period.date_stop
 
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)
94
145
        return super(msf_accrual_line, self).create(cr, uid, vals, context=context)
95
 
    
 
146
 
96
147
    def write(self, cr, uid, ids, vals, context=None):
97
148
        if context is None:
98
149
            context = {}
99
 
        if 'third_party_type' in vals:
100
 
            if vals['third_party_type'] == 'hr.employee' and 'employee_id' in vals:
101
 
                employee = self.pool.get('hr.employee').browse(cr, uid, vals['employee_id'], context=context)
102
 
                vals['third_party_name'] = employee.name
103
 
            elif vals['third_party_type'] == 'res.partner' and 'partner_id' in vals:
104
 
                partner = self.pool.get('res.partner').browse(cr, uid, vals['partner_id'], context=context)
105
 
                vals['third_party_name'] = partner.name
106
 
        if 'period_id' in vals:
107
 
            period = self.pool.get('account.period').browse(cr, uid, vals['period_id'], context=context)
108
 
            vals['date'] = period.date_stop
 
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)
109
161
        return super(msf_accrual_line, self).write(cr, uid, ids, vals, context=context)
110
162
    
111
163
    def button_cancel(self, cr, uid, ids, context=None):
119
171
        for accrual_line in self.browse(cr, uid, ids, context=context):
120
172
            # check for periods, distribution, etc.
121
173
            if accrual_line.state != 'posted':
122
 
                raise osv.except_osv(_('Warning !'), _("The line '%s' is already posted!" % accrual_line.description))
123
 
            else:
124
 
                move_date = accrual_line.period_id.date_stop
125
 
                reversal_move_date = (datetime.datetime.strptime(move_date, '%Y-%m-%d') + relativedelta(days=1)).strftime('%Y-%m-%d')
126
 
                # check if periods are open
127
 
                reversal_period_ids = period_obj.find(cr, uid, reversal_move_date, context=context)
128
 
                reversal_period_id = reversal_period_ids[0]
129
 
                # Create moves
130
 
                move_vals = {
131
 
                    'ref': accrual_line.reference,
132
 
                    'period_id': accrual_line.period_id.id,
133
 
                    'journal_id': accrual_line.journal_id.id,
134
 
                    'date': move_date
135
 
                }
136
 
                reversal_move_vals = {
137
 
                    'ref': accrual_line.reference,
138
 
                    'period_id': reversal_period_id,
139
 
                    'journal_id': accrual_line.journal_id.id,
140
 
                    'date': reversal_move_date
141
 
                }
142
 
                move_id = move_obj.create(cr, uid, move_vals, context=context)
143
 
                reversal_move_id = move_obj.create(cr, uid, reversal_move_vals, context=context)
144
 
                
145
 
                cancel_description = "CANCEL - " + accrual_line.description
146
 
                reverse_cancel_description = "CANCEL - REV - " + accrual_line.description
147
 
                
148
 
                # Create move lines
149
 
                accrual_move_line_vals = {
150
 
                    'accrual': True,
151
 
                    'move_id': move_id,
152
 
                    'date': move_date,
153
 
                    'journal_id': accrual_line.journal_id.id,
154
 
                    'period_id': accrual_line.period_id.id,
155
 
                    'reference': accrual_line.reference,
156
 
                    'name': cancel_description,
157
 
                    'account_id': accrual_line.accrual_account_id.id,
158
 
                    'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
159
 
                    'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
160
 
                    'debit_currency': accrual_line.accrual_amount,
161
 
                    'currency_id': accrual_line.currency_id.id,
162
 
                }
163
 
                expense_move_line_vals = {
164
 
                    'accrual': True,
165
 
                    'move_id': move_id,
166
 
                    'date': move_date,
167
 
                    'journal_id': accrual_line.journal_id.id,
168
 
                    'period_id': accrual_line.period_id.id,
169
 
                    'reference': accrual_line.reference,
170
 
                    'name': cancel_description,
171
 
                    'account_id': accrual_line.expense_account_id.id,
172
 
                    'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
173
 
                    'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
174
 
                    'credit_currency': accrual_line.accrual_amount,
175
 
                    'currency_id': accrual_line.currency_id.id,
176
 
                    'analytic_distribution_id': accrual_line.analytic_distribution_id.id,
177
 
                }
178
 
                
179
 
                # and their reversal (source_date to keep the old change rate)
180
 
                reversal_accrual_move_line_vals = {
181
 
                    'accrual': True,
182
 
                    'move_id': reversal_move_id,
183
 
                    'date': reversal_move_date,
184
 
                    'source_date': move_date,
185
 
                    'journal_id': accrual_line.journal_id.id,
186
 
                    'period_id': reversal_period_id,
187
 
                    'reference': accrual_line.reference,
188
 
                    'name': reverse_cancel_description,
189
 
                    'account_id': accrual_line.accrual_account_id.id,
190
 
                    'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
191
 
                    'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
192
 
                    'credit_currency': accrual_line.accrual_amount,
193
 
                    'currency_id': accrual_line.currency_id.id,
194
 
                }
195
 
                reversal_expense_move_line_vals = {
196
 
                    'accrual': True,
197
 
                    'move_id': reversal_move_id,
198
 
                    'date': reversal_move_date,
199
 
                    'source_date': move_date,
200
 
                    'journal_id': accrual_line.journal_id.id,
201
 
                    'period_id': reversal_period_id,
202
 
                    'reference': accrual_line.reference,
203
 
                    'name': reverse_cancel_description,
204
 
                    'account_id': accrual_line.expense_account_id.id,
205
 
                    'partner_id': ((accrual_line.partner_id) and accrual_line.partner_id.id) or False,
206
 
                    'employee_id': ((accrual_line.employee_id) and accrual_line.employee_id.id) or False,
207
 
                    'debit_currency': accrual_line.accrual_amount,
208
 
                    'currency_id': accrual_line.currency_id.id,
209
 
                    'analytic_distribution_id': accrual_line.analytic_distribution_id.id,
210
 
                }
211
 
                
212
 
                accrual_move_line_id = move_line_obj.create(cr, uid, accrual_move_line_vals, context=context)
213
 
                expense_move_line_id = move_line_obj.create(cr, uid, expense_move_line_vals, context=context)
214
 
                reversal_accrual_move_line_id = move_line_obj.create(cr, uid, reversal_accrual_move_line_vals, context=context)
215
 
                reversal_expense_move_line_id = move_line_obj.create(cr, uid, reversal_expense_move_line_vals, context=context)
216
 
                
217
 
                # Post the moves
218
 
                move_obj.post(cr, uid, [move_id, reversal_move_id], context=context)
219
 
                # Reconcile the accrual move line with its reversal
220
 
                move_line_obj.reconcile_partial(cr, uid, [accrual_move_line_id, reversal_accrual_move_line_id], context=context)
221
 
                # validate the accrual line
222
 
                self.write(cr, uid, [accrual_line.id], {'state': 'cancel'}, context=context)
 
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)
223
287
        return True
224
288
    
225
289
    def button_duplicate(self, cr, uid, ids, context=None):
266
330
            'currency_id': currency or False,
267
331
            'state': 'dispatch',
268
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,
269
335
        }
270
336
        if distrib_id:
271
337
            vals.update({'distribution_id': distrib_id,})
280
346
        })
281
347
        # Open it!
282
348
        return {
283
 
                'name': 'Analytic distribution',
 
349
                'name': _('Analytic distribution'),
284
350
                'type': 'ir.actions.act_window',
285
351
                'res_model': 'analytic.distribution.wizard',
286
352
                'view_type': 'form',
289
355
                'res_id': [wiz_id],
290
356
                'context': context,
291
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)
292
371
    
293
372
msf_accrual_line()
294
373
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: