2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-07-27 07:11:45 +00:00
#
2009-10-14 11:15:34 +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
2010-07-27 07:11:45 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2012-11-27 12:07:37 +00:00
import time
2006-12-07 13:41:40 +00:00
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
2007-09-11 14:14:18 +00:00
2006-12-07 13:41:40 +00:00
class hr_timesheet_invoice_factor ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " hr_timesheet_invoice.factor "
2010-05-19 18:32:32 +00:00
_description = " Invoice Rate "
2012-11-03 13:45:12 +00:00
_order = ' factor '
2008-07-22 15:11:28 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Internal Name ' , required = True , translate = True ) ,
' customer_name ' : fields . char ( ' Name ' , help = " Label for the customer " ) ,
2010-08-19 09:38:02 +00:00
' factor ' : fields . float ( ' Discount ( % ) ' , required = True , help = " Discount in percentage " ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
' factor ' : lambda * a : 0.0 ,
}
2007-09-11 14:14:18 +00:00
2006-12-07 13:41:40 +00:00
2007-09-11 14:14:18 +00:00
2006-12-07 13:41:40 +00:00
class account_analytic_account ( osv . osv ) :
2010-07-27 07:11:45 +00:00
def _invoiced_calc ( self , cr , uid , ids , name , arg , context = None ) :
obj_invoice = self . pool . get ( ' account.invoice ' )
2008-07-22 15:11:28 +00:00
res = { }
2010-09-23 14:23:34 +00:00
2010-10-17 17:30:00 +00:00
cr . execute ( ' SELECT account_id as account_id, l.invoice_id '
' FROM hr_analytic_timesheet h LEFT JOIN account_analytic_line l '
' ON (h.line_id=l.id) '
' WHERE l.account_id = ANY( %s ) ' , ( ids , ) )
2010-09-23 14:23:34 +00:00
account_to_invoice_map = { }
for rec in cr . dictfetchall ( ) :
account_to_invoice_map . setdefault ( rec [ ' account_id ' ] , [ ] ) . append ( rec [ ' invoice_id ' ] )
2010-07-27 07:11:45 +00:00
for account in self . browse ( cr , uid , ids , context = context ) :
2010-09-23 14:23:34 +00:00
invoice_ids = filter ( None , list ( set ( account_to_invoice_map . get ( account . id , [ ] ) ) ) )
2010-07-27 07:11:45 +00:00
for invoice in obj_invoice . browse ( cr , uid , invoice_ids , context = context ) :
2008-07-22 15:11:28 +00:00
res . setdefault ( account . id , 0.0 )
res [ account . id ] + = invoice . amount_untaxed
for id in ids :
res [ id ] = round ( res . get ( id , 0.0 ) , 2 )
2010-09-23 14:23:34 +00:00
return res
2009-12-17 15:12:33 +00:00
2008-07-22 15:11:28 +00:00
_inherit = " account.analytic.account "
_columns = {
2012-05-30 13:34:36 +00:00
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Pricelist ' ,
2012-07-13 10:17:51 +00:00
help = " The product to invoice is defined on the employee form, the price will be deducted by this pricelist on the product. " ) ,
2011-12-18 19:36:38 +00:00
' amount_max ' : fields . float ( ' Max. Invoice Price ' ,
help = " Keep empty if this contract is not limited to a total fixed price. " ) ,
2011-07-01 23:41:24 +00:00
' amount_invoiced ' : fields . function ( _invoiced_calc , string = ' Invoiced Amount ' ,
2008-10-27 15:29:13 +00:00
help = " Total invoiced " ) ,
2012-06-25 15:55:40 +00:00
' to_invoice ' : fields . many2one ( ' hr_timesheet_invoice.factor ' , ' Timesheet Invoicing Ratio ' ,
2012-12-21 16:48:08 +00:00
help = " You usually invoice 100 % o f the timesheets. But if you mix fixed price and timesheet invoicing, you may use another ratio. For instance, if you do a 20 % a dvance invoice (fixed price, based on a sales order), you should invoice the rest on timesheet with a 80 % r atio. " ) ,
2008-10-27 15:29:13 +00:00
}
2012-06-15 08:12:54 +00:00
2012-10-02 10:29:15 +00:00
def on_change_partner_id ( self , cr , uid , ids , partner_id , name , context = None ) :
2012-09-19 13:39:50 +00:00
res = super ( account_analytic_account , self ) . on_change_partner_id ( cr , uid , ids , partner_id , name , context = context )
2014-01-13 16:56:47 +00:00
if partner_id :
part = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_id , context = context )
pricelist = part . property_product_pricelist and part . property_product_pricelist . id or False
if pricelist :
res [ ' value ' ] [ ' pricelist_id ' ] = pricelist
2011-11-16 14:59:58 +00:00
return res
2010-12-22 11:05:57 +00:00
def set_close ( self , cr , uid , ids , context = None ) :
2012-12-19 17:31:27 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' close ' } , context = context )
2012-03-30 09:51:17 +00:00
2010-12-22 11:05:57 +00:00
def set_cancel ( self , cr , uid , ids , context = None ) :
2012-12-19 17:31:27 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' cancelled ' } , context = context )
2012-03-30 09:51:17 +00:00
2010-12-22 11:05:57 +00:00
def set_open ( self , cr , uid , ids , context = None ) :
2012-12-19 17:31:27 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' open ' } , context = context )
2012-03-30 09:51:17 +00:00
2010-12-22 11:05:57 +00:00
def set_pending ( self , cr , uid , ids , context = None ) :
2012-12-19 17:31:27 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' pending ' } , context = context )
2006-12-07 13:41:40 +00:00
2007-09-11 14:14:18 +00:00
2006-12-07 13:41:40 +00:00
class account_analytic_line ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = ' account.analytic.line '
_columns = {
2010-07-27 07:11:45 +00:00
' invoice_id ' : fields . many2one ( ' account.invoice ' , ' Invoice ' , ondelete = " set null " ) ,
2012-11-03 13:45:12 +00:00
' to_invoice ' : fields . many2one ( ' hr_timesheet_invoice.factor ' , ' Invoiceable ' , help = " It allows to set the discount while making invoice, keep empty if the activities should not be invoiced. " ) ,
2008-07-22 15:11:28 +00:00
}
2012-07-31 10:54:02 +00:00
def _default_journal ( self , cr , uid , context = None ) :
proxy = self . pool . get ( ' hr.employee ' )
record_ids = proxy . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] , context = context )
2012-10-09 11:59:28 +00:00
if record_ids :
employee = proxy . browse ( cr , uid , record_ids [ 0 ] , context = context )
return employee . journal_id and employee . journal_id . id or False
return False
2012-07-31 10:54:02 +00:00
def _default_general_account ( self , cr , uid , context = None ) :
proxy = self . pool . get ( ' hr.employee ' )
record_ids = proxy . search ( cr , uid , [ ( ' user_id ' , ' = ' , uid ) ] , context = context )
2012-10-09 11:59:28 +00:00
if record_ids :
employee = proxy . browse ( cr , uid , record_ids [ 0 ] , context = context )
if employee . product_id and employee . product_id . property_account_income :
return employee . product_id . property_account_income . id
2012-07-31 10:54:02 +00:00
return False
_defaults = {
' journal_id ' : _default_journal ,
' general_account_id ' : _default_general_account ,
}
2008-07-22 15:11:28 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2010-07-27 07:11:45 +00:00
self . _check_inv ( cr , uid , ids , vals )
2008-07-22 15:11:28 +00:00
return super ( account_analytic_line , self ) . write ( cr , uid , ids , vals ,
context = context )
2010-07-27 07:11:45 +00:00
def _check_inv ( self , cr , uid , ids , vals ) :
2008-07-22 15:11:28 +00:00
select = ids
if isinstance ( select , ( int , long ) ) :
select = [ ids ]
2009-12-17 15:12:33 +00:00
if ( not vals . has_key ( ' invoice_id ' ) ) or vals [ ' invoice_id ' ] == False :
for line in self . browse ( cr , uid , select ) :
if line . invoice_id :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) ,
2011-09-18 13:53:10 +00:00
_ ( ' You cannot modify an invoiced analytic line! ' ) )
2008-07-22 15:11:28 +00:00
return True
def copy ( self , cursor , user , obj_id , default = None , context = None ) :
if default is None :
default = { }
default = default . copy ( )
default . update ( { ' invoice_id ' : False } )
return super ( account_analytic_line , self ) . copy ( cursor , user , obj_id ,
2010-07-27 07:11:45 +00:00
default , context = context )
2007-12-11 08:14:37 +00:00
2012-11-27 12:07:37 +00:00
def _get_invoice_price ( self , cr , uid , account , product_id , user_id , qty , context = { } ) :
pro_price_obj = self . pool . get ( ' product.pricelist ' )
if account . pricelist_id :
pl = account . pricelist_id . id
price = pro_price_obj . price_get ( cr , uid , [ pl ] , product_id , qty or 1.0 , account . partner_id . id , context = context ) [ pl ]
else :
price = 0.0
return price
def invoice_cost_create ( self , cr , uid , ids , data = None , context = None ) :
analytic_account_obj = self . pool . get ( ' account.analytic.account ' )
account_payment_term_obj = self . pool . get ( ' account.payment.term ' )
invoice_obj = self . pool . get ( ' account.invoice ' )
product_obj = self . pool . get ( ' product.product ' )
invoice_factor_obj = self . pool . get ( ' hr_timesheet_invoice.factor ' )
fiscal_pos_obj = self . pool . get ( ' account.fiscal.position ' )
product_uom_obj = self . pool . get ( ' product.uom ' )
invoice_line_obj = self . pool . get ( ' account.invoice.line ' )
invoices = [ ]
if context is None :
context = { }
if data is None :
data = { }
journal_types = { }
2013-07-25 10:04:02 +00:00
# prepare for iteration on journal and accounts
2012-11-27 12:07:37 +00:00
for line in self . pool . get ( ' account.analytic.line ' ) . browse ( cr , uid , ids , context = context ) :
if line . journal_id . type not in journal_types :
journal_types [ line . journal_id . type ] = set ( )
journal_types [ line . journal_id . type ] . add ( line . account_id . id )
for journal_type , account_ids in journal_types . items ( ) :
for account in analytic_account_obj . browse ( cr , uid , list ( account_ids ) , context = context ) :
partner = account . partner_id
if ( not partner ) or not ( account . pricelist_id ) :
2013-06-07 11:38:29 +00:00
raise osv . except_osv ( _ ( ' Analytic Account Incomplete! ' ) ,
2012-11-28 11:40:02 +00:00
_ ( ' Contract incomplete. Please fill in the Customer and Pricelist fields. ' ) )
2012-11-27 12:07:37 +00:00
date_due = False
if partner . property_payment_term :
pterm_list = account_payment_term_obj . compute ( cr , uid ,
partner . property_payment_term . id , value = 1 ,
date_ref = time . strftime ( ' % Y- % m- %d ' ) )
if pterm_list :
pterm_list = [ line [ 0 ] for line in pterm_list ]
pterm_list . sort ( )
date_due = pterm_list [ - 1 ]
curr_invoice = {
' name ' : time . strftime ( ' %d / % m/ % Y ' ) + ' - ' + account . name ,
' partner_id ' : account . partner_id . id ,
' company_id ' : account . company_id . id ,
' payment_term ' : partner . property_payment_term . id or False ,
' account_id ' : partner . property_account_receivable . id ,
' currency_id ' : account . pricelist_id . currency_id . id ,
' date_due ' : date_due ,
' fiscal_position ' : account . partner_id . property_account_position . id
}
context2 = context . copy ( )
context2 [ ' lang ' ] = partner . lang
# set company_id in context, so the correct default journal will be selected
context2 [ ' force_company ' ] = curr_invoice [ ' company_id ' ]
# set force_company in context so the correct product properties are selected (eg. income account)
context2 [ ' company_id ' ] = curr_invoice [ ' company_id ' ]
last_invoice = invoice_obj . create ( cr , uid , curr_invoice , context = context2 )
invoices . append ( last_invoice )
2013-07-25 10:04:02 +00:00
cr . execute ( """ SELECT product_id, user_id, to_invoice, sum(amount), sum(unit_amount), product_uom_id
2012-11-27 12:07:37 +00:00
FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON ( line . journal_id = journal . id )
WHERE account_id = % s
2012-11-28 11:26:45 +00:00
AND line . id IN % s AND journal . type = % s AND to_invoice IS NOT NULL
2012-11-27 12:07:37 +00:00
GROUP BY product_id , user_id , to_invoice , product_uom_id """ , (account.id, tuple(ids), journal_type))
2013-07-25 10:04:02 +00:00
for product_id , user_id , factor_id , total_price , qty , uom in cr . fetchall ( ) :
context2 . update ( { ' uom ' : uom } )
2012-11-27 12:07:37 +00:00
if data . get ( ' product ' ) :
2013-07-25 10:04:02 +00:00
# force product, use its public price
2012-11-27 12:07:37 +00:00
product_id = data [ ' product ' ] [ 0 ]
2013-07-25 10:04:02 +00:00
unit_price = self . _get_invoice_price ( cr , uid , account , product_id , user_id , qty , context2 )
elif journal_type == ' general ' and product_id :
# timesheets, use sale price
unit_price = self . _get_invoice_price ( cr , uid , account , product_id , user_id , qty , context2 )
else :
# expenses, using price from amount field
unit_price = total_price * - 1.0 / qty
2012-11-27 12:07:37 +00:00
factor = invoice_factor_obj . browse ( cr , uid , factor_id , context = context2 )
2013-07-25 10:04:02 +00:00
# factor_name = factor.customer_name and line_name + ' - ' + factor.customer_name or line_name
factor_name = factor . customer_name
2012-11-27 12:07:37 +00:00
curr_line = {
2013-07-25 10:04:02 +00:00
' price_unit ' : unit_price ,
2012-11-27 12:07:37 +00:00
' quantity ' : qty ,
2013-07-25 10:04:02 +00:00
' product_id ' : product_id or False ,
2013-06-21 12:26:39 +00:00
' discount ' : factor . factor ,
2012-11-27 12:07:37 +00:00
' invoice_id ' : last_invoice ,
' name ' : factor_name ,
' uos_id ' : uom ,
' account_analytic_id ' : account . id ,
}
2013-07-25 10:04:02 +00:00
product = product_obj . browse ( cr , uid , product_id , context = context2 )
2013-06-12 08:45:04 +00:00
if product :
factor_name = product_obj . name_get ( cr , uid , [ product_id ] , context = context2 ) [ 0 ] [ 1 ]
if factor . customer_name :
factor_name + = ' - ' + factor . customer_name
general_account = product . property_account_income or product . categ_id . property_account_income_categ
if not general_account :
raise osv . except_osv ( _ ( " Configuration Error! " ) , _ ( " Please define income account for product ' %s ' . " ) % product . name )
taxes = product . taxes_id or general_account . tax_ids
tax = fiscal_pos_obj . map_tax ( cr , uid , account . partner_id . property_account_position , taxes )
curr_line . update ( {
2013-06-21 12:26:39 +00:00
' invoice_line_tax_id ' : [ ( 6 , 0 , tax ) ] ,
' name ' : factor_name ,
' invoice_line_tax_id ' : [ ( 6 , 0 , tax ) ] ,
' account_id ' : general_account . id ,
} )
2012-11-27 12:07:37 +00:00
#
# Compute for lines
#
cr . execute ( " SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id= %s and to_invoice= %s ORDER BY account_analytic_line.date " , ( account . id , tuple ( ids ) , product_id , factor_id ) )
line_ids = cr . dictfetchall ( )
note = [ ]
for line in line_ids :
# set invoice_line_note
details = [ ]
if data . get ( ' date ' , False ) :
details . append ( line [ ' date ' ] )
if data . get ( ' time ' , False ) :
if line [ ' product_uom_id ' ] :
details . append ( " %s %s " % ( line [ ' unit_amount ' ] , product_uom_obj . browse ( cr , uid , [ line [ ' product_uom_id ' ] ] , context2 ) [ 0 ] . name ) )
else :
details . append ( " %s " % ( line [ ' unit_amount ' ] , ) )
if data . get ( ' name ' , False ) :
details . append ( line [ ' name ' ] )
note . append ( u ' - ' . join ( map ( lambda x : unicode ( x ) or ' ' , details ) ) )
if note :
curr_line [ ' name ' ] + = " \n " + ( " \n " . join ( map ( lambda x : unicode ( x ) or ' ' , note ) ) )
invoice_line_obj . create ( cr , uid , curr_line , context = context )
cr . execute ( " update account_analytic_line set invoice_id= %s WHERE account_id = %s and id IN %s " , ( last_invoice , account . id , tuple ( ids ) ) )
invoice_obj . button_reset_taxes ( cr , uid , [ last_invoice ] , context )
return invoices
2006-12-07 13:41:40 +00:00
2007-09-11 14:14:18 +00:00
2007-06-25 14:36:49 +00:00
class hr_analytic_timesheet ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = " hr.analytic.timesheet "
2012-10-11 17:41:26 +00:00
def on_change_account_id ( self , cr , uid , ids , account_id , user_id = False ) :
2008-07-22 15:11:28 +00:00
res = { }
if not account_id :
return res
res . setdefault ( ' value ' , { } )
2008-11-21 11:04:55 +00:00
acc = self . pool . get ( ' account.analytic.account ' ) . browse ( cr , uid , account_id )
st = acc . to_invoice . id
2008-07-22 15:11:28 +00:00
res [ ' value ' ] [ ' to_invoice ' ] = st or False
2008-11-21 11:04:55 +00:00
if acc . state == ' pending ' :
res [ ' warning ' ] = {
' title ' : ' Warning ' ,
' message ' : ' The analytic account is in pending state. \n You should not work on this account ! '
}
2008-07-22 15:11:28 +00:00
return res
def copy ( self , cursor , user , obj_id , default = None , context = None ) :
if default is None :
default = { }
default = default . copy ( )
default . update ( { ' invoice_id ' : False } )
return super ( hr_analytic_timesheet , self ) . copy ( cursor , user , obj_id ,
2010-07-27 07:11:45 +00:00
default , context = context )
2007-12-11 08:14:37 +00:00
2007-06-25 14:36:49 +00:00
2012-10-02 10:29:15 +00:00
2007-06-25 14:36:49 +00:00
class account_invoice ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = " account.invoice "
2011-12-15 09:07:30 +00:00
def _get_analytic_lines ( self , cr , uid , id , context = None ) :
iml = super ( account_invoice , self ) . _get_analytic_lines ( cr , uid , id , context = context )
2008-07-22 15:11:28 +00:00
2012-01-03 12:33:39 +00:00
inv = self . browse ( cr , uid , [ id ] , context = context ) [ 0 ]
2008-07-22 15:11:28 +00:00
if inv . type == ' in_invoice ' :
2010-07-30 08:59:35 +00:00
obj_analytic_account = self . pool . get ( ' account.analytic.account ' )
2008-07-22 15:11:28 +00:00
for il in iml :
if il [ ' account_analytic_id ' ] :
2010-10-17 17:30:00 +00:00
# *-* browse (or refactor to avoid read inside the loop)
2012-01-03 12:33:39 +00:00
to_invoice = obj_analytic_account . read ( cr , uid , [ il [ ' account_analytic_id ' ] ] , [ ' to_invoice ' ] , context = context ) [ 0 ] [ ' to_invoice ' ]
2008-07-22 15:11:28 +00:00
if to_invoice :
il [ ' analytic_lines ' ] [ 0 ] [ 2 ] [ ' to_invoice ' ] = to_invoice [ 0 ]
return iml
2007-09-11 14:14:18 +00:00
2008-07-23 14:41:47 +00:00
2012-10-02 10:29:15 +00:00
2011-04-07 05:36:42 +00:00
class account_move_line ( osv . osv ) :
_inherit = " account.move.line "
def create_analytic_lines ( self , cr , uid , ids , context = None ) :
res = super ( account_move_line , self ) . create_analytic_lines ( cr , uid , ids , context = context )
analytic_line_obj = self . pool . get ( ' account.analytic.line ' )
2011-04-07 05:42:43 +00:00
for move_line in self . browse ( cr , uid , ids , context = context ) :
2012-11-28 12:29:40 +00:00
#For customer invoice, link analytic line to the invoice so it is not proposed for invoicing in Bill Tasks Work
invoice_id = move_line . invoice and move_line . invoice . type in ( ' out_invoice ' , ' out_refund ' ) and move_line . invoice . id or False
2011-04-07 05:36:42 +00:00
for line in move_line . analytic_lines :
2012-11-28 12:29:40 +00:00
analytic_line_obj . write ( cr , uid , line . id , {
' invoice_id ' : invoice_id ,
' to_invoice ' : line . account_id . to_invoice and line . account_id . to_invoice . id or False
} , context = context )
2011-04-07 05:36:42 +00:00
return res
2008-07-23 14:41:47 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: