[FIX] account_voucher: use resolve_2many_commands() instead of buggy-hand-written...
[odoo/odoo.git] / addons / account_voucher / account_voucher.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
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.
11 #
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.
16 #
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/>.
19 #
20 ##############################################################################
21
22 import time
23 from lxml import etree
24
25 from openerp.osv import fields, osv
26 import openerp.addons.decimal_precision as dp
27 from openerp.tools.translate import _
28 from openerp.tools import float_compare
29 from openerp.report import report_sxw
30 import openerp
31
32 class res_currency(osv.osv):
33     _inherit = "res.currency"
34
35     def _get_current_rate(self, cr, uid, ids, raise_on_no_rate=True, context=None):
36         if context is None:
37             context = {}
38         res = super(res_currency, self)._get_current_rate(cr, uid, ids, raise_on_no_rate, context=context)
39         if context.get('voucher_special_currency') in ids and context.get('voucher_special_currency_rate'):
40             res[context.get('voucher_special_currency')] = context.get('voucher_special_currency_rate')
41         return res
42
43
44 class account_voucher(osv.osv):
45     def _check_paid(self, cr, uid, ids, name, args, context=None):
46         res = {}
47         for voucher in self.browse(cr, uid, ids, context=context):
48             res[voucher.id] = any([((line.account_id.type, 'in', ('receivable', 'payable')) and line.reconcile_id) for line in voucher.move_ids])
49         return res
50
51     def _get_type(self, cr, uid, context=None):
52         if context is None:
53             context = {}
54         return context.get('type', False)
55
56     def _get_period(self, cr, uid, context=None):
57         if context is None: context = {}
58         if context.get('period_id', False):
59             return context.get('period_id')
60         periods = self.pool.get('account.period').find(cr, uid, context=context)
61         return periods and periods[0] or False
62
63     def _make_journal_search(self, cr, uid, ttype, context=None):
64         journal_pool = self.pool.get('account.journal')
65         return journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
66
67     def _get_journal(self, cr, uid, context=None):
68         if context is None: context = {}
69         invoice_pool = self.pool.get('account.invoice')
70         journal_pool = self.pool.get('account.journal')
71         if context.get('invoice_id', False):
72             currency_id = invoice_pool.browse(cr, uid, context['invoice_id'], context=context).currency_id.id
73             journal_id = journal_pool.search(cr, uid, [('currency', '=', currency_id)], limit=1)
74             return journal_id and journal_id[0] or False
75         if context.get('journal_id', False):
76             return context.get('journal_id')
77         if not context.get('journal_id', False) and context.get('search_default_journal_id', False):
78             return context.get('search_default_journal_id')
79
80         ttype = context.get('type', 'bank')
81         if ttype in ('payment', 'receipt'):
82             ttype = 'bank'
83         res = self._make_journal_search(cr, uid, ttype, context=context)
84         return res and res[0] or False
85
86     def _get_tax(self, cr, uid, context=None):
87         if context is None: context = {}
88         journal_pool = self.pool.get('account.journal')
89         journal_id = context.get('journal_id', False)
90         if not journal_id:
91             ttype = context.get('type', 'bank')
92             res = journal_pool.search(cr, uid, [('type', '=', ttype)], limit=1)
93             if not res:
94                 return False
95             journal_id = res[0]
96
97         if not journal_id:
98             return False
99         journal = journal_pool.browse(cr, uid, journal_id, context=context)
100         account_id = journal.default_credit_account_id or journal.default_debit_account_id
101         if account_id and account_id.tax_ids:
102             tax_id = account_id.tax_ids[0].id
103             return tax_id
104         return False
105
106     def _get_payment_rate_currency(self, cr, uid, context=None):
107         """
108         Return the default value for field payment_rate_currency_id: the currency of the journal
109         if there is one, otherwise the currency of the user's company
110         """
111         if context is None: context = {}
112         journal_pool = self.pool.get('account.journal')
113         journal_id = context.get('journal_id', False)
114         if journal_id:
115             journal = journal_pool.browse(cr, uid, journal_id, context=context)
116             if journal.currency:
117                 return journal.currency.id
118         #no journal given in the context, use company currency as default
119         return self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
120
121     def _get_currency(self, cr, uid, context=None):
122         if context is None: context = {}
123         journal_pool = self.pool.get('account.journal')
124         journal_id = context.get('journal_id', False)
125         if journal_id:
126             journal = journal_pool.browse(cr, uid, journal_id, context=context)
127             if journal.currency:
128                 return journal.currency.id
129         return self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
130
131     def _get_partner(self, cr, uid, context=None):
132         if context is None: context = {}
133         return context.get('partner_id', False)
134
135     def _get_reference(self, cr, uid, context=None):
136         if context is None: context = {}
137         return context.get('reference', False)
138
139     def _get_narration(self, cr, uid, context=None):
140         if context is None: context = {}
141         return context.get('narration', False)
142
143     def _get_amount(self, cr, uid, context=None):
144         if context is None:
145             context= {}
146         return context.get('amount', 0.0)
147
148     def name_get(self, cr, uid, ids, context=None):
149         if not ids:
150             return []
151         if context is None: context = {}
152         return [(r['id'], (r['number'] or _('Voucher'))) for r in self.read(cr, uid, ids, ['number'], context, load='_classic_write')]
153
154     def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
155         mod_obj = self.pool.get('ir.model.data')
156         if context is None: context = {}
157
158         if view_type == 'form':
159             if not view_id and context.get('invoice_type'):
160                 if context.get('invoice_type') in ('out_invoice', 'out_refund'):
161                     result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
162                 else:
163                     result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_payment_form')
164                 result = result and result[1] or False
165                 view_id = result
166             if not view_id and context.get('line_type'):
167                 if context.get('line_type') == 'customer':
168                     result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_receipt_form')
169                 else:
170                     result = mod_obj.get_object_reference(cr, uid, 'account_voucher', 'view_vendor_payment_form')
171                 result = result and result[1] or False
172                 view_id = result
173
174         res = super(account_voucher, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
175         doc = etree.XML(res['arch'])
176
177         if context.get('type', 'sale') in ('purchase', 'payment'):
178             nodes = doc.xpath("//field[@name='partner_id']")
179             for node in nodes:
180                 node.set('context', "{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}")
181                 if context.get('invoice_type','') in ('in_invoice', 'in_refund'):
182                     node.set('string', _("Supplier"))
183         res['arch'] = etree.tostring(doc)
184         return res
185
186     def _compute_writeoff_amount(self, cr, uid, line_dr_ids, line_cr_ids, amount, type):
187         debit = credit = 0.0
188         sign = type == 'payment' and -1 or 1
189         for l in line_dr_ids:
190             if isinstance(l, dict):
191                 debit += l['amount']
192         for l in line_cr_ids:
193             if isinstance(l, dict):
194                 credit += l['amount']
195         return amount - sign * (credit - debit)
196
197     def onchange_line_ids(self, cr, uid, ids, line_dr_ids, line_cr_ids, amount, voucher_currency, type, context=None):
198         context = context or {}
199         if not line_dr_ids and not line_cr_ids:
200             return {'value':{'writeoff_amount': 0.0}}
201         # resolve lists of commands into lists of dicts
202         line_dr_ids = self.resolve_2many_commands(cr, uid, 'line_dr_ids', line_dr_ids, ['amount'], context)
203         line_cr_ids = self.resolve_2many_commands(cr, uid, 'line_cr_ids', line_cr_ids, ['amount'], context)
204         #compute the field is_multi_currency that is used to hide/display options linked to secondary currency on the voucher
205         is_multi_currency = False
206         #loop on the voucher lines to see if one of these has a secondary currency. If yes, we need to see the options
207         for voucher_line in line_dr_ids+line_cr_ids:
208             line_id = voucher_line.get('id') and self.pool.get('account.voucher.line').browse(cr, uid, voucher_line['id'], context=context).move_line_id.id or voucher_line.get('move_line_id')
209             if line_id and self.pool.get('account.move.line').browse(cr, uid, line_id, context=context).currency_id:
210                 is_multi_currency = True
211                 break
212         return {'value': {'writeoff_amount': self._compute_writeoff_amount(cr, uid, line_dr_ids, line_cr_ids, amount, type), 'is_multi_currency': is_multi_currency}}
213
214     def _get_journal_currency(self, cr, uid, ids, name, args, context=None):
215         res = {}
216         for voucher in self.browse(cr, uid, ids, context=context):
217             res[voucher.id] = voucher.journal_id.currency and voucher.journal_id.currency.id or voucher.company_id.currency_id.id
218         return res
219
220     def _get_writeoff_amount(self, cr, uid, ids, name, args, context=None):
221         if not ids: return {}
222         currency_obj = self.pool.get('res.currency')
223         res = {}
224         debit = credit = 0.0
225         for voucher in self.browse(cr, uid, ids, context=context):
226             sign = voucher.type == 'payment' and -1 or 1
227             for l in voucher.line_dr_ids:
228                 debit += l.amount
229             for l in voucher.line_cr_ids:
230                 credit += l.amount
231             currency = voucher.currency_id or voucher.company_id.currency_id
232             res[voucher.id] =  currency_obj.round(cr, uid, currency, voucher.amount - sign * (credit - debit))
233         return res
234
235     def _paid_amount_in_company_currency(self, cr, uid, ids, name, args, context=None):
236         if context is None:
237             context = {}
238         res = {}
239         ctx = context.copy()
240         for v in self.browse(cr, uid, ids, context=context):
241             ctx.update({'date': v.date})
242             #make a new call to browse in order to have the right date in the context, to get the right currency rate
243             voucher = self.browse(cr, uid, v.id, context=ctx)
244             ctx.update({
245               'voucher_special_currency': voucher.payment_rate_currency_id and voucher.payment_rate_currency_id.id or False,
246               'voucher_special_currency_rate': voucher.currency_id.rate * voucher.payment_rate,})
247             res[voucher.id] =  self.pool.get('res.currency').compute(cr, uid, voucher.currency_id.id, voucher.company_id.currency_id.id, voucher.amount, context=ctx)
248         return res
249
250     def _get_currency_help_label(self, cr, uid, currency_id, payment_rate, payment_rate_currency_id, context=None):
251         """
252         This function builds a string to help the users to understand the behavior of the payment rate fields they can specify on the voucher. 
253         This string is only used to improve the usability in the voucher form view and has no other effect.
254
255         :param currency_id: the voucher currency
256         :type currency_id: integer
257         :param payment_rate: the value of the payment_rate field of the voucher
258         :type payment_rate: float
259         :param payment_rate_currency_id: the value of the payment_rate_currency_id field of the voucher
260         :type payment_rate_currency_id: integer
261         :return: translated string giving a tip on what's the effect of the current payment rate specified
262         :rtype: str
263         """
264         rml_parser = report_sxw.rml_parse(cr, uid, 'currency_help_label', context=context)
265         currency_pool = self.pool.get('res.currency')
266         currency_str = payment_rate_str = ''
267         if currency_id:
268             currency_str = rml_parser.formatLang(1, currency_obj=currency_pool.browse(cr, uid, currency_id, context=context))
269         if payment_rate_currency_id:
270             payment_rate_str  = rml_parser.formatLang(payment_rate, currency_obj=currency_pool.browse(cr, uid, payment_rate_currency_id, context=context))
271         currency_help_label = _('At the operation date, the exchange rate was\n%s = %s') % (currency_str, payment_rate_str)
272         return currency_help_label
273
274     def _fnct_currency_help_label(self, cr, uid, ids, name, args, context=None):
275         res = {}
276         for voucher in self.browse(cr, uid, ids, context=context):
277             res[voucher.id] = self._get_currency_help_label(cr, uid, voucher.currency_id.id, voucher.payment_rate, voucher.payment_rate_currency_id.id, context=context)
278         return res
279
280     _name = 'account.voucher'
281     _description = 'Accounting Voucher'
282     _inherit = ['mail.thread']
283     _order = "date desc, id desc"
284 #    _rec_name = 'number'
285     _track = {
286         'state': {
287             'account_voucher.mt_voucher_state_change': lambda self, cr, uid, obj, ctx=None: True,
288         },
289     }
290
291     _columns = {
292         'type':fields.selection([
293             ('sale','Sale'),
294             ('purchase','Purchase'),
295             ('payment','Payment'),
296             ('receipt','Receipt'),
297         ],'Default Type', readonly=True, states={'draft':[('readonly',False)]}),
298         'name':fields.char('Memo', readonly=True, states={'draft':[('readonly',False)]}),
299         'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]},
300                            help="Effective date for accounting entries", copy=False),
301         'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
302         'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}),
303         'line_ids':fields.one2many('account.voucher.line', 'voucher_id', 'Voucher Lines',
304                                    readonly=True, copy=True,
305                                    states={'draft':[('readonly',False)]}),
306         'line_cr_ids':fields.one2many('account.voucher.line','voucher_id','Credits',
307             domain=[('type','=','cr')], context={'default_type':'cr'}, readonly=True, states={'draft':[('readonly',False)]}),
308         'line_dr_ids':fields.one2many('account.voucher.line','voucher_id','Debits',
309             domain=[('type','=','dr')], context={'default_type':'dr'}, readonly=True, states={'draft':[('readonly',False)]}),
310         'period_id': fields.many2one('account.period', 'Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
311         'narration':fields.text('Notes', readonly=True, states={'draft':[('readonly',False)]}),
312         'currency_id': fields.function(_get_journal_currency, type='many2one', relation='res.currency', string='Currency', readonly=True, required=True),
313         'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
314         'state':fields.selection(
315             [('draft','Draft'),
316              ('cancel','Cancelled'),
317              ('proforma','Pro-forma'),
318              ('posted','Posted')
319             ], 'Status', readonly=True, track_visibility='onchange', copy=False,
320             help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Voucher. \
321                         \n* The \'Pro-forma\' when voucher is in Pro-forma status,voucher does not have an voucher number. \
322                         \n* The \'Posted\' status is used when user create voucher,a voucher number is generated and voucher entries are created in account \
323                         \n* The \'Cancelled\' status is used when user cancel voucher.'),
324         'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}),
325         'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True),
326         'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]},
327                                  help="Transaction reference number.", copy=False),
328         'number': fields.char('Number', readonly=True, copy=False),
329         'move_id':fields.many2one('account.move', 'Account Entry', copy=False),
330         'move_ids': fields.related('move_id','line_id', type='one2many', relation='account.move.line', string='Journal Items', readonly=True),
331         'partner_id':fields.many2one('res.partner', 'Partner', change_default=1, readonly=True, states={'draft':[('readonly',False)]}),
332         'audit': fields.related('move_id','to_check', type='boolean', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.', relation='account.move', string='To Review'),
333         'paid': fields.function(_check_paid, string='Paid', type='boolean', help="The Voucher has been totally paid."),
334         'pay_now':fields.selection([
335             ('pay_now','Pay Directly'),
336             ('pay_later','Pay Later or Group Funds'),
337         ],'Payment', select=True, readonly=True, states={'draft':[('readonly',False)]}),
338         'tax_id': fields.many2one('account.tax', 'Tax', readonly=True, states={'draft':[('readonly',False)]}, domain=[('price_include','=', False)], help="Only for tax excluded from price"),
339         'pre_line':fields.boolean('Previous Payments ?', required=False),
340         'date_due': fields.date('Due Date', readonly=True, select=True, states={'draft':[('readonly',False)]}),
341         'payment_option':fields.selection([
342                                            ('without_writeoff', 'Keep Open'),
343                                            ('with_writeoff', 'Reconcile Payment Balance'),
344                                            ], 'Payment Difference', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="This field helps you to choose what you want to do with the eventual difference between the paid amount and the sum of allocated amounts. You can either choose to keep open this difference on the partner's account, or reconcile it with the payment(s)"),
345         'writeoff_acc_id': fields.many2one('account.account', 'Counterpart Account', readonly=True, states={'draft': [('readonly', False)]}),
346         'comment': fields.char('Counterpart Comment', required=True, readonly=True, states={'draft': [('readonly', False)]}),
347         'analytic_id': fields.many2one('account.analytic.account','Write-Off Analytic Account', readonly=True, states={'draft': [('readonly', False)]}),
348         'writeoff_amount': fields.function(_get_writeoff_amount, string='Difference Amount', type='float', readonly=True, help="Computed as the difference between the amount stated in the voucher and the sum of allocation on the voucher lines."),
349         'payment_rate_currency_id': fields.many2one('res.currency', 'Payment Rate Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
350         'payment_rate': fields.float('Exchange Rate', digits=(12,6), required=True, readonly=True, states={'draft': [('readonly', False)]},
351             help='The specific rate that will be used, in this voucher, between the selected currency (in \'Payment Rate Currency\' field)  and the voucher currency.'),
352         'paid_amount_in_company_currency': fields.function(_paid_amount_in_company_currency, string='Paid Amount in Company Currency', type='float', readonly=True),
353         'is_multi_currency': fields.boolean('Multi Currency Voucher', help='Fields with internal purpose only that depicts if the voucher is a multi currency one or not'),
354         'currency_help_label': fields.function(_fnct_currency_help_label, type='text', string="Helping Sentence", help="This sentence helps you to know how to specify the payment rate by giving you the direct effect it has"), 
355     }
356     _defaults = {
357         'period_id': _get_period,
358         'partner_id': _get_partner,
359         'journal_id':_get_journal,
360         'currency_id': _get_currency,
361         'reference': _get_reference,
362         'narration':_get_narration,
363         'amount': _get_amount,
364         'type':_get_type,
365         'state': 'draft',
366         'pay_now': 'pay_now',
367         'name': '',
368         'date': lambda *a: time.strftime('%Y-%m-%d'),
369         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.voucher',context=c),
370         'tax_id': _get_tax,
371         'payment_option': 'without_writeoff',
372         'comment': _('Write-Off'),
373         'payment_rate': 1.0,
374         'payment_rate_currency_id': _get_payment_rate_currency,
375     }
376
377     def compute_tax(self, cr, uid, ids, context=None):
378         tax_pool = self.pool.get('account.tax')
379         partner_pool = self.pool.get('res.partner')
380         position_pool = self.pool.get('account.fiscal.position')
381         voucher_line_pool = self.pool.get('account.voucher.line')
382         voucher_pool = self.pool.get('account.voucher')
383         if context is None: context = {}
384
385         for voucher in voucher_pool.browse(cr, uid, ids, context=context):
386             voucher_amount = 0.0
387             for line in voucher.line_ids:
388                 voucher_amount += line.untax_amount or line.amount
389                 line.amount = line.untax_amount or line.amount
390                 voucher_line_pool.write(cr, uid, [line.id], {'amount':line.amount, 'untax_amount':line.untax_amount})
391
392             if not voucher.tax_id:
393                 self.write(cr, uid, [voucher.id], {'amount':voucher_amount, 'tax_amount':0.0})
394                 continue
395
396             tax = [tax_pool.browse(cr, uid, voucher.tax_id.id, context=context)]
397             partner = partner_pool.browse(cr, uid, voucher.partner_id.id, context=context) or False
398             taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax)
399             tax = tax_pool.browse(cr, uid, taxes, context=context)
400
401             total = voucher_amount
402             total_tax = 0.0
403
404             if not tax[0].price_include:
405                 for line in voucher.line_ids:
406                     for tax_line in tax_pool.compute_all(cr, uid, tax, line.amount, 1).get('taxes', []):
407                         total_tax += tax_line.get('amount', 0.0)
408                 total += total_tax
409             else:
410                 for line in voucher.line_ids:
411                     line_total = 0.0
412                     line_tax = 0.0
413
414                     for tax_line in tax_pool.compute_all(cr, uid, tax, line.untax_amount or line.amount, 1).get('taxes', []):
415                         line_tax += tax_line.get('amount', 0.0)
416                         line_total += tax_line.get('price_unit')
417                     total_tax += line_tax
418                     untax_amount = line.untax_amount or line.amount
419                     voucher_line_pool.write(cr, uid, [line.id], {'amount':line_total, 'untax_amount':untax_amount})
420
421             self.write(cr, uid, [voucher.id], {'amount':total, 'tax_amount':total_tax})
422         return True
423
424     def onchange_price(self, cr, uid, ids, line_ids, tax_id, partner_id=False, context=None):
425         context = context or {}
426         tax_pool = self.pool.get('account.tax')
427         partner_pool = self.pool.get('res.partner')
428         position_pool = self.pool.get('account.fiscal.position')
429         if not line_ids:
430             line_ids = []
431         res = {
432             'tax_amount': False,
433             'amount': False,
434         }
435         voucher_total = 0.0
436
437         # resolve the list of commands into a list of dicts
438         line_ids = self.resolve_2many_commands(cr, uid, 'line_ids', line_ids, ['amount'], context)
439
440         total_tax = 0.0
441         for line in line_ids:
442             line_amount = 0.0
443             line_amount = line.get('amount',0.0)
444
445             if tax_id:
446                 tax = [tax_pool.browse(cr, uid, tax_id, context=context)]
447                 if partner_id:
448                     partner = partner_pool.browse(cr, uid, partner_id, context=context) or False
449                     taxes = position_pool.map_tax(cr, uid, partner and partner.property_account_position or False, tax)
450                     tax = tax_pool.browse(cr, uid, taxes, context=context)
451
452                 if not tax[0].price_include:
453                     for tax_line in tax_pool.compute_all(cr, uid, tax, line_amount, 1).get('taxes', []):
454                         total_tax += tax_line.get('amount')
455
456             voucher_total += line_amount
457         total = voucher_total + total_tax
458
459         res.update({
460             'amount': total or voucher_total,
461             'tax_amount': total_tax
462         })
463         return {
464             'value': res
465         }
466
467     def onchange_term_id(self, cr, uid, ids, term_id, amount):
468         term_pool = self.pool.get('account.payment.term')
469         terms = False
470         due_date = False
471         default = {'date_due':False}
472         if term_id and amount:
473             terms = term_pool.compute(cr, uid, term_id, amount)
474         if terms:
475             due_date = terms[-1][0]
476             default.update({
477                 'date_due':due_date
478             })
479         return {'value':default}
480
481     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, company_id=False, context=None):
482         """price
483         Returns a dict that contains new values and context
484
485         @param partner_id: latest value from user input for field partner_id
486         @param args: other arguments
487         @param context: context arguments, like lang, time zone
488
489         @return: Returns a dict which contains new values, and context
490         """
491         default = {
492             'value':{},
493         }
494
495         if not partner_id or not journal_id:
496             return default
497
498         partner_pool = self.pool.get('res.partner')
499         journal_pool = self.pool.get('account.journal')
500
501         journal = journal_pool.browse(cr, uid, journal_id, context=context)
502         partner = partner_pool.browse(cr, uid, partner_id, context=context)
503         account_id = False
504         tr_type = False
505         if journal.type in ('sale','sale_refund'):
506             account_id = partner.property_account_receivable.id
507             tr_type = 'sale'
508         elif journal.type in ('purchase', 'purchase_refund','expense'):
509             account_id = partner.property_account_payable.id
510             tr_type = 'purchase'
511         else:
512             if not journal.default_credit_account_id or not journal.default_debit_account_id:
513                 raise osv.except_osv(_('Error!'), _('Please define default credit/debit accounts on the journal "%s".') % (journal.name))
514             account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
515             tr_type = 'receipt'
516
517         default['value']['account_id'] = account_id
518         default['value']['type'] = ttype or tr_type
519
520         vals = self.onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id, partner_id, time.strftime('%Y-%m-%d'), price, ttype, company_id, context)
521         default['value'].update(vals.get('value'))
522
523         return default
524
525     def onchange_rate(self, cr, uid, ids, rate, amount, currency_id, payment_rate_currency_id, company_id, context=None):
526         res =  {'value': {'paid_amount_in_company_currency': amount, 'currency_help_label': self._get_currency_help_label(cr, uid, currency_id, rate, payment_rate_currency_id, context=context)}}
527         if rate and amount and currency_id:
528             company_currency = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id
529             #context should contain the date, the payment currency and the payment rate specified on the voucher
530             amount_in_company_currency = self.pool.get('res.currency').compute(cr, uid, currency_id, company_currency.id, amount, context=context)
531             res['value']['paid_amount_in_company_currency'] = amount_in_company_currency
532         return res
533
534     def onchange_amount(self, cr, uid, ids, amount, rate, partner_id, journal_id, currency_id, ttype, date, payment_rate_currency_id, company_id, context=None):
535         if context is None:
536             context = {}
537         ctx = context.copy()
538         ctx.update({'date': date})
539         #read the voucher rate with the right date in the context
540         currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id
541         voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
542         ctx.update({
543             'voucher_special_currency': payment_rate_currency_id,
544             'voucher_special_currency_rate': rate * voucher_rate})
545         res = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=ctx)
546         vals = self.onchange_rate(cr, uid, ids, rate, amount, currency_id, payment_rate_currency_id, company_id, context=ctx)
547         for key in vals.keys():
548             res[key].update(vals[key])
549         return res
550
551     def recompute_payment_rate(self, cr, uid, ids, vals, currency_id, date, ttype, journal_id, amount, context=None):
552         if context is None:
553             context = {}
554         #on change of the journal, we need to set also the default value for payment_rate and payment_rate_currency_id
555         currency_obj = self.pool.get('res.currency')
556         journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
557         company_id = journal.company_id.id
558         payment_rate = 1.0
559         currency_id = currency_id or journal.company_id.currency_id.id
560         payment_rate_currency_id = currency_id
561         ctx = context.copy()
562         ctx.update({'date': date})
563         o2m_to_loop = False
564         if ttype == 'receipt':
565             o2m_to_loop = 'line_cr_ids'
566         elif ttype == 'payment':
567             o2m_to_loop = 'line_dr_ids'
568         if o2m_to_loop and 'value' in vals and o2m_to_loop in vals['value']:
569             for voucher_line in vals['value'][o2m_to_loop]:
570                 if not isinstance(voucher_line, dict):
571                     continue
572                 if voucher_line['currency_id'] != currency_id:
573                     # we take as default value for the payment_rate_currency_id, the currency of the first invoice that
574                     # is not in the voucher currency
575                     payment_rate_currency_id = voucher_line['currency_id']
576                     tmp = currency_obj.browse(cr, uid, payment_rate_currency_id, context=ctx).rate
577                     payment_rate = tmp / currency_obj.browse(cr, uid, currency_id, context=ctx).rate
578                     break
579         vals['value'].update({
580             'payment_rate': payment_rate,
581             'currency_id': currency_id,
582             'payment_rate_currency_id': payment_rate_currency_id
583         })
584         #read the voucher rate with the right date in the context
585         voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
586         ctx.update({
587             'voucher_special_currency_rate': payment_rate * voucher_rate,
588             'voucher_special_currency': payment_rate_currency_id})
589         res = self.onchange_rate(cr, uid, ids, payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context=ctx)
590         for key in res.keys():
591             vals[key].update(res[key])
592         return vals
593
594     def basic_onchange_partner(self, cr, uid, ids, partner_id, journal_id, ttype, context=None):
595         partner_pool = self.pool.get('res.partner')
596         journal_pool = self.pool.get('account.journal')
597         res = {'value': {'account_id': False}}
598         if not partner_id or not journal_id:
599             return res
600
601         journal = journal_pool.browse(cr, uid, journal_id, context=context)
602         partner = partner_pool.browse(cr, uid, partner_id, context=context)
603         account_id = False
604         if journal.type in ('sale','sale_refund'):
605             account_id = partner.property_account_receivable.id
606         elif journal.type in ('purchase', 'purchase_refund','expense'):
607             account_id = partner.property_account_payable.id
608         else:
609             account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
610
611         res['value']['account_id'] = account_id
612         return res
613
614     def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=None):
615         if not journal_id:
616             return {}
617         if context is None:
618             context = {}
619         #TODO: comment me and use me directly in the sales/purchases views
620         res = self.basic_onchange_partner(cr, uid, ids, partner_id, journal_id, ttype, context=context)
621         if ttype in ['sale', 'purchase']:
622             return res
623         ctx = context.copy()
624         # not passing the payment_rate currency and the payment_rate in the context but it's ok because they are reset in recompute_payment_rate
625         ctx.update({'date': date})
626         vals = self.recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=ctx)
627         vals2 = self.recompute_payment_rate(cr, uid, ids, vals, currency_id, date, ttype, journal_id, amount, context=context)
628         for key in vals.keys():
629             res[key].update(vals[key])
630         for key in vals2.keys():
631             res[key].update(vals2[key])
632         #TODO: can probably be removed now
633         #TODO: onchange_partner_id() should not returns [pre_line, line_dr_ids, payment_rate...] for type sale, and not 
634         # [pre_line, line_cr_ids, payment_rate...] for type purchase.
635         # We should definitively split account.voucher object in two and make distinct on_change functions. In the 
636         # meanwhile, bellow lines must be there because the fields aren't present in the view, what crashes if the 
637         # onchange returns a value for them
638         if ttype == 'sale':
639             del(res['value']['line_dr_ids'])
640             del(res['value']['pre_line'])
641             del(res['value']['payment_rate'])
642         elif ttype == 'purchase':
643             del(res['value']['line_cr_ids'])
644             del(res['value']['pre_line'])
645             del(res['value']['payment_rate'])
646         return res
647
648     def recompute_voucher_lines(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None):
649         """
650         Returns a dict that contains new values and context
651
652         @param partner_id: latest value from user input for field partner_id
653         @param args: other arguments
654         @param context: context arguments, like lang, time zone
655
656         @return: Returns a dict which contains new values, and context
657         """
658         def _remove_noise_in_o2m():
659             """if the line is partially reconciled, then we must pay attention to display it only once and
660                 in the good o2m.
661                 This function returns True if the line is considered as noise and should not be displayed
662             """
663             if line.reconcile_partial_id:
664                 if currency_id == line.currency_id.id:
665                     if line.amount_residual_currency <= 0:
666                         return True
667                 else:
668                     if line.amount_residual <= 0:
669                         return True
670             return False
671
672         if context is None:
673             context = {}
674         context_multi_currency = context.copy()
675
676         currency_pool = self.pool.get('res.currency')
677         move_line_pool = self.pool.get('account.move.line')
678         partner_pool = self.pool.get('res.partner')
679         journal_pool = self.pool.get('account.journal')
680         line_pool = self.pool.get('account.voucher.line')
681
682         #set default values
683         default = {
684             'value': {'line_dr_ids': [], 'line_cr_ids': [], 'pre_line': False},
685         }
686
687         # drop existing lines
688         line_ids = ids and line_pool.search(cr, uid, [('voucher_id', '=', ids[0])])
689         for line in line_pool.browse(cr, uid, line_ids, context=context):
690             if line.type == 'cr':
691                 default['value']['line_cr_ids'].append((2, line.id))
692             else:
693                 default['value']['line_dr_ids'].append((2, line.id))
694
695         if not partner_id or not journal_id:
696             return default
697
698         journal = journal_pool.browse(cr, uid, journal_id, context=context)
699         partner = partner_pool.browse(cr, uid, partner_id, context=context)
700         currency_id = currency_id or journal.company_id.currency_id.id
701
702         total_credit = 0.0
703         total_debit = 0.0
704         account_type = None
705         if context.get('account_id'):
706             account_type = self.pool['account.account'].browse(cr, uid, context['account_id'], context=context).type
707         if ttype == 'payment':
708             if not account_type:
709                 account_type = 'payable'
710             total_debit = price or 0.0
711         else:
712             total_credit = price or 0.0
713             if not account_type:
714                 account_type = 'receivable'
715
716         if not context.get('move_line_ids', False):
717             ids = move_line_pool.search(cr, uid, [('state','=','valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)], context=context)
718         else:
719             ids = context['move_line_ids']
720         invoice_id = context.get('invoice_id', False)
721         company_currency = journal.company_id.currency_id.id
722         move_lines_found = []
723
724         #order the lines by most old first
725         ids.reverse()
726         account_move_lines = move_line_pool.browse(cr, uid, ids, context=context)
727
728         #compute the total debit/credit and look for a matching open amount or invoice
729         for line in account_move_lines:
730             if _remove_noise_in_o2m():
731                 continue
732
733             if invoice_id:
734                 if line.invoice.id == invoice_id:
735                     #if the invoice linked to the voucher line is equal to the invoice_id in context
736                     #then we assign the amount on that line, whatever the other voucher lines
737                     move_lines_found.append(line.id)
738             elif currency_id == company_currency:
739                 #otherwise treatments is the same but with other field names
740                 if line.amount_residual == price:
741                     #if the amount residual is equal the amount voucher, we assign it to that voucher
742                     #line, whatever the other voucher lines
743                     move_lines_found.append(line.id)
744                     break
745                 #otherwise we will split the voucher amount on each line (by most old first)
746                 total_credit += line.credit or 0.0
747                 total_debit += line.debit or 0.0
748             elif currency_id == line.currency_id.id:
749                 if line.amount_residual_currency == price:
750                     move_lines_found.append(line.id)
751                     break
752                 total_credit += line.credit and line.amount_currency or 0.0
753                 total_debit += line.debit and line.amount_currency or 0.0
754
755         remaining_amount = price
756         #voucher line creation
757         for line in account_move_lines:
758
759             if _remove_noise_in_o2m():
760                 continue
761
762             if line.currency_id and currency_id == line.currency_id.id:
763                 amount_original = abs(line.amount_currency)
764                 amount_unreconciled = abs(line.amount_residual_currency)
765             else:
766                 #always use the amount booked in the company currency as the basis of the conversion into the voucher currency
767                 amount_original = currency_pool.compute(cr, uid, company_currency, currency_id, line.credit or line.debit or 0.0, context=context_multi_currency)
768                 amount_unreconciled = currency_pool.compute(cr, uid, company_currency, currency_id, abs(line.amount_residual), context=context_multi_currency)
769             line_currency_id = line.currency_id and line.currency_id.id or company_currency
770             rs = {
771                 'name':line.move_id.name,
772                 'type': line.credit and 'dr' or 'cr',
773                 'move_line_id':line.id,
774                 'account_id':line.account_id.id,
775                 'amount_original': amount_original,
776                 'amount': (line.id in move_lines_found) and min(abs(remaining_amount), amount_unreconciled) or 0.0,
777                 'date_original':line.date,
778                 'date_due':line.date_maturity,
779                 'amount_unreconciled': amount_unreconciled,
780                 'currency_id': line_currency_id,
781             }
782             remaining_amount -= rs['amount']
783             #in case a corresponding move_line hasn't been found, we now try to assign the voucher amount
784             #on existing invoices: we split voucher amount by most old first, but only for lines in the same currency
785             if not move_lines_found:
786                 if currency_id == line_currency_id:
787                     if line.credit:
788                         amount = min(amount_unreconciled, abs(total_debit))
789                         rs['amount'] = amount
790                         total_debit -= amount
791                     else:
792                         amount = min(amount_unreconciled, abs(total_credit))
793                         rs['amount'] = amount
794                         total_credit -= amount
795
796             if rs['amount_unreconciled'] == rs['amount']:
797                 rs['reconcile'] = True
798
799             if rs['type'] == 'cr':
800                 default['value']['line_cr_ids'].append(rs)
801             else:
802                 default['value']['line_dr_ids'].append(rs)
803
804             if len(default['value']['line_cr_ids']) > 0:
805                 default['value']['pre_line'] = 1
806             elif len(default['value']['line_dr_ids']) > 0:
807                 default['value']['pre_line'] = 1
808             default['value']['writeoff_amount'] = self._compute_writeoff_amount(cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price, ttype)
809         return default
810
811     def onchange_payment_rate_currency(self, cr, uid, ids, currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context=None):
812         if context is None:
813             context = {}
814         res = {'value': {}}
815         if currency_id:
816             #set the default payment rate of the voucher and compute the paid amount in company currency
817             ctx = context.copy()
818             ctx.update({'date': date})
819             #read the voucher rate with the right date in the context
820             voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate']
821             ctx.update({
822                 'voucher_special_currency_rate': payment_rate * voucher_rate, 
823                 'voucher_special_currency': payment_rate_currency_id})
824             vals = self.onchange_rate(cr, uid, ids, payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context=ctx)
825             for key in vals.keys():
826                 res[key].update(vals[key])
827         return res
828
829     def onchange_date(self, cr, uid, ids, date, currency_id, payment_rate_currency_id, amount, company_id, context=None):
830         """
831         @param date: latest value from user input for field date
832         @param args: other arguments
833         @param context: context arguments, like lang, time zone
834         @return: Returns a dict which contains new values, and context
835         """
836         if context is None:
837             context ={}
838         res = {'value': {}}
839         #set the period of the voucher
840         period_pool = self.pool.get('account.period')
841         currency_obj = self.pool.get('res.currency')
842         ctx = context.copy()
843         ctx.update({'company_id': company_id, 'account_period_prefer_normal': True})
844         voucher_currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id
845         pids = period_pool.find(cr, uid, date, context=ctx)
846         if pids:
847             res['value'].update({'period_id':pids[0]})
848         if payment_rate_currency_id:
849             ctx.update({'date': date})
850             payment_rate = 1.0
851             if payment_rate_currency_id != currency_id:
852                 tmp = currency_obj.browse(cr, uid, payment_rate_currency_id, context=ctx).rate
853                 payment_rate = tmp / currency_obj.browse(cr, uid, voucher_currency_id, context=ctx).rate
854             vals = self.onchange_payment_rate_currency(cr, uid, ids, voucher_currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context=context)
855             vals['value'].update({'payment_rate': payment_rate})
856             for key in vals.keys():
857                 res[key].update(vals[key])
858         return res
859
860     def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, date, amount, ttype, company_id, context=None):
861         if context is None:
862             context = {}
863         if not journal_id:
864             return False
865         journal_pool = self.pool.get('account.journal')
866         journal = journal_pool.browse(cr, uid, journal_id, context=context)
867         account_id = journal.default_credit_account_id or journal.default_debit_account_id
868         tax_id = False
869         if account_id and account_id.tax_ids:
870             tax_id = account_id.tax_ids[0].id
871
872         vals = {'value':{} }
873         if ttype in ('sale', 'purchase'):
874             vals = self.onchange_price(cr, uid, ids, line_ids, tax_id, partner_id, context)
875             vals['value'].update({'tax_id':tax_id,'amount': amount})
876         currency_id = False
877         if journal.currency:
878             currency_id = journal.currency.id
879         else:
880             currency_id = journal.company_id.currency_id.id
881         vals['value'].update({'currency_id': currency_id})
882         #in case we want to register the payment directly from an invoice, it's confusing to allow to switch the journal 
883         #without seeing that the amount is expressed in the journal currency, and not in the invoice currency. So to avoid
884         #this common mistake, we simply reset the amount to 0 if the currency is not the invoice currency.
885         if context.get('payment_expected_currency') and currency_id != context.get('payment_expected_currency'):
886             vals['value']['amount'] = 0
887             amount = 0
888         if partner_id:
889             res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context)
890             for key in res.keys():
891                 vals[key].update(res[key])
892         return vals
893
894     def button_proforma_voucher(self, cr, uid, ids, context=None):
895         self.signal_workflow(cr, uid, ids, 'proforma_voucher')
896         return {'type': 'ir.actions.act_window_close'}
897
898     def proforma_voucher(self, cr, uid, ids, context=None):
899         self.action_move_line_create(cr, uid, ids, context=context)
900         return True
901
902     def action_cancel_draft(self, cr, uid, ids, context=None):
903         self.create_workflow(cr, uid, ids)
904         self.write(cr, uid, ids, {'state':'draft'})
905         return True
906
907     def cancel_voucher(self, cr, uid, ids, context=None):
908         reconcile_pool = self.pool.get('account.move.reconcile')
909         move_pool = self.pool.get('account.move')
910         move_line_pool = self.pool.get('account.move.line')
911         for voucher in self.browse(cr, uid, ids, context=context):
912             # refresh to make sure you don't unlink an already removed move
913             voucher.refresh()
914             for line in voucher.move_ids:
915                 # refresh to make sure you don't unreconcile an already unreconciled entry
916                 line.refresh()
917                 if line.reconcile_id:
918                     move_lines = [move_line.id for move_line in line.reconcile_id.line_id]
919                     move_lines.remove(line.id)
920                     reconcile_pool.unlink(cr, uid, [line.reconcile_id.id])
921                     if len(move_lines) >= 2:
922                         move_line_pool.reconcile_partial(cr, uid, move_lines, 'auto',context=context)
923             if voucher.move_id:
924                 move_pool.button_cancel(cr, uid, [voucher.move_id.id])
925                 move_pool.unlink(cr, uid, [voucher.move_id.id])
926         res = {
927             'state':'cancel',
928             'move_id':False,
929         }
930         self.write(cr, uid, ids, res)
931         return True
932
933     def unlink(self, cr, uid, ids, context=None):
934         for t in self.read(cr, uid, ids, ['state'], context=context):
935             if t['state'] not in ('draft', 'cancel'):
936                 raise osv.except_osv(_('Invalid Action!'), _('Cannot delete voucher(s) which are already opened or paid.'))
937         return super(account_voucher, self).unlink(cr, uid, ids, context=context)
938
939     def onchange_payment(self, cr, uid, ids, pay_now, journal_id, partner_id, ttype='sale'):
940         res = {}
941         if not partner_id:
942             return res
943         res = {}
944         partner_pool = self.pool.get('res.partner')
945         journal_pool = self.pool.get('account.journal')
946         if pay_now == 'pay_later':
947             partner = partner_pool.browse(cr, uid, partner_id)
948             journal = journal_pool.browse(cr, uid, journal_id)
949             if journal.type in ('sale','sale_refund'):
950                 account_id = partner.property_account_receivable.id
951             elif journal.type in ('purchase', 'purchase_refund','expense'):
952                 account_id = partner.property_account_payable.id
953             else:
954                 account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
955             if account_id:
956                 res['account_id'] = account_id
957         return {'value':res}
958
959     def _sel_context(self, cr, uid, voucher_id, context=None):
960         """
961         Select the context to use accordingly if it needs to be multicurrency or not.
962
963         :param voucher_id: Id of the actual voucher
964         :return: The returned context will be the same as given in parameter if the voucher currency is the same
965                  than the company currency, otherwise it's a copy of the parameter with an extra key 'date' containing
966                  the date of the voucher.
967         :rtype: dict
968         """
969         company_currency = self._get_company_currency(cr, uid, voucher_id, context)
970         current_currency = self._get_current_currency(cr, uid, voucher_id, context)
971         if current_currency <> company_currency:
972             context_multi_currency = context.copy()
973             voucher = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context)
974             context_multi_currency.update({'date': voucher.date})
975             return context_multi_currency
976         return context
977
978     def first_move_line_get(self, cr, uid, voucher_id, move_id, company_currency, current_currency, context=None):
979         '''
980         Return a dict to be use to create the first account move line of given voucher.
981
982         :param voucher_id: Id of voucher what we are creating account_move.
983         :param move_id: Id of account move where this line will be added.
984         :param company_currency: id of currency of the company to which the voucher belong
985         :param current_currency: id of currency of the voucher
986         :return: mapping between fieldname and value of account move line to create
987         :rtype: dict
988         '''
989         voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
990         debit = credit = 0.0
991         # TODO: is there any other alternative then the voucher type ??
992         # ANSWER: We can have payment and receipt "In Advance".
993         # TODO: Make this logic available.
994         # -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
995         if voucher.type in ('purchase', 'payment'):
996             credit = voucher.paid_amount_in_company_currency
997         elif voucher.type in ('sale', 'receipt'):
998             debit = voucher.paid_amount_in_company_currency
999         if debit < 0: credit = -debit; debit = 0.0
1000         if credit < 0: debit = -credit; credit = 0.0
1001         sign = debit - credit < 0 and -1 or 1
1002         #set the first line of the voucher
1003         move_line = {
1004                 'name': voucher.name or '/',
1005                 'debit': debit,
1006                 'credit': credit,
1007                 'account_id': voucher.account_id.id,
1008                 'move_id': move_id,
1009                 'journal_id': voucher.journal_id.id,
1010                 'period_id': voucher.period_id.id,
1011                 'partner_id': voucher.partner_id.id,
1012                 'currency_id': company_currency <> current_currency and  current_currency or False,
1013                 'amount_currency': (sign * abs(voucher.amount) # amount < 0 for refunds
1014                     if company_currency != current_currency else 0.0),
1015                 'date': voucher.date,
1016                 'date_maturity': voucher.date_due
1017             }
1018         return move_line
1019
1020     def account_move_get(self, cr, uid, voucher_id, context=None):
1021         '''
1022         This method prepare the creation of the account move related to the given voucher.
1023
1024         :param voucher_id: Id of voucher for which we are creating account_move.
1025         :return: mapping between fieldname and value of account move to create
1026         :rtype: dict
1027         '''
1028         seq_obj = self.pool.get('ir.sequence')
1029         voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
1030         if voucher.number:
1031             name = voucher.number
1032         elif voucher.journal_id.sequence_id:
1033             if not voucher.journal_id.sequence_id.active:
1034                 raise osv.except_osv(_('Configuration Error !'),
1035                     _('Please activate the sequence of selected journal !'))
1036             c = dict(context)
1037             c.update({'fiscalyear_id': voucher.period_id.fiscalyear_id.id})
1038             name = seq_obj.next_by_id(cr, uid, voucher.journal_id.sequence_id.id, context=c)
1039         else:
1040             raise osv.except_osv(_('Error!'),
1041                         _('Please define a sequence on the journal.'))
1042         if not voucher.reference:
1043             ref = name.replace('/','')
1044         else:
1045             ref = voucher.reference
1046
1047         move = {
1048             'name': name,
1049             'journal_id': voucher.journal_id.id,
1050             'narration': voucher.narration,
1051             'date': voucher.date,
1052             'ref': ref,
1053             'period_id': voucher.period_id.id,
1054         }
1055         return move
1056
1057     def _get_exchange_lines(self, cr, uid, line, move_id, amount_residual, company_currency, current_currency, context=None):
1058         '''
1059         Prepare the two lines in company currency due to currency rate difference.
1060
1061         :param line: browse record of the voucher.line for which we want to create currency rate difference accounting
1062             entries
1063         :param move_id: Account move wher the move lines will be.
1064         :param amount_residual: Amount to be posted.
1065         :param company_currency: id of currency of the company to which the voucher belong
1066         :param current_currency: id of currency of the voucher
1067         :return: the account move line and its counterpart to create, depicted as mapping between fieldname and value
1068         :rtype: tuple of dict
1069         '''
1070         if amount_residual > 0:
1071             account_id = line.voucher_id.company_id.expense_currency_exchange_account_id
1072             if not account_id:
1073                 model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_form')
1074                 msg = _("You should configure the 'Loss Exchange Rate Account' to manage automatically the booking of accounting entries related to differences between exchange rates.")
1075                 raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel'))
1076         else:
1077             account_id = line.voucher_id.company_id.income_currency_exchange_account_id
1078             if not account_id:
1079                 model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_form')
1080                 msg = _("You should configure the 'Gain Exchange Rate Account' to manage automatically the booking of accounting entries related to differences between exchange rates.")
1081                 raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel'))
1082         # Even if the amount_currency is never filled, we need to pass the foreign currency because otherwise
1083         # the receivable/payable account may have a secondary currency, which render this field mandatory
1084         if line.account_id.currency_id:
1085             account_currency_id = line.account_id.currency_id.id
1086         else:
1087             account_currency_id = company_currency <> current_currency and current_currency or False
1088         move_line = {
1089             'journal_id': line.voucher_id.journal_id.id,
1090             'period_id': line.voucher_id.period_id.id,
1091             'name': _('change')+': '+(line.name or '/'),
1092             'account_id': line.account_id.id,
1093             'move_id': move_id,
1094             'partner_id': line.voucher_id.partner_id.id,
1095             'currency_id': account_currency_id,
1096             'amount_currency': 0.0,
1097             'quantity': 1,
1098             'credit': amount_residual > 0 and amount_residual or 0.0,
1099             'debit': amount_residual < 0 and -amount_residual or 0.0,
1100             'date': line.voucher_id.date,
1101         }
1102         move_line_counterpart = {
1103             'journal_id': line.voucher_id.journal_id.id,
1104             'period_id': line.voucher_id.period_id.id,
1105             'name': _('change')+': '+(line.name or '/'),
1106             'account_id': account_id.id,
1107             'move_id': move_id,
1108             'amount_currency': 0.0,
1109             'partner_id': line.voucher_id.partner_id.id,
1110             'currency_id': account_currency_id,
1111             'quantity': 1,
1112             'debit': amount_residual > 0 and amount_residual or 0.0,
1113             'credit': amount_residual < 0 and -amount_residual or 0.0,
1114             'date': line.voucher_id.date,
1115         }
1116         return (move_line, move_line_counterpart)
1117
1118     def _convert_amount(self, cr, uid, amount, voucher_id, context=None):
1119         '''
1120         This function convert the amount given in company currency. It takes either the rate in the voucher (if the
1121         payment_rate_currency_id is relevant) either the rate encoded in the system.
1122
1123         :param amount: float. The amount to convert
1124         :param voucher: id of the voucher on which we want the conversion
1125         :param context: to context to use for the conversion. It may contain the key 'date' set to the voucher date
1126             field in order to select the good rate to use.
1127         :return: the amount in the currency of the voucher's company
1128         :rtype: float
1129         '''
1130         if context is None:
1131             context = {}
1132         currency_obj = self.pool.get('res.currency')
1133         voucher = self.browse(cr, uid, voucher_id, context=context)
1134         return currency_obj.compute(cr, uid, voucher.currency_id.id, voucher.company_id.currency_id.id, amount, context=context)
1135
1136     def voucher_move_line_create(self, cr, uid, voucher_id, line_total, move_id, company_currency, current_currency, context=None):
1137         '''
1138         Create one account move line, on the given account move, per voucher line where amount is not 0.0.
1139         It returns Tuple with tot_line what is total of difference between debit and credit and
1140         a list of lists with ids to be reconciled with this format (total_deb_cred,list_of_lists).
1141
1142         :param voucher_id: Voucher id what we are working with
1143         :param line_total: Amount of the first line, which correspond to the amount we should totally split among all voucher lines.
1144         :param move_id: Account move wher those lines will be joined.
1145         :param company_currency: id of currency of the company to which the voucher belong
1146         :param current_currency: id of currency of the voucher
1147         :return: Tuple build as (remaining amount not allocated on voucher lines, list of account_move_line created in this method)
1148         :rtype: tuple(float, list of int)
1149         '''
1150         if context is None:
1151             context = {}
1152         move_line_obj = self.pool.get('account.move.line')
1153         currency_obj = self.pool.get('res.currency')
1154         tax_obj = self.pool.get('account.tax')
1155         tot_line = line_total
1156         rec_lst_ids = []
1157
1158         date = self.read(cr, uid, [voucher_id], ['date'], context=context)[0]['date']
1159         ctx = context.copy()
1160         ctx.update({'date': date})
1161         voucher = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context=ctx)
1162         voucher_currency = voucher.journal_id.currency or voucher.company_id.currency_id
1163         ctx.update({
1164             'voucher_special_currency_rate': voucher_currency.rate * voucher.payment_rate ,
1165             'voucher_special_currency': voucher.payment_rate_currency_id and voucher.payment_rate_currency_id.id or False,})
1166         prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
1167         for line in voucher.line_ids:
1168             #create one move line per voucher line where amount is not 0.0
1169             # AND (second part of the clause) only if the original move line was not having debit = credit = 0 (which is a legal value)
1170             if not line.amount and not (line.move_line_id and not float_compare(line.move_line_id.debit, line.move_line_id.credit, precision_digits=prec) and not float_compare(line.move_line_id.debit, 0.0, precision_digits=prec)):
1171                 continue
1172             # convert the amount set on the voucher line into the currency of the voucher's company
1173             # this calls res_curreny.compute() with the right context, so that it will take either the rate on the voucher if it is relevant or will use the default behaviour
1174             amount = self._convert_amount(cr, uid, line.untax_amount or line.amount, voucher.id, context=ctx)
1175             # if the amount encoded in voucher is equal to the amount unreconciled, we need to compute the
1176             # currency rate difference
1177             if line.amount == line.amount_unreconciled:
1178                 if not line.move_line_id:
1179                     raise osv.except_osv(_('Wrong voucher line'),_("The invoice you are willing to pay is not valid anymore."))
1180                 sign = line.type =='dr' and -1 or 1
1181                 currency_rate_difference = sign * (line.move_line_id.amount_residual - amount)
1182             else:
1183                 currency_rate_difference = 0.0
1184             move_line = {
1185                 'journal_id': voucher.journal_id.id,
1186                 'period_id': voucher.period_id.id,
1187                 'name': line.name or '/',
1188                 'account_id': line.account_id.id,
1189                 'move_id': move_id,
1190                 'partner_id': voucher.partner_id.id,
1191                 'currency_id': line.move_line_id and (company_currency <> line.move_line_id.currency_id.id and line.move_line_id.currency_id.id) or False,
1192                 'analytic_account_id': line.account_analytic_id and line.account_analytic_id.id or False,
1193                 'quantity': 1,
1194                 'credit': 0.0,
1195                 'debit': 0.0,
1196                 'date': voucher.date
1197             }
1198             if amount < 0:
1199                 amount = -amount
1200                 if line.type == 'dr':
1201                     line.type = 'cr'
1202                 else:
1203                     line.type = 'dr'
1204
1205             if (line.type=='dr'):
1206                 tot_line += amount
1207                 move_line['debit'] = amount
1208             else:
1209                 tot_line -= amount
1210                 move_line['credit'] = amount
1211
1212             if voucher.tax_id and voucher.type in ('sale', 'purchase'):
1213                 move_line.update({
1214                     'account_tax_id': voucher.tax_id.id,
1215                 })
1216
1217             if move_line.get('account_tax_id', False):
1218                 tax_data = tax_obj.browse(cr, uid, [move_line['account_tax_id']], context=context)[0]
1219                 if not (tax_data.base_code_id and tax_data.tax_code_id):
1220                     raise osv.except_osv(_('No Account Base Code and Account Tax Code!'),_("You have to configure account base code and account tax code on the '%s' tax!") % (tax_data.name))
1221
1222             # compute the amount in foreign currency
1223             foreign_currency_diff = 0.0
1224             amount_currency = False
1225             if line.move_line_id:
1226                 # We want to set it on the account move line as soon as the original line had a foreign currency
1227                 if line.move_line_id.currency_id and line.move_line_id.currency_id.id != company_currency:
1228                     # we compute the amount in that foreign currency.
1229                     if line.move_line_id.currency_id.id == current_currency:
1230                         # if the voucher and the voucher line share the same currency, there is no computation to do
1231                         sign = (move_line['debit'] - move_line['credit']) < 0 and -1 or 1
1232                         amount_currency = sign * (line.amount)
1233                     else:
1234                         # if the rate is specified on the voucher, it will be used thanks to the special keys in the context
1235                         # otherwise we use the rates of the system
1236                         amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx)
1237                 if line.amount == line.amount_unreconciled:
1238                     foreign_currency_diff = line.move_line_id.amount_residual_currency - abs(amount_currency)
1239
1240             move_line['amount_currency'] = amount_currency
1241             voucher_line = move_line_obj.create(cr, uid, move_line)
1242             rec_ids = [voucher_line, line.move_line_id.id]
1243
1244             if not currency_obj.is_zero(cr, uid, voucher.company_id.currency_id, currency_rate_difference):
1245                 # Change difference entry in company currency
1246                 exch_lines = self._get_exchange_lines(cr, uid, line, move_id, currency_rate_difference, company_currency, current_currency, context=context)
1247                 new_id = move_line_obj.create(cr, uid, exch_lines[0],context)
1248                 move_line_obj.create(cr, uid, exch_lines[1], context)
1249                 rec_ids.append(new_id)
1250
1251             if line.move_line_id and line.move_line_id.currency_id and not currency_obj.is_zero(cr, uid, line.move_line_id.currency_id, foreign_currency_diff):
1252                 # Change difference entry in voucher currency
1253                 move_line_foreign_currency = {
1254                     'journal_id': line.voucher_id.journal_id.id,
1255                     'period_id': line.voucher_id.period_id.id,
1256                     'name': _('change')+': '+(line.name or '/'),
1257                     'account_id': line.account_id.id,
1258                     'move_id': move_id,
1259                     'partner_id': line.voucher_id.partner_id.id,
1260                     'currency_id': line.move_line_id.currency_id.id,
1261                     'amount_currency': -1 * foreign_currency_diff,
1262                     'quantity': 1,
1263                     'credit': 0.0,
1264                     'debit': 0.0,
1265                     'date': line.voucher_id.date,
1266                 }
1267                 new_id = move_line_obj.create(cr, uid, move_line_foreign_currency, context=context)
1268                 rec_ids.append(new_id)
1269             if line.move_line_id.id:
1270                 rec_lst_ids.append(rec_ids)
1271         return (tot_line, rec_lst_ids)
1272
1273     def writeoff_move_line_get(self, cr, uid, voucher_id, line_total, move_id, name, company_currency, current_currency, context=None):
1274         '''
1275         Set a dict to be use to create the writeoff move line.
1276
1277         :param voucher_id: Id of voucher what we are creating account_move.
1278         :param line_total: Amount remaining to be allocated on lines.
1279         :param move_id: Id of account move where this line will be added.
1280         :param name: Description of account move line.
1281         :param company_currency: id of currency of the company to which the voucher belong
1282         :param current_currency: id of currency of the voucher
1283         :return: mapping between fieldname and value of account move line to create
1284         :rtype: dict
1285         '''
1286         currency_obj = self.pool.get('res.currency')
1287         move_line = {}
1288
1289         voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
1290         current_currency_obj = voucher.currency_id or voucher.journal_id.company_id.currency_id
1291
1292         if not currency_obj.is_zero(cr, uid, current_currency_obj, line_total):
1293             diff = line_total
1294             account_id = False
1295             write_off_name = ''
1296             if voucher.payment_option == 'with_writeoff':
1297                 account_id = voucher.writeoff_acc_id.id
1298                 write_off_name = voucher.comment
1299             elif voucher.partner_id:
1300                 if voucher.type in ('sale', 'receipt'):
1301                     account_id = voucher.partner_id.property_account_receivable.id
1302                 else:
1303                     account_id = voucher.partner_id.property_account_payable.id
1304             else:
1305                 # fallback on account of voucher
1306                 account_id = voucher.account_id.id
1307             sign = voucher.type == 'payment' and -1 or 1
1308             move_line = {
1309                 'name': write_off_name or name,
1310                 'account_id': account_id,
1311                 'move_id': move_id,
1312                 'partner_id': voucher.partner_id.id,
1313                 'date': voucher.date,
1314                 'credit': diff > 0 and diff or 0.0,
1315                 'debit': diff < 0 and -diff or 0.0,
1316                 'amount_currency': company_currency <> current_currency and (sign * -1 * voucher.writeoff_amount) or 0.0,
1317                 'currency_id': company_currency <> current_currency and current_currency or False,
1318                 'analytic_account_id': voucher.analytic_id and voucher.analytic_id.id or False,
1319             }
1320
1321         return move_line
1322
1323     def _get_company_currency(self, cr, uid, voucher_id, context=None):
1324         '''
1325         Get the currency of the actual company.
1326
1327         :param voucher_id: Id of the voucher what i want to obtain company currency.
1328         :return: currency id of the company of the voucher
1329         :rtype: int
1330         '''
1331         return self.pool.get('account.voucher').browse(cr,uid,voucher_id,context).journal_id.company_id.currency_id.id
1332
1333     def _get_current_currency(self, cr, uid, voucher_id, context=None):
1334         '''
1335         Get the currency of the voucher.
1336
1337         :param voucher_id: Id of the voucher what i want to obtain current currency.
1338         :return: currency id of the voucher
1339         :rtype: int
1340         '''
1341         voucher = self.pool.get('account.voucher').browse(cr,uid,voucher_id,context)
1342         return voucher.currency_id.id or self._get_company_currency(cr,uid,voucher.id,context)
1343
1344     def action_move_line_create(self, cr, uid, ids, context=None):
1345         '''
1346         Confirm the vouchers given in ids and create the journal entries for each of them
1347         '''
1348         if context is None:
1349             context = {}
1350         move_pool = self.pool.get('account.move')
1351         move_line_pool = self.pool.get('account.move.line')
1352         for voucher in self.browse(cr, uid, ids, context=context):
1353             local_context = dict(context, force_company=voucher.journal_id.company_id.id)
1354             if voucher.move_id:
1355                 continue
1356             company_currency = self._get_company_currency(cr, uid, voucher.id, context)
1357             current_currency = self._get_current_currency(cr, uid, voucher.id, context)
1358             # we select the context to use accordingly if it's a multicurrency case or not
1359             context = self._sel_context(cr, uid, voucher.id, context)
1360             # But for the operations made by _convert_amount, we always need to give the date in the context
1361             ctx = context.copy()
1362             ctx.update({'date': voucher.date})
1363             # Create the account move record.
1364             move_id = move_pool.create(cr, uid, self.account_move_get(cr, uid, voucher.id, context=context), context=context)
1365             # Get the name of the account_move just created
1366             name = move_pool.browse(cr, uid, move_id, context=context).name
1367             # Create the first line of the voucher
1368             move_line_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, local_context), local_context)
1369             move_line_brw = move_line_pool.browse(cr, uid, move_line_id, context=context)
1370             line_total = move_line_brw.debit - move_line_brw.credit
1371             rec_list_ids = []
1372             if voucher.type == 'sale':
1373                 line_total = line_total - self._convert_amount(cr, uid, voucher.tax_amount, voucher.id, context=ctx)
1374             elif voucher.type == 'purchase':
1375                 line_total = line_total + self._convert_amount(cr, uid, voucher.tax_amount, voucher.id, context=ctx)
1376             # Create one move line per voucher line where amount is not 0.0
1377             line_total, rec_list_ids = self.voucher_move_line_create(cr, uid, voucher.id, line_total, move_id, company_currency, current_currency, context)
1378
1379             # Create the writeoff line if needed
1380             ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, local_context)
1381             if ml_writeoff:
1382                 move_line_pool.create(cr, uid, ml_writeoff, local_context)
1383             # We post the voucher.
1384             self.write(cr, uid, [voucher.id], {
1385                 'move_id': move_id,
1386                 'state': 'posted',
1387                 'number': name,
1388             })
1389             if voucher.journal_id.entry_posted:
1390                 move_pool.post(cr, uid, [move_id], context={})
1391             # We automatically reconcile the account move lines.
1392             reconcile = False
1393             for rec_ids in rec_list_ids:
1394                 if len(rec_ids) >= 2:
1395                     reconcile = move_line_pool.reconcile_partial(cr, uid, rec_ids, writeoff_acc_id=voucher.writeoff_acc_id.id, writeoff_period_id=voucher.period_id.id, writeoff_journal_id=voucher.journal_id.id)
1396         return True
1397
1398 class account_voucher_line(osv.osv):
1399     _name = 'account.voucher.line'
1400     _description = 'Voucher Lines'
1401     _order = "move_line_id"
1402
1403     # If the payment is in the same currency than the invoice, we keep the same amount
1404     # Otherwise, we compute from invoice currency to payment currency
1405     def _compute_balance(self, cr, uid, ids, name, args, context=None):
1406         currency_pool = self.pool.get('res.currency')
1407         rs_data = {}
1408         for line in self.browse(cr, uid, ids, context=context):
1409             ctx = context.copy()
1410             ctx.update({'date': line.voucher_id.date})
1411             voucher_rate = self.pool.get('res.currency').read(cr, uid, line.voucher_id.currency_id.id, ['rate'], context=ctx)['rate']
1412             ctx.update({
1413                 'voucher_special_currency': line.voucher_id.payment_rate_currency_id and line.voucher_id.payment_rate_currency_id.id or False,
1414                 'voucher_special_currency_rate': line.voucher_id.payment_rate * voucher_rate})
1415             res = {}
1416             company_currency = line.voucher_id.journal_id.company_id.currency_id.id
1417             voucher_currency = line.voucher_id.currency_id and line.voucher_id.currency_id.id or company_currency
1418             move_line = line.move_line_id or False
1419
1420             if not move_line:
1421                 res['amount_original'] = 0.0
1422                 res['amount_unreconciled'] = 0.0
1423             elif move_line.currency_id and voucher_currency==move_line.currency_id.id:
1424                 res['amount_original'] = abs(move_line.amount_currency)
1425                 res['amount_unreconciled'] = abs(move_line.amount_residual_currency)
1426             else:
1427                 #always use the amount booked in the company currency as the basis of the conversion into the voucher currency
1428                 res['amount_original'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, move_line.credit or move_line.debit or 0.0, context=ctx)
1429                 res['amount_unreconciled'] = currency_pool.compute(cr, uid, company_currency, voucher_currency, abs(move_line.amount_residual), context=ctx)
1430
1431             rs_data[line.id] = res
1432         return rs_data
1433
1434     def _currency_id(self, cr, uid, ids, name, args, context=None):
1435         '''
1436         This function returns the currency id of a voucher line. It's either the currency of the
1437         associated move line (if any) or the currency of the voucher or the company currency.
1438         '''
1439         res = {}
1440         for line in self.browse(cr, uid, ids, context=context):
1441             move_line = line.move_line_id
1442             if move_line:
1443                 res[line.id] = move_line.currency_id and move_line.currency_id.id or move_line.company_id.currency_id.id
1444             else:
1445                 res[line.id] = line.voucher_id.currency_id and line.voucher_id.currency_id.id or line.voucher_id.company_id.currency_id.id
1446         return res
1447
1448     _columns = {
1449         'voucher_id':fields.many2one('account.voucher', 'Voucher', required=1, ondelete='cascade'),
1450         'name':fields.char('Description',),
1451         'account_id':fields.many2one('account.account','Account', required=True),
1452         'partner_id':fields.related('voucher_id', 'partner_id', type='many2one', relation='res.partner', string='Partner'),
1453         'untax_amount':fields.float('Untax Amount'),
1454         'amount':fields.float('Amount', digits_compute=dp.get_precision('Account')),
1455         'reconcile': fields.boolean('Full Reconcile'),
1456         'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Dr/Cr'),
1457         'account_analytic_id':  fields.many2one('account.analytic.account', 'Analytic Account'),
1458         'move_line_id': fields.many2one('account.move.line', 'Journal Item', copy=False),
1459         'date_original': fields.related('move_line_id','date', type='date', relation='account.move.line', string='Date', readonly=1),
1460         'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=1),
1461         'amount_original': fields.function(_compute_balance, multi='dc', type='float', string='Original Amount', store=True, digits_compute=dp.get_precision('Account')),
1462         'amount_unreconciled': fields.function(_compute_balance, multi='dc', type='float', string='Open Balance', store=True, digits_compute=dp.get_precision('Account')),
1463         'company_id': fields.related('voucher_id','company_id', relation='res.company', type='many2one', string='Company', store=True, readonly=True),
1464         'currency_id': fields.function(_currency_id, string='Currency', type='many2one', relation='res.currency', readonly=True),
1465     }
1466     _defaults = {
1467         'name': '',
1468     }
1469
1470     def onchange_reconcile(self, cr, uid, ids, reconcile, amount, amount_unreconciled, context=None):
1471         vals = {'amount': 0.0}
1472         if reconcile:
1473             vals = { 'amount': amount_unreconciled}
1474         return {'value': vals}
1475
1476     def onchange_amount(self, cr, uid, ids, amount, amount_unreconciled, context=None):
1477         vals = {}
1478         if amount:
1479             vals['reconcile'] = (amount == amount_unreconciled)
1480         return {'value': vals}
1481
1482     def onchange_move_line_id(self, cr, user, ids, move_line_id, context=None):
1483         """
1484         Returns a dict that contains new values and context
1485
1486         @param move_line_id: latest value from user input for field move_line_id
1487         @param args: other arguments
1488         @param context: context arguments, like lang, time zone
1489
1490         @return: Returns a dict which contains new values, and context
1491         """
1492         res = {}
1493         move_line_pool = self.pool.get('account.move.line')
1494         if move_line_id:
1495             move_line = move_line_pool.browse(cr, user, move_line_id, context=context)
1496             if move_line.credit:
1497                 ttype = 'dr'
1498             else:
1499                 ttype = 'cr'
1500             res.update({
1501                 'account_id': move_line.account_id.id,
1502                 'type': ttype,
1503                 'currency_id': move_line.currency_id and move_line.currency_id.id or move_line.company_id.currency_id.id,
1504             })
1505         return {
1506             'value':res,
1507         }
1508
1509     def default_get(self, cr, user, fields_list, context=None):
1510         """
1511         Returns default values for fields
1512         @param fields_list: list of fields, for which default values are required to be read
1513         @param context: context arguments, like lang, time zone
1514
1515         @return: Returns a dict that contains default values for fields
1516         """
1517         if context is None:
1518             context = {}
1519         journal_id = context.get('journal_id', False)
1520         partner_id = context.get('partner_id', False)
1521         journal_pool = self.pool.get('account.journal')
1522         partner_pool = self.pool.get('res.partner')
1523         values = super(account_voucher_line, self).default_get(cr, user, fields_list, context=context)
1524         if (not journal_id) or ('account_id' not in fields_list):
1525             return values
1526         journal = journal_pool.browse(cr, user, journal_id, context=context)
1527         account_id = False
1528         ttype = 'cr'
1529         if journal.type in ('sale', 'sale_refund'):
1530             account_id = journal.default_credit_account_id and journal.default_credit_account_id.id or False
1531             ttype = 'cr'
1532         elif journal.type in ('purchase', 'expense', 'purchase_refund'):
1533             account_id = journal.default_debit_account_id and journal.default_debit_account_id.id or False
1534             ttype = 'dr'
1535         elif partner_id:
1536             partner = partner_pool.browse(cr, user, partner_id, context=context)
1537             if context.get('type') == 'payment':
1538                 ttype = 'dr'
1539                 account_id = partner.property_account_payable.id
1540             elif context.get('type') == 'receipt':
1541                 account_id = partner.property_account_receivable.id
1542
1543         values.update({
1544             'account_id':account_id,
1545             'type':ttype
1546         })
1547         return values
1548
1549 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: