1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
25 from osv import fields, osv
29 from mx.DateTime import RelativeDateTime
30 from tools import config
32 class ir_sequence_type(osv.osv):
33 _inherit = "ir.sequence.type"
35 'name': fields.char('Sequence Name',size=128, required=True),
36 'code': fields.char('Sequence Code',size=128, required=True),
40 class account_voucher(osv.osv):
41 def _get_period(self, cr, uid, context):
42 periods = self.pool.get('account.period').find(cr, uid)
48 def _get_type(self, cr, uid, context={}):
49 type = context.get('type', 'rec_voucher')
52 def _get_reference_type(self, cursor, user, context=None):
53 return [('none', 'Free Reference')]
55 def _get_journal(self, cr, uid, context):
56 type_inv = 'rec_voucher'
58 if type(context) == type(''):
60 elif type(context) == type({}):
61 type_inv = context.get('type', 'rec_voucher')
64 'rec_voucher': 'cash',
65 'bank_rec_voucher': 'cash',
66 'pay_voucher': 'cash',
67 'bank_pay_voucher': 'cash',
68 'cont_voucher': 'cash',
69 'journal_sale_vou': 'sale',
70 'journal_pur_voucher': 'purchase',
71 'journal_voucher':'expanse'
74 journal_obj = self.pool.get('account.journal')
75 ttype = type2journal.get(type_inv, 'cash')
76 res = journal_obj.search(cr, uid, [('type', '=', ttype)], limit=1)
83 def _get_currency(self, cr, uid, context):
84 user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
86 return user.company_id.currency_id.id
88 return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
90 _name = 'account.voucher'
91 _description = 'Accounting Voucher'
94 'name':fields.char('Name', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
95 'type': fields.selection([
96 ('pay_voucher','Cash Payment Voucher'),
97 ('bank_pay_voucher','Bank Payment Voucher'),
98 ('rec_voucher','Cash Receipt Voucher'),
99 ('bank_rec_voucher','Bank Receipt Voucher'),
100 ('cont_voucher','Contra Voucher'),
101 ('journal_sale_vou','Journal Sale Voucher'),
102 ('journal_pur_voucher','Journal Purchase Voucher'),
103 ('journal_voucher','Journal Voucher'),
104 ],'Type', readonly=True, select=True , size=128),
105 'date':fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}),
106 'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
107 'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
108 'payment_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=False, states={'proforma':[('readonly',True)]}),
109 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}),
110 'narration':fields.text('Narration', readonly=True, states={'draft':[('readonly',False)]}, required=True),
111 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
112 'company_id': fields.many2one('res.company', 'Company', required=True),
113 'state':fields.selection(
115 ('proforma','Pro-forma'),
120 'amount':fields.float('Amount', readonly=True),
121 'number':fields.char('Number', size=32, readonly=True),
122 'reference': fields.char('Voucher Reference', size=64),
123 'reference_type': fields.selection(_get_reference_type, 'Reference Type',
125 'move_id':fields.many2one('account.move', 'Account Entry'),
126 'move_ids':fields.many2many('account.move.line', 'voucher_id', 'account_id', 'rel_account_move', 'Real Entry'),
127 'partner_id':fields.many2one('res.partner', 'Partner', readonly=True, states={'draft':[('readonly',False)]})
131 'state': lambda *a: 'draft',
132 'date' : lambda *a: time.strftime('%Y-%m-%d'),
133 'period_id': _get_period,
135 'reference_type': lambda *a: 'none',
136 'journal_id':_get_journal,
137 'company_id': lambda self, cr, uid, context: \
138 self.pool.get('res.users').browse(cr, uid, uid,
139 context=context).company_id.id,
140 'currency_id': _get_currency,
143 def _get_analityc_lines(self, cr, uid, id):
144 inv = self.browse(cr, uid, [id])[0]
145 cur_obj = self.pool.get('res.currency')
147 def onchange_account(self, cr, uid, ids, account_id):
149 return {'value':{'amount':False}}
150 account = self.pool.get('account.account').browse(cr,uid,account_id)
151 balance=account.balance
152 return {'value':{'amount':balance}}
154 def onchange_journal(self, cr, uid, ids, journal_id,type):
156 return {'value':{'account_id':False}}
157 journal = self.pool.get('account.journal')
158 if journal_id and (type in ('rec_voucher','bank_rec_voucher','journal_pur_voucher','journal_voucher')):
159 account_id = journal.browse(cr, uid, journal_id).default_debit_account_id
160 return {'value':{'account_id':account_id.id}}
161 elif journal_id and (type in ('pay_voucher','bank_pay_voucher','journal_sale_vou')) :
162 account_id = journal.browse(cr, uid, journal_id).default_credit_account_id
163 return {'value':{'account_id':account_id.id}}
165 account_id = journal.browse(cr, uid, journal_id).default_credit_account_id
166 return {'value':{'account_id':account_id.id}}
168 def open_voucher(self, cr, uid, ids, context={}):
169 obj=self.pool.get('account.voucher').browse(cr,uid,ids)
171 for i in obj[0].payment_ids:
173 self.write(cr,uid,ids,{'amount':total})
174 self.write(cr, uid, ids, {'state':'proforma'})
177 def proforma_voucher(self, cr, uid, ids, context={}):
178 self.action_number(cr, uid, ids)
179 self.action_move_line_create(cr, uid, ids)
180 self.write(cr, uid, ids, {'state':'posted'})
183 def cancel_voucher(self,cr,uid,ids,context={}):
184 self.action_cancel(cr, uid, ids)
185 self.write(cr, uid, ids, {'state':'cancel'})
188 def action_cancel_draft(self, cr, uid, ids, *args):
189 self.write(cr, uid, ids, {'state':'draft'})
192 def unlink(self, cr, uid, ids, context={}):
193 vouchers = self.read(cr, uid, ids, ['state'])
196 if t['state'] in ('draft', 'cancel'):
197 unlink_ids.append(t['id'])
199 raise osv.except_osv('Invalid action !', 'Cannot delete invoice(s) which are already opened or paid !')
200 osv.osv.unlink(self, cr, uid, unlink_ids)
203 def _get_analytic_lines(self, cr, uid, id):
204 inv = self.browse(cr, uid, [id])[0]
205 cur_obj = self.pool.get('res.currency')
207 company_currency = inv.company_id.currency_id.id
208 if inv.type in ('rec_voucher'):
213 iml = self.pool.get('account.voucher.line').move_line_get(cr, uid, inv.id)
216 if il['account_analytic_id']:
217 if inv.type in ('pay_voucher', 'rec_voucher','cont_voucher','bank_pay_voucher','bank_rec_voucher','journal_sale_vou','journal_pur_voucher'):
220 ref = self._convert_ref(cr, uid, inv.number)
222 il['analytic_lines'] = [(0, 0, {
225 'account_id': il['account_analytic_id'],
226 'amount': inv['amount'] * sign,
227 'general_account_id': il['account_id'] or False,
228 'journal_id': self.pool.get('account.voucher').browse(cr, uid, id).journal_id.analytic_journal_id.id or False,
233 def action_move_line_create(self, cr, uid, ids, *args):
234 for inv in self.browse(cr, uid, ids):
237 company_currency = inv.company_id.currency_id.id
239 line_ids = self.read(cr, uid, [inv.id], ['payment_ids'])[0]['payment_ids']
240 ils = self.pool.get('account.voucher.line').read(cr, uid, line_ids)
242 iml = self._get_analytic_lines(cr, uid, inv.id)
244 diff_currency_p = inv.currency_id.id <> company_currency
247 if inv.type in ('pay_voucher', 'journal_voucher', 'rec_voucher','cont_voucher','bank_pay_voucher','bank_rec_voucher','journal_sale_vou','journal_pur_voucher'):
250 ref = self._convert_ref(cr, uid, inv.number)
256 partner_id=i['partner_id']
257 acc_id = i['account_id']
258 if inv.currency_id.id != company_currency:
259 i['currency_id'] = inv.currency_id.id
260 i['amount_currency'] = i['amount']
262 i['amount_currency'] = False
263 i['currency_id'] = False
264 if inv.type in ('rec_voucher','bank_rec_voucher','journal_pur_voucher','journal_voucher'):
266 total_currency += i['amount_currency'] or i['amount']
267 i['amount'] = - i['amount']
270 total_currency -= i['amount_currency'] or i['amount']
272 name = inv['name'] or '/'
278 'amount': total or False,
279 'account_id': acc_id,
280 'amount_currency': diff_currency_p \
281 and total_currency or False,
282 'currency_id': diff_currency_p \
283 and inv.currency_id.id or False,
285 'partner_id':partner_id or False,
291 line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x,date, context={})) ,iml)
292 an_journal_id=inv.journal_id.analytic_journal_id.id
293 journal_id = inv.journal_id.id
295 journal = self.pool.get('account.journal').browse(cr, uid, journal_id)
296 if journal.sequence_id:
297 name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id)
301 'journal_id': journal_id,
302 'voucher_type':inv.type,
303 'narration' : inv.narration
306 move['period_id'] = inv.period_id.id
308 i[2]['period_id'] = inv.period_id.id
309 move_id = self.pool.get('account.move').create(cr, uid, move)
313 #create the first line our self
318 'account_id': inv.account_id.id or False,
320 'journal_id':journal_id ,
321 'period_id':inv.period_id.id,
326 if inv.type in ('rec_voucher', 'bank_rec_voucher', 'journal_pur_voucher', 'journal_voucher'):
327 move_line['debit'] = inv.amount
329 move_line['credit'] = inv.amount * (-1)
330 self.pool.get('account.move.line').create(cr, uid, move_line)
332 for line in inv.payment_ids:
338 'account_id':line.account_id.id or False,
340 'journal_id':journal_id ,
341 'period_id':inv.period_id.id,
342 'partner_id':line.partner_id.id or False,
347 if line.type == 'dr':
348 move_line['debit'] = line.amount or False
350 elif line.type == 'cr':
351 move_line['credit'] = line.amount or False
352 amount=line.amount * (-1)
354 ml_id=self.pool.get('account.move.line').create(cr, uid, move_line)
357 line.name=inv.narration
361 if line.account_analytic_id:
366 'account_id':line.account_analytic_id.id or False,
368 'journal_id':an_journal_id ,
369 'general_account_id':line.account_id.id,
372 self.pool.get('account.analytic.line').create(cr,uid,an_line)
374 self.write(cr, uid, [inv.id], {'move_id': move_id})
375 obj=self.pool.get('account.move').browse(cr, uid, move_id)
377 for line in obj.line_id :
378 cr.execute('insert into voucher_id (account_id,rel_account_move) values (%d, %d)',(int(ids[0]),int(line.id)))
383 def line_get_convert(self, cr, uid, x, date, context={}):
387 'date_maturity': x.get('date_maturity', False),
388 'partner_id':x.get('partner_id',False),
389 'name':x['name'][:64],
390 'debit':x['amount']>0 and x['amount'],
391 'credit':x['amount']<0 and -x['amount'],
392 'account_id':x['account_id'],
393 'analytic_lines':x.get('analytic_lines', []),
394 'amount_currency':x.get('amount_currency', False),
395 'currency_id':x.get('currency_id', False),
396 'tax_code_id': x.get('tax_code_id', False),
397 'tax_amount': x.get('tax_amount', False),
398 'ref':x.get('ref',False)
400 def _convert_ref(self, cr, uid, ref):
401 return (ref or '').replace('/','')
404 def action_number(self, cr, uid, ids, *args):
405 cr.execute('SELECT id, type, number, move_id, reference ' \
406 'FROM account_voucher ' \
407 'WHERE id IN ('+','.join(map(str,ids))+')')
408 for (id, invtype, number, move_id, reference) in cr.fetchall():
410 number = self.pool.get('ir.sequence').get(cr, uid, invtype)
412 if type in ('pay_voucher', 'journal_voucher', 'rec_voucher','cont_voucher','bank_pay_voucher','bank_rec_voucher','journal_sale_vou','journal_pur_voucher'):
415 ref = self._convert_ref(cr, uid, number)
417 cr.execute('UPDATE account_voucher SET number=%s ' \
418 'WHERE id=%d', (number, id))
419 cr.execute('UPDATE account_move_line SET ref=%s ' \
420 'WHERE move_id=%d AND (ref is null OR ref = \'\')',
422 cr.execute('UPDATE account_analytic_line SET ref=%s ' \
423 'FROM account_move_line ' \
424 'WHERE account_move_line.move_id = %d ' \
425 'AND account_analytic_line.move_id = account_move_line.id',
431 def name_get(self, cr, uid, ids, context={}):
435 'pay_voucher': 'CPV: ',
436 'rec_voucher': 'CRV: ',
437 'cont_voucher': 'CV: ',
438 'bank_pay_voucher': 'BPV: ',
439 'bank_rec_voucher': 'BRV: ',
440 'journal_sale_vou': 'JSV: ',
441 'journal_pur_voucher': 'JPV: ',
442 'journal_voucher':'JV'
444 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')]
446 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
453 ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
455 ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
456 return self.name_get(cr, user, ids, context)
458 def copy(self, cr, uid, id, default=None, context=None):
461 default = default.copy()
462 default.update({'state':'draft', 'number':False, 'move_id':False, 'move_ids':False, 'payment_ids':False})
463 if 'date' not in default:
464 default['date'] = time.strftime('%Y-%m-%d')
465 return super(account_voucher, self).copy(cr, uid, id, default, context)
467 def action_cancel(self, cr, uid, ids, *args):
468 account_move_obj = self.pool.get('account.move')
469 voucher = self.read(cr, uid, ids, ['move_id'])
472 account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
473 # delete the move this invoice was pointing to
474 # Note that the corresponding move_lines and move_reconciles
475 # will be automatically deleted too
476 account_move_obj.unlink(cr, uid, [i['move_id'][0]])
477 self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
482 class VoucherLine(osv.osv):
483 _name = 'account.voucher.line'
484 _description = 'Voucher Line'
486 'voucher_id':fields.many2one('account.voucher', 'Voucher'),
487 'name':fields.char('Description', size=256, required=True),
488 'account_id':fields.many2one('account.account','Account', required=True),
489 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True),
490 'amount':fields.float('Amount'),
491 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Type'),
492 'ref':fields.char('Ref.', size=32),
493 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account')
496 'type': lambda *a: 'cr'
499 def move_line_get(self, cr, uid, voucher_id, context={}):
502 cur_obj = self.pool.get('res.currency')
503 inv = self.pool.get('account.voucher').browse(cr, uid, voucher_id)
504 company_currency = inv.company_id.currency_id.id
505 cur = inv.currency_id
507 for line in inv.payment_ids:
508 res.append(self.move_line_get_item(cr, uid, line, context))
511 def onchange_partner(self, cr, uid, ids, partner_id, ttype ,type1):
513 return {'value' : {'account_id' : False, 'type' : False ,'amount':False}}
514 obj = self.pool.get('res.partner')
516 if type1 in ('rec_voucher','bank_rec_voucher', 'journal_voucher'):
517 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
518 balance = obj.browse(cr,uid,partner_id).credit
520 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
521 account_id = obj.browse(cr, uid, partner_id).property_account_payable
522 balance = obj.browse(cr,uid,partner_id).debit
524 elif type1 in ('journal_sale_vou') :
525 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
526 balance = obj.browse(cr,uid,partner_id).credit
528 elif type1 in ('journal_pur_voucher') :
529 account_id = obj.browse(cr, uid, partner_id).property_account_payable
530 balance = obj.browse(cr,uid,partner_id).debit
534 'value' : {'account_id' : account_id.id, 'type' : ttype, 'amount':balance}
537 def onchange_amount(self, cr, uid, ids,partner_id,amount, type,type1):
539 return {'value' : {}}
542 obj = self.pool.get('res.partner')
543 if type1 in ('rec_voucher', 'bank_rec_voucher', 'journal_voucher'):
545 account_id = obj.browse(cr, uid, partner_id).property_account_payable
548 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
551 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
553 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
556 account_id = obj.browse(cr, uid, partner_id).property_account_payable
559 elif type1 in ('journal_sale_vou') :
561 account_id = obj.browse(cr, uid, partner_id).property_account_payable
564 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
567 elif type1 in ('journal_pur_voucher') :
569 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
572 account_id = obj.browse(cr, uid, partner_id).property_account_payable
575 if type1 in ('rec_voucher', 'bank_rec_voucher', 'journal_voucher'):
583 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
591 elif type1 in ('journal_sale_vou') :
599 elif type1 in ('journal_pur_voucher') :
608 'value' : { 'type' : type , 'amount':amount}
611 def onchange_type(self, cr, uid, ids,partner_id,amount,type,type1):
614 obj = self.pool.get('res.partner')
616 if type1 in ('rec_voucher','bank_rec_voucher', 'journal_voucher'):
618 account_id = obj.browse(cr, uid, partner_id).property_account_payable
621 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
624 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
626 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
629 account_id = obj.browse(cr, uid, partner_id).property_account_payable
632 elif type1 in ('journal_sale_vou') :
634 account_id = obj.browse(cr, uid, partner_id).property_account_payable
637 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
640 elif type1 in ('journal_pur_voucher') :
642 account_id = obj.browse(cr, uid, partner_id).property_account_receivable
645 account_id = obj.browse(cr, uid, partner_id).property_account_payable
648 if type1 in ('rec_voucher','bank_rec_voucher', 'journal_voucher'):
656 elif type1 in ('pay_voucher','bank_pay_voucher','cont_voucher') :
664 elif type1 in ('journal_sale_vou') :
672 elif type1 in ('journal_pur_voucher') :
681 'value' : {'type' : type , 'amount':total}
684 def move_line_get_item(self, cr, uid, line, context={}):
687 'name': line.name[:64],
688 'amount':line.amount,
689 'account_id':line.account_id.id,
690 'partner_id':line.partner_id.id or False ,
691 'account_analytic_id':line.account_analytic_id.id or False,