1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
23 from osv import fields
25 from tools.translate import _
29 'bank':'bank_rec_voucher',
31 'sale':'journal_sale_vou',
32 'purchase':'journal_pur_voucher',
33 'general':'journal_voucher'
37 'rec_voucher': 'cash',
38 'bank_rec_voucher': 'bank',
39 'pay_voucher': 'cash',
40 'bank_pay_voucher': 'bank',
41 'cont_voucher': 'cash',
42 'journal_sale_vou': 'sale',
43 'journal_pur_voucher': 'purchase',
44 'journal_voucher':'general'
47 class ir_sequence_type(osv.osv):
48 _inherit = "ir.sequence.type"
50 'name': fields.char('Sequence Name',size=128, required=True),
51 'code': fields.char('Sequence Code',size=128, required=True),
55 class account_journal(osv.osv):
56 _inherit = "account.journal"
58 'max_amount': fields.float('Verify Transaction', digits=(16, 2), help="Validate voucher entry twice before posting it, if transection amount more then entered here"),
62 class account_voucher(osv.osv):
64 def _get_period(self, cr, uid, context={}):
65 if context.get('period_id', False):
66 return context.get('period_id')
68 periods = self.pool.get('account.period').find(cr, uid)
74 def _get_type(self, cr, uid, context={}):
75 vtype = context.get('type', 'bank')
76 voucher_type = journal2type.get(vtype)
79 def _get_reference_type(self, cursor, user, context=None):
80 return [('none', 'Free Reference')]
82 def _get_journal(self, cr, uid, context={}):
83 journal_pool = self.pool.get('account.journal')
85 if context.get('journal_id', False):
86 return context.get('journal_id')
88 type_inv = context.get('type', 'rec_voucher')
90 ttype = type2journal.get(type_inv, type_inv)
91 res = journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
98 def _get_currency(self, cr, uid, context):
99 user = self.pool.get('res.users').browse(cr, uid, uid)
101 return user.company_id.currency_id.id
103 return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
105 _name = 'account.voucher'
106 _description = 'Accounting Voucher'
109 'name':fields.char('Name', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
110 'type': fields.selection([
111 ('pay_voucher','Cash Payment'),
112 ('bank_pay_voucher','Bank Payment'),
113 ('rec_voucher','Cash Receipt'),
114 ('bank_rec_voucher','Bank Receipt'),
115 ('journal_sale_vou','Journal Sale'),
116 ('journal_pur_voucher','Journal Purchase'),
117 ('journal_voucher','Journal Voucher'),
118 ],'Entry Type', select=True , size=128, readonly=True, states={'draft':[('readonly',False)]}),
119 'date':fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}, help="Effective date for accounting entries"),
120 'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
121 'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, domain=[('type','<>','view')]),
122 'payment_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=True, states={'draft':[('readonly',False)]}),
123 'period_id': fields.many2one('account.period', 'Period', required=True, readonly=True, states={'posted':[('readonly',False)]}),
124 'narration':fields.text('Narration', readonly=True, states={'draft':[('readonly',False)]}),
125 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
126 'company_id': fields.many2one('res.company', 'Company', required=True),
127 'state':fields.selection(
129 ('proforma','Pro-forma'),
131 ('recheck','Waiting for Re-checking'),
133 ('audit','Audit Complete')
134 ], 'State', readonly=True, size=32,
135 help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed Voucher. \
136 \n* The \'Pro-forma\' when voucher is in Pro-forma state,voucher does not have an voucher number. \
137 \n* The \'Posted\' state is used when user create voucher,a voucher number is generated and voucher entries are created in account \
138 \n* The \'Cancelled\' state is used when user cancel voucher.'),
139 'amount':fields.float('Amount', readonly=True),
140 'reference': fields.char('Reference', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Payment or Receipt transaction number, i.e. Bank cheque number or payorder number or Wire transfer number or Acknowledge number."),
141 'reference_type': fields.selection(_get_reference_type, 'Reference Type', required=True),
142 'number': fields.related('move_id', 'name', type="char", readonly=True, string='Number'),
143 'move_id':fields.many2one('account.move', 'Account Entry'),
144 'move_ids':fields.many2many('account.move.line', 'voucher_id', 'account_id', 'rel_account_move', 'Real Entry'),
145 'partner_id':fields.many2one('res.partner', 'Partner', readonly=True, states={'draft':[('readonly',False)]})
149 'period_id': _get_period,
151 'journal_id':_get_journal,
152 'currency_id': _get_currency,
153 'state': lambda *a: 'draft',
154 'date' : lambda *a: time.strftime('%Y-%m-%d'),
155 'reference_type': lambda *a: "none",
156 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.voucher',context=c),
159 def onchange_account(self, cr, uid, ids, account_id):
162 'value':{'amount':False}
164 account = self.pool.get('account.account').browse(cr, uid, account_id)
165 balance=account.balance
167 'value':{'amount':balance}
170 def onchange_journal(self, cr, uid, ids, journal_id, type):
173 'value':{'account_id':False}
175 journal = self.pool.get('account.journal')
177 if journal_id and (type in ('rec_voucher','bank_rec_voucher','journal_pur_voucher','journal_voucher')):
178 account_id = journal.browse(cr, uid, journal_id).default_debit_account_id
180 'value':{'account_id':account_id.id}
182 elif journal_id and (type in ('pay_voucher','bank_pay_voucher','journal_sale_vou')) :
183 account_id = journal.browse(cr, uid, journal_id).default_credit_account_id
185 'value':{'account_id':account_id.id}
188 account_id = journal.browse(cr, uid, journal_id).default_credit_account_id
190 'value':{'account_id':account_id.id}
193 def open_voucher(self, cr, uid, ids, context={}):
194 voucher = self.pool.get('account.voucher').browse(cr, uid, ids)[0]
196 for line in voucher.payment_ids:
204 self.write(cr, uid, ids, res)
206 raise osv.except_osv(_('Invalid action !'), _('You can not post to Pro-Forma a voucher with Total amount = 0 !'))
209 def proforma_voucher(self, cr, uid, ids, context={}):
210 self.action_move_line_create(cr, uid, ids)
211 self.write(cr, uid, ids, {'state':'posted'})
214 def action_cancel_draft(self, cr, uid, ids, context={}):
215 self.write(cr, uid, ids, {'state':'draft'})
218 def audit_pass(self, cr, uid, ids, context={}):
219 move_pool = self.pool.get('account.move')
222 for voucher in self.browse(cr, uid, ids):
223 if voucher.move_id and voucher.move_id.state == 'draft':
224 result = result and move_pool.button_validate(cr, uid, [voucher.move_id.id])
225 audit_pass += [voucher.id]
227 self.write(cr, uid, audit_pass, {'state':'audit'})
230 def cancel_voucher(self, cr, uid, ids, context={}):
231 move_pool = self.pool.get('account.move')
233 for voucher in self.browse(cr, uid, ids):
235 move_pool.button_cancel(cr, uid, [voucher.move_id.id])
236 move_pool.unlink(cr, uid, [voucher.move_id.id])
241 'move_ids':[(6, 0,[])]
243 self.write(cr, uid, ids, res)
246 def unlink(self, cr, uid, ids, context=None):
247 vouchers = self.read(cr, uid, ids, ['state'])
250 if t['state'] in ('draft', 'cancel'):
251 unlink_ids.append(t['id'])
253 raise osv.except_osv('Invalid action !', 'Cannot delete Voucher(s) which are already opened or paid !')
254 return super(account_voucher, self).unlink(cr, uid, unlink_ids, context=context)
256 def action_move_line_create(self, cr, uid, ids, *args):
258 journal_pool = self.pool.get('account.journal')
259 sequence_pool = self.pool.get('ir.sequence')
260 move_pool = self.pool.get('account.move')
261 move_line_pool = self.pool.get('account.move.line')
262 analytic_pool = self.pool.get('account.analytic.line')
263 currency_pool = self.pool.get('res.currency')
264 invoice_pool = self.pool.get('account.invoice')
266 for inv in self.browse(cr, uid, ids):
271 journal = journal_pool.browse(cr, uid, inv.journal_id.id)
272 if inv.type in ('journal_pur_voucher', 'journal_sale_vou'):
273 if journal.invoice_sequence_id:
274 name = sequence_pool.get_id(cr, uid, journal.invoice_sequence_id.id)
276 raise osv.except_osv(_('Error !'), _('Please define invoice sequence on %s journal !' % (journal.name)))
278 if journal.sequence_id:
279 name = sequence_pool.get_id(cr, uid, journal.sequence_id.id)
281 raise osv.except_osv(_('Error !'), _('Please define sequence on journal !'))
284 if inv.type in ('journal_pur_voucher', 'bank_rec_voucher', 'rec_voucher'):
287 ref = invoice_pool._convert_ref(cr, uid, name)
289 company_currency = inv.company_id.currency_id.id
290 diff_currency_p = inv.currency_id.id <> company_currency
294 'journal_id':journal.id,
296 'narration':inv.narration and inv.narration or inv.name,
303 'period_id': inv.period_id.id
306 move_id = move_pool.create(cr, uid, move)
308 #create the first line manually
313 'account_id': inv.account_id.id or False,
315 'journal_id': inv.journal_id.id,
316 'period_id': inv.period_id.id,
322 amount_currency = currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.amount)
323 inv.amount = amount_currency
325 'amount_currency':amount_currency,
326 'currency_id':inv.currency_id.id
329 if inv.type in ('rec_voucher', 'bank_rec_voucher', 'journal_pur_voucher', 'journal_voucher'):
330 move_line['debit'] = inv.amount
332 move_line['credit'] = inv.amount
335 line_ids += [move_line_pool.create(cr, uid, move_line)]
336 for line in inv.payment_ids:
339 if inv.type in ('bank_pay_voucher', 'pay_voucher', 'journal_voucher'):
346 'account_id':line.account_id.id or False,
348 'journal_id':inv.journal_id.id,
349 'period_id':inv.period_id.id,
350 'partner_id':line.partner_id.id or False,
353 'analytic_account_id':False
357 amount_currency = currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, line.amount)
358 line.amount = amount_currency
360 'amount_currency':amount_currency,
361 'currency_id':inv.currency_id.id
364 if line.account_analytic_id:
366 'analytic_account_id':line.account_analytic_id.id
369 if line.type == 'dr':
371 'debit': line.amount or False
375 elif line.type == 'cr':
377 'credit': line.amount or False
379 amount = line.amount * (-1)
381 move_line_id = move_line_pool.create(cr, uid, move_line)
382 line_ids += [move_line_id]
386 'move_ids':[(6, 0,line_ids)]
389 message = _('Voucher ') + " '" + inv.name + "' "+ _("is confirmed")
390 self.log(cr, uid, inv.id, message)
392 self.write(cr, uid, [inv.id], rec)
396 def _convert_ref(self, cr, uid, ref):
397 return (ref or '').replace('/','')
399 def name_get(self, cr, uid, ids, context={}):
403 'pay_voucher': 'CPV: ',
404 'rec_voucher': 'CRV: ',
405 'cont_voucher': 'CV: ',
406 'bank_pay_voucher': 'BPV: ',
407 'bank_rec_voucher': 'BRV: ',
408 'journal_sale_vou': 'JSV: ',
409 'journal_pur_voucher': 'JPV: ',
410 'journal_voucher':'JV'
412 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')]
414 def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
421 ids = self.search(cr, user, [('number','=',name)]+args, limit=limit, context=context)
423 ids = self.search(cr, user, [('name',operator,name)]+args, limit=limit, context=context)
424 return self.name_get(cr, user, ids, context)
426 def copy(self, cr, uid, id, default=None, context=None):
429 default = default.copy()
430 default.update({'state':'draft', 'number':False, 'move_id':False, 'move_ids':False, 'payment_ids':False})
431 if 'date' not in default:
432 default['date'] = time.strftime('%Y-%m-%d')
433 return super(account_voucher, self).copy(cr, uid, id, default, context)
437 class account_voucher_line(osv.osv):
438 _name = 'account.voucher.line'
439 _description = 'Voucher Line'
441 'voucher_id':fields.many2one('account.voucher', 'Voucher'),
442 'name':fields.char('Description', size=256, required=True),
443 'account_id':fields.many2one('account.account','Account', required=True, domain=[('type','<>','view')]),
444 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True),
445 'amount':fields.float('Amount'),
446 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Type'),
447 'ref':fields.char('Reference', size=32),
448 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account')
451 'type': lambda *a: 'cr'
454 def onchange_partner(self, cr, uid, ids, partner_id, ttype ,type1, currency):
455 currency_pool = self.pool.get('res.currency')
456 company = self.pool.get('res.users').browse(cr, uid, uid).company_id
469 partner_pool = self.pool.get('res.partner')
472 partner = partner_pool.browse(cr, uid, partner_id)
475 if type1 in ('rec_voucher', 'bank_rec_voucher', 'journal_voucher'):
476 account_id = partner.property_account_receivable.id
477 balance = partner.credit
480 elif type1 in ('pay_voucher', 'bank_pay_voucher', 'journal_voucher') :
481 account_id = partner.property_account_payable.id
482 balance = partner.debit
485 elif type1 in ('journal_sale_vou') :
486 account_id = partner.property_account_receivable.id
489 elif type1 in ('journal_pur_voucher') :
490 account_id = partner.property_account_payable.id
493 if company.currency_id != currency:
494 balance = currency_pool.compute(cr, uid, company.currency_id.id, currency, balance)
497 'account_id': account_id,
506 account_voucher_line()