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 = {
2014-07-06 14:44:26 +00:00
' invoice_id ' : fields . many2one ( ' account.invoice ' , ' Invoice ' , ondelete = " set null " , copy = False ) ,
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
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
2015-04-16 11:46:09 +00:00
def _prepare_cost_invoice ( self , cr , uid , partner , company_id , currency_id , analytic_lines , context = None ) :
""" returns values used to create main invoice from analytic lines """
account_payment_term_obj = self . pool [ ' account.payment.term ' ]
invoice_name = analytic_lines [ 0 ] . account_id . name
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 ]
return {
' name ' : " %s - %s " % ( time . strftime ( ' %d / % m/ % Y ' ) , invoice_name ) ,
' partner_id ' : partner . id ,
' company_id ' : company_id ,
' payment_term ' : partner . property_payment_term . id or False ,
' account_id ' : partner . property_account_receivable . id ,
' currency_id ' : currency_id ,
' date_due ' : date_due ,
' fiscal_position ' : partner . property_account_position . id
}
def _prepare_cost_invoice_line ( self , cr , uid , invoice_id , product_id , uom , user_id ,
factor_id , account , analytic_lines , journal_type , data , context = None ) :
product_obj = self . pool [ ' product.product ' ]
uom_context = dict ( context or { } , uom = uom )
total_price = sum ( l . amount for l in analytic_lines )
total_qty = sum ( l . unit_amount for l in analytic_lines )
if data . get ( ' product ' ) :
# force product, use its public price
if isinstance ( data [ ' product ' ] , ( tuple , list ) ) :
product_id = data [ ' product ' ] [ 0 ]
else :
product_id = data [ ' product ' ]
unit_price = self . _get_invoice_price ( cr , uid , account , product_id , user_id , total_qty , uom_context )
elif journal_type == ' general ' and product_id :
# timesheets, use sale price
unit_price = self . _get_invoice_price ( cr , uid , account , product_id , user_id , total_qty , uom_context )
else :
# expenses, using price from amount field
unit_price = total_price * - 1.0 / total_qty
factor = self . pool [ ' hr_timesheet_invoice.factor ' ] . browse ( cr , uid , factor_id , context = uom_context )
2015-07-01 12:33:21 +00:00
factor_name = factor . customer_name or ' '
2015-04-16 11:46:09 +00:00
curr_invoice_line = {
' price_unit ' : unit_price ,
' quantity ' : total_qty ,
' product_id ' : product_id ,
' discount ' : factor . factor ,
' invoice_id ' : invoice_id ,
' name ' : factor_name ,
' uos_id ' : uom ,
' account_analytic_id ' : account . id ,
}
if product_id :
product = product_obj . browse ( cr , uid , product_id , context = uom_context )
factor_name = product_obj . name_get ( cr , uid , [ product_id ] , context = uom_context ) [ 0 ] [ 1 ]
if factor . customer_name :
factor_name + = ' - ' + factor . customer_name
2015-07-30 20:41:45 +00:00
general_account = product . property_account_income or product . categ_id . property_account_income_categ
if not general_account :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " Configuration Error! " ) + ' \n ' + _ ( " Please define income account for product ' %s ' . " ) % product . name )
taxes = product . taxes_id or general_account . tax_ids
tax = self . pool [ ' account.fiscal.position ' ] . map_tax ( cr , uid , account . partner_id . property_account_position , taxes )
curr_invoice_line . update ( {
' invoice_line_tax_id ' : [ ( 6 , 0 , tax ) ] ,
' name ' : factor_name ,
' invoice_line_tax_id ' : [ ( 6 , 0 , tax ) ] ,
' account_id ' : general_account . id ,
} )
2015-04-16 11:46:09 +00:00
note = [ ]
for line in analytic_lines :
# 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 , line . product_uom_id . name ) )
else :
details . append ( " %s " % ( line [ ' unit_amount ' ] , ) )
if data . get ( ' name ' , False ) :
details . append ( line [ ' name ' ] )
if details :
note . append ( u ' - ' . join ( map ( lambda x : unicode ( x ) or ' ' , details ) ) )
if note :
curr_invoice_line [ ' name ' ] + = " \n " + ( " \n " . join ( map ( lambda x : unicode ( x ) or ' ' , note ) ) )
return curr_invoice_line
2012-11-27 12:07:37 +00:00
def invoice_cost_create ( self , cr , uid , ids , data = None , context = None ) :
invoice_obj = self . pool . get ( ' account.invoice ' )
invoice_line_obj = self . pool . get ( ' account.invoice.line ' )
2015-07-30 07:42:07 +00:00
analytic_line_obj = self . pool . get ( ' account.analytic.line ' )
2012-11-27 12:07:37 +00:00
invoices = [ ]
if context is None :
context = { }
if data is None :
data = { }
2015-04-16 11:46:09 +00:00
# use key (partner/account, company, currency)
# creates one invoice per key
invoice_grouping = { }
2013-07-25 10:04:02 +00:00
2015-04-16 11:46:09 +00:00
currency_id = False
2013-07-25 10:04:02 +00:00
# prepare for iteration on journal and accounts
2015-04-16 11:46:09 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
key = ( line . account_id . id ,
line . account_id . company_id . id ,
line . account_id . pricelist_id . currency_id . id )
invoice_grouping . setdefault ( key , [ ] ) . append ( line )
for ( key_id , company_id , currency_id ) , analytic_lines in invoice_grouping . items ( ) :
# key_id is an account.analytic.account
2015-08-19 12:26:04 +00:00
account = analytic_lines [ 0 ] . account_id
partner = account . partner_id # will be the same for every line
if ( not partner ) or not ( currency_id ) :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Contract incomplete. Please fill in the Customer and Pricelist fields for %s . ' ) % ( account . name ) )
2015-04-16 11:46:09 +00:00
curr_invoice = self . _prepare_cost_invoice ( cr , uid , partner , company_id , currency_id , analytic_lines , context = context )
invoice_context = dict ( context ,
lang = partner . lang ,
force_company = company_id , # set force_company in context so the correct product properties are selected (eg. income account)
company_id = company_id ) # set company_id in context, so the correct default journal will be selected
last_invoice = invoice_obj . create ( cr , uid , curr_invoice , context = invoice_context )
invoices . append ( last_invoice )
# use key (product, uom, user, invoiceable, analytic account, journal type)
# creates one invoice line per key
invoice_lines_grouping = { }
for analytic_line in analytic_lines :
account = analytic_line . account_id
if not analytic_line . to_invoice :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Trying to invoice non invoiceable line for %s . ' ) % ( analytic_line . product_id . name ) )
key = ( analytic_line . product_id . id ,
analytic_line . product_uom_id . id ,
analytic_line . user_id . id ,
analytic_line . to_invoice . id ,
analytic_line . account_id ,
analytic_line . journal_id . type )
2015-07-30 07:42:07 +00:00
# We want to retrieve the data in the partner language for the invoice creation
analytic_line = analytic_line_obj . browse ( cr , uid , [ line . id for line in analytic_line ] , context = invoice_context )
2015-04-16 11:46:09 +00:00
invoice_lines_grouping . setdefault ( key , [ ] ) . append ( analytic_line )
# finally creates the invoice line
for ( product_id , uom , user_id , factor_id , account , journal_type ) , lines_to_invoice in invoice_lines_grouping . items ( ) :
curr_invoice_line = self . _prepare_cost_invoice_line ( cr , uid , last_invoice ,
product_id , uom , user_id , factor_id , account , lines_to_invoice ,
2015-07-30 07:42:07 +00:00
journal_type , data , context = invoice_context )
2015-04-16 11:46:09 +00:00
invoice_line_obj . create ( cr , uid , curr_invoice_line , context = context )
self . write ( cr , uid , [ l . id for l in analytic_lines ] , { ' invoice_id ' : last_invoice } , context = context )
invoice_obj . button_reset_taxes ( cr , uid , [ last_invoice ] , context )
2012-11-27 12:07:37 +00:00
return invoices
2006-12-07 13:41:40 +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 ) :
2015-08-27 20:09:13 +00:00
res = super ( hr_analytic_timesheet , self ) . on_change_account_id (
2015-08-31 13:22:11 +00:00
cr , uid , ids , account_id , context = user_id )
2008-07-22 15:11:28 +00:00
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 ' ] = {
2015-04-01 10:04:46 +00:00
' title ' : _ ( ' Warning ' ) ,
' message ' : _ ( ' The analytic account is in pending state. \n You should not work on this account ! ' )
2008-11-21 11:04:55 +00:00
}
2008-07-22 15:11:28 +00:00
return res
2007-06-25 14:36:49 +00:00
class account_invoice ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = " account.invoice "
2014-07-06 14:44:26 +00:00
def _get_analytic_lines ( self , cr , uid , ids , context = None ) :
iml = super ( account_invoice , self ) . _get_analytic_lines ( cr , uid , ids , context = context )
2008-07-22 15:11:28 +00:00
2014-07-06 14:44:26 +00:00
inv = self . browse ( cr , uid , ids , 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: