2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2009-11-13 05:41:16 +00:00
#
2009-02-11 13:32:54 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 11:15:34 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# 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
2009-10-14 11:15:34 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2009-11-13 05:41:16 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
import time
2012-12-06 14:56:32 +00:00
from openerp import netsvc
from openerp . osv import fields , osv
from openerp . tools . translate import _
import openerp . addons . decimal_precision as dp
2006-12-07 13:41:40 +00:00
2010-04-14 10:27:06 +00:00
def _employee_get ( obj , cr , uid , context = None ) :
2010-07-27 07:11:45 +00:00
if context is None :
context = { }
ids = obj . pool . get ( ' hr.employee ' ) . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] , context = context )
2008-07-22 15:11:28 +00:00
if ids :
return ids [ 0 ]
return False
2006-12-07 13:41:40 +00:00
class hr_expense_expense ( osv . osv ) :
2010-04-14 10:27:06 +00:00
2010-07-03 10:21:52 +00:00
def _amount ( self , cr , uid , ids , field_name , arg , context = None ) :
2012-07-13 11:38:12 +00:00
res = { }
for expense in self . browse ( cr , uid , ids , context = context ) :
total = 0.0
for line in expense . line_ids :
total + = line . unit_amount * line . unit_quantity
res [ expense . id ] = total
2008-07-22 15:11:28 +00:00
return res
2010-07-03 10:21:52 +00:00
def _get_currency ( self , cr , uid , context = None ) :
2010-07-27 07:11:45 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , [ uid ] , context = context ) [ 0 ]
2013-05-22 17:49:45 +00:00
return user . company_id . currency_id . id
2008-07-22 15:11:28 +00:00
_name = " hr.expense.expense "
2012-07-05 09:29:21 +00:00
_inherit = [ ' mail.thread ' ]
2010-05-19 18:32:32 +00:00
_description = " Expense "
2011-12-07 17:14:57 +00:00
_order = " id desc "
2012-12-18 16:57:08 +00:00
_track = {
' state ' : {
2012-12-18 23:49:07 +00:00
' hr_expense.mt_expense_approved ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] == ' accepted ' ,
' hr_expense.mt_expense_refused ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] == ' cancelled ' ,
' hr_expense.mt_expense_confirmed ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] == ' confirm ' ,
2012-12-18 16:57:08 +00:00
} ,
}
2008-07-22 15:11:28 +00:00
_columns = {
2012-10-23 12:14:07 +00:00
' name ' : fields . char ( ' Description ' , size = 128 , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
' id ' : fields . integer ( ' Sheet ID ' , readonly = True ) ,
2012-10-23 12:14:07 +00:00
' date ' : fields . date ( ' Date ' , select = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2012-07-17 10:42:14 +00:00
' journal_id ' : fields . many2one ( ' account.journal ' , ' Force Journal ' , help = " The journal used when the expense is done. " ) ,
2012-10-23 12:14:07 +00:00
' employee_id ' : fields . many2one ( ' hr.employee ' , " Employee " , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' User ' , required = True ) ,
2012-10-25 15:10:13 +00:00
' date_confirm ' : fields . date ( ' Confirmation Date ' , select = True , help = " Date of the confirmation of the sheet expense. It ' s filled when the button Confirm is pressed. " ) ,
' date_valid ' : fields . date ( ' Validation Date ' , select = True , help = " Date of the acceptation of the sheet expense. It ' s filled when the button Accept is pressed. " ) ,
2012-11-13 07:31:26 +00:00
' user_valid ' : fields . many2one ( ' res.users ' , ' Validation By ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2009-11-13 05:41:16 +00:00
' account_move_id ' : fields . many2one ( ' account.move ' , ' Ledger Posting ' ) ,
2009-03-09 07:05:47 +00:00
' line_ids ' : fields . one2many ( ' hr.expense.line ' , ' expense_id ' , ' Expense Lines ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
' note ' : fields . text ( ' Note ' ) ,
2012-05-10 13:48:31 +00:00
' amount ' : fields . function ( _amount , string = ' Total Amount ' , digits_compute = dp . get_precision ( ' Account ' ) ) ,
2012-08-16 05:28:46 +00:00
' voucher_id ' : fields . many2one ( ' account.voucher ' , " Employee ' s Receipt " ) ,
2012-10-23 12:14:07 +00:00
' currency_id ' : fields . many2one ( ' res.currency ' , ' Currency ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
' department_id ' : fields . many2one ( ' hr.department ' , ' Department ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirm ' : [ ( ' readonly ' , False ) ] } ) ,
2010-02-17 09:41:19 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
2008-07-22 15:11:28 +00:00
' state ' : fields . selection ( [
2011-09-15 05:43:40 +00:00
( ' draft ' , ' New ' ) ,
2012-04-27 09:51:37 +00:00
( ' cancelled ' , ' Refused ' ) ,
2012-05-01 12:34:46 +00:00
( ' confirm ' , ' Waiting Approval ' ) ,
2010-09-06 10:31:34 +00:00
( ' accepted ' , ' Approved ' ) ,
2013-05-22 17:49:45 +00:00
( ' done ' , ' Waiting Payment ' ) ,
( ' paid ' , ' Paid ' ) ,
2012-04-27 09:51:37 +00:00
] ,
2012-12-20 11:47:30 +00:00
' Status ' , readonly = True , track_visibility = ' onchange ' ,
2012-12-18 16:57:08 +00:00
help = ' When the expense request is created the status is \' Draft \' . \n It is confirmed by the user and request is sent to admin, the status is \' Waiting Confirmation \' . \
2013-05-22 17:49:45 +00:00
\nIf the admin accepts it , the status is \' Accepted \' . \n If the accounting entries are made for the expense request, the status is \' Waiting Payment \' . ' ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2011-11-28 08:06:57 +00:00
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' hr.employee ' , context = c ) ,
2012-02-13 18:07:41 +00:00
' date ' : fields . date . context_today ,
2010-08-19 09:45:46 +00:00
' state ' : ' draft ' ,
2010-10-27 12:49:59 +00:00
' employee_id ' : _employee_get ,
' user_id ' : lambda cr , uid , id , c = { } : id ,
2008-07-22 15:11:28 +00:00
' currency_id ' : _get_currency ,
}
2010-10-08 13:22:47 +00:00
2013-05-22 17:49:45 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
if default is None :
default = { }
2013-09-02 09:32:34 +00:00
default . update (
account_move_id = False ,
voucher_id = False ,
date_confirm = False ,
date_valid = False ,
user_valid = False )
2013-05-22 17:49:45 +00:00
return super ( hr_expense_expense , self ) . copy ( cr , uid , id , default = default , context = context )
2012-10-23 12:39:10 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
for rec in self . browse ( cr , uid , ids , context = context ) :
if rec . state != ' draft ' :
2012-11-13 07:31:26 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You can only delete draft expenses! ' ) )
2012-10-23 12:39:10 +00:00
return super ( hr_expense_expense , self ) . unlink ( cr , uid , ids , context )
2012-08-14 11:33:20 +00:00
def onchange_currency_id ( self , cr , uid , ids , currency_id = False , company_id = False , context = None ) :
res = { ' value ' : { ' journal_id ' : False } }
2012-08-16 07:14:52 +00:00
journal_ids = self . pool . get ( ' account.journal ' ) . search ( cr , uid , [ ( ' type ' , ' = ' , ' purchase ' ) , ( ' currency ' , ' = ' , currency_id ) , ( ' company_id ' , ' = ' , company_id ) ] , context = context )
if journal_ids :
res [ ' value ' ] [ ' journal_id ' ] = journal_ids [ 0 ]
2012-08-14 11:33:20 +00:00
return res
2010-10-08 13:22:47 +00:00
def onchange_employee_id ( self , cr , uid , ids , employee_id , context = None ) :
2011-06-15 13:13:26 +00:00
emp_obj = self . pool . get ( ' hr.employee ' )
2010-10-21 12:20:20 +00:00
department_id = False
2011-06-15 13:13:26 +00:00
company_id = False
2010-10-21 12:20:20 +00:00
if employee_id :
2011-06-15 13:13:26 +00:00
employee = emp_obj . browse ( cr , uid , employee_id , context = context )
2011-06-17 10:00:09 +00:00
department_id = employee . department_id . id
company_id = employee . company_id . id
return { ' value ' : { ' department_id ' : department_id , ' company_id ' : company_id } }
2010-10-08 13:22:47 +00:00
2012-12-18 16:57:08 +00:00
def expense_confirm ( self , cr , uid , ids , context = None ) :
2012-09-25 01:37:47 +00:00
for expense in self . browse ( cr , uid , ids ) :
2012-09-25 17:33:20 +00:00
if expense . employee_id and expense . employee_id . parent_id . user_id :
self . message_subscribe_users ( cr , uid , [ expense . id ] , user_ids = [ expense . employee_id . parent_id . user_id . id ] )
2012-12-18 16:57:08 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' confirm ' , ' date_confirm ' : time . strftime ( ' % Y- % m- %d ' ) } , context = context )
def expense_accept ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' state ' : ' accepted ' , ' date_valid ' : time . strftime ( ' % Y- % m- %d ' ) , ' user_valid ' : uid } , context = context )
def expense_canceled ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' state ' : ' cancelled ' } , context = context )
2008-07-22 15:11:28 +00:00
2013-03-06 14:05:54 +00:00
def account_move_get ( self , cr , uid , expense_id , context = None ) :
2013-03-04 13:53:29 +00:00
'''
This method prepare the creation of the account move related to the given expense .
: param expense_id : Id of voucher for which we are creating account_move .
: return : mapping between fieldname and value of account move to create
: rtype : dict
'''
2013-03-06 14:05:54 +00:00
journal_obj = self . pool . get ( ' account.journal ' )
expense = self . browse ( cr , uid , expense_id , context = context )
company_id = expense . company_id . id
date = expense . date_confirm
ref = expense . name
journal_id = False
if expense . journal_id :
journal_id = expense . journal_id . id
2013-03-04 13:53:29 +00:00
else :
2013-03-06 14:05:54 +00:00
journal_id = journal_obj . search ( cr , uid , [ ( ' type ' , ' = ' , ' purchase ' ) , ( ' company_id ' , ' = ' , company_id ) ] )
if not journal_id :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " No expense journal found. Please make sure you have a journal with type ' purchase ' configured. " ) )
journal_id = journal_id [ 0 ]
2013-03-06 14:38:37 +00:00
return self . pool . get ( ' account.move ' ) . account_move_prepare ( cr , uid , journal_id , date = date , ref = ref , company_id = company_id , context = context )
2013-03-04 13:53:29 +00:00
def line_get_convert ( self , cr , uid , x , part , date , context = None ) :
2013-03-07 09:50:31 +00:00
partner_id = self . pool . get ( ' res.partner ' ) . _find_accounting_partner ( part ) . id
2013-03-04 13:53:29 +00:00
return {
' date_maturity ' : x . get ( ' date_maturity ' , False ) ,
2013-03-06 14:05:54 +00:00
' partner_id ' : partner_id ,
2013-03-04 13:53:29 +00:00
' name ' : x [ ' name ' ] [ : 64 ] ,
' date ' : date ,
' 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 ' , False ) ,
' amount_currency ' : x [ ' price ' ] > 0 and abs ( x . get ( ' amount_currency ' , False ) ) or - abs ( 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 ) ,
' quantity ' : x . get ( ' quantity ' , 1.00 ) ,
' product_id ' : x . get ( ' product_id ' , False ) ,
' product_uom_id ' : x . get ( ' uos_id ' , False ) ,
' analytic_account_id ' : x . get ( ' account_analytic_id ' , False ) ,
}
2013-03-06 14:05:54 +00:00
def compute_expense_totals ( self , cr , uid , exp , company_currency , ref , account_move_lines , context = None ) :
'''
internal method used for computation of total amount of an expense in the company currency and
in the expense currency , given the account_move_lines that will be created . It also do some small
transformations at these account_move_lines ( for multi - currency purposes )
: param account_move_lines : list of dict
: rtype : tuple of 3 elements ( a , b , c )
a : total in company currency
b : total in hr . expense currency
c : account_move_lines potentially modified
'''
cur_obj = self . pool . get ( ' res.currency ' )
2012-07-25 13:31:54 +00:00
if context is None :
2013-03-04 13:53:29 +00:00
context = { }
2013-03-06 14:05:54 +00:00
context . update ( { ' date ' : exp . date_confirm or time . strftime ( ' % Y- % m- %d ' ) } )
total = 0.0
total_currency = 0.0
for i in account_move_lines :
if exp . currency_id . id != company_currency :
i [ ' currency_id ' ] = exp . currency_id . id
2013-03-04 13:53:29 +00:00
i [ ' amount_currency ' ] = i [ ' price ' ]
2013-03-06 14:05:54 +00:00
i [ ' price ' ] = cur_obj . compute ( cr , uid , exp . currency_id . id ,
2013-03-04 13:53:29 +00:00
company_currency , i [ ' price ' ] ,
context = context )
2012-08-14 11:33:20 +00:00
else :
2013-03-04 13:53:29 +00:00
i [ ' amount_currency ' ] = False
i [ ' currency_id ' ] = False
total - = i [ ' price ' ]
total_currency - = i [ ' amount_currency ' ] or i [ ' price ' ]
2013-03-06 14:05:54 +00:00
return total , total_currency , account_move_lines
2013-03-04 13:53:29 +00:00
2013-03-13 14:37:26 +00:00
def action_receipt_create ( self , cr , uid , ids , context = None ) :
2013-03-13 12:44:09 +00:00
'''
main function that is called when trying to create the accounting entries related to an expense
'''
2013-03-04 13:53:29 +00:00
move_obj = self . pool . get ( ' account.move ' )
for exp in self . browse ( cr , uid , ids , context = context ) :
2010-12-07 11:08:35 +00:00
if not exp . employee_id . address_home_id :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' The employee must have a home address. ' ) )
2013-03-27 10:15:49 +00:00
if not exp . employee_id . address_home_id . property_account_payable . id :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' The employee must have a payable account set on his home address. ' ) )
2013-03-06 14:05:54 +00:00
company_currency = exp . company_id . currency_id . id
diff_currency_p = exp . currency_id . id < > company_currency
2013-03-04 13:53:29 +00:00
2013-03-06 14:05:54 +00:00
#create the move that will contain the accounting entries
move_id = move_obj . create ( cr , uid , self . account_move_get ( cr , uid , exp . id , context = context ) , context = context )
2013-03-13 12:44:09 +00:00
#one account.move.line per expense line (+taxes..)
eml = self . move_line_get ( cr , uid , exp . id , context = context )
2013-03-04 13:53:29 +00:00
2013-03-13 12:44:09 +00:00
#create one more move line, a counterline for the total on payable account
total , total_currency , eml = self . compute_expense_totals ( cr , uid , exp , company_currency , exp . name , eml , context = context )
2012-03-07 10:45:30 +00:00
acc = exp . employee_id . address_home_id . property_account_payable . id
2013-03-13 12:44:09 +00:00
eml . append ( {
2013-03-06 14:05:54 +00:00
' type ' : ' dest ' ,
' name ' : ' / ' ,
' price ' : total ,
2013-03-04 13:53:29 +00:00
' account_id ' : acc ,
' date_maturity ' : exp . date_confirm ,
' amount_currency ' : diff_currency_p and total_currency or False ,
' currency_id ' : diff_currency_p and exp . currency_id . id or False ,
' ref ' : exp . name
} )
2013-03-13 12:44:09 +00:00
#convert eml into an osv-valid format
lines = map ( lambda x : ( 0 , 0 , self . line_get_convert ( cr , uid , x , exp . employee_id . address_home_id , exp . date_confirm , context = context ) ) , eml )
2014-02-10 13:15:14 +00:00
journal_id = move_obj . browse ( cr , uid , move_id , context ) . journal_id
# post the journal entry if 'Skip 'Draft' State for Manual Entries' is checked
if journal_id . entry_posted :
move_obj . button_validate ( cr , uid , [ move_id ] , context )
2013-03-06 14:05:54 +00:00
move_obj . write ( cr , uid , [ move_id ] , { ' line_id ' : lines } , context = context )
self . write ( cr , uid , ids , { ' account_move_id ' : move_id , ' state ' : ' done ' } , context = context )
2012-07-17 09:07:38 +00:00
return True
2013-03-04 13:53:29 +00:00
def move_line_get ( self , cr , uid , expense_id , context = None ) :
res = [ ]
tax_obj = self . pool . get ( ' account.tax ' )
cur_obj = self . pool . get ( ' res.currency ' )
if context is None :
context = { }
exp = self . browse ( cr , uid , expense_id , context = context )
company_currency = exp . company_id . currency_id . id
for line in exp . line_ids :
mres = self . move_line_get_item ( cr , uid , line , context )
if not mres :
continue
res . append ( mres )
#Calculate tax according to default tax on product
2013-03-07 09:49:58 +00:00
taxes = [ ]
2013-03-04 13:53:29 +00:00
#Taken from product_id_onchange in account.invoice
if line . product_id :
fposition_id = False
fpos_obj = self . pool . get ( ' account.fiscal.position ' )
fpos = fposition_id and fpos_obj . browse ( cr , uid , fposition_id , context = context ) or False
product = line . product_id
taxes = product . supplier_taxes_id
#If taxes are not related to the product, maybe they are in the account
if not taxes :
a = product . property_account_expense . id #Why is not there a check here?
if not a :
a = product . categ_id . property_account_expense_categ . id
a = fpos_obj . map_account ( cr , uid , fpos , a )
taxes = a and self . pool . get ( ' account.account ' ) . browse ( cr , uid , a , context = context ) . tax_ids or False
2013-03-04 15:55:50 +00:00
if not taxes :
2013-03-07 09:49:58 +00:00
continue
2014-09-09 11:43:04 +00:00
tax_l = [ ]
2014-09-30 13:31:52 +00:00
base_tax_amount = line . total_amount
2013-03-04 13:53:29 +00:00
#Calculating tax on the line and creating move?
for tax in tax_obj . compute_all ( cr , uid , taxes ,
line . unit_amount ,
line . unit_quantity , line . product_id ,
exp . user_id . partner_id ) [ ' taxes ' ] :
tax_code_id = tax [ ' base_code_id ' ]
2014-09-09 11:43:04 +00:00
if not tax_code_id :
2013-03-04 13:53:29 +00:00
continue
res [ - 1 ] [ ' tax_code_id ' ] = tax_code_id
2013-05-22 09:40:17 +00:00
##
is_price_include = tax_obj . read ( cr , uid , tax [ ' id ' ] , [ ' price_include ' ] , context ) [ ' price_include ' ]
if is_price_include :
## We need to deduce the price for the tax
res [ - 1 ] [ ' price ' ] = res [ - 1 ] [ ' price ' ] - ( tax [ ' amount ' ] * tax [ ' base_sign ' ] or 0.0 )
2014-09-10 16:06:38 +00:00
# tax amount countains base amount without the tax
2014-09-30 13:31:52 +00:00
base_tax_amount = ( base_tax_amount - tax [ ' amount ' ] ) * tax [ ' base_sign ' ]
2014-09-10 16:06:38 +00:00
else :
2014-09-30 13:31:52 +00:00
base_tax_amount = base_tax_amount * tax [ ' base_sign ' ]
2013-03-04 13:53:29 +00:00
assoc_tax = {
' type ' : ' tax ' ,
' name ' : tax [ ' name ' ] ,
' price_unit ' : tax [ ' price_unit ' ] ,
' quantity ' : 1 ,
' price ' : tax [ ' amount ' ] * tax [ ' base_sign ' ] or 0.0 ,
2013-04-04 16:41:53 +00:00
' account_id ' : tax [ ' account_collected_id ' ] or mres [ ' account_id ' ] ,
2013-03-04 13:53:29 +00:00
' tax_code_id ' : tax [ ' tax_code_id ' ] ,
' tax_amount ' : tax [ ' amount ' ] * tax [ ' base_sign ' ] ,
}
2014-09-09 11:43:04 +00:00
tax_l . append ( assoc_tax )
2014-09-30 13:31:52 +00:00
res [ - 1 ] [ ' tax_amount ' ] = cur_obj . compute ( cr , uid , exp . currency_id . id , company_currency , base_tax_amount , context = { ' date ' : exp . date_confirm } )
2014-09-09 11:43:04 +00:00
res + = tax_l
2013-03-04 13:53:29 +00:00
return res
def move_line_get_item ( self , cr , uid , line , context = None ) :
company = line . expense_id . company_id
property_obj = self . pool . get ( ' ir.property ' )
if line . product_id :
acc = line . product_id . property_account_expense
if not acc :
acc = line . product_id . categ_id . property_account_expense_categ
2013-03-27 10:15:49 +00:00
if not acc :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' No purchase account found for the product %s (or for his category), please configure one. ' ) % ( line . product_id . name ) )
2013-03-04 13:53:29 +00:00
else :
acc = property_obj . get ( cr , uid , ' property_account_expense_categ ' , ' product.category ' , context = { ' force_company ' : company . id } )
if not acc :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please configure Default Expense account for Product purchase: `property_account_expense_categ`. ' ) )
return {
' type ' : ' src ' ,
' name ' : line . name . split ( ' \n ' ) [ 0 ] [ : 64 ] ,
' price_unit ' : line . unit_amount ,
' quantity ' : line . unit_quantity ,
' price ' : line . total_amount ,
' account_id ' : acc . id ,
' product_id ' : line . product_id . id ,
' uos_id ' : line . uom_id . id ,
' account_analytic_id ' : line . analytic_account . id ,
}
2012-07-13 12:39:52 +00:00
def action_view_receipt ( self , cr , uid , ids , context = None ) :
'''
2013-03-06 16:39:31 +00:00
This function returns an action that display existing account . move of given expense ids .
2012-07-13 12:39:52 +00:00
'''
2012-07-16 07:11:50 +00:00
assert len ( ids ) == 1 , ' This option should only be used for a single id at a time '
2013-03-06 17:17:36 +00:00
expense = self . browse ( cr , uid , ids [ 0 ] , context = context )
assert expense . account_move_id
2013-03-06 16:39:31 +00:00
try :
dummy , view_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' account ' , ' view_move_form ' )
2013-03-06 17:17:36 +00:00
except ValueError , e :
2013-03-06 16:39:31 +00:00
view_id = False
2012-07-13 12:39:52 +00:00
result = {
2013-03-06 16:39:31 +00:00
' name ' : _ ( ' Expense Account Move ' ) ,
2012-07-13 12:39:52 +00:00
' view_type ' : ' form ' ,
' view_mode ' : ' form ' ,
2013-03-06 16:39:31 +00:00
' view_id ' : view_id ,
' res_model ' : ' account.move ' ,
2012-07-13 12:39:52 +00:00
' type ' : ' ir.actions.act_window ' ,
' nodestroy ' : True ,
' target ' : ' current ' ,
2013-03-06 17:17:36 +00:00
' res_id ' : expense . account_move_id . id ,
2012-07-13 12:39:52 +00:00
}
return result
2010-07-03 10:21:52 +00:00
2006-12-07 13:41:40 +00:00
hr_expense_expense ( )
2009-04-02 09:44:17 +00:00
class product_product ( osv . osv ) :
_inherit = " product.product "
_columns = {
2012-11-02 17:46:03 +00:00
' hr_expense_ok ' : fields . boolean ( ' Can be Expensed ' , help = " Specify if the product can be selected in an HR expense line. " ) ,
2010-08-26 04:52:04 +00:00
}
2009-11-13 05:41:16 +00:00
2009-04-02 09:44:17 +00:00
product_product ( )
2006-12-07 13:41:40 +00:00
class hr_expense_line ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " hr.expense.line "
_description = " Expense Line "
2010-04-14 10:27:06 +00:00
2010-07-03 10:21:52 +00:00
def _amount ( self , cr , uid , ids , field_name , arg , context = None ) :
2010-10-27 12:49:59 +00:00
if not ids :
2008-07-22 15:11:28 +00:00
return { }
2010-06-10 13:34:19 +00:00
cr . execute ( " SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id IN %s GROUP BY l.id " , ( tuple ( ids ) , ) )
2008-07-22 15:11:28 +00:00
res = dict ( cr . fetchall ( ) )
return res
2012-08-09 08:44:30 +00:00
def _get_uom_id ( self , cr , uid , context = None ) :
2012-08-16 12:36:17 +00:00
result = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' product ' , ' product_uom_unit ' )
return result and result [ 1 ] or False
2012-08-09 08:44:30 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2010-10-17 22:08:23 +00:00
' name ' : fields . char ( ' Expense Note ' , size = 128 , required = True ) ,
2008-07-22 15:11:28 +00:00
' date_value ' : fields . date ( ' Date ' , required = True ) ,
' expense_id ' : fields . many2one ( ' hr.expense.expense ' , ' Expense ' , ondelete = ' cascade ' , select = True ) ,
2011-07-01 23:41:24 +00:00
' total_amount ' : fields . function ( _amount , string = ' Total ' , digits_compute = dp . get_precision ( ' Account ' ) ) ,
2012-07-11 13:06:15 +00:00
' unit_amount ' : fields . float ( ' Unit Price ' , digits_compute = dp . get_precision ( ' Product Price ' ) ) ,
2012-07-10 12:28:46 +00:00
' unit_quantity ' : fields . float ( ' Quantities ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ) ,
2009-04-02 09:55:23 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , domain = [ ( ' hr_expense_ok ' , ' = ' , True ) ] ) ,
2012-08-09 08:44:30 +00:00
' uom_id ' : fields . many2one ( ' product.uom ' , ' Unit of Measure ' , required = True ) ,
2008-07-22 15:11:28 +00:00
' description ' : fields . text ( ' Description ' ) ,
' analytic_account ' : fields . many2one ( ' account.analytic.account ' , ' Analytic account ' ) ,
' ref ' : fields . char ( ' Reference ' , size = 32 ) ,
2011-12-09 06:03:08 +00:00
' sequence ' : fields . integer ( ' Sequence ' , select = True , help = " Gives the sequence order when displaying a list of expense lines. " ) ,
2010-07-03 10:21:52 +00:00
}
2008-07-22 15:11:28 +00:00
_defaults = {
2010-07-03 10:21:52 +00:00
' unit_quantity ' : 1 ,
2010-11-04 12:42:42 +00:00
' date_value ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
2012-08-09 08:44:30 +00:00
' uom_id ' : _get_uom_id ,
2010-08-26 04:52:04 +00:00
}
2010-11-04 07:23:19 +00:00
_order = " sequence, date_value desc "
2010-04-14 10:27:06 +00:00
2012-08-09 08:44:30 +00:00
def onchange_product_id ( self , cr , uid , ids , product_id , context = None ) :
2010-10-21 12:20:20 +00:00
res = { }
2008-07-22 15:11:28 +00:00
if product_id :
2010-07-27 07:11:45 +00:00
product = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id , context = context )
2010-10-21 12:20:20 +00:00
res [ ' name ' ] = product . name
2011-09-13 13:45:51 +00:00
amount_unit = product . price_get ( ' standard_price ' ) [ product . id ]
2010-10-21 12:20:20 +00:00
res [ ' unit_amount ' ] = amount_unit
2012-08-09 08:44:30 +00:00
res [ ' uom_id ' ] = product . uom_id . id
2010-10-21 12:20:20 +00:00
return { ' value ' : res }
2006-12-07 13:41:40 +00:00
2012-08-09 08:44:30 +00:00
def onchange_uom ( self , cr , uid , ids , product_id , uom_id , context = None ) :
res = { ' value ' : { } }
2012-08-16 13:41:49 +00:00
if not uom_id or not product_id :
2012-08-16 12:36:17 +00:00
return res
2012-08-16 13:41:49 +00:00
product = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id , context = context )
uom = self . pool . get ( ' product.uom ' ) . browse ( cr , uid , uom_id , context = context )
if uom . category_id . id != product . uom_id . category_id . id :
res [ ' warning ' ] = { ' title ' : _ ( ' Warning ' ) , ' message ' : _ ( ' Selected Unit of Measure does not belong to the same category as the product Unit of Measure ' ) }
2012-08-17 08:42:42 +00:00
res [ ' value ' ] . update ( { ' uom_id ' : product . uom_id . id } )
2012-08-09 08:44:30 +00:00
return res
2006-12-07 13:41:40 +00:00
hr_expense_line ( )
2013-05-22 17:49:45 +00:00
class account_move_line ( osv . osv ) :
_inherit = " account.move.line "
def reconcile ( self , cr , uid , ids , type = ' auto ' , writeoff_acc_id = False , writeoff_period_id = False , writeoff_journal_id = False , context = None ) :
res = super ( account_move_line , self ) . reconcile ( cr , uid , ids , type = type , writeoff_acc_id = writeoff_acc_id , writeoff_period_id = writeoff_period_id , writeoff_journal_id = writeoff_journal_id , context = context )
#when making a full reconciliation of account move lines 'ids', we may need to recompute the state of some hr.expense
account_move_ids = [ aml . move_id . id for aml in self . browse ( cr , uid , ids , context = context ) ]
expense_obj = self . pool . get ( ' hr.expense.expense ' )
currency_obj = self . pool . get ( ' res.currency ' )
if account_move_ids :
expense_ids = expense_obj . search ( cr , uid , [ ( ' account_move_id ' , ' in ' , account_move_ids ) ] , context = context )
for expense in expense_obj . browse ( cr , uid , expense_ids , context = context ) :
if expense . state == ' done ' :
#making the postulate it has to be set paid, then trying to invalidate it
new_status_is_paid = True
for aml in expense . account_move_id . line_id :
if aml . account_id . type == ' payable ' and not currency_obj . is_zero ( cr , uid , expense . company_id . currency_id , aml . amount_residual ) :
new_status_is_paid = False
if new_status_is_paid :
expense_obj . write ( cr , uid , [ expense . id ] , { ' state ' : ' paid ' } , context = context )
return res
2010-04-18 15:32:44 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: