2006-12-07 13:41:40 +00:00
# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import time
import netsvc
from osv import fields , osv
import ir
class account_invoice ( osv . osv ) :
def _amount_untaxed ( self , cr , uid , ids , prop , unknow_none , unknow_dict ) :
id_set = " , " . join ( map ( str , ids ) )
cr . execute ( " SELECT s.id,COALESCE(SUM(l.price_unit*l.quantity*(100-l.discount))/100.0,0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_line l ON (s.id=l.invoice_id) WHERE s.id IN ( " + id_set + " ) GROUP BY s.id " )
res = dict ( cr . fetchall ( ) )
return res
def _amount_tax ( self , cr , uid , ids , prop , unknow_none , unknow_dict ) :
id_set = " , " . join ( map ( str , ids ) )
cr . execute ( " SELECT s.id,COALESCE(SUM(l.amount),0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_tax l ON (s.id=l.invoice_id) WHERE s.id IN ( " + id_set + " ) GROUP BY s.id " )
res = dict ( cr . fetchall ( ) )
return res
def _amount_total ( self , cr , uid , ids , prop , unknow_none , unknow_dict ) :
untax = self . _amount_untaxed ( cr , uid , ids , prop , unknow_none , unknow_dict )
tax = self . _amount_tax ( cr , uid , ids , prop , unknow_none , unknow_dict )
res = { }
for id in ids :
res [ id ] = untax . get ( id , 0.0 ) + tax . get ( id , 0.0 )
return res
def _get_journal ( self , cr , uid , context ) :
type_inv = context . get ( ' type ' , ' out_invoice ' )
type2journal = { ' out_invoice ' : ' sale ' , ' in_invoice ' : ' purchase ' , ' out_refund ' : ' sale ' , ' in_refund ' : ' purchase ' }
cr . execute ( " select id from account_journal where type= %s limit 1 " , ( type2journal . get ( type_inv , ' sale ' ) , ) )
return cr . fetchone ( ) [ 0 ]
def _get_currency ( self , cr , uid , context ) :
cr . execute ( " select id from res_currency where rate=1.0 " )
res = cr . fetchone ( )
return res and res [ 0 ] or False
def _get_journal_analytic ( self , cr , uid , type_inv , context = { } ) :
type2journal = { ' out_invoice ' : ' sale ' , ' in_invoice ' : ' purchase ' , ' out_refund ' : ' sale ' , ' in_refund ' : ' purchase ' }
tt = type2journal . get ( type_inv , ' sale ' )
cr . execute ( " select id from account_analytic_journal where type= %s limit 1 " , ( tt , ) )
result = cr . fetchone ( )
if not result :
raise osv . except_osv ( ' No Analytic Journal ! ' , " You have to define an analytic journal of type ' %s ' ! " % ( tt , ) )
return result [ 0 ]
_name = " account.invoice "
_description = ' Invoice '
_order = " number "
_columns = {
' name ' : fields . char ( ' Invoice Description ' , size = 64 , required = True , select = True ) ,
' origin ' : fields . char ( ' Origin ' , size = 64 ) ,
' type ' : fields . selection ( [
( ' out_invoice ' , ' Customer Invoice ' ) ,
( ' in_invoice ' , ' Supplier Invoice ' ) ,
( ' out_refund ' , ' Customer Refund ' ) ,
( ' in_refund ' , ' Supplier Refund ' ) ,
] , ' Type ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , select = True ) ,
2006-12-18 15:06:11 +00:00
' number ' : fields . char ( ' Invoice Number ' , size = 32 , readonly = True ) ,
2006-12-07 13:41:40 +00:00
' reference ' : fields . char ( ' Invoice Reference ' , size = 64 ) ,
' project_id ' : fields . many2one ( ' account.analytic.account ' , ' Analytic Account ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' comment ' : fields . text ( ' Additionnal Information ' ) ,
' state ' : fields . selection ( [
( ' draft ' , ' Draft ' ) ,
( ' proforma ' , ' Pro-forma ' ) ,
( ' open ' , ' Open ' ) ,
( ' paid ' , ' Paid ' ) ,
( ' cancel ' , ' Canceled ' )
] , ' State ' , readonly = True , select = True ) ,
' date_invoice ' : fields . date ( ' Date Invoiced ' , required = True , states = { ' open ' : [ ( ' readonly ' , True ) ] , ' close ' : [ ( ' readonly ' , True ) ] } ) ,
' date_due ' : fields . date ( ' Due Date ' , states = { ' open ' : [ ( ' readonly ' , True ) ] , ' close ' : [ ( ' readonly ' , True ) ] } ) ,
' partner_id ' : fields . many2one ( ' res.partner ' , ' Partner ' , change_default = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , relate = True ) ,
' address_contact_id ' : fields . many2one ( ' res.partner.address ' , ' Contact Address ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' address_invoice_id ' : fields . many2one ( ' res.partner.address ' , ' Invoice Address ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' partner_contact ' : fields . char ( ' Partner Contact ' , size = 64 ) ,
' partner_ref ' : fields . char ( ' Partner Reference ' , size = 64 ) ,
' payment_term ' : fields . many2one ( ' account.payment.term ' , ' Payment Term ' ) ,
' period_id ' : fields . many2one ( ' account.period ' , ' Force Period ' , help = " Keep empty to use the period of the validation date. " ) ,
' account_id ' : fields . many2one ( ' account.account ' , ' Dest Account ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' invoice_line ' : fields . one2many ( ' account.invoice.line ' , ' invoice_id ' , ' Invoice Lines ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' tax_line ' : fields . one2many ( ' account.invoice.tax ' , ' invoice_id ' , ' Tax Lines ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' move_id ' : fields . many2one ( ' account.move ' , ' Invoice Movement ' , readonly = True ) ,
' amount_untaxed ' : fields . function ( _amount_untaxed , method = True , digits = ( 16 , 2 ) , string = ' Untaxed Amount ' ) ,
' amount_tax ' : fields . function ( _amount_tax , method = True , string = ' Tax ' ) ,
' amount_total ' : fields . function ( _amount_total , method = True , string = ' Total ' ) ,
' currency_id ' : fields . many2one ( ' res.currency ' , ' Currency ' , required = True ) ,
' journal_id ' : fields . many2one ( ' account.journal ' , ' Journal ' , required = True , relate = True ) ,
}
_defaults = {
' type ' : lambda * a : ' out_invoice ' ,
' date_invoice ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
' state ' : lambda * a : ' draft ' ,
' journal_id ' : _get_journal ,
' currency_id ' : _get_currency ,
}
def unlink ( self , cr , uid , ids ) :
invoices = self . read ( cr , uid , ids , [ ' state ' ] )
unlink_ids = [ ]
for t in invoices :
if t [ ' state ' ] in ( ' draft ' , ' cancel ' ) :
unlink_ids . append ( t [ ' id ' ] )
else :
raise osv . except_osv ( ' Invalid action ! ' , ' Cannot delete invoice(s) which are already opened or paid ! ' )
osv . osv . unlink ( self , cr , uid , unlink_ids )
return True
# def get_invoice_address(self, cr, uid, ids):
# res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
# return [{}]
def onchange_partner_id ( self , cr , uid , ids , type , partner_id ) :
invoice_addr_id = False
contact_addr_id = False
acc_id = False
opt = [ ( ' uid ' , str ( uid ) ) ]
if partner_id :
opt . insert ( 0 , ( ' id ' , partner_id ) )
res = self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ partner_id ] , [ ' contact ' , ' invoice ' ] )
contact_addr_id = res [ ' contact ' ]
invoice_addr_id = res [ ' invoice ' ]
p = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_id )
if type in ( ' out_invoice ' , ' out_refund ' ) :
acc_id = p . property_account_receivable
else :
acc_id = p . property_account_payable
result = { ' value ' : { ' address_contact_id ' : contact_addr_id , ' address_invoice_id ' : invoice_addr_id , ' account_id ' : acc_id } }
if partner_id and p . property_payment_term :
result [ ' value ' ] [ ' payment_term ' ] = p . property_payment_term
return result
def onchange_currency_id ( self , cr , uid , ids , curr_id ) :
return { }
# go from canceled state to draft state
def action_cancel_draft ( self , cr , uid , ids , * args ) :
self . write ( cr , uid , ids , { ' state ' : ' draft ' } )
wf_service = netsvc . LocalService ( " workflow " )
for inv_id in ids :
wf_service . trg_create ( uid , ' account.invoice ' , inv_id , cr )
return True
# Workflow stuff
#################
2006-12-12 09:16:20 +00:00
# return the ids of the move lines which has the same account than the invoice
2006-12-07 13:41:40 +00:00
# whose id is in ids
def move_line_id_payment_get ( self , cr , uid , ids , * args ) :
ml = self . pool . get ( ' account.move.line ' )
2006-12-12 09:16:20 +00:00
res = [ ]
2006-12-07 13:41:40 +00:00
for inv in self . read ( cr , uid , ids , [ ' move_id ' , ' account_id ' ] ) :
if inv [ ' move_id ' ] :
move_line_ids = ml . search ( cr , uid , [ ( ' move_id ' , ' = ' , inv [ ' move_id ' ] [ 0 ] ) ] )
for line in ml . read ( cr , uid , move_line_ids , [ ' account_id ' ] ) :
if line [ ' account_id ' ] == inv [ ' account_id ' ] :
2006-12-12 09:16:20 +00:00
res . append ( line [ ' id ' ] )
return res
2006-12-07 13:41:40 +00:00
def copy ( self , cr , uid , id , default = None , context = { } ) :
if not default : default = { }
default . update ( { ' state ' : ' draft ' , ' number ' : False , ' move_id ' : False } )
return super ( account_invoice , self ) . copy ( cr , uid , id , default , context )
def test_paid ( self , cr , uid , ids , * args ) :
res = self . move_line_id_payment_get ( cr , uid , ids )
if not res :
return False
ok = True
for id in res :
cr . execute ( ' select reconcile_id from account_move_line where id= %d ' , ( id , ) )
ok = ok and bool ( cr . fetchone ( ) [ 0 ] )
return ok
def button_compute ( self , cr , uid , ids , context = { } ) :
for id in ids :
self . pool . get ( ' account.invoice.line ' ) . move_line_get ( cr , uid , id )
return True
def action_move_create ( self , cr , uid , ids , * args ) :
company_currency = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id . currency_id . id
id_set = ' , ' . join ( [ str ( i ) for i in ids ] )
cr . execute ( ' SELECT * FROM account_invoice WHERE move_id IS NULL AND id IN ( ' + id_set + ' ) ' )
for inv in cr . dictfetchall ( ) :
# create the analytical lines
line_ids = self . read ( cr , uid , [ inv [ ' id ' ] ] , [ ' invoice_line ' ] ) [ 0 ] [ ' invoice_line ' ]
ils = self . pool . get ( ' account.invoice.line ' ) . read ( cr , uid , line_ids )
if inv [ ' type ' ] in ( ' out_invoice ' , ' in_refund ' ) :
sign = 1
else :
sign = - 1
# one move line per invoice line
iml = self . pool . get ( ' account.invoice.line ' ) . move_line_get ( cr , uid , inv [ ' id ' ] )
if inv [ ' project_id ' ] :
for il in iml :
il [ ' analytic_lines ' ] = [ ( 0 , 0 , {
' name ' : il [ ' name ' ] ,
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
' account_id ' : inv [ ' project_id ' ] ,
' unit_amount ' : il [ ' quantity ' ] ,
' amount ' : il [ ' price ' ] * sign ,
#'product_id': il['product_id'] and il['product_id'][0],
#'product_uom_id': il['uos_id'] and il['uos_id'][0],
' general_account_id ' : il [ ' account_id ' ] ,
' journal_id ' : self . _get_journal_analytic ( cr , uid , inv [ ' type ' ] )
} ) ]
# one move line per tax line
iml + = self . pool . get ( ' account.invoice.tax ' ) . move_line_get ( cr , uid , inv [ ' id ' ] )
# create one move line for the total and possibly adjust the other lines amount
total = 0
total_currency = 0
for i in iml :
if inv [ ' currency_id ' ] != company_currency :
i [ ' currency_id ' ] = inv [ ' currency_id ' ]
i [ ' amount_currency ' ] = i [ ' price ' ]
i [ ' price ' ] = round ( self . pool . get ( ' res.currency ' ) . compute ( cr , uid , inv [ ' currency_id ' ] , company_currency , i [ ' price ' ] ) , 2 )
else :
i [ ' amount_currency ' ] = False
i [ ' currency_id ' ] = False
i [ ' ref ' ] = inv [ ' number ' ]
if inv [ ' type ' ] in ( ' out_invoice ' , ' in_refund ' ) :
total + = i [ ' price ' ]
total_currency + = i [ ' amount_currency ' ] or i [ ' price ' ]
i [ ' price ' ] = - i [ ' price ' ]
else :
total - = i [ ' price ' ]
total_currency - = i [ ' amount_currency ' ] or i [ ' price ' ]
acc_id = inv [ ' account_id ' ]
name = inv [ ' name ' ]
if inv [ ' payment_term ' ] :
totlines = self . pool . get ( ' account.payment.term ' ) . compute ( cr , uid , inv [ ' payment_term ' ] , total )
for t in totlines :
iml . append ( { ' type ' : ' dest ' , ' name ' : name , ' price ' : t [ 1 ] , ' date_maturity ' : t [ 0 ] , ' account_id ' : acc_id , ' ref ' : inv [ ' number ' ] } )
else :
iml . append ( { ' type ' : ' dest ' , ' name ' : name , ' price ' : total , ' account_id ' : acc_id , ' date_maturity ' : inv [ ' date_due ' ] or False , ' amount_currency ' : total_currency , ' currency_id ' : inv [ ' currency_id ' ] , ' ref ' : inv [ ' number ' ] } )
date = inv [ ' date_invoice ' ]
part = inv [ ' partner_id ' ]
line = map ( lambda x : ( 0 , 0 , {
' date ' : date ,
' date_maturity ' : x . get ( ' date_maturity ' , False ) ,
' partner_id ' : part ,
' name ' : x [ ' name ' ] ,
' debit ' : x [ ' price ' ] > 0 and x [ ' price ' ] ,
' credit ' : x [ ' price ' ] < 0 and - x [ ' price ' ] ,
' account_id ' : x [ ' account_id ' ] ,
' analytic_lines ' : x . get ( ' analytic_lines ' , [ ] ) ,
' amount_currency ' : x . get ( ' amount_currency ' , False ) ,
' currency_id ' : x . get ( ' currency_id ' , False ) ,
' tax_code_id ' : x . get ( ' tax_code_id ' , False ) ,
' tax_amount ' : x . get ( ' tax_amount ' , False ) ,
' ref ' : x . get ( ' ref ' , False ) } ) , iml )
journal_id = inv [ ' journal_id ' ] #self._get_journal(cr, uid, {'type': inv['type']})
journal = self . pool . get ( ' account.journal ' ) . browse ( cr , uid , journal_id )
if journal . sequence_id :
name = self . pool . get ( ' ir.sequence ' ) . get_id ( cr , uid , journal . sequence_id . id )
move = { ' name ' : name , ' line_id ' : line , ' journal_id ' : journal_id }
if inv [ ' period_id ' ] :
move [ ' period_id ' ] = inv [ ' period_id ' ]
for i in line :
i [ 2 ] [ ' period_id ' ] = inv [ ' period_id ' ]
move_id = self . pool . get ( ' account.move ' ) . create ( cr , uid , move )
# make the invoice point to that move
self . write ( cr , uid , [ inv [ ' id ' ] ] , { ' move_id ' : move_id } )
self . _log_event ( cr , uid , ids )
return True
def action_number ( self , cr , uid , ids , * args ) :
cr . execute ( ' SELECT id, type, number, move_id FROM account_invoice WHERE id IN ( ' + ' , ' . join ( map ( str , ids ) ) + ' ) ' )
for ( id , invtype , number , move_id ) in cr . fetchall ( ) :
if not number :
number = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' account.invoice. ' + invtype )
cr . execute ( ' UPDATE account_invoice SET number= %s WHERE id= %d ' , ( number , id ) )
cr . execute ( ' UPDATE account_move_line SET ref= %s WHERE move_id= %d and ref is null ' , ( number , move_id ) )
return True
def action_cancel ( self , cr , uid , ids , * args ) :
invoices = self . read ( cr , uid , ids , [ ' move_id ' ] )
for i in invoices :
if i [ ' move_id ' ] :
# delete the move this invoice was pointing to
# Note that the corresponding move_lines and move_reconciles
# will be automatically deleted too
self . pool . get ( ' account.move ' ) . unlink ( cr , uid , [ i [ ' move_id ' ] [ 0 ] ] )
self . write ( cr , uid , ids , { ' state ' : ' cancel ' , ' move_id ' : False } )
self . _log_event ( cr , uid , ids , - 1.0 , ' Cancel Invoice ' )
return True
###################
def list_distinct_taxes ( self , cr , uid , ids ) :
invoices = self . browse ( cr , uid , ids )
taxes = { }
for inv in invoices :
for tax in inv . tax_line :
if not tax [ ' name ' ] in taxes :
taxes [ tax [ ' name ' ] ] = { ' name ' : tax [ ' name ' ] }
return taxes . values ( )
def _log_event ( self , cr , uid , ids , factor = 1.0 , name = ' Open Invoice ' ) :
invs = self . read ( cr , uid , ids , [ ' type ' , ' partner_id ' , ' amount_untaxed ' ] )
for inv in invs :
part = inv [ ' partner_id ' ] and inv [ ' partner_id ' ] [ 0 ]
pc = pr = 0.0
cr . execute ( ' select sum(quantity*price_unit) from account_invoice_line where invoice_id= %d ' , ( inv [ ' id ' ] , ) )
total = inv [ ' amount_untaxed ' ]
if inv [ ' type ' ] in ( ' in_invoice ' , ' in_refund ' ) :
partnertype = ' supplier '
eventtype = ' purchase '
pc = total * factor
else :
partnertype = ' customer '
eventtype = ' sale '
pr = total * factor
if self . pool . get ( ' res.partner.event.type ' ) . check ( cr , uid , ' invoice_open ' ) :
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 } )
return len ( invs )
def name_search ( self , cr , user , name , args = [ ] , operator = ' ilike ' , context = { } ) :
ids = [ ]
if name :
ids = self . search ( cr , user , [ ( ' number ' , ' = ' , name ) ] + args )
if not ids :
ids = self . search ( cr , user , [ ( ' name ' , operator , name ) ] + args )
return self . name_get ( cr , user , ids )
def refund ( self , cr , uid , ids ) :
invoices = self . read ( cr , uid , ids , [ ' name ' , ' type ' , ' number ' , ' reference ' , ' project_id ' , ' 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 ' ] )
new_ids = [ ]
for invoice in invoices :
del invoice [ ' id ' ]
type_dict = {
' out_invoice ' : ' out_refund ' , # Customer Invoice
' in_invoice ' : ' in_refund ' , # Supplier Invoice
' out_refund ' : ' out_invoice ' , # Customer Refund
' in_refund ' : ' in_invoice ' , # Supplier Refund
}
def cleanup_lines ( lines ) :
for line in lines :
del line [ ' id ' ]
del line [ ' invoice_id ' ]
line [ ' account_id ' ] = line [ ' account_id ' ] and line [ ' account_id ' ] [ 0 ]
line [ ' product_id ' ] = line [ ' product_id ' ] and line [ ' product_id ' ] [ 0 ]
line [ ' uos_id ' ] = line [ ' uos_id ' ] and line [ ' uos_id ' ] [ 0 ]
line [ ' invoice_line_tax_id ' ] = [ ( 6 , 0 , line . get ( ' invoice_line_tax_id ' , [ ] ) ) ]
return map ( lambda x : ( 0 , 0 , x ) , lines )
invoice_lines = self . pool . get ( ' account.invoice.line ' ) . read ( cr , uid , invoice [ ' invoice_line ' ] )
invoice_lines = cleanup_lines ( invoice_lines )
tax_lines = self . pool . get ( ' account.invoice.tax ' ) . read ( cr , uid , invoice [ ' tax_line ' ] )
tax_lines = filter ( lambda l : l [ ' manual ' ] , tax_lines )
tax_lines = cleanup_lines ( tax_lines )
invoice . update ( {
' type ' : type_dict [ invoice [ ' type ' ] ] ,
' date_invoice ' : time . strftime ( ' % Y- % m- %d ' ) ,
' state ' : ' draft ' ,
' number ' : False ,
' invoice_line ' : invoice_lines ,
' tax_line ' : tax_lines
} )
# take the id part of the tuple returned for many2one fields
for field in ( ' address_contact_id ' , ' address_invoice_id ' , ' partner_id ' ,
' project_id ' , ' account_id ' , ' currency_id ' , ' payment_term ' ) :
invoice [ field ] = invoice [ field ] and invoice [ field ] [ 0 ]
# create the new invoice
new_ids . append ( self . create ( cr , uid , invoice ) )
return new_ids
def pay_and_reconcile ( self , cr , uid , ids , pay_amount , pay_account_id , pay_journal_id , writeoff_acc_id , writeoff_period_id , writeoff_journal_id , context = { } ) :
assert len ( ids ) == 1 , " Can only pay one invoice at a time "
invoice = self . browse ( cr , uid , ids [ 0 ] )
src_account_id = invoice . account_id . id
journal = self . pool . get ( ' account.journal ' ) . browse ( cr , uid , pay_journal_id )
if journal . sequence_id :
name = self . pool . get ( ' ir.sequence ' ) . get_id ( cr , uid , journal . sequence_id . id )
else :
raise osv . except_osv ( ' No piece number ! ' , ' Can not create an automatic sequence for this piece ! \n \n Put a sequence in the journal definition for automatic numbering or create a sequence manually for this piece. ' )
types = { ' out_invoice ' : - 1 , ' in_invoice ' : 1 , ' out_refund ' : 1 , ' in_refund ' : - 1 }
direction = types [ invoice . type ]
l1 = {
' name ' : name ,
' debit ' : direction == 1 and pay_amount ,
' credit ' : direction == - 1 and pay_amount ,
' account_id ' : src_account_id ,
' partner_id ' : invoice . partner_id . id
}
l2 = {
' name ' : name ,
' debit ' : direction == - 1 and pay_amount ,
' credit ' : direction == 1 and pay_amount ,
' account_id ' : pay_account_id ,
' partner_id ' : invoice . partner_id . id
}
lines = [ ( 0 , 0 , l1 ) , ( 0 , 0 , l2 ) ]
move = { ' name ' : name , ' line_id ' : lines , ' journal_id ' : pay_journal_id }
move_id = self . pool . get ( ' account.move ' ) . create ( cr , uid , move )
line_ids = [ ]
line = self . pool . get ( ' account.move.line ' )
cr . execute ( ' select id from account_move_line where move_id in ( ' + str ( move_id ) + ' , ' + str ( invoice . move_id . id ) + ' ) ' )
lines = line . browse ( cr , uid , map ( lambda x : x [ 0 ] , cr . fetchall ( ) ) )
for l in lines :
if l . account_id . id == src_account_id :
line_ids . append ( l . id )
self . pool . get ( ' account.move.line ' ) . reconcile ( cr , uid , line_ids , ' manual ' , writeoff_acc_id , writeoff_period_id , writeoff_journal_id , context )
return True
account_invoice ( )
class account_invoice_line ( osv . osv ) :
def _amount_line ( self , cr , uid , ids , prop , unknow_none , unknow_dict ) :
res = { }
for line in self . browse ( cr , uid , ids ) :
res [ line . id ] = round ( line . price_unit * line . quantity * ( 1 - ( line . discount or 0.0 ) / 100.0 ) , 2 )
return res
_name = " account.invoice.line "
_description = " Invoice line "
_columns = {
' name ' : fields . char ( ' Description ' , size = 256 , required = True ) ,
' invoice_id ' : fields . many2one ( ' account.invoice ' , ' Invoice Ref ' , ondelete = ' cascade ' , select = True ) ,
' uos_id ' : fields . many2one ( ' product.uom ' , ' Unit ' , ondelete = ' set null ' ) ,
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , ondelete = ' set null ' ) ,
' account_id ' : fields . many2one ( ' account.account ' , ' Source Account ' , required = True , domain = [ ( ' type ' , ' <> ' , ' view ' ) ] ) ,
' price_unit ' : fields . float ( ' Unit Price ' , required = True ) ,
' price_subtotal ' : fields . function ( _amount_line , method = True , string = ' Subtotal ' ) ,
' quantity ' : fields . float ( ' Quantity ' , required = True ) ,
' discount ' : fields . float ( ' Discount ( % ) ' , digits = ( 16 , 2 ) ) ,
' invoice_line_tax_id ' : fields . many2many ( ' account.tax ' , ' account_invoice_line_tax ' , ' invoice_line_id ' , ' tax_id ' , ' Taxes ' , domain = [ ( ' parent_id ' , ' = ' , False ) ] ) ,
' note ' : fields . text ( ' Notes ' ) ,
}
_defaults = {
' quantity ' : lambda * a : 1 ,
' discount ' : lambda * a : 0.0
}
def product_id_change ( self , cr , uid , ids , product , uom , qty = 0 , name = ' ' , type = ' out_invoice ' ) :
if not product :
return { ' value ' : { ' price_unit ' : 0.0 } , ' domain ' : { ' product_uom ' : [ ] } }
res = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product )
result = { ' price_unit ' : res . list_price , ' invoice_line_tax_id ' : map ( lambda x : x . id , res . taxes_id ) }
if not name :
result [ ' name ' ] = res . name
if type in ( ' out_invoice ' , ' out_refund ' ) :
a = res . product_tmpl_id . property_account_income
if not a :
a = res . categ_id . property_account_income_categ
result [ ' account_id ' ] = a [ 0 ]
else :
a = res . product_tmpl_id . property_account_expense
if not a :
a = res . categ_id . property_account_expense_categ
result [ ' account_id ' ] = a [ 0 ]
domain = { }
if not uom :
result [ ' product_uom ' ] = res . uom_id . id or False
if result [ ' product_uom ' ] :
res2 = self . pool . get ( ' product.uom ' ) . read ( cr , uid , [ result [ ' product_uom ' ] ] , [ ' category_id ' ] )
if res2 and res2 [ 0 ] [ ' category_id ' ] :
domain = { ' product_uom ' : [ ( ' category_id ' , ' = ' , res2 [ 0 ] [ ' category_id ' ] [ 0 ] ) ] }
return { ' value ' : result , ' domain ' : domain }
def move_line_get ( self , cr , uid , invoice_id , context = { } ) :
res = [ ]
tax_grouped = { }
tax_obj = self . pool . get ( ' account.tax ' )
#TODO: rewrite using browse instead of the manual SQL queries
# cr.execute('SELECT id FROM account_invoice_line WHERE invoice_id=%d', (invoice_id,))
inv = self . pool . get ( ' account.invoice ' ) . browse ( cr , uid , invoice_id )
for line in inv . invoice_line :
res . append ( {
' type ' : ' src ' ,
' name ' : line . name ,
' price_unit ' : line . price_unit ,
' quantity ' : line . quantity ,
' price ' : round ( line . quantity * line . price_unit * ( 1.0 - ( line . discount or 0.0 ) / 100.0 ) , 2 ) ,
' account_id ' : line . account_id . id
} )
for tax2 in line . invoice_line_tax_id :
for tax in tax_obj . compute ( cr , uid , [ tax2 . id ] , line . price_unit , line . quantity , inv . address_invoice_id . id ) :
tax [ ' amount ' ] = tax [ ' amount ' ] * ( 1.0 - ( line [ ' discount ' ] or 0.0 ) / 100.0 )
tax [ ' sequence ' ] = tax2 . sequence
#
# Setting the tax account and amount for the line
#
if inv . type in ( ' out_invoice ' , ' out_refund ' ) :
res [ - 1 ] [ ' tax_code_id ' ] = tax2 . base_code_id . id
res [ - 1 ] [ ' tax_amount ' ] = line [ ' price_unit ' ] * line [ ' quantity ' ] * ( 1.0 - ( line [ ' discount ' ] or 0.0 ) / 100.0 ) * tax2 . base_sign
else :
res [ - 1 ] [ ' tax_code_id ' ] = tax2 . ref_base_code_id . id
res [ - 1 ] [ ' tax_amount ' ] = line [ ' price_unit ' ] * line [ ' quantity ' ] * ( 1.0 - ( line [ ' discount ' ] or 0.0 ) / 100.0 ) * tax2 . ref_base_sign
if inv . type in ( ' out_refund ' , ' in_refund ' ) :
res [ - 1 ] [ ' tax_amount ' ] = - res [ - 1 ] [ ' tax_amount ' ]
if inv . type in ( ' out_invoice ' , ' out_refund ' ) :
tax [ ' account_id ' ] = tax [ ' account_collected_id ' ] or line . account_id . id
else :
tax [ ' account_id ' ] = tax [ ' account_paid_id ' ] or line . account_id . id
#
# Revoir la cl<63> : tax.id ?
#
key = ( res [ - 1 ] [ ' tax_code_id ' ] , tax [ ' account_id ' ] )
if not key in tax_grouped :
tax_grouped [ key ] = tax
tax_grouped [ key ] [ ' base ' ] = line [ ' price_unit ' ] * line [ ' quantity ' ] * ( 1.0 - ( line [ ' discount ' ] or 0.0 ) / 100.0 )
else :
tax_grouped [ key ] [ ' amount ' ] + = tax [ ' amount ' ]
tax_grouped [ key ] [ ' base ' ] + = line [ ' price_unit ' ] * line [ ' quantity ' ] * ( 1.0 - ( line [ ' discount ' ] or 0.0 ) / 100.0 )
# delete automatic tax lines for this invoice
cr . execute ( " DELETE FROM account_invoice_tax WHERE NOT manual AND invoice_id= %d " , ( invoice_id , ) )
self . move_line_tax_create ( cr , uid , inv , tax_grouped , context )
return res
def move_line_tax_create ( self , cr , uid , inv , tax_grouped , context = { } ) :
# (re)create them
ait = self . pool . get ( ' account.invoice.tax ' )
for t in tax_grouped . values ( ) :
tax = self . pool . get ( ' account.tax ' ) . browse ( cr , uid , t [ ' id ' ] )
val = {
' invoice_id ' : inv . id ,
' name ' : t [ ' name ' ] ,
' account_id ' : t [ ' account_id ' ] ,
' amount ' : t [ ' amount ' ] ,
' manual ' : False ,
' sequence ' : t [ ' sequence ' ] ,
' base ' : t [ ' base ' ] ,
}
if inv . type in ( ' out_invoice ' , ' out_refund ' ) :
val . update ( {
' base_code_id ' : tax . base_code_id . id or False ,
' tax_code_id ' : tax . tax_code_id . id or False ,
' base_amount ' : tax . base_sign * t [ ' base ' ] ,
' tax_amount ' : tax . tax_sign * t [ ' amount ' ]
} )
elif inv . type in ( ' in_invoice ' , ' in_refund ' ) :
val . update ( {
' base_code_id ' : tax . ref_base_code_id . id or False ,
' tax_code_id ' : tax . ref_tax_code_id . id or False ,
' base_amount ' : tax . ref_base_sign * t [ ' base ' ] ,
' tax_amount ' : tax . ref_tax_sign * t [ ' amount ' ]
} )
if inv . type in ( ' out_refund ' , ' in_refund ' ) :
val . update ( {
' base_amount ' : - val [ ' base_amount ' ] ,
' tax_amount ' : - val [ ' tax_amount ' ]
} )
ait . create ( cr , uid , val )
#ait.create(cr, uid, {'invoice_id':invoice_id, 'name':t['name'], 'account_id':t['account_id'], 'amount':t['amount'], 'manual':False, 'sequence':t['sequence'], 'base':t['base']})
return True
#
# Set the tax field according to the account and the partner
#
def onchange_account_id ( self , cr , uid , ids , partner_id , account_id ) :
if not ( partner_id and account_id ) :
return { }
taxes = self . pool . get ( ' account.account ' ) . browse ( cr , uid , account_id ) . tax_ids
taxep = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_id ) . property_account_tax
if not taxep :
return { ' value ' : { ' invoice_line_tax_id ' : map ( lambda x : x . id , taxes or [ ] ) } }
res = [ taxep [ 0 ] ]
tp = self . pool . get ( ' account.tax ' ) . browse ( cr , uid , taxep [ 0 ] )
for t in taxes :
if not t . tax_group == tp . tax_group :
res . append ( t . id )
r = { ' value ' : { ' invoice_line_tax_id ' : res } }
return r
account_invoice_line ( )
class account_invoice_tax ( osv . osv ) :
_name = " account.invoice.tax "
_description = " Invoice Tax "
_columns = {
' invoice_id ' : fields . many2one ( ' account.invoice ' , ' Invoice Line ' , ondelete = ' cascade ' , select = True ) ,
' name ' : fields . char ( ' Tax Description ' , size = 64 , required = True ) ,
' account_id ' : fields . many2one ( ' account.account ' , ' Tax Account ' , required = True , domain = [ ( ' type ' , ' <> ' , ' view ' ) , ( ' type ' , ' <> ' , ' income ' ) ] ) ,
' base ' : fields . float ( ' Base ' , digits = ( 16 , 2 ) ) ,
' amount ' : fields . float ( ' Amount ' , digits = ( 16 , 2 ) ) ,
' manual ' : fields . boolean ( ' Manual ' ) ,
' sequence ' : fields . integer ( ' Sequence ' ) ,
' base_code_id ' : fields . many2one ( ' account.tax.code ' , ' Base Code ' ) ,
' base_amount ' : fields . float ( ' Base Code Amount ' , digits = ( 16 , 2 ) ) ,
' tax_code_id ' : fields . many2one ( ' account.tax.code ' , ' Tax Code ' ) ,
' tax_amount ' : fields . float ( ' Tax Code Amount ' , digits = ( 16 , 2 ) ) ,
}
_order = ' sequence '
_defaults = {
' manual ' : lambda * a : 1 ,
' base_amount ' : lambda * a : 0.0 ,
' tax_amount ' : lambda * a : 0.0 ,
}
def move_line_get ( self , cr , uid , invoice_id ) :
res = [ ]
cr . execute ( ' SELECT * FROM account_invoice_tax WHERE invoice_id= %d ' , ( invoice_id , ) )
for t in cr . dictfetchall ( ) :
res . append ( {
' type ' : ' tax ' ,
' name ' : t [ ' name ' ] ,
' price_unit ' : t [ ' amount ' ] ,
' quantity ' : 1 ,
' price ' : t [ ' amount ' ] or 0.0 ,
' account_id ' : t [ ' account_id ' ] ,
' tax_code_id ' : t [ ' tax_code_id ' ] ,
' tax_amount ' : t [ ' tax_amount ' ]
} )
return res
account_invoice_tax ( )