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 lxml import etree
26 from osv import fields
28 from tools.translate import _
30 class account_move_line(osv.osv):
31 _inherit = 'account.move.line'
32 def _unreconciled(self, cr, uid, ids, prop, unknow_none, context):
34 for line in self.browse(cr, uid, ids, context=context):
35 res[line.id] = line.debit - line.credit
36 if line.reconcile_partial_id:
38 for partial in line.reconcile_partial_id.line_partial_ids:
39 res[line.id] += partial.debit - partial.credit
40 res[line.id] = abs(res[line.id])
44 'amount_unreconciled': fields.function(_unreconciled, method=True, string='Unreconciled Amount'),
48 class account_voucher(osv.osv):
49 def _get_type(self, cr, uid, ids, context={}):
50 return context.get('type', False)
52 def _get_period(self, cr, uid, context={}):
53 if context.get('period_id', False):
54 return context.get('period_id')
55 periods = self.pool.get('account.period').find(cr, uid)
56 return periods and periods[0] or False
58 def _get_journal(self, cr, uid, context={}):
59 journal_pool = self.pool.get('account.journal')
60 if context.get('journal_id', False):
61 return context.get('journal_id')
62 if not context.get('journal_id', False) and context.get('search_default_journal_id', False):
63 return context.get('search_default_journal_id')
65 ttype = context.get('type', 'bank')
66 if ttype in ('payment', 'receipt'):
68 res = journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
69 return res and res[0] or False
71 def _get_tax(self, cr, uid, context={}):
72 journal_pool = self.pool.get('account.journal')
73 journal_id = context.get('journal_id', False)
75 ttype = context.get('type', 'bank')
76 res = journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
83 journal = journal_pool.browse(cr, uid, journal_id)
84 account_id = journal.default_credit_account_id or journal.default_debit_account_id
85 if account_id and account_id.tax_ids:
86 tax_id = account_id.tax_ids[0].id
90 def _get_currency(self, cr, uid, context):
91 journal_pool = self.pool.get('account.journal')
92 journal_id = context.get('journal_id', False)
94 journal = journal_pool.browse(cr, uid, journal_id)
95 currency_id = journal.company_id.currency_id.id
97 currency_id = journal.currency.id
100 def _get_partner(self, cr, uid, context={}):
101 return context.get('partner_id', False)
103 def _get_reference(self, cr, uid, context={}):
104 return context.get('reference', False)
106 def _get_narration(self, cr, uid, context={}):
107 return context.get('narration', False)
109 def name_get(self, cr, uid, ids, context=None):
112 return [(r['id'], (str("%.2f" % r['amount']) or '')) for r in self.read(cr, uid, ids, ['amount'], context, load='_classic_write')]
114 def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
115 if not view_id and context.get('invoice_type',False):
116 mod_obj = self.pool.get('ir.model.data')
117 act_obj = self.pool.get('ir.actions.act_window')
118 if context.get('invoice_type') in ('out_invoice','out_refund'):
119 result = mod_obj._get_id(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
121 result = mod_obj._get_id(cr, uid, 'account_voucher', 'view_vendor_payment_form')
122 result = mod_obj.read(cr, uid, [result], ['res_id'], context=context)[0]['res_id']
124 res = super(account_voucher,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
125 doc = etree.XML(res['arch'])
126 nodes = doc.xpath("//field[@name='partner_id']")
127 if context.get('type', 'sale') in ('purchase', 'payment'):
129 node.set('domain', "[('supplier', '=', True)]")
130 res['arch'] = etree.tostring(doc)
133 _name = 'account.voucher'
134 _description = 'Accounting Voucher'
135 _order = "date desc, id desc"
136 # _rec_name = 'number'
138 'type':fields.selection([
140 ('purchase','Purchase'),
141 ('payment','Payment'),
142 ('receipt','Receipt'),
143 ],'Default Type', readonly=True, states={'draft':[('readonly',False)]}),
144 'name':fields.char('Memo', size=256, readonly=True, states={'draft':[('readonly',False)]}),
145 'date':fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}, help="Effective date for accounting entries"),
146 'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
147 'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
148 'line_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=True, states={'draft':[('readonly',False)]}),
149 'line_cr_ids':fields.one2many('account.voucher.line','voucher_id','Credits',
150 domain=[('type','=','cr')], context={'default_type':'cr'}, readonly=True, states={'draft':[('readonly',False)]}),
151 'line_dr_ids':fields.one2many('account.voucher.line','voucher_id','Debits',
152 domain=[('type','=','dr')], context={'default_type':'dr'}, readonly=True, states={'draft':[('readonly',False)]}),
153 'period_id': fields.many2one('account.period', 'Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
154 'narration':fields.text('Notes', readonly=True, states={'draft':[('readonly',False)]}),
155 'currency_id':fields.many2one('res.currency', 'Currency', readonly=True, states={'draft':[('readonly',False)]}),
156 # 'currency_id': fields.related('journal_id','currency', type='many2one', relation='res.currency', string='Currency', store=True, readonly=True, states={'draft':[('readonly',False)]}),
157 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
158 'state':fields.selection(
160 ('proforma','Pro-forma'),
162 ('cancel','Cancelled')
163 ], 'State', readonly=True, size=32,
164 help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed Voucher. \
165 \n* The \'Pro-forma\' when voucher is in Pro-forma state,voucher does not have an voucher number. \
166 \n* The \'Posted\' state is used when user create voucher,a voucher number is generated and voucher entries are created in account \
167 \n* The \'Cancelled\' state is used when user cancel voucher.'),
168 'amount': fields.float('Total', digits=(16, 2), required=True, readonly=True, states={'draft':[('readonly',False)]}),
169 'tax_amount':fields.float('Tax Amount', digits=(14,2), readonly=True, states={'draft':[('readonly',False)]}),
170 'reference': fields.char('Ref #', size=64, readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."),
171 'number': fields.char('Number', size=32, readonly=True,),
172 'move_id':fields.many2one('account.move', 'Account Entry'),
173 'move_ids': fields.related('move_id','line_id', type='one2many', relation='account.move.line', string='Journal Items', readonly=True),
174 'partner_id':fields.many2one('res.partner', 'Partner', change_default=1, readonly=True, states={'draft':[('readonly',False)]}),
175 'audit': fields.related('move_id','to_check', type='boolean', relation='account.move', string='Audit Complete ?'),
176 'pay_now':fields.selection([
177 ('pay_now','Pay Directly'),
178 ('pay_later','Pay Later or Group Funds'),
179 ],'Payment', select=True, readonly=True, states={'draft':[('readonly',False)]}),
180 'tax_id':fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}),
181 'pre_line':fields.boolean('Previous Payments ?', required=False),
182 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}),
185 'period_id': _get_period,
186 'partner_id': _get_partner,
187 'journal_id':_get_journal,
188 'currency_id': _get_currency,
189 'reference': _get_reference,
190 'narration':_get_narration,
193 'pay_now': 'pay_later',
195 'date' : time.strftime('%Y-%m-%d'),
196 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.voucher',context=c),
200 def compute_tax(self, cr, uid, ids, context={}):
201 tax_pool = self.pool.get('account.tax')
202 partner_pool = self.pool.get('res.partner')
203 position_pool = self.pool.get('account.fiscal.position')
204 voucher_line_pool = self.pool.get('account.voucher.line')
205 voucher_pool = self.pool.get('account.voucher')
207 for voucher in voucher_pool.browse(cr, uid, ids, context):
209 for line in voucher.line_ids:
210 voucher_amount += line.untax_amount or line.amount
211 line.amount = line.untax_amount or line.amount
212 voucher_line_pool.write(cr, uid, [line.id], {'amount':line.amount, 'untax_amount':line.untax_amount})
214 if not voucher.tax_id:
215 self.write(cr, uid, [voucher.id], {'amount':voucher_amount, 'tax_amount':0.0})
218 tax = [tax_pool.browse(cr, uid, voucher.tax_id.id)]
219 partner = partner_pool.browse(cr, uid, voucher.partner_id.id) or False
220 taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax)
221 tax = tax_pool.browse(cr, uid, taxes)
223 total = voucher_amount
226 if not tax[0].price_include:
227 for tax_line in tax_pool.compute_all(cr, uid, tax, voucher_amount, 1).get('taxes',[]):
228 total_tax += tax_line.get('amount', 0.0)
232 for line in voucher.line_ids:
236 for tax_line in tax_pool.compute_all(cr, uid, tax, line.untax_amount or line.amount, 1).get('taxes',[]):
237 line_tax += tax_line.get('amount', 0.0)
238 line_total += tax_line.get('price_unit')
239 total_tax += line_tax
240 untax_amount = line.untax_amount or line.amount
241 voucher_line_pool.write(cr, uid, [line.id], {'amount':line_total, 'untax_amount':untax_amount})
243 self.write(cr, uid, [voucher.id], {'amount':total, 'tax_amount':total_tax})
246 def onchange_price(self, cr, uid, ids, line_ids, tax_id, partner_id=False, context={}):
247 tax_pool = self.pool.get('account.tax')
248 partner_pool = self.pool.get('res.partner')
249 position_pool = self.pool.get('account.fiscal.position')
250 voucher_line_pool = self.pool.get('account.voucher.line')
255 voucher_total_tax = 0.0
257 voucher_line_ids = []
262 for line in line_ids:
264 line_amount = line[2].get('amount')
265 voucher_line_ids += [line[1]]
266 voucher_total += line_amount
268 total = voucher_total
271 tax = [tax_pool.browse(cr, uid, tax_id)]
273 partner = partner_pool.browse(cr, uid, partner_id) or False
274 taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax)
275 tax = tax_pool.browse(cr, uid, taxes)
277 if not tax[0].price_include:
278 for tax_line in tax_pool.compute_all(cr, uid, tax, voucher_total, 1).get('taxes',[]):
279 total_tax += tax_line.get('amount')
283 'amount':total or voucher_total,
284 'tax_amount':total_tax
290 def onchange_term_id(self, cr, uid, ids, term_id, amount):
291 term_pool = self.pool.get('account.payment.term')
294 default = {'date_due':False}
295 if term_id and amount:
296 terms = term_pool.compute(cr, uid, term_id, amount)
298 due_date = terms[-1][0]
302 return {'value':default}
304 def onchange_journal_voucher(self, cr, uid, ids, line_ids=False, tax_id=False, price=0.0, partner_id=False, journal_id=False, ttype=False, context={}):
306 Returns a dict that contains new values and context
308 @param partner_id: latest value from user input for field partner_id
309 @param args: other arguments
310 @param context: context arguments, like lang, time zone
312 @return: Returns a dict which contains new values, and context
318 if not partner_id or not journal_id:
321 partner_pool = self.pool.get('res.partner')
322 journal_pool = self.pool.get('account.journal')
324 journal = journal_pool.browse(cr, uid, journal_id)
325 partner = partner_pool.browse(cr, uid, partner_id)
328 if journal.type in ('sale','sale_refund'):
329 account_id = partner.property_account_receivable.id
331 elif journal.type in ('purchase', 'purchase_refund','expense'):
332 account_id = partner.property_account_payable.id
335 account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
338 default['value']['account_id'] = account_id
339 default['value']['type'] = ttype or tr_type
341 vals = self.onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context)
342 default['value'].update(vals.get('value'))
346 def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, context=None):
348 Returns a dict that contains new values and context
350 @param partner_id: latest value from user input for field partner_id
351 @param args: other arguments
352 @param context: context arguments, like lang, time zone
354 @return: Returns a dict which contains new values, and context
361 currency_pool = self.pool.get('res.currency')
362 move_pool = self.pool.get('account.move')
363 line_pool = self.pool.get('account.voucher.line')
364 move_line_pool = self.pool.get('account.move.line')
365 partner_pool = self.pool.get('res.partner')
366 journal_pool = self.pool.get('account.journal')
368 vals = self.onchange_journal(cr, uid, ids, journal_id, [], False, partner_id, context)
369 vals = vals.get('value')
370 currency_id = vals.get('currency_id', currency_id)
372 'value':{'line_ids':[], 'line_dr_ids':[], 'line_cr_ids':[], 'pre_line': False, 'currency_id':currency_id},
378 if not partner_id and ids:
379 line_ids = line_pool.search(cr, uid, [('voucher_id','=',ids[0])])
381 line_pool.unlink(cr, uid, line_ids)
384 journal = journal_pool.browse(cr, uid, journal_id)
385 partner = partner_pool.browse(cr, uid, partner_id)
387 if journal.type in ('sale','sale_refund'):
388 account_id = partner.property_account_receivable.id
389 elif journal.type in ('purchase', 'purchase_refund','expense'):
390 account_id = partner.property_account_payable.id
392 account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
394 default['value']['account_id'] = account_id
396 if journal.type not in ('cash', 'bank'):
401 account_type = 'receivable'
402 if ttype == 'payment':
403 account_type = 'payable'
404 total_debit = price or 0.0
406 total_credit = price or 0.0
407 account_type = 'receivable'
409 if not context.get('move_line_ids', False):
410 ids = move_line_pool.search(cr, uid, [('account_id.type','=', account_type), ('reconcile_id','=', False), ('partner_id','=',partner_id)], context=context)
412 ids = context['move_line_ids']
414 moves = move_line_pool.browse(cr, uid, ids)
416 company_currency = journal.company_id.currency_id.id
417 if company_currency != currency_id and ttype == 'payment':
418 total_debit = currency_pool.compute(cr, uid, currency_id, company_currency, total_debit)
419 elif company_currency != currency_id and ttype == 'receipt':
420 total_credit = currency_pool.compute(cr, uid, currency_id, company_currency, total_credit)
423 if line.credit and line.reconcile_partial_id and ttype == 'receipt':
425 if line.debit and line.reconcile_partial_id and ttype == 'payment':
427 total_credit += line.credit or 0.0
428 total_debit += line.debit or 0.0
431 if line.credit and line.reconcile_partial_id and ttype == 'receipt':
433 if line.debit and line.reconcile_partial_id and ttype == 'payment':
436 orignal_amount = line.credit or line.debit or 0.0
438 'name':line.move_id.name,
439 'type': line.credit and 'dr' or 'cr',
440 'move_line_id':line.id,
441 'account_id':line.account_id.id,
442 'amount_original':currency_pool.compute(cr, uid, company_currency, currency_id, orignal_amount),
443 'date_original':line.date,
444 'date_due':line.date_maturity,
445 'amount_unreconciled':currency_pool.compute(cr, uid, company_currency, currency_id, line.amount_unreconciled)
448 amount = min(line.amount_unreconciled, total_debit)
449 rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
450 total_debit -= amount
452 amount = min(line.amount_unreconciled, total_credit)
453 rs['amount'] = currency_pool.compute(cr, uid, company_currency, currency_id, amount)
454 total_credit -= amount
456 default['value']['line_ids'].append(rs)
457 if rs['type'] == 'cr':
458 default['value']['line_cr_ids'].append(rs)
460 default['value']['line_dr_ids'].append(rs)
462 if ttype == 'payment' and len(default['value']['line_cr_ids']) > 0:
463 default['value']['pre_line'] = 1
464 elif ttype == 'receipt' and len(default['value']['line_dr_ids']) > 0:
465 default['value']['pre_line'] = 1
469 def onchange_date(self, cr, user, ids, date, context={}):
471 @param date: latest value from user input for field date
472 @param args: other arguments
473 @param context: context arguments, like lang, time zone
474 @return: Returns a dict which contains new values, and context
476 period_pool = self.pool.get('account.period')
477 pids = period_pool.search(cr, user, [('date_start','<=',date), ('date_stop','>=',date)])
486 def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, context={}):
489 journal_pool = self.pool.get('account.journal')
490 journal = journal_pool.browse(cr, uid, journal_id)
491 account_id = journal.default_credit_account_id or journal.default_debit_account_id
493 if account_id and account_id.tax_ids:
494 tax_id = account_id.tax_ids[0].id
496 vals = self.onchange_price(cr, uid, ids, line_ids, tax_id, partner_id, context)
497 vals['value'].update({'tax_id':tax_id})
498 currency_id = journal.company_id.currency_id.id
500 currency_id = journal.currency.id
501 vals['value'].update({'currency_id':currency_id})
504 def proforma_voucher(self, cr, uid, ids, context=None):
505 self.action_move_line_create(cr, uid, ids, context=context)
508 def action_cancel_draft(self, cr, uid, ids, context={}):
509 wf_service = netsvc.LocalService("workflow")
510 for voucher_id in ids:
511 wf_service.trg_create(uid, 'account.voucher', voucher_id, cr)
512 self.write(cr, uid, ids, {'state':'draft'})
515 def cancel_voucher(self, cr, uid, ids, context={}):
516 reconcile_pool = self.pool.get('account.move.reconcile')
517 move_pool = self.pool.get('account.move')
518 voucher_line_pool = self.pool.get('account.voucher.line')
520 for voucher in self.browse(cr, uid, ids):
522 for line in voucher.move_ids:
523 if line.reconcile_id:
524 recs += [line.reconcile_id.id]
525 if line.reconcile_partial_id:
526 recs += [line.reconcile_partial_id.id]
528 reconcile_pool.unlink(cr, uid, recs)
531 move_pool.button_cancel(cr, uid, [voucher.move_id.id])
532 move_pool.unlink(cr, uid, [voucher.move_id.id])
537 self.write(cr, uid, ids, res)
540 def unlink(self, cr, uid, ids, context=None):
541 for t in self.read(cr, uid, ids, ['state'], context=context):
542 if t['state'] not in ('draft', 'cancel'):
543 raise osv.except_osv(_('Invalid action !'), _('Cannot delete Voucher(s) which are already opened or paid !'))
544 return super(account_voucher, self).unlink(cr, uid, ids, context=context)
546 # TODO: may be we can remove this method if not used anyware
547 def onchange_payment(self, cr, uid, ids, pay_now, journal_id, partner_id, ttype='sale'):
551 res = {'account_id':False}
552 partner_pool = self.pool.get('res.partner')
553 journal_pool = self.pool.get('account.journal')
554 if pay_now == 'pay_later':
555 partner = partner_pool.browse(cr, uid, partner_id)
556 journal = journal_pool.browse(cr, uid, journal_id)
557 if journal.type in ('sale','sale_refund'):
558 account_id = partner.property_account_receivable.id
559 elif journal.type in ('purchase', 'purchase_refund','expense'):
560 account_id = partner.property_account_payable.id
562 account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
563 res['account_id'] = account_id
566 def action_move_line_create(self, cr, uid, ids, context=None):
568 def _get_payment_term_lines(term_id, amount):
569 term_pool = self.pool.get('account.payment.term')
570 if term_id and amount:
571 terms = term_pool.compute(cr, uid, term_id, amount)
576 move_pool = self.pool.get('account.move')
577 move_line_pool = self.pool.get('account.move.line')
578 analytic_pool = self.pool.get('account.analytic.line')
579 currency_pool = self.pool.get('res.currency')
580 invoice_pool = self.pool.get('account.invoice')
581 bank_st_line_obj = self.pool.get('account.bank.statement.line')
582 for inv in self.browse(cr, uid, ids):
587 elif inv.journal_id.sequence_id:
588 name = self.pool.get('ir.sequence').get_id(cr, uid, inv.journal_id.sequence_id.id)
590 raise osv.except_osv(_('Error !'), _('Please define a sequence on the journal !'))
594 'journal_id': inv.journal_id.id,
595 'narration' : inv.narration,
598 'period_id': inv.period_id and inv.period_id.id or False
600 move_id = move_pool.create(cr, uid, move)
601 line_bank_ids = bank_st_line_obj.search(cr, uid, [('voucher_id', '=', inv.id)], context=context)
603 bank_st_line_obj.write(cr, uid, line_bank_ids, {
604 'move_ids': [(4, move_id, False)]
607 #create the first line manually
608 company_currency = inv.journal_id.company_id.currency_id.id
611 # TODO: is there any other alternative then the voucher type ??
612 # -for sale, purchase we have but for the payment and receipt we do not have as based on the bank/cash journal we can not know its payment or receipt
613 if inv.type in ('purchase', 'payment'):
614 credit = currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.amount)
615 elif inv.type in ('sale', 'receipt'):
616 debit = currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.amount)
625 'name':inv.name or '/',
628 'account_id':inv.account_id.id,
630 'journal_id':inv.journal_id.id,
631 'period_id':inv.period_id.id,
632 'partner_id':inv.partner_id.id,
633 'currency_id':inv.currency_id.id,
634 'amount_currency':inv.amount,
636 'date_maturity':inv.date_due
639 if (debit == 0.0 or credit == 0.0 or debit+credit > 0) and (debit > 0.0 or credit > 0.0):
640 master_line = move_line_pool.create(cr, uid, move_line)
643 line_total = debit - credit
644 if inv.type == 'sale':
645 line_total = line_total - currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount)
646 elif inv.type == 'purchase':
647 line_total = line_total + currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, inv.tax_amount)
649 for line in inv.line_ids:
652 amount = currency_pool.compute(cr, uid, inv.currency_id.id, company_currency, line.amount)
655 'journal_id':inv.journal_id.id,
656 'period_id':inv.period_id.id,
657 'name':line.name and line.name or '/',
658 'account_id':line.account_id.id,
660 'partner_id':inv.partner_id.id,
661 'currency_id':inv.currency_id.id,
662 'amount_currency':line.amount,
663 'analytic_account_id':line.account_analytic_id and line.account_analytic_id.id or False,
671 if line.type == 'dr':
676 if (line.type=='dr'):
678 move_line['debit'] = amount
681 move_line['credit'] = amount
683 if inv.tax_id and inv.type in ('sale', 'purchase'):
685 'account_tax_id':inv.tax_id.id,
688 master_line = move_line_pool.create(cr, uid, move_line)
689 if line.move_line_id.id:
690 rec_ids = [master_line, line.move_line_id.id]
691 rec_list_ids.append(rec_ids)
693 if not self.pool.get('res.currency').is_zero(cr, uid, inv.currency_id, line_total):
699 'partner_id':inv.partner_id.id,
701 'credit':diff>0 and diff or 0.0,
702 'debit':diff<0 and -diff or 0.0,
705 if inv.type in ('sale', 'receipt'):
706 # if inv.journal_id.type in ('sale','sale_refund', 'cash', 'bank'):
707 account_id = inv.partner_id.property_account_receivable.id
709 account_id = inv.partner_id.property_account_payable.id
710 move_line['account_id'] = account_id
712 move_line_id = move_line_pool.create(cr, uid, move_line)
714 self.write(cr, uid, [inv.id], {
719 move_pool.post(cr, uid, [move_id], context={})
720 for rec_ids in rec_list_ids:
721 if len(rec_ids) >= 2:
722 move_line_pool.reconcile_partial(cr, uid, rec_ids)
725 def copy(self, cr, uid, id, default={}, context=None):
734 if 'date' not in default:
735 default['date'] = time.strftime('%Y-%m-%d')
736 return super(account_voucher, self).copy(cr, uid, id, default, context)
740 class account_voucher_line(osv.osv):
741 _name = 'account.voucher.line'
742 _description = 'Voucher Lines'
743 _order = "move_line_id"
745 def _compute_balance(self, cr, uid, ids, name, args, context=None):
746 currency_pool = self.pool.get('res.currency')
748 for line in self.browse(cr, uid, ids):
750 company_currency = line.voucher_id.journal_id.company_id.currency_id.id
751 voucher_currency = line.voucher_id.currency_id.id
752 move_line = line.move_line_id or False
755 res['amount_original'] = 0.0
756 res['amount_unreconciled'] = 0.0
758 elif move_line and move_line.credit > 0:
759 res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.credit)
761 res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.debit)
764 res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.amount_unreconciled)
765 rs_data[line.id] = res
769 'voucher_id':fields.many2one('account.voucher', 'Voucher', required=1, ondelete='cascade'),
770 'name':fields.char('Description', size=256),
771 'account_id':fields.many2one('account.account','Account', required=True),
772 'partner_id':fields.related('voucher_id', 'partner_id', type='many2one', relation='res.partner', string='Partner'),
773 'untax_amount':fields.float('Untax Amount'),
774 'amount':fields.float('Amount', digits=(14,2)),
775 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Cr/Dr'),
776 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
777 'move_line_id': fields.many2one('account.move.line', 'Journal Item'),
778 'date_original': fields.related('move_line_id','date', type='date', relation='account.move.line', string='Date', readonly=1),
779 'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=1),
780 'amount_original': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Originial Amount', store=True),
781 'amount_unreconciled': fields.function(_compute_balance, method=True, multi='dc', type='float', string='Open Balance', store=True),
782 'company_id': fields.related('voucher_id','company_id', relation='res.company', string='Company', store=True),
785 'name': lambda *a: ''
788 def onchange_move_line_id(self, cr, user, ids, move_line_id, context={}):
790 Returns a dict that contains new values and context
792 @param move_line_id: latest value from user input for field move_line_id
793 @param args: other arguments
794 @param context: context arguments, like lang, time zone
796 @return: Returns a dict which contains new values, and context
799 move_line_pool = self.pool.get('account.move.line')
801 move_line = move_line_pool.browse(cr, user, move_line_id, context=context)
802 move_id = move_line.move_id.id
805 amount = move_line.credit
808 amount = move_line.debit
809 account_id = move_line.account_id.id
811 'account_id':account_id,
818 def default_get(self, cr, user, fields_list, context=None):
820 Returns default values for fields
821 @param fields_list: list of fields, for which default values are required to be read
822 @param context: context arguments, like lang, time zone
824 @return: Returns a dict that contains default values for fields
826 journal_id = context.get('journal_id', False)
827 partner_id = context.get('partner_id', False)
828 journal_pool = self.pool.get('account.journal')
829 partner_pool = self.pool.get('res.partner')
830 values = super(account_voucher_line, self).default_get(cr, user, fields_list, context=context)
831 if (not journal_id) or ('account_id' not in fields_list):
833 journal = journal_pool.browse(cr, user, journal_id)
836 if journal.type in ('sale', 'sale_refund'):
837 account_id = journal.default_credit_account_id and journal.default_credit_account_id.id or False
839 elif journal.type in ('purchase', 'expense', 'purchase_refund'):
840 account_id = journal.default_debit_account_id and journal.default_debit_account_id.id or False
843 partner = partner_pool.browse(cr, user, partner_id, context=context)
844 if context.get('type') == 'payment':
846 account_id = partner.property_account_payable.id
847 elif context.get('type') == 'receipt':
848 account_id = partner.property_account_receivable.id
850 if (not account_id) and 'account_id' in fields_list:
851 raise osv.except_osv(_('Invalid Error !'), _('Please change partner and try again !'))
853 'account_id':account_id,
857 account_voucher_line()
859 class account_bank_statement(osv.osv):
860 _inherit = 'account.bank.statement'
862 def button_cancel(self, cr, uid, ids, context=None):
864 for st in self.browse(cr, uid, ids, context):
866 for line in st.line_ids:
868 voucher_ids.append(line.voucher_id.id)
869 self.pool.get('account.voucher').cancel_voucher(cr, uid, voucher_ids, context)
870 return super(account_bank_statement, self).button_cancel(cr, uid, ids, context=context)
872 def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, next_number, context=None):
873 voucher_obj = self.pool.get('account.voucher')
874 wf_service = netsvc.LocalService("workflow")
875 st_line = self.pool.get('account.bank.statement.line').browse(cr, uid, st_line_id, context=context)
876 if st_line.voucher_id:
877 voucher_obj.write(cr, uid, [st_line.voucher_id.id], {'number': next_number}, context=context)
878 if st_line.voucher_id.state == 'cancel':
879 voucher_obj.action_cancel_draft(cr, uid, [st_line.voucher_id.id], context=context)
880 wf_service.trg_validate(uid, 'account.voucher', st_line.voucher_id.id, 'proforma_voucher', cr)
881 return self.pool.get('account.move.line').write(cr, uid, [x.id for x in st_line.voucher_id.move_ids], {'statement_id': st_line.statement_id.id}, context=context)
882 return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line.id, company_currency_id, next_number, context=context)
884 account_bank_statement()
886 class account_bank_statement_line(osv.osv):
887 _inherit = 'account.bank.statement.line'
889 def _amount_reconciled(self, cursor, user, ids, name, args, context=None):
893 res_currency_obj = self.pool.get('res.currency')
895 company_currency_id = False
896 for line in self.browse(cursor, user, ids, context=context):
897 # if not company_currency_id:
898 # company_currency_id = line.company_id.id
900 res[line.id] = line.voucher_id.amount#
901 # res_currency_obj.compute(cursor, user,
902 # company_currency_id, line.statement_id.currency.id,
903 # line.voucher_id.amount, context=context)
909 'amount_reconciled': fields.function(_amount_reconciled,
910 string='Amount reconciled', method=True, type='float'),
911 'voucher_id': fields.many2one('account.voucher', 'Payment'),
915 def unlink(self, cr, uid, ids, context=None):
916 statement_line = self.browse(cr, uid, ids, context)
918 for st_line in statement_line:
919 if st_line.voucher_id:
920 unlink_ids.append(st_line.voucher_id.id)
921 self.pool.get('account.voucher').unlink(cr, uid, unlink_ids, context=context)
922 return super(account_bank_statement_line, self).unlink(cr, uid, ids, context=context)
924 account_bank_statement_line()