~openerp-commiter/openobject-addons/trunk-extra-addons

3547.2.46 by pso (tiny)
personal_base:made compatible to 5.0
1
# -*- encoding: utf-8 -*-
2
##############################################################################
3
#
4
# Copyright (c) 2008 Sandas. (http://www.sandas.eu) All Rights Reserved.
5
#
6
# WARNING: This program as such is intended to be used by professional
7
# programmers who take the whole responsability of assessing all potential
8
# consequences resulting from its eventual inadequacies and bugs
9
# End users who are looking for a ready-to-use solution with commercial
10
# garantees and support are strongly adviced to contract a Free Software
11
# Service Company
12
#
13
# This program is Free Software; you can redistribute it and/or
14
# modify it under the terms of the GNU General Public License
15
# as published by the Free Software Foundation; either version 2
16
# of the License, or (at your option) any later version.
17
#
18
# This program is distributed in the hope that it will be useful,
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
# GNU General Public License for more details.
22
#
23
# You should have received a copy of the GNU General Public License
24
# along with this program; if not, write to the Free Software
25
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26
#
27
##############################################################################
28
29
import time
30
from osv import fields, osv
31
from base import base_delete_unit_test_records, base_get_default_currency
32
from decimal import Decimal
33
34
class personal_account_entry(osv.osv):
35
	_name = "personal.base.account.entry"
36
	_description = "Account Entry"
37
	_order = "date DESC"
38
	
39
	def _get_accounts_fnc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
40
		res = {}
41
		for id in ids:
42
			res[id] = []
43
			for entry in self.browse(cr, uid, [id]):
44
				for line in entry.line_ids:
45
					res[id].append([line.account_id.id])
46
		return res
47
	
48
	_columns = {
49
		'user_id': fields.many2one('res.users', 'User', required=True),
50
		'date': fields.date('Date', required=True, select=True, states={'confirmed':[('readonly',True)]}),
51
		'name': fields.char('Description', size=64, required=True, select=True, states={'confirmed':[('readonly',True)]}),
52
		'line_ids': fields.one2many('personal.base.account.entry.line', 'parent_id', 'Entries', states={'confirmed':[('readonly',True)]}),
53
		'currency_id': fields.many2one('res.currency', 'Currency', states={'confirmed':[('readonly',True)]}),
54
		'note': fields.text('Note'),
55
		'state': fields.selection([('draft', 'Draft'),('confirmed', 'Confirmed')], 'State', required=True, readonly=True),
56
		'created_in_model_id': fields.many2one('ir.model', 'Created in Model', required=True, readonly=True),
57
		
58
		#fields for unit_test
59
        'unit_test': fields.boolean(string='unit_test'),
60
	}
61
	_defaults = {
62
		'user_id': lambda self, cr, uid, context: uid,
63
		'date': lambda *a: time.strftime('%Y-%m-%d'),
64
		'currency_id': lambda self, cr, uid, context: base_get_default_currency(cr, uid, context),
65
		'created_in_model_id': lambda self, cr, uid, context: self.pool.get('ir.model').search(cr, uid, [('model','=',self._name)])[0],
66
		'state': lambda *a: 'draft',
67
		'unit_test': lambda *a: False,
68
	}
69
	
70
	#def test_create(self, cr, uid, vals, context={}):
71
	#	id = self.create(cr, user, vals, context=context)
72
	#	print "entry_create", vals
73
	#	print "entry_create", id
74
	
75
	def search(self, cr, uid, args, offset=0, limit=2000, order=None, context=None, count=False):
76
		args.append(('user_id','=',uid))
77
		for arg in args:
78
			if arg[0] == 'created_in_model_id':
79
				try:
80
					int(arg[2])
81
					created_in_model_is_string = False
82
				except:
83
					created_in_model_is_string = True
84
				if created_in_model_is_string:
85
					model_ids =	self.pool.get('ir.model').search(cr, uid,
86
																 [('model','=',arg[2])])
87
					if model_ids == []:
88
						raise osv.except_osv('Error', 'Unknown model %s!' % arg[2])
89
					arg = (arg[0], arg[1], model_ids[0])
90
				break
91
			
92
		return osv.orm.orm.search(self, cr, uid, args, offset, limit, order,
93
								  context=context)
94
	
95
	def unlink(self, cr, uid, ids, context={}, check=True):
96
		for entry in self.browse(cr, uid, ids, context):
97
			if entry.state <> 'draft':
98
				raise osv.except_osv('Error',
99
						'You can not delete confirmed entry: "%s"!' % entry.name)
100
			for line in entry.line_ids:
101
				line.unlink(cr, uid, [line.id])
102
		return super(personal_account_entry, self).unlink(cr, uid, ids, context)
103
	
104
	def confirm(self, cr, uid, ids, *args):
105
		rec_lines = self.pool.get('personal.base.account.entry.line')
106
		rec_currency = self.pool.get('res.currency')
107
		for entry in self.browse(cr, uid, ids):
108
			if entry.user_id.id != uid:
109
				raise osv.except_osv('Error', 'Wrong user!')
110
			if entry.state != 'draft':
111
				raise osv.except_osv('Error', 'Only Draft entries can be confirmed!')
112
			
113
			amount = Decimal(0)
114
			_debit_amount = Decimal(0)
115
			_credit_amount = Decimal(0)
116
			currency = None
117
			for line in entry.line_ids:
118
				if line.state != 'draft':
119
					raise osv.except_osv('Error', 'Only Draft entries can be confirmed!')
120
				if line.date != entry.date:
121
					rec_lines.write(cr, uid, [line.id], {'date': entry.date})
122
				
123
				if not currency:
124
					currency = line.currency_id
125
				if line.debit_amount != 0:
126
					line_amount = line.debit_amount
127
				else:
128
					line_amount =  -line.credit_amount
129
				
130
				_debit_amount += Decimal(str(rec_currency.compute_with_currency_rate(cr, uid, line.currency_id.rate, currency.id, line.debit_amount)))
131
				_credit_amount += Decimal(str(rec_currency.compute_with_currency_rate(cr, uid, line.currency_id.rate, currency.id, line.credit_amount)))
132
				amount += Decimal(str(rec_currency.compute_with_currency_rate(cr, uid, line.currency_id.rate, currency.id, line_amount)))
133
			
134
			if amount != 0:
135
				raise osv.except_osv('Error', 'Sum of all lines amounts must be zero! You have %s in debit and %s in credit.' % (str(_debit_amount), str(_credit_amount)))
136
		
137
		for entry in self.browse(cr, uid, ids):
138
			for line in entry.line_ids:
139
				if line.state=='draft':
140
					self.pool.get('personal.base.account.entry.line').write(cr, uid, [line.id], {'state':'confirmed'})
141
				
142
		self.write(cr, uid, ids, {'state':'confirmed'})
143
		return True
144
	
145
	def return_entry(self, cr, uid, ids, *args):
146
		self.write(cr, uid, ids, {'state':'draft'})
147
		
148
		for entry in self.browse(cr, uid, ids):
149
			for line in entry.line_ids:
150
				if line.state=='confirmed':
151
					self.pool.get('personal.base.account.entry.line').write(cr, uid, [line.id], {'state':'draft'})
152
		return True
153
	
154
	#for unit tests
155
	def delete_unit_test_records(self, cr, uid):
156
		user_rec = self.pool.get('res.users')
157
		for user in user_rec.search(cr, uid, [('unit_test','=',True)]):
158
			ids = self.search(cr, user, [('unit_test','=',True)])
159
			self.return_entry(cr, user, ids)
160
		base_delete_unit_test_records(self, cr, uid)
161
	
162
	def test_if_confirm_possible(self, cr, uid, ids):
163
		try:
164
			self.confirm(cr, uid, ids)
165
		except:
166
			return
167
		assert False, "Entries confirmation checked out not everything."
168
	
169
personal_account_entry()
170
171
class personal_account_entry_line(osv.osv):
172
	_name = "personal.base.account.entry.line"
173
	_description = "Account Entry Line"
174
	_order = "date, id"
175
	
176
	def _get_balance_fnc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
177
		res = {}
178
		for id in ids:
179
			line = self.browse(cr, uid, id)
180
			res[id] = 0.0
181
			for search_line_id in self.search(cr, uid, 
182
					[('account_id', '=', line.account_id.id),
183
					 ('date', '<=', line.date),
184
					 ('state', '=', 'confirmed')]
185
			):
186
				sum_line = self.browse(cr, uid, search_line_id)
187
				if (sum_line.date < line.date) or (search_line_id <= line.id):
188
					res[id] = res[id] + (sum_line.amount_base * line.account_id.type_id.sign)
189
        
190
#            for child_id in self.read(cr, uid, [id], ['child_ids'])[0]['child_ids']:
191
#                for child in self.browse(cr, uid, [child_id]):
192
#                    res[child_id] = child.balance
193
#                    res[id] = res[id] + res[child_id]
194
		return res
195
	
196
	def _get_amount_base_with_sign(self, cr, uid, ids, prop, unknow_none, context):
197
		res = {}
198
		for id in ids:
199
			line = self.browse(cr, uid, id)
200
			res[id] = line.amount_base * line.account_id.type_id.sign
201
		return res
202
	
203
	def _get_default_date(self, cr, uid, context):
204
		if 'date' in context:
205
			return context['date']
206
		else:
207
			return time.strftime('%Y-%m-%d')
208
	
209
	def _get_default_currency(self, cr, uid, context):
210
		if 'currency_id' in context:
211
			return context['currency_id']
212
		else:
213
			return 0
214
	
215
	def _check_amounts(self, cr, uid, ids):
216
		currency_obj = self.pool.get('res.currency')
217
		lines = self.browse(cr, uid, ids)
218
		for l in lines:
219
			if (l.debit_amount != 0) and (l.credit_amount != 0):
220
				return False
221
222
			currency_rate = currency_obj.personal_calc_currency_rate(cr, uid, l.account_id.id, l.currency_id.id)
223
			if (l.debit_amount != 0):
224
				local_amount_base = self._calc_amount_base(cr, uid, l.account_id.id, currency_rate, l.debit_amount)
225
			else:
226
				local_amount_base = self._calc_amount_base(cr, uid, l.account_id.id, currency_rate, -l.credit_amount)
227
			
228
			hasChanges = False
229
			if (currency_obj.round(cr, uid, l.currency_id, l.amount_base) != currency_obj.round(cr, uid, l.currency_id, local_amount_base)):
230
				self.write(cr, uid, [l['id']], {'amount_base': local_amount_base})
231
		return True
232
	
233
	_columns = {
234
		'user_id': fields.many2one('res.users', 'User', required=True),
235
	    'date': fields.date('Date', required=True, states={'confirmed':[('readonly',True)]}),
236
		'parent_id': fields.many2one('personal.base.account.entry', 'Entry', required=True, states={'confirmed':[('readonly',True)]}),
237
		'name': fields.char('Description', size=64, select=True, states={'confirmed':[('readonly',True)]}),
238
		'account_id': fields.many2one('personal.base.account', 'Account', required=True, select=True, states={'confirmed':[('readonly',True)]}),
239
		'currency_id': fields.many2one('res.currency', 'Currency', required=True, states={'confirmed':[('readonly',True)]}),
240
		'currency_rate': fields.float('Currency Rate', digits=(12,6), required=True, states={'confirmed':[('readonly',True)]}),
241
		'debit_amount': fields.float('Debit Amount', digits=(10,2), states={'confirmed':[('readonly',True)]}),
242
		'credit_amount': fields.float('Credit Amount', digits=(10,2), states={'confirmed':[('readonly',True)]}),
243
		#'amount': fields.float('Amount', digits=(10,2)),
244
		'amount_base': fields.float('Amount Base', digits=(10,2), states={'confirmed':[('readonly',True)]}),
245
		'state': fields.selection([('draft', 'Draft'),('confirmed', 'Confirmed')], 'State', required=True, readonly=True),
246
247
		'balance': fields.function(_get_balance_fnc, method=True, type="float", digits=(10,2), string='Balance'),
248
		'amount_base_with_sign': fields.function(_get_amount_base_with_sign, method=True, type="float", digits=(10,2), string='Amount'),
249
		
250
		#fields for unit_test
251
        'unit_test': fields.boolean(string='unit_test')
252
	}
253
	_defaults = {
254
		'user_id': lambda self, cr, uid, context: uid,
255
		'date': lambda self, cr, uid, context: \
256
				self._get_default_date(cr, uid, context=context),
257
		'currency_id': lambda self, cr, uid, context: \
258
				self._get_default_currency(cr, uid, context=context),
259
		'currency_rate': lambda *a: 1,
260
		'state': lambda *a: 'draft',
261
		
262
		'unit_test': lambda *a: False,
263
	}
264
	_constraints = [
265
        (_check_amounts, 'Error: You must enter only one amount.', ['debit_amount', 'credit_amount'])
266
    ]	
267
	_fields_from_parent = ['date', 'currency_id']
268
	
269
	def create(self, cr, uid, vals, context={}):
270
		#without this don't work 'parent_id' in unit tests
271
		if ("unit_test" in vals) and (vals["unit_test"]):	
272
			cr.commit()
273
		
274
		entry_rec = self.pool.get('personal.base.account.entry')
275
		parent = entry_rec.read(cr, uid, [vals['parent_id']], self._fields_from_parent)
276
		for field in self._fields_from_parent:
277
			if not (field in context): 
278
				if parent[0][field]:
279
					if ('_id' in field):
280
						field_value = parent[0][field][0]
281
					else:
282
						field_value = parent[0][field]
283
				if not (field in vals):
284
					context[field] = field_value
285
				else:
286
					context[field] = field_value
287
					
288
		#needed for unit test, because <record> tag haven't uid attribute
289
		user = uid
290
		if ('user_id' in vals) and (vals['user_id'] != user):
291
			user = vals['user_id']
292
		
293
		if ("account_id" in vals):
294
			acc_rec = self.pool.get('personal.base.account')
295
			if acc_rec.read(cr, user, [vals["account_id"]], ['user_id'])[0]['user_id'][0] != user:
296
				raise osv.except_osv('Error', 'Wrong account!')
297
		if ("parent_id" in vals):
298
			if entry_rec.read(cr, user, [vals["parent_id"]], ['user_id'])[0]['user_id'][0] != user:
299
				raise osv.except_osv('Error', 'Wrong entry line parent!')
300
				
301
		return super(personal_account_entry_line, self).create(cr, user, vals, context=context)
302
	
303
	def search(self, cr, uid, args, offset=0, limit=2000, order=None, context=None, count=False):
304
		args.append(('user_id','=',uid))
305
		if context:
306
			if 'filter_by_active_id' in context:
307
				args.append(('parent_id','=',
308
					self.read(cr, uid, [context['filter_by_active_id']], ['parent_id'])['parent_id'][0]))
309
		return osv.orm.orm.search(self, cr, uid, args, offset, limit, order,
310
								  context=context)
311
		
312
	def confirm(self, cr, uid, ids):
313
		for line in self.browse(cr, uid, ids):
314
			if ((line.debit_amount == 0) and (line.credit_amount == 0)) or (line.amount_base == 0):
315
				raise osv.except_osv('Error', 'Not found line amount!')
316
		
317
		self.write(cr, uid, ids, {'state':'confirmed'})
318
	
319
	def unlink(self, cr, uid, ids, context={}, check=True):
320
		for line in self.browse(cr, uid, ids, context):
321
			if line.state <> 'draft':
322
				raise osv.except_osv('Error', 'You can not delete confirmed entry: "%s"!' % (line.name or ''))
323
		return super(personal_account_entry_line, self).unlink(cr, uid, ids, context)
324
	
325
	
326
	
327
	def return_entry(self, cr, uid, ids):
328
		self.write(cr, uid, ids, {'state':'draft'})
329
	
330
	def _calc_amount_base(self, cr, uid, account_id, currency_rate, amount):
331
		if not account_id:
332
			return amount
333
		
334
		acc_pool = self.pool.get('personal.base.account')
335
		currency_pool = self.pool.get('res.currency')
336
		
337
		account = acc_pool.browse(cr, uid, account_id)
338
		to_currency = account.currency_id.id
339
		if currency_rate != 1:
340
			return currency_pool.compute_with_currency_rate(cr, uid, currency_rate, to_currency, amount)
341
		else:
342
			return amount
343
		#line_rec = self.pool.get('personal.base.account.entry.line')
344
		#acc_rec = self.pool.get('personal.base.account')
345
		#currency_rec = self.pool.get('res.currency')
346
		#for id in ids:
347
		#	curr_line = self.read(cr, uid, [id], ["amount", "account_id", "currency_id"])[0]
348
		#	curr_acc = acc_rec.read(cr, uid, [curr_line["account_id"][0]], ["currency_id"])[0]
349
		#	from_currency = curr_line["currency_id"][0]
350
		#	to_currency = curr_acc["currency_id"][0]
351
		#	if from_currency != to_currency:
352
		#		self.write(cr, uid, id, 
353
		#			{'amount_base': currency_rec.compute(cr, uid, from_currency, to_currency, curr_line["amount"])})
354
		#	else:
355
		#		self.write(cr, uid, id, 
356
		#			{'amount_base': currency_rec.compute(cr, uid, from_currency, to_currency, curr_line["amount"])})
357
	
358
	def onchange_base(self, cr, uid, ids, state):
359
		if state == 'confirmed':
360
			raise osv.except_osv('Error', 'It is not allowed to edit confirmed entries!')
361
		
362
		return {'value':{}}
363
364
	def onchange_debit_amount(self, cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state):
365
		debit_amount -= credit_amount
366
		credit_amount = 0
367
		return self.onchange_amount(cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state)
368
	
369
	def onchange_credit_amount(self, cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state):
370
		credit_amount += debit_amount
371
		debit_amount = 0
372
		return self.onchange_amount(cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state)
373
	
374
	#This is allways the last onchange function
375
	def onchange_amount(self, cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state):
376
		self.onchange_base(cr, uid, ids, state)
377
		
378
		v = {'value':{}}
379
		if debit_amount>0:
380
			_amount = debit_amount
381
		else:
382
			_amount = -credit_amount
383
		v['value']['currency_id'] = currency_id
384
		v['value']['currency_rate'] = currency_rate
385
		v['value']['credit_amount'] = credit_amount
386
		v['value']['debit_amount'] = debit_amount
387
		v['value']['amount_base'] = self._calc_amount_base(cr, uid, account_id, currency_rate, _amount)
388
		return v
389
	
390
	def onchange_currency_id(self, cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state):
391
		self.onchange_base(cr, uid, ids, state)
392
		
393
		currency_pool = self.pool.get('res.currency')
394
		currency_rate = currency_pool.personal_calc_currency_rate(cr, uid, account_id, currency_id)
395
		return self.onchange_amount(cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state)
396
	
397
	def onchange_account_id(self, cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state):
398
		self.onchange_base(cr, uid, ids, state)
399
		
400
		#v = {'value':{}}
401
		if (account_id and (not currency_id or not currency_rate)):
402
			acc_pool = self.pool.get('personal.base.account')
403
			account = acc_pool.browse(cr, uid, account_id)
404
			currency_id = account.currency_id.id
405
		return self.onchange_currency_id(cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state)
406
	
407
	def onchange_amount_base(self, cr, uid, ids, account_id, currency_id, currency_rate, amount_base, state):
408
		self.onchange_base(cr, uid, ids, state)
409
		
410
		currency_pool = self.pool.get('res.currency')
411
		currency_rate = currency_pool.personal_calc_currency_rate(cr, uid, account_id, currency_id)
412
		debit_amount = 0
413
		credit_amount = 0
414
		if currency_rate != 0:
415
			if amount_base > 0:
416
				debit_amount = amount_base / currency_rate
417
			else:
418
				credit_amount = (-amount_base) / currency_rate
419
		else:
420
			amount_base = 0
421
		return self.onchange_amount(cr, uid, ids, account_id, currency_id, currency_rate, debit_amount, credit_amount, state)
422
	
423
	#for unit tests
424
	def delete_unit_test_records(self, cr, uid):
425
		base_delete_unit_test_records(self, cr, uid)
426
	
427
	def test_if_create_is_impossible(self, cr, uid, collumns):
428
		#if not ('parent_id' in collumns):
429
		#	return
430
		#parent = self.pool.get('personal.base.account.entry').read(cr, uid, collumns['parent_id'], ['date', 'currency_id'])
431
		try:
432
			new_id = self.create(cr, uid, collumns)
433
		except:
434
			cr.commit()
435
			return
436
		self.unlink(cr, uid, [new_id])
437
		assert False, "Entry Lines creation checked out not everything."
438
	
439
personal_account_entry_line()