3 from osv import fields, osv
7 from mx.DateTime import RelativeDateTime
9 from tools import config
11 class account_voucher(osv.osv):
12 def _get_period(self, cr, uid, context):
13 periods = self.pool.get('account.period').find(cr, uid)
19 def _get_type(self, cr, uid, context={}):
20 type = context.get('type', 'rec_voucher')
23 def _get_reference_type(self, cursor, user, context=None):
24 return [('none', 'Free Reference')]
26 def _get_journal(self, cr, uid, context):
27 type_inv = context #.get('type', 'rec_voucher')
28 type2journal = {'rec_voucher': 'cash', 'bank_rec_voucher': 'cash','pay_voucher': 'cash','bank_pay_voucher': 'cash', 'cont_voucher': 'cash','journal_sale_voucher': 'sale','journal_pur_voucher': 'purchase' }
29 journal_obj = self.pool.get('account.journal')
30 res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'cash'))], limit=1)
36 def _get_currency(self, cr, uid, context):
37 user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
39 return user.company_id.currency_id.id
41 return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
43 _name = 'account.voucher'
44 _description = 'Accounting Voucher'
47 'name':fields.char('Name', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
48 'type': fields.selection([
49 ('pay_voucher','Cash Payment Voucher'),
50 ('bank_pay_voucher','Bank Payment Voucher'),
51 ('rec_voucher','Cash Receipt Voucher'),
52 ('bank_rec_voucher','Bank Receipt Voucher'),
53 ('cont_voucher','Contra Voucher'),
54 ('journal_sale_vou','Journal Sale Voucher'),
55 ('journal_pur_voucher','Journal Purchase Voucher'),
56 ],'Type', readonly=True, select=True),
57 'date':fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}),
58 'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
59 'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
60 'payment_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=False, states={'proforma':[('readonly',True)]}),
61 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
62 'narration':fields.text('Narration', readonly=True, states={'draft':[('readonly',False)]}),
63 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
64 'company_id': fields.many2one('res.company', 'Company', required=True),
65 'state':fields.selection(
67 ('proforma','Pro-forma'),
72 'amount':fields.float('Amount'),
73 'number':fields.char('Number', size=32, readonly=True),
74 'reference': fields.char('Voucher Reference', size=64),
75 'reference_type': fields.selection(_get_reference_type, 'Reference Type',
77 'move_id':fields.many2one('account.move', 'Account Entry'),
78 'move_ids':fields.many2many('account.move.line', 'voucher_id', 'account_id', 'rel_account_move', 'Real Entry'),
81 # def get_bank(self, cr, uid, context={}):
82 # type = context.get('type', 'bank_payment')
83 # journal = self.pool.get('account.journal')
84 # if type == 'bank_payment':
85 # id = journal.search(cr, uid, [('name','ilike','Bank')])
87 # elif type == 'cash_payment':
88 # id = journal.search(cr, uid, [('name','ilike','Cash')])
94 #'journal_id':get_bank,
95 'state': lambda *a: 'draft',
96 'date' : lambda *a: time.strftime('%Y-%m-%d'),
97 'period_id': _get_period,
99 'reference_type': lambda *a: 'none',
100 'journal_id':_get_journal,
101 'company_id': lambda self, cr, uid, context: \
102 self.pool.get('res.users').browse(cr, uid, uid,
103 context=context).company_id.id,
104 'currency_id': _get_currency,
109 def _get_analityc_lines(self, cr, uid, id):
110 inv = self.browse(cr, uid, [id])[0]
111 cur_obj = self.pool.get('res.currency')
113 def onchange_account(self, cr, uid, ids, account_id):
115 return {'value':{'amount':False}}
116 account = self.pool.get('account.account').browse(cr,uid,account_id)
117 balance=account.balance
118 return {'value':{'amount':balance}}
121 def onchange_journal(self, cr, uid, ids, journal_id,type):
123 return {'value':{'account_id':False}}
124 journal = self.pool.get('account.journal')
125 if journal_id and (type in ('rec_voucher','bank_rec_voucher','journal_pur_voucher')):
126 account_id = journal.browse(cr, uid, journal_id).default_debit_account_id
127 return {'value':{'account_id':account_id.id}}
128 elif journal_id and (type in ('pay_voucher','bank_pay_voucher','journal_sale_vou')) :
129 account_id = journal.browse(cr, uid, journal_id).default_credit_account_id
130 return {'value':{'account_id':account_id.id}}
132 account_id = journal.browse(cr, uid, journal_id).default_credit_account_id
133 return {'value':{'account_id':account_id.id}}
138 def open_voucher(self, cr, uid, ids, context={}):
139 obj=self.pool.get('account.voucher').browse(cr,uid,ids)
141 for i in obj[0].payment_ids:
143 self.write(cr,uid,ids,{'amount':total})
144 self.write(cr, uid, ids, {'state':'proforma'})
147 def proforma_voucher(self, cr, uid, ids, context={}):
148 self.action_move_line_create(cr, uid, ids)
149 self.action_number(cr, uid, ids)
150 self.write(cr, uid, ids, {'state':'posted'})
153 def cancel_voucher(self,cr,uid,ids,context={}):
154 self.action_cancel(cr, uid, ids)
155 self.write(cr, uid, ids, {'state':'cancel'})
158 def action_cancel_draft(self, cr, uid, ids, *args):
159 self.write(cr, uid, ids, {'state':'draft'})
163 def unlink(self, cr, uid, ids):
164 vouchers = self.read(cr, uid, ids, ['state'])
167 if t['state'] in ('draft', 'cancel'):
168 unlink_ids.append(t['id'])
170 raise osv.except_osv('Invalid action !', 'Cannot delete invoice(s) which are already opened or paid !')
171 osv.osv.unlink(self, cr, uid, unlink_ids)
175 def _get_analityc_lines(self, cr, uid, id):
176 inv = self.browse(cr, uid, [id])[0]
177 cur_obj = self.pool.get('res.currency')
179 company_currency = inv.company_id.currency_id.id
180 if inv.type in ('rec_voucher'):
185 iml = self.pool.get('account.voucher.line').move_line_get(cr, uid, inv.id)
188 if il['account_analytic_id']:
189 if inv.type in ('pay_voucher', 'rec_voucher','cont_voucher','bank_pay_voucher','bank_rec_voucher','journal_sale_vou','journal_pur_voucher'):
192 ref = self._convert_ref(cr, uid, inv.number)
194 il['analytic_lines'] = [(0,0, {
197 'account_id': il['account_analytic_id'],
198 'amount': inv['amount'] * sign,
199 #'partner_id': il['partner_id'] or False,
200 'general_account_id': il['account_id'] or False,
201 'journal_id': self._get_journal(cr, uid, inv.type),
207 def action_move_line_create(self, cr, uid, ids, *args):
209 for inv in self.browse(cr, uid, ids):
214 company_currency = inv.company_id.currency_id.id
216 # create the analytical lines
217 line_ids = self.read(cr, uid, [inv.id], ['payment_ids'])[0]['payment_ids']
218 ils = self.pool.get('account.voucher.line').read(cr, uid, line_ids)
219 # one move line per invoice line
220 iml = self._get_analityc_lines(cr, uid, inv.id)
221 # check if taxes are all computed
222 diff_currency_p = inv.currency_id.id <> company_currency
223 # create one move line for the total and possibly adjust the other lines amount
225 if inv.type in ('pay_voucher', 'rec_voucher','cont_voucher','bank_pay_voucher','bank_rec_voucher','journal_sale_vou','journal_pur_voucher'):
228 ref = self._convert_ref(cr, uid, inv.number)
232 if inv.currency_id.id != company_currency:
233 i['currency_id'] = inv.currency_id.id
234 i['amount_currency'] = i['amount']
237 i['amount_currency'] = False
238 i['currency_id'] = False
240 if inv.type in ('rec_voucher','bank_rec_voucher','journal_pur_voucher'):
242 total_currency += i['amount_currency'] or i['amount']
243 i['amount'] = - i['amount']
246 total_currency -= i['amount_currency'] or i['amount']
247 acc_id = inv.account_id.id
249 name = inv['name'] or '/'
256 'account_id': acc_id,
257 'amount_currency': diff_currency_p \
258 and total_currency or False,
259 'currency_id': diff_currency_p \
260 and inv.currency_id.id or False,
267 line = map(lambda x:(0, 0, self.line_get_convert(cr, uid, x,date, context={})), iml)
269 journal_id = inv.journal_id.id #self._get_journal(cr, uid, {'type': inv['type']})
270 journal = self.pool.get('account.journal').browse(cr, uid, journal_id)
271 if journal.sequence_id:
272 name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
274 move = {'name': name, 'line_id': line, 'journal_id': journal_id}
277 move['period_id'] = inv.period_id.id
279 i[2]['period_id'] = inv.period_id.id
280 move_id = self.pool.get('account.move').create(cr, uid, move)
281 # make the invoice point to that move
282 self.write(cr, uid, [inv.id], {'move_id': move_id})
283 obj=self.pool.get('account.move').browse(cr,uid,move_id)
284 for line in obj.line_id :
285 cr.execute('insert into voucher_id (account_id,rel_account_move) values (%d, %d)',(int(ids[0]),int(line.id)))
289 def line_get_convert(self, cr, uid, x, date, context={}):
292 'date_maturity': x.get('date_maturity', False),
293 'partner_id':x.get('partner_id',False),
294 'name':x['name'][:64],
295 'debit':x['amount']>0 and x['amount'],
296 'credit':x['amount']<0 and -x['amount'],
297 'account_id':x['account_id'],
298 'analytic_lines':x.get('analytic_lines', []),
299 'amount_currency':x.get('amount_currency', False),
300 'currency_id':x.get('currency_id', False),
301 'tax_code_id': x.get('tax_code_id', False),
302 'tax_amount': x.get('tax_amount', False),
303 'ref':x.get('ref',False)
305 def _convert_ref(self, cr, uid, ref):
306 return (ref or '').replace('/','')
309 def action_number(self, cr, uid, ids, *args):
310 cr.execute('SELECT id, type, number, move_id, reference ' \
311 'FROM account_voucher ' \
312 'WHERE id IN ('+','.join(map(str,ids))+')')
313 for (id, invtype, number, move_id, reference) in cr.fetchall():
315 number = self.pool.get('ir.sequence').get(cr, uid,
316 'account.voucher.' + invtype)
318 if type in ('pay_voucher', 'rec_voucher','cont_voucher','bank_pay_voucher','bank_rec_voucher','journal_sale_vou','journal_pur_voucher'):
321 ref = self._convert_ref(cr, uid, number)
322 cr.execute('UPDATE account_voucher SET number=%s ' \
323 'WHERE id=%d', (number, id))
324 cr.execute('UPDATE account_move_line SET ref=%s ' \
325 'WHERE move_id=%d AND (ref is null OR ref = \'\')',
327 cr.execute('UPDATE account_analytic_line SET ref=%s ' \
328 'FROM account_move_line ' \
329 'WHERE account_move_line.move_id = %d ' \
330 'AND account_analytic_line.move_id = account_move_line.id',
336 def name_get(self, cr, uid, ids, context={}):
340 'pay_voucher': 'CPV: ',
341 'rec_voucher': 'CRV: ',
342 'cont_voucher': 'CV: ',
343 'bank_pay_voucher': 'BPV: ',
344 'bank_rec_voucher': 'BRV: ',
345 'journal_sale_vou': 'JSV: ',
346 'journal_pur_voucher': 'JPV: ',
349 return [(r['id'], types[r['type']]+(r['number'] or '')+' '+(r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
351 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
358 ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
360 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
361 return self.name_get(cr, user, ids, context)
363 def copy(self, cr, uid, id, default=None, context=None):
366 default = default.copy()
367 default.update({'state':'draft', 'number':False, 'move_id':False, 'move_ids':False})
368 if 'date' not in default:
369 default['date'] = time.strftime('%Y-%m-%d')
370 return super(account_voucher, self).copy(cr, uid, id, default, context)
372 def action_cancel(self, cr, uid, ids, *args):
373 account_move_obj = self.pool.get('account.move')
374 voucher = self.read(cr, uid, ids, ['move_id'])
377 account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
378 # delete the move this invoice was pointing to
379 # Note that the corresponding move_lines and move_reconciles
380 # will be automatically deleted too
381 account_move_obj.unlink(cr, uid, [i['move_id'][0]])
382 self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
383 # self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
388 class VoucherLine(osv.osv):
389 _name = 'account.voucher.line'
390 _description = 'Voucher Line'
392 'voucher_id':fields.many2one('account.voucher', 'Voucher'),
393 'name':fields.char('Description', size=256, required=True),
394 'account_id':fields.many2one('account.account','Account', required=True),
395 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, required=True, ),
396 'amount':fields.float('Amount'),
397 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Type'),
398 'ref':fields.char('Ref.', size=32),
399 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account')
402 'type': lambda *a: 'cr'
404 def move_line_get(self, cr, uid, voucher_id, context={}):
407 cur_obj = self.pool.get('res.currency')
408 inv = self.pool.get('account.voucher').browse(cr, uid, voucher_id)
409 company_currency = inv.company_id.currency_id.id
410 cur = inv.currency_id
412 for line in inv.payment_ids:
413 res.append(self.move_line_get_item(cr, uid, line, context))
416 def onchange_partner(self, cr, uid, ids, partner_id, type,type1):
418 return {'value' : {'account_id' : False, 'type' : False ,'amount':False}}
419 obj = self.pool.get('res.partner')
422 if type1 in ('rec_voucher','bank_rec_voucher'):
423 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
424 balance=obj.browse(cr,uid,partner_id).credit
426 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
427 account_id = obj.browse(cr, uid, partner_id).property_account_payable
428 balance=obj.browse(cr,uid,partner_id).debit
430 elif type1 in ('journal_sale_vou') :
431 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
432 balance=obj.browse(cr,uid,partner_id).credit
435 elif type1 in ('journal_pur_vou') :
436 account_id = obj.browse(cr, uid, partner_id).property_account_payable
437 balance=obj.browse(cr,uid,partner_id).debit
441 'value' : {'account_id' : account_id.id, 'type' : type, 'amount':balance}
444 def onchange_amount(self, cr, uid, ids,partner_id,amount, type,type1):
446 return {'value' : {'type' : False}}
447 obj = self.pool.get('res.partner')
448 if type1 in ('rec_voucher','bank_rec_voucher'):
450 account_id = obj.browse(cr, uid, partner_id).property_account_payable
453 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
456 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
458 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
461 account_id = obj.browse(cr, uid, partner_id).property_account_payable
464 elif type1 in ('journal_sale_vou') :
466 account_id = obj.browse(cr, uid, partner_id).property_account_payable
469 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
472 elif type1 in ('journal_pur_vou') :
474 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
477 account_id = obj.browse(cr, uid, partner_id).property_account_payable
481 'value' : { 'type' : type , 'amount':amount}
485 def onchange_type(self, cr, uid, ids,partner_id,amount,type,type1):
487 return {'value' : {'type' : False}}
488 obj = self.pool.get('res.partner')
490 if type1 in ('rec_voucher','bank_rec_voucher'):
492 account_id = obj.browse(cr, uid, partner_id).property_account_payable
495 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
498 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
500 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
503 account_id = obj.browse(cr, uid, partner_id).property_account_payable
506 elif type1 in ('journal_sale_vou') :
508 account_id = obj.browse(cr, uid, partner_id).property_account_payable
511 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
514 elif type1 in ('journal_pur_vou') :
516 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
519 account_id = obj.browse(cr, uid, partner_id).property_account_payable
523 'value' : {'type' : type , 'amount':total}
527 def move_line_get_item(self, cr, uid, line, context={}):
530 'name': line.name[:64],
531 'amount':line.amount,
532 'account_id':line.account_id.id,
533 'partner_id':line.partner_id.id or False ,
534 'account_analytic_id':line.account_analytic_id.id or False,