~willowit-openerp-team/willowit-openerp-addons/development

« back to all changes in this revision

Viewing changes to account/invoice.py

  • Committer: Deepak Seshadri
  • Date: 2011-04-04 07:04:07 UTC
  • Revision ID: deepak@willowit.com.au-20110404070407-8j9mnxzzgh53o24t
Remove irrelevant modules from this branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
import netsvc
24
 
from osv import fields, osv
25
 
import ir
26
 
import pooler
27
 
import mx.DateTime
28
 
from mx.DateTime import RelativeDateTime
29
 
from tools import config
30
 
from tools.translate import _
31
 
 
32
 
#class fiscalyear_seq(osv.osv):
33
 
#    _name = "fiscalyear.seq"
34
 
#    _description = "Maintains Invoice sequences with Fiscal Year"
35
 
#    _rec_name = 'fiscalyear_id'
36
 
#    _columns = {
37
 
#        'journal_id': fields.many2one('account.journal', 'Journal'),
38
 
#        'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year',required=True),
39
 
#        'sequence_id':fields.many2one('ir.sequence', 'Sequence',required=True),
40
 
#    }
41
 
#
42
 
#fiscalyear_seq()
43
 
 
44
 
class account_invoice(osv.osv):
45
 
    def _amount_all(self, cr, uid, ids, name, args, context=None):
46
 
        res = {}
47
 
        for invoice in self.browse(cr,uid,ids, context=context):
48
 
            res[invoice.id] = {
49
 
                'amount_untaxed': 0.0,
50
 
                'amount_tax': 0.0,
51
 
                'amount_total': 0.0
52
 
            }
53
 
            for line in invoice.invoice_line:
54
 
                res[invoice.id]['amount_untaxed'] += line.price_subtotal
55
 
            for line in invoice.tax_line:
56
 
                res[invoice.id]['amount_tax'] += line.amount
57
 
            res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed']
58
 
        return res
59
 
 
60
 
    def _get_journal(self, cr, uid, context):
61
 
        if context is None:
62
 
            context = {}
63
 
        type_inv = context.get('type', 'out_invoice')
64
 
        user = self.pool.get('res.users').browse(cr, uid, uid)
65
 
        company_id = context.get('company_id', user.company_id.id)
66
 
        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
67
 
        journal_obj = self.pool.get('account.journal')
68
 
        res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')),
69
 
                                            ('company_id', '=', company_id)],
70
 
                                                limit=1)
71
 
        if res:
72
 
            return res[0]
73
 
        else:
74
 
            return False
75
 
 
76
 
    def _get_currency(self, cr, uid, context):
77
 
        user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
78
 
        if user.company_id:
79
 
            return user.company_id.currency_id.id
80
 
        else:
81
 
            return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
82
 
 
83
 
    def _get_journal_analytic(self, cr, uid, type_inv, context=None):
84
 
        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
85
 
        tt = type2journal.get(type_inv, 'sale')
86
 
        result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
87
 
        if not result:
88
 
            raise osv.except_osv(_('No Analytic Journal !'),_("You must define an analytic journal of type '%s' !") % (tt,))
89
 
        return result[0]
90
 
 
91
 
    def _get_type(self, cr, uid, context=None):
92
 
        if context is None:
93
 
            context = {}
94
 
        type = context.get('type', 'out_invoice')
95
 
        return type
96
 
 
97
 
    def _reconciled(self, cr, uid, ids, name, args, context):
98
 
        res = {}
99
 
        for id in ids:
100
 
            res[id] = self.test_paid(cr, uid, [id])
101
 
        return res
102
 
 
103
 
    def _get_reference_type(self, cr, uid, context=None):
104
 
        return [('none', _('Free Reference'))]
105
 
 
106
 
    def _amount_residual(self, cr, uid, ids, name, args, context=None):
107
 
        res = {}
108
 
        data_inv = self.browse(cr, uid, ids)
109
 
        cur_obj = self.pool.get('res.currency')
110
 
        for inv in data_inv:
111
 
            debit = credit = 0.0
112
 
            context.update({'date':inv.date_invoice})
113
 
            context_unreconciled=context.copy()
114
 
            for lines in inv.move_lines:
115
 
                debit_tmp = lines.debit
116
 
                credit_tmp = lines.credit
117
 
                # If currency conversion needed
118
 
                if inv.company_id.currency_id.id <> inv.currency_id.id:
119
 
                    # If invoice paid, compute currency amount according to invoice date
120
 
                    # otherwise, take the line date
121
 
                    if not inv.reconciled:
122
 
                        context.update({'date':lines.date})
123
 
                    context_unreconciled.update({'date':lines.date})
124
 
                    # If amount currency setted, compute for debit and credit in company currency
125
 
                    if lines.amount_currency < 0:
126
 
                        credit_tmp=abs(cur_obj.compute(cr, uid, lines.currency_id.id, inv.company_id.currency_id.id, lines.amount_currency, round=False,context=context_unreconciled))
127
 
                    elif lines.amount_currency > 0:
128
 
                        debit_tmp=abs(cur_obj.compute(cr, uid, lines.currency_id.id, inv.company_id.currency_id.id, lines.amount_currency, round=False,context=context_unreconciled))
129
 
                    # Then, recomput into invoice currency to avoid rounding trouble !
130
 
                    debit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, debit_tmp, round=False,context=context)
131
 
                    credit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, credit_tmp, round=False,context=context)
132
 
                else:
133
 
                    debit+=debit_tmp
134
 
                    credit+=credit_tmp
135
 
 
136
 
            if not inv.amount_total:
137
 
                result = 0.0
138
 
            elif inv.type in ('out_invoice','in_refund'):
139
 
                amount = credit-debit
140
 
                result = inv.amount_total - amount
141
 
            else:
142
 
                amount = debit-credit
143
 
                result = inv.amount_total - amount
144
 
            # Use is_zero function to avoid rounding trouble => should be fixed into ORM
145
 
            res[inv.id] = not self.pool.get('res.currency').is_zero(cr, uid, inv.company_id.currency_id,result) and result or 0.0
146
 
 
147
 
        return res
148
 
 
149
 
    def _get_lines(self, cr, uid, ids, name, arg, context=None):
150
 
        res = {}
151
 
        for id in ids:
152
 
            move_lines = self.move_line_id_payment_get(cr,uid,[id])
153
 
            if not move_lines:
154
 
                res[id] = []
155
 
                continue
156
 
            res[id] = []
157
 
            data_lines = self.pool.get('account.move.line').browse(cr,uid,move_lines)
158
 
            partial_ids = []# Keeps the track of ids where partial payments are done with payment terms
159
 
            for line in data_lines:
160
 
                ids_line = []
161
 
                if line.reconcile_id:
162
 
                    ids_line = line.reconcile_id.line_id
163
 
                elif line.reconcile_partial_id:
164
 
                    ids_line = line.reconcile_partial_id.line_partial_ids
165
 
                l = map(lambda x: x.id, ids_line)
166
 
                partial_ids.append(line.id)
167
 
                res[id] =[x for x in l if x <> line.id and x not in partial_ids]
168
 
        return res
169
 
 
170
 
    def _get_invoice_line(self, cr, uid, ids, context=None):
171
 
        result = {}
172
 
        for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
173
 
            result[line.invoice_id.id] = True
174
 
        return result.keys()
175
 
 
176
 
    def _get_invoice_tax(self, cr, uid, ids, context=None):
177
 
        result = {}
178
 
        for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context):
179
 
            result[tax.invoice_id.id] = True
180
 
        return result.keys()
181
 
 
182
 
    def _compute_lines(self, cr, uid, ids, name, args, context=None):
183
 
        result = {}
184
 
        for invoice in self.browse(cr, uid, ids, context):
185
 
            moves = self.move_line_id_payment_get(cr, uid, [invoice.id])
186
 
            src = []
187
 
            lines = []
188
 
            for m in self.pool.get('account.move.line').browse(cr, uid, moves, context):
189
 
                temp_lines = []#Added temp list to avoid duplicate records
190
 
                if m.reconcile_id:
191
 
                    temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
192
 
                elif m.reconcile_partial_id:
193
 
                    temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
194
 
                lines += [x for x in temp_lines if x not in lines]
195
 
                src.append(m.id)
196
 
                
197
 
            lines = filter(lambda x: x not in src, lines)
198
 
            result[invoice.id] = lines
199
 
        return result
200
 
 
201
 
    def _get_invoice_from_line(self, cr, uid, ids, context={}):
202
 
        move = {}
203
 
        for line in self.pool.get('account.move.line').browse(cr, uid, ids):
204
 
            if line.reconcile_partial_id:
205
 
                for line2 in line.reconcile_partial_id.line_partial_ids:
206
 
                    move[line2.move_id.id] = True
207
 
            if line.reconcile_id:
208
 
                for line2 in line.reconcile_id.line_id:
209
 
                    move[line2.move_id.id] = True
210
 
        invoice_ids = []
211
 
        if move:
212
 
            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
213
 
        return invoice_ids
214
 
 
215
 
    def _get_invoice_from_reconcile(self, cr, uid, ids, context={}):
216
 
        move = {}
217
 
        for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
218
 
            for line in r.line_partial_ids:
219
 
                move[line.move_id.id] = True
220
 
            for line in r.line_id:
221
 
                move[line.move_id.id] = True
222
 
 
223
 
        invoice_ids = []
224
 
        if move:
225
 
            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
226
 
        return invoice_ids
227
 
 
228
 
    _name = "account.invoice"
229
 
    _description = 'Invoice'
230
 
    _order = "number"
231
 
    _columns = {
232
 
        'name': fields.char('Description', size=64, select=True,readonly=True, states={'draft':[('readonly',False)]}),
233
 
        'origin': fields.char('Origin', size=64, help="Reference of the document that produced this invoice."),
234
 
        'type': fields.selection([
235
 
            ('out_invoice','Customer Invoice'),
236
 
            ('in_invoice','Supplier Invoice'),
237
 
            ('out_refund','Customer Refund'),
238
 
            ('in_refund','Supplier Refund'),
239
 
            ],'Type', readonly=True, select=True, change_default=True),
240
 
 
241
 
        'number': fields.char('Invoice Number', size=32, readonly=True, help="Unique number of the invoice, computed automatically when the invoice is created."),
242
 
        'reference': fields.char('Invoice Reference', size=64, help="The partner reference of this invoice."),
243
 
        'reference_type': fields.selection(_get_reference_type, 'Reference Type',
244
 
            required=True),
245
 
        'comment': fields.text('Additional Information', translate=True),
246
 
 
247
 
        'state': fields.selection([
248
 
            ('draft','Draft'),
249
 
            ('proforma','Pro-forma'),
250
 
            ('proforma2','Pro-forma'),
251
 
            ('open','Open'),
252
 
            ('paid','Done'),
253
 
            ('cancel','Cancelled')
254
 
            ],'State', select=True, readonly=True,
255
 
            help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed Invoice. \
256
 
            \n* The \'Pro-forma\' when invoice is in Pro-forma state,invoice does not have an invoice number. \
257
 
            \n* The \'Open\' state is used when user create invoice,a invoice number is generated.Its in open state till user does not pay invoice. \
258
 
            \n* The \'Done\' state is set automatically when invoice is paid.\
259
 
            \n* The \'Cancelled\' state is used when user cancel invoice.'),
260
 
        'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}, help="Keep empty to use the current date"),
261
 
        'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]},
262
 
            help="If you use payment terms, the due date will be computed automatically at the generation "\
263
 
                "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. The payment term may compute several due dates, for example 50% now, 50% in one month."),
264
 
        'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}),
265
 
        'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}),
266
 
        'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}),
267
 
        'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]},
268
 
            help="If you use payment terms, the due date will be computed automatically at the generation "\
269
 
                "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\
270
 
                "The payment term may compute several due dates, for example 50% now, 50% in one month."),
271
 
        'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}),
272
 
 
273
 
        'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
274
 
        'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
275
 
        'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
276
 
 
277
 
        'move_id': fields.many2one('account.move', 'Invoice Movement', readonly=True, help="Links to the automatically generated Ledger Postings."),
278
 
        'amount_untaxed': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])),string='Untaxed',
279
 
            store={
280
 
                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
281
 
                'account.invoice.tax': (_get_invoice_tax, None, 20),
282
 
                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
283
 
            },
284
 
            multi='all'),
285
 
        'amount_tax': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Tax',
286
 
            store={
287
 
                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
288
 
                'account.invoice.tax': (_get_invoice_tax, None, 20),
289
 
                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
290
 
            },
291
 
            multi='all'),
292
 
        'amount_total': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Total',
293
 
            store={
294
 
                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
295
 
                'account.invoice.tax': (_get_invoice_tax, None, 20),
296
 
                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
297
 
            },
298
 
            multi='all'),
299
 
        'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
300
 
        'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}),
301
 
        'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True),
302
 
        'check_total': fields.float('Total', digits=(16, int(config['price_accuracy'])), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
303
 
        'reconciled': fields.function(_reconciled, method=True, string='Paid/Reconciled', type='boolean',
304
 
            store={
305
 
                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
306
 
                'account.move.line': (_get_invoice_from_line, None, 50),
307
 
                'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
308
 
            }, help="The Ledger Postings of the invoice have been reconciled with Ledger Postings of the payment(s)."),
309
 
        'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',
310
 
            help='The bank account to pay to or to be paid from'),
311
 
        'move_lines':fields.function(_get_lines , method=True,type='many2many' , relation='account.move.line',string='Entry Lines'),
312
 
        'residual': fields.function(_amount_residual, method=True, digits=(16, int(config['price_accuracy'])),string='Residual',
313
 
            store={
314
 
                'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50),
315
 
                'account.invoice.tax': (_get_invoice_tax, None, 50),
316
 
                'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50),
317
 
                'account.move.line': (_get_invoice_from_line, None, 50),
318
 
                'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
319
 
            },
320
 
            help="Remaining amount due."),
321
 
        'payment_ids': fields.function(_compute_lines, method=True, relation='account.move.line', type="many2many", string='Payments'),
322
 
        'move_name': fields.char('Ledger Posting', size=64),
323
 
        'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position')
324
 
    }
325
 
    _defaults = {
326
 
        'type': _get_type,
327
 
        #'date_invoice': lambda *a: time.strftime('%Y-%m-%d'),
328
 
        'state': lambda *a: 'draft',
329
 
        'journal_id': _get_journal,
330
 
        'currency_id': _get_currency,
331
 
        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.invoice', context=c),
332
 
        'reference_type': lambda *a: 'none',
333
 
        'check_total': lambda *a: 0.0,
334
 
    }
335
 
 
336
 
    def unlink(self, cr, uid, ids, context=None):
337
 
        invoices = self.read(cr, uid, ids, ['state'])
338
 
        unlink_ids = []
339
 
        for t in invoices:
340
 
            if t['state'] in ('draft', 'cancel'):
341
 
                unlink_ids.append(t['id'])
342
 
            else:
343
 
                raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
344
 
        osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
345
 
        return True
346
 
 
347
 
#   def get_invoice_address(self, cr, uid, ids):
348
 
#       res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
349
 
#       return [{}]
350
 
    def onchange_partner_id(self, cr, uid, ids, type, partner_id,
351
 
            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
352
 
        invoice_addr_id = False
353
 
        contact_addr_id = False
354
 
        partner_payment_term = False
355
 
        acc_id = False
356
 
        bank_id = False
357
 
        fiscal_position = False
358
 
 
359
 
        opt = [('uid', str(uid))]
360
 
        if partner_id:
361
 
 
362
 
            opt.insert(0, ('id', partner_id))
363
 
            res = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['contact', 'invoice'])
364
 
            contact_addr_id = res['contact']
365
 
            invoice_addr_id = res['invoice']
366
 
            p = self.pool.get('res.partner').browse(cr, uid, partner_id)
367
 
            if company_id:
368
 
                if p.property_account_receivable.company_id.id != company_id and p.property_account_payable.company_id.id != company_id:
369
 
                    rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)])
370
 
                    pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)])
371
 
                    if not rec_pro_id:
372
 
                        rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id)])
373
 
                    if not pay_pro_id:
374
 
                        pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id)])
375
 
                    rec_line_data = self.pool.get('ir.property').read(cr,uid,rec_pro_id,['name','value','res_id'])
376
 
                    pay_line_data = self.pool.get('ir.property').read(cr,uid,pay_pro_id,['name','value','res_id'])
377
 
                    rec_res_id = rec_line_data and int(rec_line_data[0]['value'].split(',')[1]) or False
378
 
                    pay_res_id = pay_line_data and int(pay_line_data[0]['value'].split(',')[1]) or False
379
 
                    if not rec_res_id and not pay_res_id:
380
 
                        raise osv.except_osv(_('Configration Error !'),
381
 
                            _('Can not find account chart for this company, Please Create account.'))
382
 
                    rec_obj_acc=self.pool.get('account.account').browse(cr,uid,[rec_res_id])
383
 
                    pay_obj_acc=self.pool.get('account.account').browse(cr,uid,[pay_res_id])
384
 
                    p.property_account_receivable = rec_obj_acc[0]
385
 
                    p.property_account_payable = pay_obj_acc[0]
386
 
 
387
 
            if type in ('out_invoice', 'out_refund'):
388
 
                acc_id = p.property_account_receivable.id
389
 
            else:
390
 
                acc_id = p.property_account_payable.id
391
 
            fiscal_position = p.property_account_position and p.property_account_position.id or False
392
 
            partner_payment_term = p.property_payment_term and p.property_payment_term.id or False
393
 
            if p.bank_ids:
394
 
                bank_id = p.bank_ids[0].id
395
 
 
396
 
        result = {'value': {
397
 
            'address_contact_id': contact_addr_id,
398
 
            'address_invoice_id': invoice_addr_id,
399
 
            'account_id': acc_id,
400
 
            'payment_term': partner_payment_term,
401
 
            'fiscal_position': fiscal_position
402
 
            }
403
 
        }
404
 
 
405
 
        if type in ('in_invoice', 'in_refund'):
406
 
            result['value']['partner_bank'] = bank_id
407
 
 
408
 
        if payment_term != partner_payment_term:
409
 
            if partner_payment_term:
410
 
                to_update = self.onchange_payment_term_date_invoice(
411
 
                    cr,uid,ids,partner_payment_term,date_invoice)
412
 
                result['value'].update(to_update['value'])
413
 
            else:
414
 
                result['value']['date_due'] = False
415
 
 
416
 
        if partner_bank_id != bank_id:
417
 
            to_update = self.onchange_partner_bank(cr, uid, ids, bank_id)
418
 
            result['value'].update(to_update['value'])
419
 
        return result
420
 
 
421
 
    def onchange_currency_id(self, cr, uid, ids, curr_id, company_id):
422
 
        if curr_id:
423
 
            currency = self.pool.get('res.currency').browse(cr, uid, curr_id)
424
 
            if currency.company_id.id != company_id:
425
 
                raise osv.except_osv(_('Configration Error !'),
426
 
                        _('Can not select currency that is not related to current company.\nPlease select accordingly !.'))
427
 
        return {}
428
 
 
429
 
    def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice):
430
 
        if not payment_term_id:
431
 
            return {}
432
 
        res={}
433
 
        pt_obj= self.pool.get('account.payment.term')
434
 
        if not date_invoice :
435
 
            date_invoice = time.strftime('%Y-%m-%d')
436
 
 
437
 
        pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
438
 
 
439
 
        if pterm_list:
440
 
            pterm_list = [line[0] for line in pterm_list]
441
 
            pterm_list.sort()
442
 
            res= {'value':{'date_due': pterm_list[-1]}}
443
 
        else:
444
 
             raise osv.except_osv(_('Data Insufficient !'), _('The Payment Term of Supplier does not have Payment Term Lines(Computation) defined !'))
445
 
 
446
 
        return res
447
 
 
448
 
    def onchange_invoice_line(self, cr, uid, ids, lines):
449
 
        return {}
450
 
 
451
 
    def onchange_partner_bank(self, cursor, user, ids, partner_bank_id):
452
 
        return {'value': {}}
453
 
 
454
 
    def onchange_company_id(self, cr, uid, ids, company_id, part_id, type, invoice_line):
455
 
        val={}
456
 
        dom={}
457
 
        if company_id and part_id and type:
458
 
            acc_id = False
459
 
            partner_obj = self.pool.get('res.partner').browse(cr,uid,part_id)
460
 
            if partner_obj.property_account_payable and partner_obj.property_account_receivable:
461
 
                if partner_obj.property_account_payable.company_id.id != company_id and partner_obj.property_account_receivable.company_id.id != company_id:
462
 
                    rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)])
463
 
                    pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)])
464
 
                    if not rec_pro_id:
465
 
                        rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id)])
466
 
                    if not pay_pro_id:
467
 
                        pay_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id)])
468
 
                    rec_line_data = self.pool.get('ir.property').read(cr,uid,rec_pro_id,['name','value','res_id'])
469
 
                    pay_line_data = self.pool.get('ir.property').read(cr,uid,pay_pro_id,['name','value','res_id'])
470
 
                    rec_res_id = rec_line_data and int(rec_line_data[0]['value'].split(',')[1]) or False
471
 
                    pay_res_id = pay_line_data and int(pay_line_data[0]['value'].split(',')[1]) or False
472
 
                    if not rec_res_id and not rec_res_id:
473
 
                        raise osv.except_osv(_('Configration Error !'),
474
 
                            _('Can not find account chart for this company, Please Create account.'))
475
 
                    if type in ('out_invoice', 'out_refund'):
476
 
                        acc_id = rec_res_id
477
 
                    else:
478
 
                        acc_id = pay_res_id
479
 
                    val= {'account_id': acc_id}
480
 
            if ids:
481
 
                if company_id:
482
 
                    inv_obj = self.browse(cr,uid,ids)
483
 
                    for line in inv_obj[0].invoice_line:
484
 
                        if line.account_id:
485
 
                            if line.account_id.company_id.id != company_id:
486
 
                                result_id = self.pool.get('account.account').search(cr,uid,[('name','=',line.account_id.name),('company_id','=',company_id)])
487
 
                                if not result_id:
488
 
                                    raise osv.except_osv(_('Configration Error !'),
489
 
                                        _('Can not find account chart for this company in invoice line account, Please Create account.'))
490
 
                                r_id = self.pool.get('account.invoice.line').write(cr,uid,[line.id],{'account_id': result_id[0]})
491
 
            else:
492
 
                if invoice_line:
493
 
                    for inv_line in invoice_line:
494
 
                        obj_l = self.pool.get('account.account').browse(cr,uid,inv_line[2]['account_id'])
495
 
                        if obj_l.company_id.id != company_id:
496
 
                            raise osv.except_osv(_('Configration Error !'),
497
 
                                _('invoice line account company is not match with invoice company.'))
498
 
                        else:
499
 
                            continue
500
 
        if company_id:
501
 
            val['journal_id']=False
502
 
            journal_ids=self.pool.get('account.journal').search(cr,uid,[('company_id','=',company_id)])
503
 
            dom={'journal_id':  [('id','in',journal_ids)]}
504
 
        else:
505
 
            journal_ids=self.pool.get('account.journal').search(cr,uid,[])
506
 
            dom={'journal_id':  [('id','in',journal_ids)]}
507
 
        return {'value' : val, 'domain': dom }
508
 
 
509
 
    # go from canceled state to draft state
510
 
    def action_cancel_draft(self, cr, uid, ids, *args):
511
 
        self.write(cr, uid, ids, {'state':'draft'})
512
 
        wf_service = netsvc.LocalService("workflow")
513
 
        for inv_id in ids:
514
 
            wf_service.trg_create(uid, 'account.invoice', inv_id, cr)
515
 
        return True
516
 
 
517
 
    # Workflow stuff
518
 
    #################
519
 
 
520
 
    # return the ids of the move lines which has the same account than the invoice
521
 
    # whose id is in ids
522
 
    def move_line_id_payment_get(self, cr, uid, ids, *args):
523
 
        res = []
524
 
        if not ids: return res
525
 
        cr.execute('select \
526
 
                l.id \
527
 
            from account_move_line l \
528
 
                left join account_invoice i on (i.move_id=l.move_id) \
529
 
            where i.id in ('+','.join(map(str,map(int, ids)))+') and l.account_id=i.account_id')
530
 
        res = map(lambda x: x[0], cr.fetchall())
531
 
        return res
532
 
 
533
 
    def copy(self, cr, uid, id, default=None, context=None):
534
 
        if default is None:
535
 
            default = {}
536
 
        default = default.copy()
537
 
        default.update({'state':'draft', 'number':False, 'move_id':False, 'move_name':False,})
538
 
        if 'date_invoice' not in default:
539
 
            default['date_invoice'] = False
540
 
        if 'date_due' not in default:
541
 
            default['date_due'] = False
542
 
        return super(account_invoice, self).copy(cr, uid, id, default, context)
543
 
 
544
 
    def test_paid(self, cr, uid, ids, *args):
545
 
        res = self.move_line_id_payment_get(cr, uid, ids)
546
 
        if not res:
547
 
            return False
548
 
        ok = True
549
 
        for id in res:
550
 
            cr.execute('select reconcile_id from account_move_line where id=%s', (id,))
551
 
            ok = ok and  bool(cr.fetchone()[0])
552
 
        return ok
553
 
 
554
 
    def button_reset_taxes(self, cr, uid, ids, context=None):
555
 
        if not context:
556
 
            context = {}
557
 
        ait_obj = self.pool.get('account.invoice.tax')
558
 
        for id in ids:
559
 
            cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s", (id,))
560
 
            partner = self.browse(cr, uid, id,context=context).partner_id
561
 
            if partner.lang:
562
 
                context.update({'lang': partner.lang})
563
 
            for taxe in ait_obj.compute(cr, uid, id, context=context).values():
564
 
                ait_obj.create(cr, uid, taxe)
565
 
         # Update the stored value (fields.function), so we write to trigger recompute
566
 
        self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=context)
567
 
#        self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
568
 
        return True
569
 
 
570
 
    def button_compute(self, cr, uid, ids, context=None, set_total=False):
571
 
        self.button_reset_taxes(cr, uid, ids, context)
572
 
        for inv in self.browse(cr, uid, ids):
573
 
            if set_total:
574
 
                self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total})
575
 
        return True
576
 
 
577
 
    def _convert_ref(self, cr, uid, ref):
578
 
        return (ref or '').replace('/','')
579
 
 
580
 
    def _get_analytic_lines(self, cr, uid, id):
581
 
        inv = self.browse(cr, uid, [id])[0]
582
 
        cur_obj = self.pool.get('res.currency')
583
 
 
584
 
        company_currency = inv.company_id.currency_id.id
585
 
        if inv.type in ('out_invoice', 'in_refund'):
586
 
            sign = 1
587
 
        else:
588
 
            sign = -1
589
 
 
590
 
        iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id)
591
 
        for il in iml:
592
 
            if il['account_analytic_id']:
593
 
                if inv.type in ('in_invoice', 'in_refund'):
594
 
                    ref = inv.reference
595
 
                else:
596
 
                    ref = self._convert_ref(cr, uid, inv.number)
597
 
                il['analytic_lines'] = [(0,0, {
598
 
                    'name': il['name'],
599
 
                    'date': inv['date_invoice'],
600
 
                    'account_id': il['account_analytic_id'],
601
 
                    'unit_amount': il['quantity'],
602
 
                    'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign,
603
 
                    'product_id': il['product_id'],
604
 
                    'product_uom_id': il['uos_id'],
605
 
                    'general_account_id': il['account_id'],
606
 
                    'journal_id': self._get_journal_analytic(cr, uid, inv.type),
607
 
                    'ref': ref,
608
 
                })]
609
 
        return iml
610
 
 
611
 
    def action_date_assign(self, cr, uid, ids, *args):
612
 
        for inv in self.browse(cr, uid, ids):
613
 
            res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice)
614
 
            if res and res['value']:
615
 
                self.write(cr, uid, [inv.id], res['value'])
616
 
        return True
617
 
 
618
 
    def action_move_create(self, cr, uid, ids, *args):
619
 
        ait_obj = self.pool.get('account.invoice.tax')
620
 
        cur_obj = self.pool.get('res.currency')
621
 
        context = {}
622
 
        for inv in self.browse(cr, uid, ids):
623
 
            if inv.move_id:
624
 
                continue
625
 
 
626
 
            if not inv.date_invoice:
627
 
                self.write(cr, uid, [inv.id], {'date_invoice':time.strftime('%Y-%m-%d')})
628
 
            company_currency = inv.company_id.currency_id.id
629
 
            # create the analytical lines
630
 
            line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line']
631
 
            # one move line per invoice line
632
 
            iml = self._get_analytic_lines(cr, uid, inv.id)
633
 
            # check if taxes are all computed
634
 
 
635
 
            context.update({'lang': inv.partner_id.lang})
636
 
            compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
637
 
            if not inv.tax_line:
638
 
                for tax in compute_taxes.values():
639
 
                    ait_obj.create(cr, uid, tax)
640
 
            else:
641
 
                tax_key = []
642
 
                for tax in inv.tax_line:
643
 
                    if tax.manual:
644
 
                        continue
645
 
                    key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
646
 
                    tax_key.append(key)
647
 
                    if not key in compute_taxes:
648
 
                        raise osv.except_osv(_('Warning !'), _('Global taxes defined, but are not in invoice lines !'))
649
 
                    base = compute_taxes[key]['base']
650
 
                    if abs(base - tax.base) > inv.company_id.currency_id.rounding:
651
 
                        raise osv.except_osv(_('Warning !'), _('Tax base different !\nClick on compute to update tax base'))
652
 
                for key in compute_taxes:
653
 
                    if not key in tax_key:
654
 
                        raise osv.except_osv(_('Warning !'), _('Taxes missing !'))
655
 
 
656
 
            if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
657
 
                raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.'))
658
 
 
659
 
            # one move line per tax line
660
 
            iml += ait_obj.move_line_get(cr, uid, inv.id)
661
 
 
662
 
            if inv.type in ('in_invoice', 'in_refund'):
663
 
                ref = inv.reference
664
 
            else:
665
 
                ref = self._convert_ref(cr, uid, inv.number)
666
 
 
667
 
            diff_currency_p = inv.currency_id.id <> company_currency
668
 
            # create one move line for the total and possibly adjust the other lines amount
669
 
            total = 0
670
 
            total_currency = 0
671
 
            for i in iml:
672
 
                if inv.currency_id.id != company_currency:
673
 
                    i['currency_id'] = inv.currency_id.id
674
 
                    i['amount_currency'] = i['price']
675
 
                    i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
676
 
                            company_currency, i['price'],
677
 
                            context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')})
678
 
                else:
679
 
                    i['amount_currency'] = False
680
 
                    i['currency_id'] = False
681
 
                i['ref'] = ref
682
 
                if inv.type in ('out_invoice','in_refund'):
683
 
                    total += i['price']
684
 
                    total_currency += i['amount_currency'] or i['price']
685
 
                    i['price'] = - i['price']
686
 
                else:
687
 
                    total -= i['price']
688
 
                    total_currency -= i['amount_currency'] or i['price']
689
 
            acc_id = inv.account_id.id
690
 
 
691
 
            name = inv['name'] or '/'
692
 
            totlines = False
693
 
            if inv.payment_term:
694
 
                totlines = self.pool.get('account.payment.term').compute(cr,
695
 
                        uid, inv.payment_term.id, total, inv.date_invoice or False)
696
 
            if totlines:
697
 
                res_amount_currency = total_currency
698
 
                i = 0
699
 
                for t in totlines:
700
 
                    if inv.currency_id.id != company_currency:
701
 
                        amount_currency = cur_obj.compute(cr, uid,
702
 
                                company_currency, inv.currency_id.id, t[1])
703
 
                    else:
704
 
                        amount_currency = False
705
 
 
706
 
                    # last line add the diff
707
 
                    res_amount_currency -= amount_currency or 0
708
 
                    i += 1
709
 
                    if i == len(totlines):
710
 
                        amount_currency += res_amount_currency
711
 
 
712
 
                    iml.append({
713
 
                        'type': 'dest',
714
 
                        'name': name,
715
 
                        'price': t[1],
716
 
                        'account_id': acc_id,
717
 
                        'date_maturity': t[0],
718
 
                        'amount_currency': diff_currency_p \
719
 
                                and  amount_currency or False,
720
 
                        'currency_id': diff_currency_p \
721
 
                                and inv.currency_id.id or False,
722
 
                        'ref': ref,
723
 
                    })
724
 
            else:
725
 
                iml.append({
726
 
                    'type': 'dest',
727
 
                    'name': name,
728
 
                    'price': total,
729
 
                    'account_id': acc_id,
730
 
                    'date_maturity' : inv.date_due or False,
731
 
                    'amount_currency': diff_currency_p \
732
 
                            and total_currency or False,
733
 
                    'currency_id': diff_currency_p \
734
 
                            and inv.currency_id.id or False,
735
 
                    'ref': ref
736
 
            })
737
 
 
738
 
            date = inv.date_invoice or time.strftime('%Y-%m-%d')
739
 
            part = inv.partner_id.id
740
 
 
741
 
            line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml)
742
 
 
743
 
            if inv.journal_id.group_invoice_lines:
744
 
                line2 = {}
745
 
                for x, y, l in line:
746
 
                    tmp = str(l['account_id'])
747
 
                    tmp += '-'+str(l.get('tax_code_id',"False"))
748
 
                    tmp += '-'+str(l.get('product_id',"False"))
749
 
                    tmp += '-'+str(l.get('analytic_account_id',"False"))
750
 
                    tmp += '-'+str(l.get('date_maturity',"False"))
751
 
 
752
 
                    if tmp in line2:
753
 
                        am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit'])
754
 
                        line2[tmp]['debit'] = (am > 0) and am or 0.0
755
 
                        line2[tmp]['credit'] = (am < 0) and -am or 0.0
756
 
                        line2[tmp]['tax_amount'] += l['tax_amount']
757
 
                        line2[tmp]['analytic_lines'] += l['analytic_lines']
758
 
                    else:
759
 
                        line2[tmp] = l
760
 
                line = []
761
 
                for key, val in line2.items():
762
 
                    line.append((0,0,val))
763
 
 
764
 
            journal_id = inv.journal_id.id #self._get_journal(cr, uid, {'type': inv['type']})
765
 
            journal = self.pool.get('account.journal').browse(cr, uid, journal_id)
766
 
            if journal.centralisation:
767
 
                raise osv.except_osv(_('UserError'),
768
 
                        _('Cannot create invoice move on centralised journal'))
769
 
            move = {'ref': inv.number, 'line_id': line, 'journal_id': journal_id, 'date': date}
770
 
            period_id=inv.period_id and inv.period_id.id or False
771
 
            if not period_id:
772
 
                period_ids= self.pool.get('account.period').search(cr,uid,[('date_start','<=',inv.date_invoice or time.strftime('%Y-%m-%d')),('date_stop','>=',inv.date_invoice or time.strftime('%Y-%m-%d'))])
773
 
                if len(period_ids):
774
 
                    period_id=period_ids[0]
775
 
            if period_id:
776
 
                move['period_id'] = period_id
777
 
                for i in line:
778
 
                    i[2]['period_id'] = period_id
779
 
 
780
 
            move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
781
 
            new_move_name = self.pool.get('account.move').browse(cr, uid, move_id).name
782
 
            # make the invoice point to that move
783
 
            self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name})
784
 
            self.pool.get('account.move').post(cr, uid, [move_id])
785
 
        self._log_event(cr, uid, ids)
786
 
        return True
787
 
 
788
 
    def line_get_convert(self, cr, uid, x, part, date, context=None):
789
 
        return {
790
 
            'date_maturity': x.get('date_maturity', False),
791
 
            'partner_id':part,
792
 
            'name':x['name'][:64],
793
 
            'date': date,
794
 
            'debit':x['price']>0 and x['price'],
795
 
            'credit':x['price']<0 and -x['price'],
796
 
            'account_id':x['account_id'],
797
 
            'analytic_lines':x.get('analytic_lines', []),
798
 
            'amount_currency':x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)),
799
 
            'currency_id':x.get('currency_id', False),
800
 
            'tax_code_id': x.get('tax_code_id', False),
801
 
            'tax_amount': x.get('tax_amount', False),
802
 
            'ref':x.get('ref',False),
803
 
            'quantity':x.get('quantity',1.00),
804
 
            'product_id':x.get('product_id', False),
805
 
            'product_uom_id':x.get('uos_id',False),
806
 
            'analytic_account_id':x.get('account_analytic_id',False),
807
 
        }
808
 
 
809
 
    def action_number(self, cr, uid, ids, *args):
810
 
        cr.execute('SELECT id, type, number, move_id, reference ' \
811
 
                'FROM account_invoice ' \
812
 
                'WHERE id IN ('+','.join(map(str, ids))+')')
813
 
        obj_inv = self.browse(cr, uid, ids)[0]
814
 
        for (id, invtype, number, move_id, reference) in cr.fetchall():
815
 
            if not number:
816
 
                if obj_inv.journal_id.invoice_sequence_id:
817
 
                    sid = obj_inv.journal_id.invoice_sequence_id.id
818
 
                    number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', {'fiscalyear_id': obj_inv.period_id.fiscalyear_id.id})
819
 
                else:
820
 
                    number = self.pool.get('ir.sequence').get(cr, uid,
821
 
                            'account.invoice.' + invtype)
822
 
                if invtype in ('in_invoice', 'in_refund'):
823
 
                    ref = reference
824
 
                else:
825
 
                    ref = self._convert_ref(cr, uid, number)
826
 
                cr.execute('UPDATE account_invoice SET number=%s ' \
827
 
                        'WHERE id=%s', (number, id))
828
 
                cr.execute('UPDATE account_move SET ref=%s ' \
829
 
                        'WHERE id=%s AND (ref is null OR ref = \'\')',
830
 
                        (ref, move_id))
831
 
                cr.execute('UPDATE account_move_line SET ref=%s ' \
832
 
                        'WHERE move_id=%s AND (ref is null OR ref = \'\')',
833
 
                        (ref, move_id))
834
 
                cr.execute('UPDATE account_analytic_line SET ref=%s ' \
835
 
                        'FROM account_move_line ' \
836
 
                        'WHERE account_move_line.move_id = %s ' \
837
 
                            'AND account_analytic_line.move_id = account_move_line.id',
838
 
                            (ref, move_id))
839
 
        return True
840
 
 
841
 
    def action_cancel(self, cr, uid, ids, *args):
842
 
        account_move_obj = self.pool.get('account.move')
843
 
        invoices = self.read(cr, uid, ids, ['move_id'])
844
 
        for i in invoices:
845
 
            if i['move_id']:
846
 
                account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
847
 
                # delete the move this invoice was pointing to
848
 
                # Note that the corresponding move_lines and move_reconciles
849
 
                # will be automatically deleted too
850
 
                account_move_obj.unlink(cr, uid, [i['move_id'][0]])
851
 
        self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
852
 
        self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
853
 
        return True
854
 
 
855
 
    ###################
856
 
 
857
 
    def list_distinct_taxes(self, cr, uid, ids):
858
 
        invoices = self.browse(cr, uid, ids)
859
 
        taxes = {}
860
 
        for inv in invoices:
861
 
            for tax in inv.tax_line:
862
 
                if not tax['name'] in taxes:
863
 
                    taxes[tax['name']] = {'name': tax['name']}
864
 
        return taxes.values()
865
 
 
866
 
    def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'):
867
 
        invs = self.read(cr, uid, ids, ['type','partner_id','amount_untaxed'])
868
 
        for inv in invs:
869
 
            part=inv['partner_id'] and inv['partner_id'][0]
870
 
            pc = pr = 0.0
871
 
            cr.execute('select sum(quantity*price_unit) from account_invoice_line where invoice_id=%s', (inv['id'],))
872
 
            total = inv['amount_untaxed']
873
 
            if inv['type'] in ('in_invoice','in_refund'):
874
 
                partnertype='supplier'
875
 
                eventtype = 'purchase'
876
 
                pc = total*factor
877
 
            else:
878
 
                partnertype = 'customer'
879
 
                eventtype = 'sale'
880
 
                pr = total*factor
881
 
            if self.pool.get('res.partner.event.type').check(cr, uid, 'invoice_open'):
882
 
                self.pool.get('res.partner.event').create(cr, uid, {'name':'Invoice: '+name, 'som':False, 'description':name+' '+str(inv['id']), 'document':name, 'partner_id':part, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'canal_id':False, 'user_id':uid, 'partner_type':partnertype, 'probability':1.0, 'planned_revenue':pr, 'planned_cost':pc, 'type':eventtype})
883
 
        return len(invs)
884
 
 
885
 
    def name_get(self, cr, uid, ids, context=None):
886
 
        if not len(ids):
887
 
            return []
888
 
        types = {
889
 
                'out_invoice': 'CI: ',
890
 
                'in_invoice': 'SI: ',
891
 
                'out_refund': 'OR: ',
892
 
                'in_refund': 'SR: ',
893
 
                }
894
 
        return [(r['id'], types[r['type']]+(r['number'] or '')+' '+(r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
895
 
 
896
 
    def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
897
 
        if not args:
898
 
            args=[]
899
 
        if context is None:
900
 
            context={}
901
 
        ids = []
902
 
        if name:
903
 
            ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
904
 
        if not ids:
905
 
            ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
906
 
        return self.name_get(cr, user, ids, context)
907
 
 
908
 
    def _refund_cleanup_lines(self, cr, uid, lines):
909
 
        for line in lines:
910
 
            del line['id']
911
 
            del line['invoice_id']
912
 
            if 'account_id' in line:
913
 
                line['account_id'] = line.get('account_id', False) and line['account_id'][0]
914
 
            if 'product_id' in line:
915
 
                line['product_id'] = line.get('product_id', False) and line['product_id'][0]
916
 
            if 'uos_id' in line:
917
 
                line['uos_id'] = line.get('uos_id', False) and line['uos_id'][0]
918
 
            if 'invoice_line_tax_id' in line:
919
 
                line['invoice_line_tax_id'] = [(6,0, line.get('invoice_line_tax_id', [])) ]
920
 
            if 'account_analytic_id' in line:
921
 
                line['account_analytic_id'] = line.get('account_analytic_id', False) and line['account_analytic_id'][0]
922
 
            if 'tax_code_id' in line :
923
 
                if isinstance(line['tax_code_id'],tuple)  and len(line['tax_code_id']) >0 :
924
 
                    line['tax_code_id'] = line['tax_code_id'][0]
925
 
            if 'base_code_id' in line :
926
 
                if isinstance(line['base_code_id'],tuple)  and len(line['base_code_id']) >0 :
927
 
                    line['base_code_id'] = line['base_code_id'][0]
928
 
        return map(lambda x: (0,0,x), lines)
929
 
 
930
 
    def refund(self, cr, uid, ids, date=None, period_id=None, description=None):
931
 
        invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'address_contact_id', 'address_invoice_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id'])
932
 
 
933
 
        new_ids = []
934
 
        for invoice in invoices:
935
 
            del invoice['id']
936
 
 
937
 
            type_dict = {
938
 
                'out_invoice': 'out_refund', # Customer Invoice
939
 
                'in_invoice': 'in_refund',   # Supplier Invoice
940
 
                'out_refund': 'out_invoice', # Customer Refund
941
 
                'in_refund': 'in_invoice',   # Supplier Refund
942
 
            }
943
 
 
944
 
 
945
 
            invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
946
 
            invoice_lines = self._refund_cleanup_lines(cr, uid, invoice_lines)
947
 
 
948
 
            tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line'])
949
 
            tax_lines = filter(lambda l: l['manual'], tax_lines)
950
 
            tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines)
951
 
            if not date :
952
 
                date = time.strftime('%Y-%m-%d')
953
 
            invoice.update({
954
 
                'type': type_dict[invoice['type']],
955
 
                'date_invoice': date,
956
 
                'state': 'draft',
957
 
                'number': False,
958
 
                'invoice_line': invoice_lines,
959
 
                'tax_line': tax_lines
960
 
            })
961
 
            if period_id :
962
 
                invoice.update({
963
 
                    'period_id': period_id,
964
 
                })
965
 
            if description :
966
 
                invoice.update({
967
 
                    'name': description,
968
 
                })
969
 
            # take the id part of the tuple returned for many2one fields
970
 
            for field in ('address_contact_id', 'address_invoice_id', 'partner_id',
971
 
                    'account_id', 'currency_id', 'payment_term', 'journal_id'):
972
 
                invoice[field] = invoice[field] and invoice[field][0]
973
 
            # create the new invoice
974
 
            new_ids.append(self.create(cr, uid, invoice))
975
 
        return new_ids
976
 
 
977
 
    def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
978
 
        if context is None:
979
 
            context = {}
980
 
        #TODO check if we can use different period for payment and the writeoff line
981
 
        assert len(ids)==1, "Can only pay one invoice at a time"
982
 
        invoice = self.browse(cr, uid, ids[0])
983
 
        src_account_id = invoice.account_id.id
984
 
        # Take the seq as name for move
985
 
        types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
986
 
        direction = types[invoice.type]
987
 
        #take the choosen date
988
 
        if 'date_p' in context and context['date_p']:
989
 
            date=context['date_p']
990
 
        else:
991
 
            date=time.strftime('%Y-%m-%d')
992
 
 
993
 
        # Take the amount in currency and the currency of the payment
994
 
        if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
995
 
            amount_currency = context['amount_currency']
996
 
            currency_id = context['currency_id']
997
 
        else:
998
 
            amount_currency = False
999
 
            currency_id = False
1000
 
        if invoice.type in ('in_invoice', 'in_refund'):
1001
 
            ref = invoice.reference
1002
 
        else:
1003
 
            ref = self._convert_ref(cr, uid, invoice.number)        
1004
 
        # Pay attention to the sign for both debit/credit AND amount_currency
1005
 
        l1 = {
1006
 
            'debit': direction * pay_amount>0 and direction * pay_amount,
1007
 
            'credit': direction * pay_amount<0 and - direction * pay_amount,
1008
 
            'account_id': src_account_id,
1009
 
            'partner_id': invoice.partner_id.id,
1010
 
            'ref':ref,
1011
 
            'date': date,
1012
 
            'currency_id':currency_id,
1013
 
            'amount_currency':amount_currency and direction * amount_currency or 0.0,
1014
 
            'company_id': invoice.company_id.id,
1015
 
        }
1016
 
        l2 = {
1017
 
            'debit': direction * pay_amount<0 and - direction * pay_amount,
1018
 
            'credit': direction * pay_amount>0 and direction * pay_amount,
1019
 
            'account_id': pay_account_id,
1020
 
            'partner_id': invoice.partner_id.id,
1021
 
            'ref':ref,
1022
 
            'date': date,
1023
 
            'currency_id':currency_id,
1024
 
            'amount_currency':amount_currency and - direction * amount_currency or 0.0,
1025
 
            'company_id': invoice.company_id.id,
1026
 
        }
1027
 
 
1028
 
        if not name:
1029
 
            name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
1030
 
        l1['name'] = name
1031
 
        l2['name'] = name
1032
 
 
1033
 
        lines = [(0, 0, l1), (0, 0, l2)]
1034
 
        move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
1035
 
        move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
1036
 
 
1037
 
        line_ids = []
1038
 
        total = 0.0
1039
 
        line = self.pool.get('account.move.line')
1040
 
        move_ids = [move_id,]
1041
 
        if invoice.move_id:
1042
 
            move_ids.append(invoice.move_id.id)
1043
 
        cr.execute('SELECT id FROM account_move_line WHERE move_id = ANY(%s)',(move_ids,))
1044
 
        lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
1045
 
        for l in lines+invoice.payment_ids:
1046
 
            if l.account_id.id==src_account_id:
1047
 
                line_ids.append(l.id)
1048
 
                total += (l.debit or 0.0) - (l.credit or 0.0)
1049
 
        if (not round(total,int(config['price_accuracy']))) or writeoff_acc_id:
1050
 
            self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
1051
 
        else:
1052
 
            self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
1053
 
 
1054
 
        # Update the stored value (fields.function), so we write to trigger recompute
1055
 
        self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
1056
 
        return True
1057
 
account_invoice()
1058
 
 
1059
 
class account_invoice_line(osv.osv):
1060
 
    def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
1061
 
        res = {}
1062
 
        cur_obj=self.pool.get('res.currency')
1063
 
        for line in self.browse(cr, uid, ids):
1064
 
            if line.invoice_id:
1065
 
                res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
1066
 
                cur = line.invoice_id.currency_id
1067
 
                res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
1068
 
            else:
1069
 
                res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),int(config['price_accuracy']))
1070
 
        return res
1071
 
 
1072
 
 
1073
 
    def _price_unit_default(self, cr, uid, context=None):
1074
 
        if context is None:
1075
 
            context = {}
1076
 
        if 'check_total' in context:
1077
 
            t = context['check_total']
1078
 
            for l in context.get('invoice_line', {}):
1079
 
                if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
1080
 
                    tax_obj = self.pool.get('account.tax')
1081
 
                    p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
1082
 
                    t = t - (p * l[2].get('quantity'))
1083
 
                    taxes = l[2].get('invoice_line_tax_id')
1084
 
                    if len(taxes[0]) >= 3 and taxes[0][2]:
1085
 
                        taxes=tax_obj.browse(cr, uid, taxes[0][2])
1086
 
                        for tax in tax_obj.compute(cr, uid, taxes, p,l[2].get('quantity'), context.get('address_invoice_id', False), l[2].get('product_id', False), context.get('partner_id', False)):
1087
 
                            t = t - tax['amount']
1088
 
            return t
1089
 
        return 0
1090
 
 
1091
 
    _name = "account.invoice.line"
1092
 
    _description = "Invoice line"
1093
 
    _columns = {
1094
 
        'name': fields.char('Description', size=256, required=True),
1095
 
        'origin': fields.char('Origin', size=256, help="Reference of the document that produced this invoice."),
1096
 
        'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True),
1097
 
        'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
1098
 
        'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
1099
 
        'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
1100
 
        'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
1101
 
        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True, type="float", digits=(16, int(config['price_accuracy']))),
1102
 
        'quantity': fields.float('Quantity', required=True),
1103
 
        'discount': fields.float('Discount (%)', digits=(16, int(config['price_accuracy']))),
1104
 
        'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
1105
 
        'note': fields.text('Notes', translate=True),
1106
 
        'account_analytic_id':  fields.many2one('account.analytic.account', 'Analytic Account'),
1107
 
        'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company',store=True)
1108
 
    }
1109
 
    _defaults = {
1110
 
        'quantity': lambda *a: 1,
1111
 
        'discount': lambda *a: 0.0,
1112
 
        'price_unit': _price_unit_default,
1113
 
    }
1114
 
 
1115
 
    def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=None):
1116
 
        tax_obj = self.pool.get('account.tax')
1117
 
        if price_unit:
1118
 
            taxes = tax_obj.browse(cr, uid, tax_id)
1119
 
            for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id):
1120
 
                price_unit = price_unit - tax['amount']
1121
 
        return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
1122
 
 
1123
 
    def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, address_invoice_id=False, currency_id=False, context=None):
1124
 
        if context is None:
1125
 
            context = {}
1126
 
        company_id = context.get('company_id',False)
1127
 
        if not partner_id:
1128
 
            raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
1129
 
        if not product:
1130
 
            if type in ('in_invoice', 'in_refund'):
1131
 
                return {'value': {'categ_id': False}, 'domain':{'product_uom':[]}}
1132
 
            else:
1133
 
                return {'value': {'price_unit': 0.0, 'categ_id': False}, 'domain':{'product_uom':[]}}
1134
 
        part = self.pool.get('res.partner').browse(cr, uid, partner_id)
1135
 
        fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
1136
 
 
1137
 
        lang=part.lang
1138
 
        context.update({'lang': lang})
1139
 
        result = {}
1140
 
        res = self.pool.get('product.product').browse(cr, uid, product, context=context)
1141
 
 
1142
 
        if company_id:
1143
 
            in_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_income'),('res_id','=','product.template,'+str(res.product_tmpl_id.id)+''),('company_id','=',company_id)])
1144
 
            if not in_pro_id:
1145
 
                in_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_income_categ'),('res_id','=','product.template,'+str(res.categ_id.id)+''),('company_id','=',company_id)])
1146
 
            exp_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_expense'),('res_id','=','product.template,'+str(res.product_tmpl_id.id)+''),('company_id','=',company_id)])
1147
 
            if not exp_pro_id:
1148
 
                exp_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_expense_categ'),('res_id','=','product.template,'+str(res.categ_id.id)+''),('company_id','=',company_id)])
1149
 
 
1150
 
            if not in_pro_id:
1151
 
                in_acc = res.product_tmpl_id.property_account_income
1152
 
                in_acc_cate = res.categ_id.property_account_income_categ
1153
 
                if in_acc:
1154
 
                    app_acc_in = in_acc
1155
 
                else:
1156
 
                    app_acc_in = in_acc_cate
1157
 
            else:
1158
 
                app_acc_in = self.pool.get('account.account').browse(cr,uid,in_pro_id)[0]
1159
 
            if not exp_pro_id:
1160
 
                ex_acc = res.product_tmpl_id.property_account_expense
1161
 
                ex_acc_cate = res.categ_id.property_account_expense_categ
1162
 
                if ex_acc:
1163
 
                    app_acc_exp = ex_acc
1164
 
                else:
1165
 
                    app_acc_exp = ex_acc_cate
1166
 
            else:
1167
 
                app_acc_exp = self.pool.get('account.account').browse(cr,uid,exp_pro_id)[0]
1168
 
            if not in_pro_id and not exp_pro_id:
1169
 
                in_acc = res.product_tmpl_id.property_account_income
1170
 
                in_acc_cate = res.categ_id.property_account_income_categ
1171
 
                ex_acc = res.product_tmpl_id.property_account_expense
1172
 
                ex_acc_cate = res.categ_id.property_account_expense_categ
1173
 
                if in_acc or ex_acc:
1174
 
                    app_acc_in = in_acc
1175
 
                    app_acc_exp = ex_acc
1176
 
                else:
1177
 
                    app_acc_in = in_acc_cate
1178
 
                    app_acc_exp = ex_acc_cate
1179
 
#            else:
1180
 
#                app_acc_in = self.pool.get('account.account').browse(cr,uid,in_pro_id)[0]
1181
 
#                app_acc_exp = self.pool.get('account.account').browse(cr,uid,exp_pro_id)[0]
1182
 
            if app_acc_in.company_id.id != company_id and app_acc_exp.company_id.id != company_id:
1183
 
                in_res_id=self.pool.get('account.account').search(cr,uid,[('name','=',app_acc_in.name),('company_id','=',company_id)])
1184
 
                exp_res_id=self.pool.get('account.account').search(cr,uid,[('name','=',app_acc_exp.name),('company_id','=',company_id)])
1185
 
                if not in_res_id and not exp_res_id:
1186
 
                    raise osv.except_osv(_('Configration Error !'),
1187
 
                        _('Can not find account chart for this company, Please Create account.'))
1188
 
                in_obj_acc=self.pool.get('account.account').browse(cr,uid,in_res_id)
1189
 
                exp_obj_acc=self.pool.get('account.account').browse(cr,uid,exp_res_id)
1190
 
                if in_acc or ex_acc:
1191
 
                    res.product_tmpl_id.property_account_income = in_obj_acc[0]
1192
 
                    res.product_tmpl_id.property_account_expense = exp_obj_acc[0]
1193
 
                else:
1194
 
                    res.categ_id.property_account_income_categ = in_obj_acc[0]
1195
 
                    res.categ_id.property_account_expense_categ = exp_obj_acc[0]
1196
 
 
1197
 
        if type in ('out_invoice','out_refund'):
1198
 
            a =  res.product_tmpl_id.property_account_income.id
1199
 
            if not a:
1200
 
                a = res.categ_id.property_account_income_categ.id
1201
 
        else:
1202
 
            a =  res.product_tmpl_id.property_account_expense.id
1203
 
            if not a:
1204
 
                a = res.categ_id.property_account_expense_categ.id
1205
 
 
1206
 
        a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
1207
 
        if a:
1208
 
            result['account_id'] = a
1209
 
 
1210
 
        taxep=None
1211
 
        tax_obj = self.pool.get('account.tax')
1212
 
        if type in ('out_invoice', 'out_refund'):
1213
 
            taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False)
1214
 
            tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1215
 
        else:
1216
 
            taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid,a).tax_ids or False)
1217
 
            tax_id = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1218
 
        if type in ('in_invoice', 'in_refund'):
1219
 
            to_update = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context)
1220
 
            result.update(to_update)
1221
 
        else:
1222
 
            result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
1223
 
 
1224
 
        if not name:
1225
 
            result['name'] = res.name
1226
 
 
1227
 
        domain = {}
1228
 
        result['uos_id'] = uom or res.uom_id.id or False
1229
 
        if result['uos_id']:
1230
 
            res2 = res.uom_id.category_id.id
1231
 
            if res2 :
1232
 
                domain = {'uos_id':[('category_id','=',res2 )]}
1233
 
 
1234
 
        prod_pool=self.pool.get('product.product')
1235
 
        result['categ_id'] = res.categ_id.id
1236
 
        res_final = {'value':result, 'domain':domain}
1237
 
 
1238
 
        if not company_id and not currency_id:
1239
 
            return res_final
1240
 
 
1241
 
        company = self.pool.get('res.company').browse(cr, uid, company_id)
1242
 
        currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
1243
 
        
1244
 
        if not currency.company_id.id == company.id:
1245
 
            raise osv.except_osv(_('Configration Error !'),
1246
 
                        _('Can not select currency that is not related to any company.\nPlease select accordingly !.'))
1247
 
        
1248
 
        if company.currency_id.id != currency.id:
1249
 
            new_price = res_final['value']['price_unit'] * currency.rate
1250
 
            res_final['value']['price_unit'] = new_price
1251
 
        return res_final
1252
 
 
1253
 
    def move_line_get(self, cr, uid, invoice_id, context=None):
1254
 
        res = []
1255
 
        tax_grouped = {}
1256
 
        tax_obj = self.pool.get('account.tax')
1257
 
        cur_obj = self.pool.get('res.currency')
1258
 
        ait_obj = self.pool.get('account.invoice.tax')
1259
 
        inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
1260
 
        company_currency = inv.company_id.currency_id.id
1261
 
        cur = inv.currency_id
1262
 
 
1263
 
        for line in inv.invoice_line:
1264
 
            mres = self.move_line_get_item(cr, uid, line, context)
1265
 
            if not mres:
1266
 
                continue
1267
 
            res.append(mres)
1268
 
            tax_code_found= False
1269
 
            for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id,
1270
 
                    (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)),
1271
 
                    line.quantity, inv.address_invoice_id.id, line.product_id,
1272
 
                    inv.partner_id):
1273
 
 
1274
 
                if inv.type in ('out_invoice', 'in_invoice'):
1275
 
                    tax_code_id = tax['base_code_id']
1276
 
                    tax_amount = line.price_subtotal * tax['base_sign']
1277
 
                else:
1278
 
                    tax_code_id = tax['ref_base_code_id']
1279
 
                    tax_amount = line.price_subtotal * tax['ref_base_sign']
1280
 
 
1281
 
                if tax_code_found:
1282
 
                    if not tax_code_id:
1283
 
                        continue
1284
 
                    res.append(self.move_line_get_item(cr, uid, line, context))
1285
 
                    res[-1]['price'] = 0.0
1286
 
                    res[-1]['account_analytic_id'] = False
1287
 
                elif not tax_code_id:
1288
 
                    continue
1289
 
                tax_code_found = True
1290
 
 
1291
 
                res[-1]['tax_code_id'] = tax_code_id
1292
 
                res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice})
1293
 
        return res
1294
 
 
1295
 
    def move_line_get_item(self, cr, uid, line, context=None):
1296
 
        return {
1297
 
            'type':'src',
1298
 
            'name': line.name[:64],
1299
 
            'price_unit':line.price_unit,
1300
 
            'quantity':line.quantity,
1301
 
            'price':line.price_subtotal,
1302
 
            'account_id':line.account_id.id,
1303
 
            'product_id':line.product_id.id,
1304
 
            'uos_id':line.uos_id.id,
1305
 
            'account_analytic_id':line.account_analytic_id.id,
1306
 
            'taxes':line.invoice_line_tax_id,
1307
 
        }
1308
 
    #
1309
 
    # Set the tax field according to the account and the fiscal position
1310
 
    #
1311
 
    def onchange_account_id(self, cr, uid, ids, fposition_id, account_id):
1312
 
        if not account_id:
1313
 
            return {}
1314
 
        taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids
1315
 
        fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
1316
 
        res = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
1317
 
        r = {'value':{'invoice_line_tax_id': res}}
1318
 
        return r
1319
 
account_invoice_line()
1320
 
 
1321
 
class account_invoice_tax(osv.osv):
1322
 
    _name = "account.invoice.tax"
1323
 
    _description = "Invoice Tax"
1324
 
    _columns = {
1325
 
        'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
1326
 
        'name': fields.char('Tax Description', size=64, required=True),
1327
 
        'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
1328
 
        'base': fields.float('Base', digits=(16,int(config['price_accuracy']))),
1329
 
        'amount': fields.float('Amount', digits=(16,int(config['price_accuracy']))),
1330
 
        'manual': fields.boolean('Manual'),
1331
 
        'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of invoice tax."),
1332
 
 
1333
 
        'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."),
1334
 
        'base_amount': fields.float('Base Code Amount', digits=(16,int(config['price_accuracy']))),
1335
 
        'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."),
1336
 
        'tax_amount': fields.float('Tax Code Amount', digits=(16,int(config['price_accuracy']))),
1337
 
        'company_id': fields.related('account_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
1338
 
    }
1339
 
 
1340
 
    def base_change(self, cr, uid, ids, base,currency_id=False,company_id=False,date_invoice=False):
1341
 
        cur_obj = self.pool.get('res.currency')
1342
 
        company_obj = self.pool.get('res.company')
1343
 
        company_currency=False
1344
 
        if company_id:
1345
 
            company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
1346
 
        if currency_id and company_currency:
1347
 
            base = cur_obj.compute(cr, uid, currency_id, company_currency, base, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1348
 
        return {'value': {'base_amount':base}}
1349
 
 
1350
 
    def amount_change(self, cr, uid, ids, amount,currency_id=False,company_id=False,date_invoice=False):
1351
 
        cur_obj = self.pool.get('res.currency')
1352
 
        company_obj = self.pool.get('res.company')
1353
 
        company_currency=False
1354
 
        if company_id:
1355
 
            company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
1356
 
        if currency_id and company_currency:
1357
 
            amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1358
 
        return {'value': {'tax_amount':amount}}
1359
 
 
1360
 
    _order = 'sequence'
1361
 
    _defaults = {
1362
 
        'manual': lambda *a: 1,
1363
 
        'base_amount': lambda *a: 0.0,
1364
 
        'tax_amount': lambda *a: 0.0,
1365
 
    }
1366
 
    def compute(self, cr, uid, invoice_id, context={}):
1367
 
        tax_grouped = {}
1368
 
        tax_obj = self.pool.get('account.tax')
1369
 
        cur_obj = self.pool.get('res.currency')
1370
 
        inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context)
1371
 
        cur = inv.currency_id
1372
 
        company_currency = inv.company_id.currency_id.id
1373
 
 
1374
 
        for line in inv.invoice_line:
1375
 
            for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id):
1376
 
                val={}
1377
 
                val['invoice_id'] = inv.id
1378
 
                val['name'] = tax['name']
1379
 
                val['amount'] = tax['amount']
1380
 
                val['manual'] = False
1381
 
                val['sequence'] = tax['sequence']
1382
 
                val['base'] = tax['price_unit'] * line['quantity']
1383
 
 
1384
 
                if inv.type in ('out_invoice','in_invoice'):
1385
 
                    val['base_code_id'] = tax['base_code_id']
1386
 
                    val['tax_code_id'] = tax['tax_code_id']
1387
 
                    val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1388
 
                    val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1389
 
                    val['account_id'] = tax['account_collected_id'] or line.account_id.id
1390
 
                else:
1391
 
                    val['base_code_id'] = tax['ref_base_code_id']
1392
 
                    val['tax_code_id'] = tax['ref_tax_code_id']
1393
 
                    val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1394
 
                    val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False)
1395
 
                    val['account_id'] = tax['account_paid_id'] or line.account_id.id
1396
 
 
1397
 
                key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
1398
 
                if not key in tax_grouped:
1399
 
                    tax_grouped[key] = val
1400
 
                else:
1401
 
                    tax_grouped[key]['amount'] += val['amount']
1402
 
                    tax_grouped[key]['base'] += val['base']
1403
 
                    tax_grouped[key]['base_amount'] += val['base_amount']
1404
 
                    tax_grouped[key]['tax_amount'] += val['tax_amount']
1405
 
 
1406
 
        for t in tax_grouped.values():
1407
 
            t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
1408
 
            t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
1409
 
            t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
1410
 
        return tax_grouped
1411
 
 
1412
 
    def move_line_get(self, cr, uid, invoice_id):
1413
 
        res = []
1414
 
        cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%s', (invoice_id,))
1415
 
        for t in cr.dictfetchall():
1416
 
            if not t['amount'] \
1417
 
                    and not t['tax_code_id'] \
1418
 
                    and not t['tax_amount']:
1419
 
                continue
1420
 
            res.append({
1421
 
                'type':'tax',
1422
 
                'name':t['name'],
1423
 
                'price_unit': t['amount'],
1424
 
                'quantity': 1,
1425
 
                'price': t['amount'] or 0.0,
1426
 
                'account_id': t['account_id'],
1427
 
                'tax_code_id': t['tax_code_id'],
1428
 
                'tax_amount': t['tax_amount']
1429
 
            })
1430
 
        return res
1431
 
account_invoice_tax()
1432
 
 
1433
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1434