2
# -*- coding: utf-8 -*-
3
##############################################################################
5
# OpenERP, Open Source Management Solution
6
# Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved
7
# Developer: Olivier DOSSMANN
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.
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.
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/>.
22
##############################################################################
25
from osv import fields
26
from tools.translate import _
27
from tools import flatten
29
class account_mcdb(osv.osv_memory):
30
_name = 'account.mcdb'
33
'journal_ids': fields.many2many(obj='account.journal', rel='account_journal_mcdb', id1='mcdb_id', id2='journal_id', string="Journal Code"),
34
'analytic_journal_ids': fields.many2many(obj='account.analytic.journal', rel='account_analytic_journal_mcdb', id1='mcdb_id', id2='analytic_journal_id', string="Analytic Journal Code"),
35
'abs_id': fields.many2one('account.bank.statement', string="Register Code"), # Change into many2many ?
36
'company_id': fields.many2one('res.company', string="Proprietary instance"),
37
'posting_date_from': fields.date('First posting date'),
38
'posting_date_to': fields.date('Ending posting date'),
39
'document_date_from': fields.date('First document date'),
40
'document_date_to': fields.date('Ending document date'),
41
'period_ids': fields.many2many(obj='account.period', rel="account_period_mcdb", id1="mcdb_id", id2="period_id", string="Accounting Period"),
42
'account_ids': fields.many2many(obj='account.account', rel='account_account_mcdb', id1='mcdb_id', id2='account_id', string="Account Code"),
43
'partner_id': fields.many2one('res.partner', string="Partner"),
44
'employee_id': fields.many2one('hr.employee', string="Employee"),
45
'transfer_journal_id': fields.many2one('account.journal', string="Journal"),
46
'reconciled': fields.selection([('reconciled', 'Reconciled'), ('unreconciled', 'NOT reconciled')], string='Reconciled?'),
47
'functional_currency_id': fields.many2one('res.currency', string="Functional currency", readonly=True),
48
'amount_func_from': fields.float('Begin amount in functional currency'),
49
'amount_func_to': fields.float('Ending amount in functional currency'),
50
'booking_currency_id': fields.many2one('res.currency', string="Booking currency"),
51
'amount_book_from': fields.float('Begin amount in booking currency'),
52
'amount_book_to': fields.float('Ending amount in booking currency'),
53
'currency_choice': fields.selection([('booking', 'Booking'), ('functional', 'Functional')], string="Currency type"),
54
'currency_id': fields.many2one('res.currency', string="Currency"),
55
'amount_from': fields.float('Begin amount in given currency type'),
56
'amount_to': fields.float('Ending amount in given currency type'),
57
'account_type_ids': fields.many2many(obj='account.account.type', rel='account_account_type_mcdb', id1='mcdb_id', id2='account_type_id',
58
string="Account type"),
59
'reconcile_id': fields.many2one('account.move.reconcile', string="Reconcile Reference"),
60
'ref': fields.char(string='Reference', size=255),
61
'name': fields.char(string='Description', size=255),
62
'rev_account_ids': fields.boolean('Exclude account selection'),
63
'model': fields.selection([('account.move.line', 'Journal Items'), ('account.analytic.line', 'Analytic Journal Items')], string="Type"),
64
'display_in_output_currency': fields.many2one('res.currency', string='Display in output currency'),
65
'fx_table_id': fields.many2one('res.currency.table', string="FX Table"),
66
'analytic_account_cc_ids': fields.many2many(obj='account.analytic.account', rel="account_analytic_mcdb", id1="mcdb_id", id2="analytic_account_id",
67
string="Funding Pool"),
68
'rev_analytic_account_cc_ids': fields.boolean('Exclude Cost Center selection'),
69
'analytic_account_fp_ids': fields.many2many(obj='account.analytic.account', rel="account_analytic_mcdb", id1="mcdb_id", id2="analytic_account_id",
70
string="Cost Center"),
71
'rev_analytic_account_fp_ids': fields.boolean('Exclude Funding Pool selection'),
72
'analytic_account_f1_ids': fields.many2many(obj='account.analytic.account', rel="account_analytic_mcdb", id1="mcdb_id", id2="analytic_account_id",
74
'rev_analytic_account_f1_ids': fields.boolean('Exclude free 1 selection'),
75
'analytic_account_f2_ids': fields.many2many(obj='account.analytic.account', rel="account_analytic_mcdb", id1="mcdb_id", id2="analytic_account_id",
77
'rev_analytic_account_f2_ids': fields.boolean('Exclude free 2 selection'),
78
'reallocated': fields.selection([('reallocated', 'Reallocated'), ('unreallocated', 'NOT reallocated')], string='Reallocated?'),
79
'reversed': fields.selection([('reversed', 'Reversed'), ('notreversed', 'NOT reversed')], string='Reversed?'),
80
'rev_journal_ids': fields.boolean('Exclude journal selection'),
81
'rev_period_ids': fields.boolean('Exclude period selection'),
82
'rev_account_type_ids': fields.boolean('Exclude account type selection'),
83
'rev_analytic_journal_ids': fields.boolean('Exclude analytic journal selection'),
87
'model': lambda self, cr, uid, c: c.get('from', 'account.move.line'),
88
'functional_currency_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
89
'currency_choice': lambda *a: 'booking',
92
def onchange_currency_choice(self, cr, uid, ids, choice, func_curr=False, mnt_from=0.0, mnt_to=0.0, context=None):
94
Permit to give default company currency if 'functional' has been choosen.
95
Delete all currency and amount fields (to not disturb normal mechanism)
98
if isinstance(ids, (int, long)):
102
# Prepare some values
105
for field in ['amount_book_from', 'amount_book_to', 'amount_func_from', 'amount_func_to', 'booking_currency_id']:
108
if choice == 'functional':
109
vals.update({'currency_id': func_curr or False})
110
elif choice == 'booking':
111
vals.update({'currency_id': False})
112
# Update amounts 'from' and 'to'.
113
update_from = self.onchange_amount(cr, uid, ids, choice, mnt_from, 'from', context=context)
114
update_to = self.onchange_amount(cr, uid, ids, choice, mnt_to, 'to', context=context)
116
vals.update(update_from.get('value'))
118
vals.update(update_to.get('value'))
119
return {'value': vals}
121
def onchange_currency(self, cr, uid, ids, choice, currency, context=None):
123
Fill in right field regarding choice and currency
125
# Prepare some values
131
if choice == 'functional':
132
vals['functional_currency_id'] = currency
133
elif choice == 'booking':
134
vals['booking_currency_id'] = currency
135
return {'value': vals}
137
def onchange_amount(self, cr, uid, ids, choice, amount, amount_type=None, context=None):
139
Fill in right amount field regarding choice
141
# Prepare some values
148
if choice == 'functional':
149
if amount_type == 'from':
150
vals['amount_func_from'] = amount
151
elif amount_type == 'to':
152
vals ['amount_func_to'] = amount
153
elif choice == 'booking':
154
if amount_type == 'from':
155
vals['amount_book_from'] = amount
156
elif amount_type == 'to':
157
vals['amount_book_to'] = amount
158
return {'value': vals}
160
def onchange_fx_table(self, cr, uid, ids, fx_table_id, context=None):
162
Update output currency domain in order to show right currencies attached to given fx table
169
res.update({'value': {'display_in_output_currency' : False}})
172
def button_validate(self, cr, uid, ids, context=None):
174
Validate current forms and give result
179
if isinstance(ids, (int, long)):
181
# Prepare some values
183
wiz = self.browse(cr, uid, [ids[0]], context=context)[0]
184
res_model = wiz and wiz.model or False
186
# Prepare domain values
187
# First MANY2MANY fields
188
m2m_fields = [('period_ids', 'period_id'), ('journal_ids', 'journal_id'), ('analytic_journal_ids', 'journal_id'),
189
('analytic_account_fp_ids', 'account_id'), ('analytic_account_cc_ids', 'account_id'),
190
('analytic_account_f1_ids', 'account_id'), ('analytic_account_f2_ids', 'account_id')]
191
if res_model == 'account.analytic.line':
192
m2m_fields.append(('account_ids', 'general_account_id'))
193
m2m_fields.append(('account_type_ids', 'general_account_id.user_type'))
195
m2m_fields.append(('account_ids', 'account_id'))
196
m2m_fields.append(('account_type_ids', 'account_id.user_type'))
197
for m2m in m2m_fields:
198
if getattr(wiz, m2m[0]):
201
# account_ids with reversal
202
if m2m[0] == 'account_ids' and wiz.rev_account_ids:
204
# analytic_account_fp_ids with reversal
205
if m2m[0] == 'analytic_account_fp_ids' and wiz.rev_analytic_account_fp_ids:
207
# analytic_account_cc_ids with reversal
208
if m2m[0] == 'analytic_account_cc_ids' and wiz.rev_analytic_account_cc_ids:
210
# analytic_account_f1_ids with reversal
211
if m2m[0] == 'analytic_account_f1_ids' and wiz.rev_analytic_account_f1_ids:
213
# analytic_account_f2_ids with reversal
214
if m2m[0] == 'analytic_account_f2_ids' and wiz.rev_analytic_account_f2_ids:
216
# period_ids with reversal
217
if m2m[0] == 'period_ids' and wiz.rev_period_ids:
219
# journal_ids with reversal
220
if m2m[0] == 'journal_ids' and wiz.rev_journal_ids:
222
# account_type_ids with reversal
223
if m2m[0] == 'account_type_ids' and wiz.rev_account_type_ids:
225
# analytic_journal_ids with reversal
226
if m2m[0] == 'analytic_journal_ids' and wiz.rev_analytic_journal_ids:
228
# Search if a view account is given
229
if m2m[0] in ['account_ids', 'analytic_account_fp_ids', 'analytic_account_cc_ids', 'analytic_account_f1_ids', 'analytic_account_f2_ids']:
231
account_obj = 'account.account'
232
if m2m[0] in ['analytic_account_fp_ids', 'analytic_account_cc_ids', 'analytic_account_f1_ids', 'analytic_account_f2_ids']:
233
account_obj = 'account.analytic.account'
234
for account in getattr(wiz, m2m[0]):
235
if account.type == 'view':
236
search_ids = self.pool.get(account_obj).search(cr, uid, [('id', 'child_of', [account.id])])
237
account_ids.append(search_ids)
239
# Add default account_ids from wizard
240
account_ids.append([x.id for x in getattr(wiz, m2m[0])])
241
# Convert list in a readable list for openerp
242
account_ids = flatten(account_ids)
243
# Create domain and NEXT element (otherwise this give a bad domain)
244
domain.append((m2m[1], operator, tuple(account_ids)))
246
domain.append((m2m[1], operator, tuple([x.id for x in getattr(wiz, m2m[0])])))
247
# Then MANY2ONE fields
248
for m2o in [('abs_id', 'statement_id'), ('company_id', 'company_id'), ('partner_id', 'partner_id'), ('employee_id', 'employee_id'),
249
('transfer_journal_id', 'transfer_journal_id'), ('booking_currency_id', 'currency_id'), ('reconcile_id', 'reconcile_id')]:
250
if getattr(wiz, m2o[0]):
251
domain.append((m2o[1], '=', getattr(wiz, m2o[0]).id))
252
# Finally others fields
254
for ll in [('ref', 'ref'), ('name', 'name')]:
255
if getattr(wiz, ll[0]):
256
domain.append((ll[1], 'ilike', '%%%s%%' % getattr(wiz, ll[0])))
258
for sup in [('posting_date_from', 'date')]:
259
if getattr(wiz, sup[0]):
260
domain.append((sup[1], '>=', getattr(wiz, sup[0])))
261
for inf in [('posting_date_to', 'date')]:
262
if getattr(wiz, inf[0]):
263
domain.append((inf[1], '<=', getattr(wiz, inf[0])))
266
if wiz.reconciled == 'reconciled':
267
domain.append(('reconcile_id', '!=', False))
268
elif wiz.reconciled == 'unreconciled':
269
domain.append(('reconcile_id', '=', False))
272
if wiz.reallocated == 'reallocated':
273
domain.append(('is_reallocated', '=', True))
274
elif wiz.reallocated == 'unreallocated':
275
domain.append(('is_reallocated', '=', False))
278
if wiz.reversed == 'reversed':
279
domain.append(('is_reversal', '=', True))
280
elif wiz.reversed == 'notreversed':
281
domain.append(('is_reversal', '=', False))
286
# NB: Amount problem has been resolved as this
287
#+ There is 4 possibilities for amounts:
288
#+ 1/ NO amount given: nothing to do
289
#+ 2/ amount FROM AND amount TO is given
290
#+ 3/ amount FROM is filled in but NOT amount TO
291
#+ 4/ amount TO is filled in but NOT amount FROM
293
#+ For each case, here is what domain should be look like:
294
#+ 1/ FROM is 0.0, TO is 0,0. Domain is []
295
#+ 2/ FROM is 400, TO is 600. Domain is
296
#+ ['|', '&', ('balance', '>=', -600), ('balance', '<=', -400), '&', ('balance', '>=', 400), ('balance', '<=', '600')]
297
#+ 3/ FROM is 400, TO is 0.0. Domain is ['|', ('balance', '<=', -400), ('balance', '>=', 400)]
298
#+ 4/ FROM is 0.0, TO is 600. Domain is ['&', ('balance', '>=', -600), ('balance', '<=', 600)]
300
# prepare tuples that would be processed
301
booking = ('amount_book_from', 'amount_book_to', 'amount_currency')
302
functional = ('amount_func_from', 'amount_func_to', 'balance')
303
for curr in [booking, functional]: #FIXME:add functional when possible
304
# Prepare some values
305
mnt_from = getattr(wiz, curr[0]) or False
306
mnt_to = getattr(wiz, curr[1]) or False
308
# specific behaviour for functional in analytic MCDB
309
if field == 'balance' and res_model == 'account.analytic.line':
311
abs_from = abs(mnt_from)
312
min_from = -1 * abs_from
315
# domain elements initialisation
317
if mnt_from and mnt_to:
318
domain_elements = ['|', '&', (field, '>=', min_to), (field, '<=', min_from), '&', (field, '>=', abs_from), (field, '<=', abs_to)]
320
domain_elements = ['|', (field, '<=', min_from), (field, '>=', abs_from)]
322
domain_elements = ['&', (field, '>=', min_to), (field, '<=', abs_to)]
323
# Add elements to domain which would be use for filtering
324
for el in domain_elements:
326
# Output currency display (with fx_table)
328
context.update({'fx_table_id': wiz.fx_table_id.id, 'currency_table_id': wiz.fx_table_id.id})
329
if wiz.display_in_output_currency:
330
context.update({'output_currency_id': wiz.display_in_output_currency.id})
331
# Return result in a search view
332
view = 'account_move_line_mcdb_search_result'
333
search_view = 'mcdb_view_account_move_line_filter'
334
name = _('Journal Items MCDB result')
335
if res_model == 'account.analytic.line':
336
view = 'account_analytic_line_mcdb_search_result'
337
search_view = 'mcdb_view_account_analytic_line_filter'
338
name = _('Analytic Journal Items MCDB result')
339
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_mcdb', view)
340
view_id = view_id and view_id[1] or False
341
search_view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_mcdb', search_view)
342
search_view_id = search_view_id and search_view_id[1] or False
345
'type': 'ir.actions.act_window',
346
'res_model': res_model,
348
'view_mode': 'tree,form',
349
'view_id': [view_id],
350
'search_view_id': search_view_id,
357
def button_clear(self, cr, uid, ids, field=False, context=None):
359
Delete all fields from this object
364
if isinstance(ids, (int, long)):
369
if field and field in (self._columns and self._columns.keys()):
370
if self._columns[field]._type == 'many2many':
371
# Don't clear all other fields
373
# Clear this many2many field
374
self.write(cr, uid, ids, {field: [(6,0,[])]}, context=context)
375
# Clear all fields if necessary
377
res_id = self.create(cr, uid, {}, context=context)
384
wiz = self.browse(cr, uid, res_id)
385
res_model = wiz and wiz.model or False
386
# Prepare some values
387
name = _('Multi-Criteria Data Browser')
389
if res_model == 'account.move.line':
390
name = _('Journal Items MCDB')
391
view_name = 'account_mcdb_form'
392
elif res_model == 'account.analytic.line':
393
name = _('Analytic Journal Items MCDB')
394
view_name = 'account_mcdb_analytic_form'
395
if not view_name or not name:
396
raise osv.except_osv(_('Error'), _('Error: System does not know from where you come from.'))
398
view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_mcdb', view_name)
399
view_id = view_id and view_id[1] or False
402
'type': 'ir.actions.act_window',
403
'res_model': 'account.mcdb',
407
'view_id': [view_id],
412
def button_journal_clear(self, cr, uid, ids, context=None):
414
Delete journal_ids field content
419
if isinstance(ids, (int, long)):
421
# Return default behaviour with 'journal_ids' field
422
return self.button_clear(cr, uid, ids, field='journal_ids', context=context)
424
def button_period_clear(self, cr, uid, ids, context=None):
426
Delete period_ids field content
431
if isinstance(ids, (int, long)):
433
# Return default behaviour with 'period_ids' field
434
return self.button_clear(cr, uid, ids, field='period_ids', context=context)
436
def button_analytic_journal_clear(self, cr, uid, ids, context=None):
438
Delete analytic_journal_ids field content
443
if isinstance(ids, (int, long)):
445
# Return default behaviour with 'analytic_journal_ids' field
446
return self.button_clear(cr, uid, ids, field='analytic_journal_ids', context=context)
448
def button_account_clear(self, cr, uid, ids, context=None):
450
Delete account_ids field content
455
if isinstance(ids, (int, long)):
457
# Return default behaviour with 'account_ids' field
458
return self.button_clear(cr, uid, ids, field='account_ids', context=context)
460
def button_account_type_clear(self, cr, uid, ids, context=None):
462
Delete account_type_ids field content
467
if isinstance(ids, (int, long)):
469
# Return default behaviour with 'account_type_ids' field
470
return self.button_clear(cr, uid, ids, field='account_type_ids', context=context)
472
def button_funding_pool_clear(self, cr, uid, ids, context=None):
474
Delete analytic_account_fp_ids field content
479
if isinstance(ids, (int, long)):
481
# Return default behaviour with 'analytic_account_fp_ids' field
482
return self.button_clear(cr, uid, ids, field='analytic_account_fp_ids', context=context)
484
def button_cost_center_clear(self, cr, uid, ids, context=None):
486
Delete analytic_account_cc_ids field content
491
if isinstance(ids, (int, long)):
493
# Return default behaviour with 'analytic_account_cc_ids' field
494
return self.button_clear(cr, uid, ids, field='analytic_account_cc_ids', context=context)
496
def button_free_1_clear(self, cr, uid, ids, context=None):
498
Delete analytic_account_f1_ids field content
503
if isinstance(ids, (int, long)):
505
# Return default behaviour with 'analytic_account_f1_ids' field
506
return self.button_clear(cr, uid, ids, field='analytic_account_f1_ids', context=context)
508
def button_free_2_clear(self, cr, uid, ids, context=None):
510
Delete analytic_account_f2_ids field content
515
if isinstance(ids, (int, long)):
517
# Return default behaviour with 'analytic_account_f2_ids' field
518
return self.button_clear(cr, uid, ids, field='analytic_account_f2_ids', context=context)
521
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: