2006-12-07 13:41:40 +00:00
# -*- encoding: utf-8 -*-
##############################################################################
#
2008-11-03 19:18:56 +00:00
# OpenERP, Open Source Management Solution
2009-01-04 22:12:50 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
2008-11-03 19:18:56 +00:00
# $Id$
2008-06-16 11:00:21 +00:00
#
2008-11-03 19:18:56 +00:00
# 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 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
# GNU General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
from osv import fields , osv
#from tools.misc import currency
from _common import rounding
2007-10-10 05:44:59 +00:00
import time
2007-10-24 06:13:33 +00:00
from tools import config
2008-12-29 14:55:49 +00:00
from tools . misc import ustr
2008-07-08 08:13:12 +00:00
from tools . translate import _
2006-12-07 13:41:40 +00:00
class price_type ( osv . osv ) :
2008-07-22 15:11:28 +00:00
"""
The price type is used to points which field in the product form
is a price and in which currency is this price expressed .
When a field is a price , you can use it in pricelists to base
sale and purchase prices based on some fields of the product .
"""
def _price_field_get ( self , cr , uid , context = { } ) :
2008-12-13 06:05:19 +00:00
mf = self . pool . get ( ' ir.model.fields ' )
ids = mf . search ( cr , uid , [ ( ' model ' , ' in ' , ( ( ' product.product ' ) , ( ' product.template ' ) ) ) , ( ' ttype ' , ' = ' , ' float ' ) ] , context = context )
2008-12-08 17:08:40 +00:00
res = [ ]
2008-12-13 06:05:19 +00:00
for field in mf . browse ( cr , uid , ids , context = context ) :
res . append ( ( field . name , field . field_description ) )
2008-12-08 17:08:40 +00:00
return res
2008-07-22 15:11:28 +00:00
def _get_currency ( self , cr , uid , ctx ) :
comp = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id
if not comp :
comp_id = self . pool . get ( ' res.company ' ) . search ( cr , uid , [ ] ) [ 0 ]
comp = self . pool . get ( ' res.company ' ) . browse ( cr , uid , comp_id )
return comp . currency_id . id
2007-10-23 19:02:01 +00:00
2008-07-22 15:11:28 +00:00
_name = " product.price.type "
_description = " Price type "
_columns = {
2008-09-22 06:06:56 +00:00
" name " : fields . char ( " Price Name " , size = 32 , required = True , translate = True , help = " Name of this kind of price. " ) ,
2008-07-22 15:11:28 +00:00
" active " : fields . boolean ( " Active " ) ,
2009-01-06 11:17:44 +00:00
" field " : fields . selection ( _price_field_get , " Product Field " , size = 32 , required = True , help = " Associated field in the product form. " ) ,
2008-09-22 06:06:56 +00:00
" currency_id " : fields . many2one ( ' res.currency ' , " Currency " , required = True , help = " The currency the field is expressed in. " ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
" active " : lambda * args : True ,
" currency_id " : _get_currency
}
2006-12-07 13:41:40 +00:00
price_type ( )
#----------------------------------------------------------
# Price lists
#----------------------------------------------------------
class product_pricelist_type ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " product.pricelist.type "
_description = " Pricelist Type "
_columns = {
2009-01-22 23:04:36 +00:00
' name ' : fields . char ( ' Name ' , size = 64 , required = True , translate = True ) ,
2008-09-22 05:56:30 +00:00
' key ' : fields . char ( ' Key ' , size = 64 , required = True , help = " Used in the code to select specific prices based on the context. Keep unchanged. " ) ,
2008-07-22 15:11:28 +00:00
}
2006-12-07 13:41:40 +00:00
product_pricelist_type ( )
class product_pricelist ( osv . osv ) :
2008-07-22 15:11:28 +00:00
def _pricelist_type_get ( self , cr , uid , context = { } ) :
cr . execute ( ' select key,name from product_pricelist_type order by name ' )
return cr . fetchall ( )
_name = " product.pricelist "
_description = " Pricelist "
_columns = {
2008-09-22 05:56:30 +00:00
' name ' : fields . char ( ' Pricelist Name ' , size = 64 , required = True , translate = True ) ,
2008-07-22 15:11:28 +00:00
' active ' : fields . boolean ( ' Active ' ) ,
' type ' : fields . selection ( _pricelist_type_get , ' Pricelist Type ' , required = True ) ,
' version_id ' : fields . one2many ( ' product.pricelist.version ' , ' pricelist_id ' , ' Pricelist Versions ' ) ,
' currency_id ' : fields . many2one ( ' res.currency ' , ' Currency ' , required = True ) ,
}
2008-12-26 06:50:18 +00:00
2008-12-24 15:50:06 +00:00
def name_get ( self , cr , uid , ids , context = { } ) :
2008-12-26 06:50:18 +00:00
result = [ ]
2008-12-24 15:50:06 +00:00
for pl in self . browse ( cr , uid , ids , context ) :
2008-12-29 16:33:26 +00:00
name = pl . name + ' ( ' + pl . currency_id . name + ' ) '
2008-12-26 06:50:18 +00:00
result . append ( ( pl . id , name ) )
2008-12-24 15:50:06 +00:00
return result
2008-12-26 06:50:18 +00:00
2008-12-24 15:50:06 +00:00
2008-07-22 15:11:28 +00:00
def _get_currency ( self , cr , uid , ctx ) :
2008-12-29 14:55:49 +00:00
comp = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id
2008-07-22 15:11:28 +00:00
if not comp :
comp_id = self . pool . get ( ' res.company ' ) . search ( cr , uid , [ ] ) [ 0 ]
comp = self . pool . get ( ' res.company ' ) . browse ( cr , uid , comp_id )
return comp . currency_id . id
2007-10-23 19:02:01 +00:00
2008-07-22 15:11:28 +00:00
_defaults = {
' active ' : lambda * a : 1 ,
" currency_id " : _get_currency
}
2006-12-07 13:41:40 +00:00
2008-07-22 15:11:28 +00:00
def price_get ( self , cr , uid , ids , prod_id , qty , partner = None , context = None ) :
'''
context = {
' uom ' : Unit of Measure ( int ) ,
' partner ' : Partner ID ( int ) ,
' date ' : Date of the pricelist ( % Y - % m - % d ) ,
}
'''
context = context or { }
currency_obj = self . pool . get ( ' res.currency ' )
product_obj = self . pool . get ( ' product.product ' )
supplierinfo_obj = self . pool . get ( ' product.supplierinfo ' )
price_type_obj = self . pool . get ( ' product.price.type ' )
2007-10-10 05:44:59 +00:00
2008-07-22 15:11:28 +00:00
if context and ( ' partner_id ' in context ) :
partner = context [ ' partner_id ' ]
context [ ' partner_id ' ] = partner
date = time . strftime ( ' % Y- % m- %d ' )
if context and ( ' date ' in context ) :
date = context [ ' date ' ]
result = { }
for id in ids :
cr . execute ( ' SELECT * ' \
' FROM product_pricelist_version ' \
2008-12-10 14:29:55 +00:00
' WHERE pricelist_id = %s AND active=True ' \
2008-07-22 15:11:28 +00:00
' AND (date_start IS NULL OR date_start <= %s ) ' \
' AND (date_end IS NULL OR date_end >= %s ) ' \
' ORDER BY id LIMIT 1 ' , ( id , date , date ) )
plversion = cr . dictfetchone ( )
2007-07-19 13:35:49 +00:00
2008-07-22 15:11:28 +00:00
if not plversion :
raise osv . except_osv ( _ ( ' Warning ! ' ) ,
_ ( ' No active version for the selected pricelist ! \n ' \
' Please create or activate one. ' ) )
2006-12-07 13:41:40 +00:00
2008-07-22 15:11:28 +00:00
cr . execute ( ' SELECT id, categ_id ' \
' FROM product_template ' \
' WHERE id = (SELECT product_tmpl_id ' \
' FROM product_product ' \
2008-12-10 14:29:55 +00:00
' WHERE id = %s ) ' , ( prod_id , ) )
2008-07-22 15:11:28 +00:00
tmpl_id , categ = cr . fetchone ( )
categ_ids = [ ]
while categ :
categ_ids . append ( str ( categ ) )
cr . execute ( ' SELECT parent_id ' \
' FROM product_category ' \
2008-12-10 14:29:55 +00:00
' WHERE id = %s ' , ( categ , ) )
2008-07-22 15:11:28 +00:00
categ = cr . fetchone ( ) [ 0 ]
if str ( categ ) in categ_ids :
raise osv . except_osv ( _ ( ' Warning ! ' ) ,
_ ( ' Could not resolve product category, ' \
' you have defined cyclic categories ' \
' of products! ' ) )
if categ_ids :
categ_where = ' (categ_id IN ( ' + ' , ' . join ( categ_ids ) + ' )) '
else :
categ_where = ' (categ_id IS NULL) '
2006-12-07 13:41:40 +00:00
2008-07-22 15:11:28 +00:00
cr . execute (
' SELECT i.*, pl.currency_id '
' FROM product_pricelist_item AS i, '
' product_pricelist_version AS v, product_pricelist AS pl '
2008-12-10 14:29:55 +00:00
' WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %s ) '
' AND (product_id IS NULL OR product_id = %s ) '
2008-07-22 15:11:28 +00:00
' AND ( ' + categ_where + ' OR (categ_id IS NULL)) '
2008-12-10 14:29:55 +00:00
' AND price_version_id = %s '
' AND (min_quantity IS NULL OR min_quantity <= %s ) '
2008-07-22 15:11:28 +00:00
' AND i.price_version_id = v.id AND v.pricelist_id = pl.id '
' ORDER BY sequence LIMIT 1 ' ,
( tmpl_id , prod_id , plversion [ ' id ' ] , qty ) )
res = cr . dictfetchone ( )
if res :
if res [ ' base ' ] == - 1 :
if not res [ ' base_pricelist_id ' ] :
price = 0.0
else :
price_tmp = self . price_get ( cr , uid ,
[ res [ ' base_pricelist_id ' ] ] , prod_id ,
qty ) [ res [ ' base_pricelist_id ' ] ]
ptype_src = self . browse ( cr , uid ,
res [ ' base_pricelist_id ' ] ) . currency_id . id
price = currency_obj . compute ( cr , uid , ptype_src ,
res [ ' currency_id ' ] , price_tmp , round = False )
elif res [ ' base ' ] == - 2 :
where = [ ]
if partner :
where = [ ( ' name ' , ' = ' , partner ) ]
sinfo = supplierinfo_obj . search ( cr , uid ,
[ ( ' product_id ' , ' = ' , tmpl_id ) ] + where )
price = 0.0
if sinfo :
cr . execute ( ' SELECT * ' \
' FROM pricelist_partnerinfo ' \
' WHERE suppinfo_id IN ( ' + \
' , ' . join ( map ( str , sinfo ) ) + ' ) ' \
2008-12-10 14:29:55 +00:00
' AND min_quantity <= %s ' \
2008-07-22 15:11:28 +00:00
' ORDER BY min_quantity DESC LIMIT 1 ' , ( qty , ) )
res2 = cr . dictfetchone ( )
if res2 :
price = res2 [ ' price ' ]
else :
price_type = price_type_obj . browse ( cr , uid , int ( res [ ' base ' ] ) )
price = currency_obj . compute ( cr , uid ,
price_type . currency_id . id , res [ ' currency_id ' ] ,
product_obj . price_get ( cr , uid , [ prod_id ] ,
2008-07-30 05:20:49 +00:00
price_type . field ) [ prod_id ] , round = False )
2007-10-05 05:24:52 +00:00
2008-07-22 15:11:28 +00:00
price_limit = price
2006-12-07 13:41:40 +00:00
2008-08-26 22:18:32 +00:00
price = price * ( 1.0 + ( res [ ' price_discount ' ] or 0.0 ) )
2008-07-22 15:11:28 +00:00
price = rounding ( price , res [ ' price_round ' ] )
price + = ( res [ ' price_surcharge ' ] or 0.0 )
if res [ ' price_min_margin ' ] :
price = max ( price , price_limit + res [ ' price_min_margin ' ] )
if res [ ' price_max_margin ' ] :
price = min ( price , price_limit + res [ ' price_max_margin ' ] )
else :
# False means no valid line found ! But we may not raise an
# exception here because it breaks the search
price = False
2008-07-30 05:20:49 +00:00
result [ id ] = price
2008-07-22 15:11:28 +00:00
if context and ( ' uom ' in context ) :
product = product_obj . browse ( cr , uid , prod_id )
uom = product . uos_id or product . uom_id
result [ id ] = self . pool . get ( ' product.uom ' ) . _compute_price ( cr ,
2008-07-30 05:20:49 +00:00
uid , uom . id , result [ id ] , context [ ' uom ' ] )
2008-07-22 15:11:28 +00:00
return result
2007-10-10 05:44:59 +00:00
2006-12-07 13:41:40 +00:00
product_pricelist ( )
2007-10-10 05:44:59 +00:00
2006-12-07 13:41:40 +00:00
class product_pricelist_version ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " product.pricelist.version "
_description = " Pricelist Version "
_columns = {
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Price List ' ,
required = True , select = True ) ,
2009-01-22 23:04:36 +00:00
' name ' : fields . char ( ' Name ' , size = 64 , required = True , translate = True ) ,
2008-07-22 15:11:28 +00:00
' active ' : fields . boolean ( ' Active ' ) ,
' items_id ' : fields . one2many ( ' product.pricelist.item ' ,
' price_version_id ' , ' Price List Items ' , required = True ) ,
2009-01-27 11:15:46 +00:00
' date_start ' : fields . date ( ' Start Date ' , help = " Starting date for this pricelist version to be valid. " ) ,
' date_end ' : fields . date ( ' End Date ' , help = " Ending date for this pricelist version to be valid. " ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
' active ' : lambda * a : 1 ,
}
2007-10-10 05:44:59 +00:00
2008-07-22 15:11:28 +00:00
#
# TODO: improve this function ?
#
def _check_date ( self , cursor , user , ids ) :
for pricelist_version in self . browse ( cursor , user , ids ) :
if not pricelist_version . active :
continue
cursor . execute ( ' SELECT id ' \
' FROM product_pricelist_version ' \
' WHERE ((date_start <= %s AND %s <= date_end ' \
' AND date_end IS NOT NULL) ' \
' OR (date_end IS NULL AND date_start IS NOT NULL ' \
' AND date_start <= %s ) ' \
' OR (date_start IS NULL AND date_end IS NOT NULL ' \
' AND %s <= date_end) ' \
' OR (date_start IS NULL AND date_end IS NULL) ' \
' OR ( %s = \' 0000-01-01 \' AND date_start IS NULL) ' \
' OR ( %s = \' 0000-01-01 \' AND date_end IS NULL) ' \
' OR ( %s = \' 0000-01-01 \' AND %s = \' 0000-01-01 \' ) ' \
' OR ( %s = \' 0000-01-01 \' AND date_start <= %s ) ' \
' OR ( %s = \' 0000-01-01 \' AND %s <= date_end)) ' \
2008-12-10 14:29:55 +00:00
' AND pricelist_id = %s ' \
2008-07-22 15:11:28 +00:00
' AND active ' \
2008-12-10 14:29:55 +00:00
' AND id <> %s ' , ( pricelist_version . date_end or ' 0000-01-01 ' ,
2008-07-22 15:11:28 +00:00
pricelist_version . date_start or ' 0000-01-01 ' ,
pricelist_version . date_end or ' 0000-01-01 ' ,
pricelist_version . date_start or ' 0000-01-01 ' ,
pricelist_version . date_start or ' 0000-01-01 ' ,
pricelist_version . date_end or ' 0000-01-01 ' ,
pricelist_version . date_start or ' 0000-01-01 ' ,
pricelist_version . date_end or ' 0000-01-01 ' ,
pricelist_version . date_start or ' 0000-01-01 ' ,
pricelist_version . date_end or ' 0000-01-01 ' ,
pricelist_version . date_end or ' 0000-01-01 ' ,
pricelist_version . date_start or ' 0000-01-01 ' ,
pricelist_version . pricelist_id . id ,
pricelist_version . id ) )
if cursor . fetchall ( ) :
return False
return True
2007-10-10 05:44:59 +00:00
2008-07-22 15:11:28 +00:00
_constraints = [
( _check_date , ' You can not have 2 pricelist version that overlaps! ' ,
[ ' date_start ' , ' date_end ' ] )
]
2007-10-10 05:44:59 +00:00
2006-12-07 13:41:40 +00:00
product_pricelist_version ( )
class product_pricelist_item ( osv . osv ) :
2008-07-22 15:11:28 +00:00
def _price_field_get ( self , cr , uid , context = { } ) :
2008-12-13 06:05:19 +00:00
pt = self . pool . get ( ' product.price.type ' )
ids = pt . search ( cr , uid , [ ] , context = context )
2008-12-08 17:08:40 +00:00
result = [ ]
2008-12-13 06:05:19 +00:00
for line in pt . browse ( cr , uid , ids , context = context ) :
result . append ( ( line . id , line . name ) )
2008-12-08 17:08:40 +00:00
result . append ( ( - 1 , _ ( ' Other Pricelist ' ) ) )
result . append ( ( - 2 , _ ( ' Partner section of the product form ' ) ) )
2008-07-22 15:11:28 +00:00
return result
2006-12-07 13:41:40 +00:00
2008-07-22 15:11:28 +00:00
_name = " product.pricelist.item "
_description = " Pricelist item "
_order = " sequence, min_quantity desc "
_defaults = {
' base ' : lambda * a : - 1 ,
' min_quantity ' : lambda * a : 0 ,
' sequence ' : lambda * a : 5 ,
' price_discount ' : lambda * a : 0 ,
}
_columns = {
2008-09-22 05:56:30 +00:00
' name ' : fields . char ( ' Rule Name ' , size = 64 , help = " Explicit rule name for this pricelist line. " ) ,
2008-07-22 15:11:28 +00:00
' price_version_id ' : fields . many2one ( ' product.pricelist.version ' , ' Price List Version ' , required = True , select = True ) ,
2008-09-22 05:56:30 +00:00
' product_tmpl_id ' : fields . many2one ( ' product.template ' , ' Product Template ' , ondelete = ' cascade ' , help = " Set a template if this rule only apply to a template of product. Keep empty for all products " ) ,
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , ondelete = ' cascade ' , help = " Set a product if this rule only apply to one product. Keep empty for all products " ) ,
' categ_id ' : fields . many2one ( ' product.category ' , ' Product Category ' , ondelete = ' cascade ' , help = " Set a category of product if this rule only apply to products of a category and his childs. Keep empty for all products " ) ,
2006-12-07 13:41:40 +00:00
2009-01-27 11:15:46 +00:00
' min_quantity ' : fields . integer ( ' Min. Quantity ' , required = True , help = " The rule only applies if the partner buys/sells more than this quantity. " ) ,
2008-07-22 15:11:28 +00:00
' sequence ' : fields . integer ( ' Sequence ' , required = True ) ,
2009-01-27 11:15:46 +00:00
' base ' : fields . selection ( _price_field_get , ' Based on ' , required = True , size = - 1 , help = " The mode for computing the price for this rule. " ) ,
2008-07-22 15:11:28 +00:00
' base_pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' If Other Pricelist ' ) ,
2006-12-07 13:41:40 +00:00
2008-07-22 15:11:28 +00:00
' price_surcharge ' : fields . float ( ' Price Surcharge ' ,
digits = ( 16 , int ( config [ ' price_accuracy ' ] ) ) ) ,
' price_discount ' : fields . float ( ' Price Discount ' , digits = ( 16 , 4 ) ) ,
' price_round ' : fields . float ( ' Price Rounding ' ,
2008-09-22 05:56:30 +00:00
digits = ( 16 , int ( config [ ' price_accuracy ' ] ) ) ,
help = " Sets the price so that it is a multiple of this value. \n " \
" Rounding is applied after the discount and before the surcharge. \n " \
2009-01-27 11:15:46 +00:00
" To have prices that end in 9.99, set rounding 10, surcharge -0.01 " \
2008-09-22 05:56:30 +00:00
) ,
2009-01-27 11:15:46 +00:00
' price_min_margin ' : fields . float ( ' Min. Price Margin ' ,
2008-07-22 15:11:28 +00:00
digits = ( 16 , int ( config [ ' price_accuracy ' ] ) ) ) ,
2009-01-27 11:15:46 +00:00
' price_max_margin ' : fields . float ( ' Max. Price Margin ' ,
2008-07-22 15:11:28 +00:00
digits = ( 16 , int ( config [ ' price_accuracy ' ] ) ) ) ,
}
def product_id_change ( self , cr , uid , ids , product_id , context = { } ) :
if not product_id :
return { }
prod = self . pool . get ( ' product.product ' ) . read ( cr , uid , [ product_id ] , [ ' code ' , ' name ' ] )
if prod [ 0 ] [ ' code ' ] :
return { ' value ' : { ' name ' : prod [ 0 ] [ ' code ' ] } }
return { }
2006-12-07 13:41:40 +00:00
product_pricelist_item ( )
2008-07-23 14:41:47 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: