1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as
9
# published by the Free Software Foundation, either version 3 of the
10
# License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Affero General Public License for more details.
17
# You should have received a copy of the GNU Affero General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
24
from osv import fields, osv
28
from mx.DateTime import RelativeDateTime
29
from tools import config
30
from tools.translate import _
32
#class fiscalyear_seq(osv.osv):
33
# _name = "fiscalyear.seq"
34
# _description = "Maintains Invoice sequences with Fiscal Year"
35
# _rec_name = 'fiscalyear_id'
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),
44
class account_invoice(osv.osv):
45
def _amount_all(self, cr, uid, ids, name, args, context=None):
47
for invoice in self.browse(cr,uid,ids, context=context):
49
'amount_untaxed': 0.0,
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']
60
def _get_journal(self, cr, uid, 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)],
76
def _get_currency(self, cr, uid, context):
77
user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
79
return user.company_id.currency_id.id
81
return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
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)
88
raise osv.except_osv(_('No Analytic Journal !'),_("You must define an analytic journal of type '%s' !") % (tt,))
91
def _get_type(self, cr, uid, context=None):
94
type = context.get('type', 'out_invoice')
97
def _reconciled(self, cr, uid, ids, name, args, context):
100
res[id] = self.test_paid(cr, uid, [id])
103
def _get_reference_type(self, cr, uid, context=None):
104
return [('none', _('Free Reference'))]
106
def _amount_residual(self, cr, uid, ids, name, args, context=None):
108
data_inv = self.browse(cr, uid, ids)
109
cur_obj = self.pool.get('res.currency')
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)
136
if not inv.amount_total:
138
elif inv.type in ('out_invoice','in_refund'):
139
amount = credit-debit
140
result = inv.amount_total - amount
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
149
def _get_lines(self, cr, uid, ids, name, arg, context=None):
152
move_lines = self.move_line_id_payment_get(cr,uid,[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:
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]
170
def _get_invoice_line(self, cr, uid, ids, context=None):
172
for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
173
result[line.invoice_id.id] = True
176
def _get_invoice_tax(self, cr, uid, ids, context=None):
178
for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context):
179
result[tax.invoice_id.id] = True
182
def _compute_lines(self, cr, uid, ids, name, args, context=None):
184
for invoice in self.browse(cr, uid, ids, context):
185
moves = self.move_line_id_payment_get(cr, uid, [invoice.id])
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
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]
197
lines = filter(lambda x: x not in src, lines)
198
result[invoice.id] = lines
201
def _get_invoice_from_line(self, cr, uid, ids, context={}):
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
212
invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
215
def _get_invoice_from_reconcile(self, cr, uid, ids, context={}):
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
225
invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
228
_name = "account.invoice"
229
_description = 'Invoice'
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),
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',
245
'comment': fields.text('Additional Information', translate=True),
247
'state': fields.selection([
249
('proforma','Pro-forma'),
250
('proforma2','Pro-forma'),
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)]}),
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)]}),
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',
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),
285
'amount_tax': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Tax',
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),
292
'amount_total': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Total',
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),
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',
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',
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),
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')
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,
336
def unlink(self, cr, uid, ids, context=None):
337
invoices = self.read(cr, uid, ids, ['state'])
340
if t['state'] in ('draft', 'cancel'):
341
unlink_ids.append(t['id'])
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)
347
# def get_invoice_address(self, cr, uid, ids):
348
# res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
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
357
fiscal_position = False
359
opt = [('uid', str(uid))]
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)
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)])
372
rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_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]
387
if type in ('out_invoice', 'out_refund'):
388
acc_id = p.property_account_receivable.id
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
394
bank_id = p.bank_ids[0].id
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
405
if type in ('in_invoice', 'in_refund'):
406
result['value']['partner_bank'] = bank_id
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'])
414
result['value']['date_due'] = False
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'])
421
def onchange_currency_id(self, cr, uid, ids, curr_id, company_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 !.'))
429
def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice):
430
if not payment_term_id:
433
pt_obj= self.pool.get('account.payment.term')
434
if not date_invoice :
435
date_invoice = time.strftime('%Y-%m-%d')
437
pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice)
440
pterm_list = [line[0] for line in pterm_list]
442
res= {'value':{'date_due': pterm_list[-1]}}
444
raise osv.except_osv(_('Data Insufficient !'), _('The Payment Term of Supplier does not have Payment Term Lines(Computation) defined !'))
448
def onchange_invoice_line(self, cr, uid, ids, lines):
451
def onchange_partner_bank(self, cursor, user, ids, partner_bank_id):
454
def onchange_company_id(self, cr, uid, ids, company_id, part_id, type, invoice_line):
457
if company_id and part_id and type:
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)])
465
rec_pro_id = self.pool.get('ir.property').search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_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'):
479
val= {'account_id': acc_id}
482
inv_obj = self.browse(cr,uid,ids)
483
for line in inv_obj[0].invoice_line:
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)])
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]})
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.'))
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)]}
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 }
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")
514
wf_service.trg_create(uid, 'account.invoice', inv_id, cr)
520
# return the ids of the move lines which has the same account than the invoice
522
def move_line_id_payment_get(self, cr, uid, ids, *args):
524
if not ids: return res
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())
533
def copy(self, cr, uid, id, default=None, context=None):
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)
544
def test_paid(self, cr, uid, ids, *args):
545
res = self.move_line_id_payment_get(cr, uid, ids)
550
cr.execute('select reconcile_id from account_move_line where id=%s', (id,))
551
ok = ok and bool(cr.fetchone()[0])
554
def button_reset_taxes(self, cr, uid, ids, context=None):
557
ait_obj = self.pool.get('account.invoice.tax')
559
cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s", (id,))
560
partner = self.browse(cr, uid, id,context=context).partner_id
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)
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):
574
self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total})
577
def _convert_ref(self, cr, uid, ref):
578
return (ref or '').replace('/','')
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')
584
company_currency = inv.company_id.currency_id.id
585
if inv.type in ('out_invoice', 'in_refund'):
590
iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id)
592
if il['account_analytic_id']:
593
if inv.type in ('in_invoice', 'in_refund'):
596
ref = self._convert_ref(cr, uid, inv.number)
597
il['analytic_lines'] = [(0,0, {
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),
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'])
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')
622
for inv in self.browse(cr, uid, ids):
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
635
context.update({'lang': inv.partner_id.lang})
636
compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
638
for tax in compute_taxes.values():
639
ait_obj.create(cr, uid, tax)
642
for tax in inv.tax_line:
645
key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
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 !'))
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.'))
659
# one move line per tax line
660
iml += ait_obj.move_line_get(cr, uid, inv.id)
662
if inv.type in ('in_invoice', 'in_refund'):
665
ref = self._convert_ref(cr, uid, inv.number)
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
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')})
679
i['amount_currency'] = False
680
i['currency_id'] = False
682
if inv.type in ('out_invoice','in_refund'):
684
total_currency += i['amount_currency'] or i['price']
685
i['price'] = - i['price']
688
total_currency -= i['amount_currency'] or i['price']
689
acc_id = inv.account_id.id
691
name = inv['name'] or '/'
694
totlines = self.pool.get('account.payment.term').compute(cr,
695
uid, inv.payment_term.id, total, inv.date_invoice or False)
697
res_amount_currency = total_currency
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])
704
amount_currency = False
706
# last line add the diff
707
res_amount_currency -= amount_currency or 0
709
if i == len(totlines):
710
amount_currency += res_amount_currency
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,
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,
738
date = inv.date_invoice or time.strftime('%Y-%m-%d')
739
part = inv.partner_id.id
741
line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml)
743
if inv.journal_id.group_invoice_lines:
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"))
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']
761
for key, val in line2.items():
762
line.append((0,0,val))
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
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'))])
774
period_id=period_ids[0]
776
move['period_id'] = period_id
778
i[2]['period_id'] = period_id
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)
788
def line_get_convert(self, cr, uid, x, part, date, context=None):
790
'date_maturity': x.get('date_maturity', False),
792
'name':x['name'][:64],
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),
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():
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})
820
number = self.pool.get('ir.sequence').get(cr, uid,
821
'account.invoice.' + invtype)
822
if invtype in ('in_invoice', 'in_refund'):
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 = \'\')',
831
cr.execute('UPDATE account_move_line SET ref=%s ' \
832
'WHERE move_id=%s AND (ref is null OR ref = \'\')',
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',
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'])
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')
857
def list_distinct_taxes(self, cr, uid, ids):
858
invoices = self.browse(cr, uid, ids)
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()
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'])
869
part=inv['partner_id'] and inv['partner_id'][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'
878
partnertype = 'customer'
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})
885
def name_get(self, cr, uid, ids, context=None):
889
'out_invoice': 'CI: ',
890
'in_invoice': 'SI: ',
891
'out_refund': 'OR: ',
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')]
896
def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
903
ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
905
ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
906
return self.name_get(cr, user, ids, context)
908
def _refund_cleanup_lines(self, cr, uid, lines):
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]
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)
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'])
934
for invoice in invoices:
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
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)
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)
952
date = time.strftime('%Y-%m-%d')
954
'type': type_dict[invoice['type']],
955
'date_invoice': date,
958
'invoice_line': invoice_lines,
959
'tax_line': tax_lines
963
'period_id': period_id,
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))
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=''):
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']
991
date=time.strftime('%Y-%m-%d')
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']
998
amount_currency = False
1000
if invoice.type in ('in_invoice', 'in_refund'):
1001
ref = invoice.reference
1003
ref = self._convert_ref(cr, uid, invoice.number)
1004
# Pay attention to the sign for both debit/credit AND amount_currency
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,
1012
'currency_id':currency_id,
1013
'amount_currency':amount_currency and direction * amount_currency or 0.0,
1014
'company_id': invoice.company_id.id,
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,
1023
'currency_id':currency_id,
1024
'amount_currency':amount_currency and - direction * amount_currency or 0.0,
1025
'company_id': invoice.company_id.id,
1029
name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number
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)
1039
line = self.pool.get('account.move.line')
1040
move_ids = [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)
1052
self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
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)
1059
class account_invoice_line(osv.osv):
1060
def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
1062
cur_obj=self.pool.get('res.currency')
1063
for line in self.browse(cr, uid, ids):
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])
1069
res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),int(config['price_accuracy']))
1073
def _price_unit_default(self, cr, uid, context=None):
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']
1091
_name = "account.invoice.line"
1092
_description = "Invoice line"
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)
1110
'quantity': lambda *a: 1,
1111
'discount': lambda *a: 0.0,
1112
'price_unit': _price_unit_default,
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')
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}
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):
1126
company_id = context.get('company_id',False)
1128
raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
1130
if type in ('in_invoice', 'in_refund'):
1131
return {'value': {'categ_id': False}, 'domain':{'product_uom':[]}}
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
1138
context.update({'lang': lang})
1140
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
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)])
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)])
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)])
1151
in_acc = res.product_tmpl_id.property_account_income
1152
in_acc_cate = res.categ_id.property_account_income_categ
1156
app_acc_in = in_acc_cate
1158
app_acc_in = self.pool.get('account.account').browse(cr,uid,in_pro_id)[0]
1160
ex_acc = res.product_tmpl_id.property_account_expense
1161
ex_acc_cate = res.categ_id.property_account_expense_categ
1163
app_acc_exp = ex_acc
1165
app_acc_exp = ex_acc_cate
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:
1175
app_acc_exp = ex_acc
1177
app_acc_in = in_acc_cate
1178
app_acc_exp = ex_acc_cate
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]
1194
res.categ_id.property_account_income_categ = in_obj_acc[0]
1195
res.categ_id.property_account_expense_categ = exp_obj_acc[0]
1197
if type in ('out_invoice','out_refund'):
1198
a = res.product_tmpl_id.property_account_income.id
1200
a = res.categ_id.property_account_income_categ.id
1202
a = res.product_tmpl_id.property_account_expense.id
1204
a = res.categ_id.property_account_expense_categ.id
1206
a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, a)
1208
result['account_id'] = a
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)
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)
1222
result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
1225
result['name'] = res.name
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
1232
domain = {'uos_id':[('category_id','=',res2 )]}
1234
prod_pool=self.pool.get('product.product')
1235
result['categ_id'] = res.categ_id.id
1236
res_final = {'value':result, 'domain':domain}
1238
if not company_id and not currency_id:
1241
company = self.pool.get('res.company').browse(cr, uid, company_id)
1242
currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
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 !.'))
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
1253
def move_line_get(self, cr, uid, invoice_id, context=None):
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
1263
for line in inv.invoice_line:
1264
mres = self.move_line_get_item(cr, uid, line, context)
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,
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']
1278
tax_code_id = tax['ref_base_code_id']
1279
tax_amount = line.price_subtotal * tax['ref_base_sign']
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:
1289
tax_code_found = True
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})
1295
def move_line_get_item(self, cr, uid, line, context=None):
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,
1309
# Set the tax field according to the account and the fiscal position
1311
def onchange_account_id(self, cr, uid, ids, fposition_id, account_id):
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}}
1319
account_invoice_line()
1321
class account_invoice_tax(osv.osv):
1322
_name = "account.invoice.tax"
1323
_description = "Invoice Tax"
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."),
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),
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
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}}
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
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}}
1362
'manual': lambda *a: 1,
1363
'base_amount': lambda *a: 0.0,
1364
'tax_amount': lambda *a: 0.0,
1366
def compute(self, cr, uid, invoice_id, context={}):
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
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):
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']
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
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
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
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']
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'])
1412
def move_line_get(self, cr, uid, invoice_id):
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']:
1423
'price_unit': t['amount'],
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']
1431
account_invoice_tax()
1433
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: