2009-10-14 11:15:34 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
#
2009-09-24 10:46:21 +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
2008-11-03 19:18:56 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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 _
2014-01-24 15:07:04 +00:00
from openerp . tools . safe_eval import safe_eval as eval
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2014-11-14 11:18:07 +00:00
from openerp . tools . float_utils import float_round
2015-06-23 09:55:30 +00:00
from openerp . exceptions import except_orm
2008-12-08 17:08:40 +00:00
2008-09-07 23:24:39 +00:00
class product_product ( osv . osv ) :
2010-06-12 23:01:10 +00:00
_inherit = " product.product "
2013-10-11 16:04:23 +00:00
2012-05-22 07:02:28 +00:00
def _stock_move_count ( self , cr , uid , ids , field_name , arg , context = None ) :
2012-05-04 14:55:51 +00:00
res = dict ( [ ( id , { ' reception_count ' : 0 , ' delivery_count ' : 0 } ) for id in ids ] )
2012-05-22 07:02:28 +00:00
move_pool = self . pool . get ( ' stock.move ' )
2012-05-22 14:37:59 +00:00
moves = move_pool . read_group ( cr , uid , [
( ' product_id ' , ' in ' , ids ) ,
2013-07-24 15:32:35 +00:00
( ' location_id.usage ' , ' != ' , ' internal ' ) ,
( ' location_dest_id.usage ' , ' = ' , ' internal ' ) ,
2012-09-03 17:54:10 +00:00
( ' state ' , ' in ' , ( ' confirmed ' , ' assigned ' , ' pending ' ) )
2012-05-22 14:37:59 +00:00
] , [ ' product_id ' ] , [ ' product_id ' ] )
for move in moves :
product_id = move [ ' product_id ' ] [ 0 ]
2012-05-22 14:54:33 +00:00
res [ product_id ] [ ' reception_count ' ] = move [ ' product_id_count ' ]
2012-05-22 14:37:59 +00:00
moves = move_pool . read_group ( cr , uid , [
( ' product_id ' , ' in ' , ids ) ,
2013-07-24 15:32:35 +00:00
( ' location_id.usage ' , ' = ' , ' internal ' ) ,
( ' location_dest_id.usage ' , ' != ' , ' internal ' ) ,
2012-09-03 17:54:10 +00:00
( ' state ' , ' in ' , ( ' confirmed ' , ' assigned ' , ' pending ' ) )
2012-05-22 14:37:59 +00:00
] , [ ' product_id ' ] , [ ' product_id ' ] )
for move in moves :
product_id = move [ ' product_id ' ] [ 0 ]
2012-06-11 10:50:38 +00:00
res [ product_id ] [ ' delivery_count ' ] = move [ ' product_id_count ' ]
2012-05-04 14:55:51 +00:00
return res
2012-04-26 10:31:33 +00:00
2010-07-06 11:56:32 +00:00
def view_header_get ( self , cr , user , view_id , view_type , context = None ) :
if context is None :
context = { }
2008-09-23 14:24:03 +00:00
res = super ( product_product , self ) . view_header_get ( cr , user , view_id , view_type , context )
if res : return res
2010-07-07 09:05:22 +00:00
if ( context . get ( ' active_id ' , False ) ) and ( context . get ( ' active_model ' ) == ' stock.location ' ) :
return _ ( ' Products: ' ) + self . pool . get ( ' stock.location ' ) . browse ( cr , user , context [ ' active_id ' ] , context ) . name
2008-09-23 14:24:03 +00:00
return res
2013-06-30 14:10:14 +00:00
def _get_domain_locations ( self , cr , uid , ids , context = None ) :
2013-05-14 13:23:22 +00:00
'''
Parses the context and returns a list of location_ids based on it .
It will return all stock locations when no parameters are given
Possible parameters are shop , warehouse , location , force_company , compute_child
'''
2013-06-29 22:17:03 +00:00
context = context or { }
2011-06-02 06:55:59 +00:00
location_obj = self . pool . get ( ' stock.location ' )
warehouse_obj = self . pool . get ( ' stock.warehouse ' )
2008-09-23 13:24:11 +00:00
2013-06-29 22:17:03 +00:00
location_ids = [ ]
2008-09-23 13:24:11 +00:00
if context . get ( ' location ' , False ) :
2015-07-08 12:00:32 +00:00
if isinstance ( context [ ' location ' ] , ( int , long ) ) :
2008-09-25 09:30:39 +00:00
location_ids = [ context [ ' location ' ] ]
2015-07-08 12:00:32 +00:00
elif isinstance ( context [ ' location ' ] , basestring ) :
2013-10-30 14:22:42 +00:00
domain = [ ( ' complete_name ' , ' ilike ' , context [ ' location ' ] ) ]
2013-05-07 14:04:43 +00:00
if context . get ( ' force_company ' , False ) :
2013-06-29 22:17:03 +00:00
domain + = [ ( ' company_id ' , ' = ' , context [ ' force_company ' ] ) ]
location_ids = location_obj . search ( cr , uid , domain , context = context )
2008-09-25 09:30:39 +00:00
else :
location_ids = context [ ' location ' ]
2008-09-23 13:24:11 +00:00
else :
2013-06-29 22:17:03 +00:00
if context . get ( ' warehouse ' , False ) :
2015-07-08 12:00:32 +00:00
if isinstance ( context [ ' warehouse ' ] , ( int , long ) ) :
wids = [ context [ ' warehouse ' ] ]
elif isinstance ( context [ ' warehouse ' ] , basestring ) :
domain = [ ( ' name ' , ' ilike ' , context [ ' warehouse ' ] ) ]
if context . get ( ' force_company ' , False ) :
domain + = [ ( ' company_id ' , ' = ' , context [ ' force_company ' ] ) ]
wids = warehouse_obj . search ( cr , uid , domain , context = context )
else :
wids = context [ ' warehouse ' ]
2013-06-29 22:17:03 +00:00
else :
wids = warehouse_obj . search ( cr , uid , [ ] , context = context )
2011-06-02 06:55:59 +00:00
for w in warehouse_obj . browse ( cr , uid , wids , context = context ) :
2014-04-03 09:19:12 +00:00
location_ids . append ( w . view_location_id . id )
2008-09-23 13:24:11 +00:00
2014-04-28 09:27:26 +00:00
operator = context . get ( ' compute_child ' , True ) and ' child_of ' or ' in '
2013-06-30 14:10:14 +00:00
domain = context . get ( ' force_company ' , False ) and [ ' & ' , ( ' company_id ' , ' = ' , context [ ' force_company ' ] ) ] or [ ]
2015-02-27 08:56:06 +00:00
locations = location_obj . browse ( cr , uid , location_ids , context = context )
if operator == " child_of " and locations and locations [ 0 ] . parent_left != 0 :
loc_domain = [ ]
dest_loc_domain = [ ]
for loc in locations :
if loc_domain :
loc_domain = [ ' | ' ] + loc_domain + [ ' & ' , ( ' location_id.parent_left ' , ' >= ' , loc . parent_left ) , ( ' location_id.parent_left ' , ' < ' , loc . parent_right ) ]
dest_loc_domain = [ ' | ' ] + dest_loc_domain + [ ' & ' , ( ' location_dest_id.parent_left ' , ' >= ' , loc . parent_left ) , ( ' location_dest_id.parent_left ' , ' < ' , loc . parent_right ) ]
else :
loc_domain + = [ ' & ' , ( ' location_id.parent_left ' , ' >= ' , loc . parent_left ) , ( ' location_id.parent_left ' , ' < ' , loc . parent_right ) ]
dest_loc_domain + = [ ' & ' , ( ' location_dest_id.parent_left ' , ' >= ' , loc . parent_left ) , ( ' location_dest_id.parent_left ' , ' < ' , loc . parent_right ) ]
return (
domain + loc_domain ,
domain + [ ' & ' ] + dest_loc_domain + [ ' ! ' ] + loc_domain ,
domain + [ ' & ' ] + loc_domain + [ ' ! ' ] + dest_loc_domain
)
else :
return (
domain + [ ( ' location_id ' , operator , location_ids ) ] ,
domain + [ ' & ' , ( ' location_dest_id ' , operator , location_ids ) , ' ! ' , ( ' location_id ' , operator , location_ids ) ] ,
domain + [ ' & ' , ( ' location_id ' , operator , location_ids ) , ' ! ' , ( ' location_dest_id ' , operator , location_ids ) ]
)
2013-04-22 13:57:19 +00:00
2013-06-30 14:10:14 +00:00
def _get_domain_dates ( self , cr , uid , ids , context ) :
2014-01-24 15:07:04 +00:00
from_date = context . get ( ' from_date ' , False )
to_date = context . get ( ' to_date ' , False )
2013-06-30 14:10:14 +00:00
domain = [ ]
if from_date :
2014-01-24 15:07:04 +00:00
domain . append ( ( ' date ' , ' >= ' , from_date ) )
2013-06-30 14:10:14 +00:00
if to_date :
2014-01-24 15:07:04 +00:00
domain . append ( ( ' date ' , ' <= ' , to_date ) )
2013-06-30 14:10:14 +00:00
return domain
2008-09-23 14:24:03 +00:00
2013-06-30 14:10:14 +00:00
def _product_available ( self , cr , uid , ids , field_names = None , arg = False , context = None ) :
context = context or { }
field_names = field_names or [ ]
2008-09-23 14:24:03 +00:00
2013-07-18 11:59:34 +00:00
domain_products = [ ( ' product_id ' , ' in ' , ids ) ]
2015-02-27 08:56:06 +00:00
domain_quant , domain_move_in , domain_move_out = [ ] , [ ] , [ ]
domain_quant_loc , domain_move_in_loc , domain_move_out_loc = self . _get_domain_locations ( cr , uid , ids , context = context )
2014-11-24 21:38:51 +00:00
domain_move_in + = self . _get_domain_dates ( cr , uid , ids , context = context ) + [ ( ' state ' , ' not in ' , ( ' done ' , ' cancel ' , ' draft ' ) ) ] + domain_products
domain_move_out + = self . _get_domain_dates ( cr , uid , ids , context = context ) + [ ( ' state ' , ' not in ' , ( ' done ' , ' cancel ' , ' draft ' ) ) ] + domain_products
2013-07-18 11:59:34 +00:00
domain_quant + = domain_products
2015-03-18 14:49:18 +00:00
if context . get ( ' lot_id ' ) :
domain_quant . append ( ( ' lot_id ' , ' = ' , context [ ' lot_id ' ] ) )
if context . get ( ' owner_id ' ) :
domain_quant . append ( ( ' owner_id ' , ' = ' , context [ ' owner_id ' ] ) )
owner_domain = ( ' restrict_partner_id ' , ' = ' , context [ ' owner_id ' ] )
domain_move_in . append ( owner_domain )
domain_move_out . append ( owner_domain )
if context . get ( ' package_id ' ) :
domain_quant . append ( ( ' package_id ' , ' = ' , context [ ' package_id ' ] ) )
domain_move_in + = domain_move_in_loc
domain_move_out + = domain_move_out_loc
moves_in = self . pool . get ( ' stock.move ' ) . read_group ( cr , uid , domain_move_in , [ ' product_id ' , ' product_qty ' ] , [ ' product_id ' ] , context = context )
moves_out = self . pool . get ( ' stock.move ' ) . read_group ( cr , uid , domain_move_out , [ ' product_id ' , ' product_qty ' ] , [ ' product_id ' ] , context = context )
2014-01-24 15:07:04 +00:00
2015-02-27 08:56:06 +00:00
domain_quant + = domain_quant_loc
2013-06-30 14:10:14 +00:00
quants = self . pool . get ( ' stock.quant ' ) . read_group ( cr , uid , domain_quant , [ ' product_id ' , ' qty ' ] , [ ' product_id ' ] , context = context )
2013-06-30 20:13:23 +00:00
quants = dict ( map ( lambda x : ( x [ ' product_id ' ] [ 0 ] , x [ ' qty ' ] ) , quants ) )
2014-01-24 15:07:04 +00:00
2013-06-30 20:13:23 +00:00
moves_in = dict ( map ( lambda x : ( x [ ' product_id ' ] [ 0 ] , x [ ' product_qty ' ] ) , moves_in ) )
moves_out = dict ( map ( lambda x : ( x [ ' product_id ' ] [ 0 ] , x [ ' product_qty ' ] ) , moves_out ) )
2008-10-21 16:28:19 +00:00
res = { }
2014-11-14 11:18:07 +00:00
for product in self . browse ( cr , uid , ids , context = context ) :
id = product . id
qty_available = float_round ( quants . get ( id , 0.0 ) , precision_rounding = product . uom_id . rounding )
incoming_qty = float_round ( moves_in . get ( id , 0.0 ) , precision_rounding = product . uom_id . rounding )
outgoing_qty = float_round ( moves_out . get ( id , 0.0 ) , precision_rounding = product . uom_id . rounding )
virtual_available = float_round ( quants . get ( id , 0.0 ) + moves_in . get ( id , 0.0 ) - moves_out . get ( id , 0.0 ) , precision_rounding = product . uom_id . rounding )
2013-06-30 14:10:14 +00:00
res [ id ] = {
2014-11-14 11:18:07 +00:00
' qty_available ' : qty_available ,
' incoming_qty ' : incoming_qty ,
' outgoing_qty ' : outgoing_qty ,
' virtual_available ' : virtual_available ,
2014-01-24 15:07:04 +00:00
}
2008-10-21 16:28:19 +00:00
return res
2008-09-23 14:24:03 +00:00
2014-01-24 15:07:04 +00:00
def _search_product_quantity ( self , cr , uid , obj , name , domain , context ) :
2014-01-17 09:20:11 +00:00
res = [ ]
2014-01-24 15:07:04 +00:00
for field , operator , value in domain :
#to prevent sql injections
assert field in ( ' qty_available ' , ' virtual_available ' , ' incoming_qty ' , ' outgoing_qty ' ) , ' Invalid domain left operand '
2014-10-01 14:50:10 +00:00
assert operator in ( ' < ' , ' > ' , ' = ' , ' != ' , ' <= ' , ' >= ' ) , ' Invalid domain operator '
2014-01-24 15:07:04 +00:00
assert isinstance ( value , ( float , int ) ) , ' Invalid domain right operand '
2014-01-17 09:20:11 +00:00
if operator == ' = ' :
operator = ' == '
2014-01-24 15:07:04 +00:00
2014-01-17 09:20:11 +00:00
ids = [ ]
2015-03-16 19:54:59 +00:00
if name == ' qty_available ' and ( value != 0.0 or operator not in ( ' == ' , ' >= ' , ' <= ' ) ) :
2015-02-26 14:00:16 +00:00
res . append ( ( ' id ' , ' in ' , self . _search_qty_available ( cr , uid , operator , value , context ) ) )
else :
product_ids = self . search ( cr , uid , [ ] , context = context )
if product_ids :
2015-03-02 11:17:15 +00:00
#TODO: Still optimization possible when searching virtual quantities
2015-02-26 14:00:16 +00:00
for element in self . browse ( cr , uid , product_ids , context = context ) :
if eval ( str ( element [ field ] ) + operator + str ( value ) ) :
ids . append ( element . id )
res . append ( ( ' id ' , ' in ' , ids ) )
2008-10-21 16:28:19 +00:00
return res
2008-09-23 14:24:03 +00:00
2015-02-26 14:00:16 +00:00
def _search_qty_available ( self , cr , uid , operator , value , context ) :
2015-03-02 11:17:15 +00:00
domain_quant = [ ]
2015-02-26 14:00:16 +00:00
if context . get ( ' lot_id ' ) :
domain_quant . append ( ( ' lot_id ' , ' = ' , context [ ' lot_id ' ] ) )
if context . get ( ' owner_id ' ) :
domain_quant . append ( ( ' owner_id ' , ' = ' , context [ ' owner_id ' ] ) )
if context . get ( ' package_id ' ) :
domain_quant . append ( ( ' package_id ' , ' = ' , context [ ' package_id ' ] ) )
2015-03-02 11:17:15 +00:00
domain_quant + = self . _get_domain_locations ( cr , uid , [ ] , context = context ) [ 0 ]
2015-02-26 14:00:16 +00:00
quants = self . pool . get ( ' stock.quant ' ) . read_group ( cr , uid , domain_quant , [ ' product_id ' , ' qty ' ] , [ ' product_id ' ] , context = context )
quants = dict ( map ( lambda x : ( x [ ' product_id ' ] [ 0 ] , x [ ' qty ' ] ) , quants ) )
quants = dict ( ( k , v ) for k , v in quants . iteritems ( ) if eval ( str ( v ) + operator + str ( value ) ) )
return ( list ( quants ) )
2014-05-27 11:39:38 +00:00
def _product_available_text ( self , cr , uid , ids , field_names = None , arg = False , context = None ) :
res = { }
for product in self . browse ( cr , uid , ids , context = context ) :
2014-06-02 08:59:33 +00:00
res [ product . id ] = str ( product . qty_available ) + _ ( " On Hand " )
2014-05-27 11:39:38 +00:00
return res
2008-09-07 23:24:39 +00:00
_columns = {
2014-07-16 09:58:22 +00:00
' reception_count ' : fields . function ( _stock_move_count , string = " Receipt " , type = ' integer ' , multi = ' pickings ' ) ,
2012-05-22 07:02:28 +00:00
' delivery_count ' : fields . function ( _stock_move_count , string = " Delivery " , type = ' integer ' , multi = ' pickings ' ) ,
2014-06-02 08:59:33 +00:00
' qty_available_text ' : fields . function ( _product_available_text , type = ' char ' ) ,
2011-10-28 15:02:09 +00:00
' qty_available ' : fields . function ( _product_available , multi = ' qty_available ' ,
2014-01-24 15:07:04 +00:00
type = ' float ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2011-10-28 15:02:09 +00:00
string = ' Quantity On Hand ' ,
2014-01-17 09:20:11 +00:00
fnct_search = _search_product_quantity ,
2011-10-28 15:02:09 +00:00
help = " Current quantity of products. \n "
" In a context with a single Stock Location, this includes "
" goods stored at this Location, or any of its children. \n "
" In a context with a single Warehouse, this includes "
" goods stored in the Stock Location of this Warehouse, or any "
" of its children. \n "
" stored in the Stock Location of the Warehouse of this Shop, "
" or any of its children. \n "
" Otherwise, this includes goods stored in any Stock Location "
2012-10-23 13:02:42 +00:00
" with ' internal ' type. " ) ,
2011-10-28 15:02:09 +00:00
' virtual_available ' : fields . function ( _product_available , multi = ' qty_available ' ,
2014-01-24 15:07:04 +00:00
type = ' float ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
string = ' Forecast Quantity ' ,
2014-01-17 09:20:11 +00:00
fnct_search = _search_product_quantity ,
2011-12-22 16:54:22 +00:00
help = " Forecast quantity (computed as Quantity On Hand "
2011-10-28 15:02:09 +00:00
" - Outgoing + Incoming) \n "
" In a context with a single Stock Location, this includes "
2012-10-23 13:02:42 +00:00
" goods stored in this location, or any of its children. \n "
2011-10-28 15:02:09 +00:00
" In a context with a single Warehouse, this includes "
" goods stored in the Stock Location of this Warehouse, or any "
" of its children. \n "
" Otherwise, this includes goods stored in any Stock Location "
2012-10-23 13:02:42 +00:00
" with ' internal ' type. " ) ,
2011-10-28 15:02:09 +00:00
' incoming_qty ' : fields . function ( _product_available , multi = ' qty_available ' ,
2014-01-24 15:07:04 +00:00
type = ' float ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2011-10-28 15:02:09 +00:00
string = ' Incoming ' ,
2014-01-24 15:07:04 +00:00
fnct_search = _search_product_quantity ,
2011-10-28 15:02:09 +00:00
help = " Quantity of products that are planned to arrive. \n "
" In a context with a single Stock Location, this includes "
" goods arriving to this Location, or any of its children. \n "
" In a context with a single Warehouse, this includes "
" goods arriving to the Stock Location of this Warehouse, or "
" any of its children. \n "
" Otherwise, this includes goods arriving to any Stock "
2012-10-23 13:02:42 +00:00
" Location with ' internal ' type. " ) ,
2011-10-28 15:02:09 +00:00
' outgoing_qty ' : fields . function ( _product_available , multi = ' qty_available ' ,
2014-01-24 15:07:04 +00:00
type = ' float ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2011-10-28 15:02:09 +00:00
string = ' Outgoing ' ,
2014-01-24 15:07:04 +00:00
fnct_search = _search_product_quantity ,
2011-10-28 15:02:09 +00:00
help = " Quantity of products that are planned to leave. \n "
" In a context with a single Stock Location, this includes "
2012-10-23 13:02:42 +00:00
" goods leaving this Location, or any of its children. \n "
2011-10-28 15:02:09 +00:00
" In a context with a single Warehouse, this includes "
2012-10-23 13:02:42 +00:00
" goods leaving the Stock Location of this Warehouse, or "
2011-10-28 15:02:09 +00:00
" any of its children. \n "
2012-10-23 13:02:42 +00:00
" Otherwise, this includes goods leaving any Stock "
" Location with ' internal ' type. " ) ,
2011-07-08 08:52:03 +00:00
' location_id ' : fields . dummy ( string = ' Location ' , relation = ' stock.location ' , type = ' many2one ' ) ,
' warehouse_id ' : fields . dummy ( string = ' Warehouse ' , relation = ' stock.warehouse ' , type = ' many2one ' ) ,
2013-09-02 14:37:43 +00:00
' orderpoint_ids ' : fields . one2many ( ' stock.warehouse.orderpoint ' , ' product_id ' , ' Minimum Stock Rules ' ) ,
2008-09-07 23:24:39 +00:00
}
2010-06-10 12:44:52 +00:00
2009-09-24 10:46:21 +00:00
def fields_view_get ( self , cr , uid , view_id = None , view_type = ' form ' , context = None , toolbar = False , submenu = False ) :
2015-04-17 20:05:55 +00:00
res = super ( product_product , self ) . fields_view_get (
cr , uid , view_id = view_id , view_type = view_type , context = context ,
toolbar = toolbar , submenu = submenu )
2010-07-06 11:56:32 +00:00
if context is None :
2009-12-29 13:05:00 +00:00
context = { }
2015-07-06 14:28:48 +00:00
if context . get ( ' location ' ) and isinstance ( context [ ' location ' ] , int ) :
2008-09-23 13:24:11 +00:00
location_info = self . pool . get ( ' stock.location ' ) . browse ( cr , uid , context [ ' location ' ] )
fields = res . get ( ' fields ' , { } )
if fields :
2008-09-23 14:24:03 +00:00
if location_info . usage == ' supplier ' :
if fields . get ( ' virtual_available ' ) :
2014-07-16 09:58:22 +00:00
res [ ' fields ' ] [ ' virtual_available ' ] [ ' string ' ] = _ ( ' Future Receipts ' )
2008-09-23 13:24:11 +00:00
if fields . get ( ' qty_available ' ) :
2008-12-08 17:08:40 +00:00
res [ ' fields ' ] [ ' qty_available ' ] [ ' string ' ] = _ ( ' Received Qty ' )
2008-09-23 14:24:03 +00:00
2008-09-23 13:24:11 +00:00
if location_info . usage == ' internal ' :
if fields . get ( ' virtual_available ' ) :
2009-11-13 05:41:16 +00:00
res [ ' fields ' ] [ ' virtual_available ' ] [ ' string ' ] = _ ( ' Future Stock ' )
2008-09-23 14:24:03 +00:00
2008-09-23 13:24:11 +00:00
if location_info . usage == ' customer ' :
if fields . get ( ' virtual_available ' ) :
2009-11-13 05:41:16 +00:00
res [ ' fields ' ] [ ' virtual_available ' ] [ ' string ' ] = _ ( ' Future Deliveries ' )
2008-09-23 13:24:11 +00:00
if fields . get ( ' qty_available ' ) :
2008-12-08 17:08:40 +00:00
res [ ' fields ' ] [ ' qty_available ' ] [ ' string ' ] = _ ( ' Delivered Qty ' )
2008-09-23 14:24:03 +00:00
2008-09-23 13:24:11 +00:00
if location_info . usage == ' inventory ' :
if fields . get ( ' virtual_available ' ) :
2009-11-13 05:41:16 +00:00
res [ ' fields ' ] [ ' virtual_available ' ] [ ' string ' ] = _ ( ' Future P&L ' )
2010-07-06 11:56:32 +00:00
if fields . get ( ' qty_available ' ) :
2010-06-18 07:12:06 +00:00
res [ ' fields ' ] [ ' qty_available ' ] [ ' string ' ] = _ ( ' P&L Qty ' )
2008-09-23 14:24:03 +00:00
2008-09-23 13:24:11 +00:00
if location_info . usage == ' procurement ' :
if fields . get ( ' virtual_available ' ) :
2009-11-13 05:41:16 +00:00
res [ ' fields ' ] [ ' virtual_available ' ] [ ' string ' ] = _ ( ' Future Qty ' )
2008-09-23 13:24:11 +00:00
if fields . get ( ' qty_available ' ) :
2008-12-08 17:08:40 +00:00
res [ ' fields ' ] [ ' qty_available ' ] [ ' string ' ] = _ ( ' Unplanned Qty ' )
2008-09-23 14:24:03 +00:00
2008-09-23 13:24:11 +00:00
if location_info . usage == ' production ' :
if fields . get ( ' virtual_available ' ) :
2009-11-13 05:41:16 +00:00
res [ ' fields ' ] [ ' virtual_available ' ] [ ' string ' ] = _ ( ' Future Productions ' )
2008-09-23 13:24:11 +00:00
if fields . get ( ' qty_available ' ) :
2008-12-08 17:08:40 +00:00
res [ ' fields ' ] [ ' qty_available ' ] [ ' string ' ] = _ ( ' Produced Qty ' )
2008-09-23 13:24:11 +00:00
return res
2008-09-07 23:24:39 +00:00
2014-06-02 11:35:45 +00:00
def action_view_routes ( self , cr , uid , ids , context = None ) :
template_obj = self . pool . get ( " product.template " )
templ_ids = list ( set ( [ x . product_tmpl_id . id for x in self . browse ( cr , uid , ids , context = context ) ] ) )
return template_obj . action_view_routes ( cr , uid , templ_ids , context = context )
2008-09-23 13:24:11 +00:00
class product_template ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = ' product.template '
_inherit = ' product.template '
2014-05-27 07:42:52 +00:00
def _product_available ( self , cr , uid , ids , name , arg , context = None ) :
2015-03-02 11:17:15 +00:00
prod_available = { }
2015-02-26 14:00:16 +00:00
product_ids = self . browse ( cr , uid , ids , context = context )
var_ids = [ ]
for product in product_ids :
var_ids + = [ p . id for p in product . product_variant_ids ]
variant_available = self . pool [ ' product.product ' ] . _product_available ( cr , uid , var_ids , context = context )
for product in product_ids :
qty_available = 0
virtual_available = 0
incoming_qty = 0
outgoing_qty = 0
for p in product . product_variant_ids :
qty_available + = variant_available [ p . id ] [ " qty_available " ]
virtual_available + = variant_available [ p . id ] [ " virtual_available " ]
incoming_qty + = variant_available [ p . id ] [ " incoming_qty " ]
outgoing_qty + = variant_available [ p . id ] [ " outgoing_qty " ]
prod_available [ product . id ] = {
2015-03-02 11:17:15 +00:00
" qty_available " : qty_available ,
" virtual_available " : virtual_available ,
" incoming_qty " : incoming_qty ,
" outgoing_qty " : outgoing_qty ,
2014-05-27 07:42:52 +00:00
}
2015-02-26 14:00:16 +00:00
return prod_available
2014-05-27 07:42:52 +00:00
def _search_product_quantity ( self , cr , uid , obj , name , domain , context ) :
prod = self . pool . get ( " product.product " )
2015-02-26 14:00:16 +00:00
product_variant_ids = prod . search ( cr , uid , domain , context = context )
return [ ( ' product_variant_ids ' , ' in ' , product_variant_ids ) ]
2014-07-07 19:45:11 +00:00
def _product_available_text ( self , cr , uid , ids , field_names = None , arg = False , context = None ) :
res = { }
for product in self . browse ( cr , uid , ids , context = context ) :
res [ product . id ] = str ( product . qty_available ) + _ ( " On Hand " )
return res
2008-07-22 15:11:28 +00:00
_columns = {
2013-09-02 14:37:43 +00:00
' type ' : fields . selection ( [ ( ' product ' , ' Stockable Product ' ) , ( ' consu ' , ' Consumable ' ) , ( ' service ' , ' Service ' ) ] , ' Product Type ' , required = True , help = " Consumable: Will not imply stock management for this product. \n Stockable product: Will imply stock management for this product. " ) ,
2014-07-07 19:45:11 +00:00
' qty_available_text ' : fields . function ( _product_available_text , type = ' char ' ) ,
2008-07-22 15:11:28 +00:00
' property_stock_procurement ' : fields . property (
type = ' many2one ' ,
relation = ' stock.location ' ,
2010-04-09 07:14:08 +00:00
string = " Procurement Location " ,
2009-10-22 13:40:16 +00:00
domain = [ ( ' usage ' , ' like ' , ' procurement ' ) ] ,
2012-10-23 13:02:42 +00:00
help = " This stock location will be used, instead of the default one, as the source location for stock moves generated by procurements. " ) ,
2008-07-22 15:11:28 +00:00
' property_stock_production ' : fields . property (
type = ' many2one ' ,
relation = ' stock.location ' ,
string = " Production Location " ,
2009-10-22 13:40:16 +00:00
domain = [ ( ' usage ' , ' like ' , ' production ' ) ] ,
2012-10-23 13:02:42 +00:00
help = " This stock location will be used, instead of the default one, as the source location for stock moves generated by manufacturing orders. " ) ,
2008-07-22 15:11:28 +00:00
' property_stock_inventory ' : fields . property (
type = ' many2one ' ,
relation = ' stock.location ' ,
string = " Inventory Location " ,
2009-10-22 13:40:16 +00:00
domain = [ ( ' usage ' , ' like ' , ' inventory ' ) ] ,
2012-10-31 10:25:24 +00:00
help = " This stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory. " ) ,
2012-10-23 13:02:42 +00:00
' sale_delay ' : fields . float ( ' Customer Lead Time ' , help = " The average delay in days between the confirmation of the customer order and the delivery of the finished products. It ' s the time you promise to your customers. " ) ,
2012-10-03 06:48:40 +00:00
' loc_rack ' : fields . char ( ' Rack ' , size = 16 ) ,
' loc_row ' : fields . char ( ' Row ' , size = 16 ) ,
' loc_case ' : fields . char ( ' Case ' , size = 16 ) ,
2014-05-27 07:42:52 +00:00
' track_incoming ' : fields . boolean ( ' Track Incoming Lots ' , help = " Forces to specify a Serial Number for all moves containing this product and coming from a Supplier Location " ) ,
' track_outgoing ' : fields . boolean ( ' Track Outgoing Lots ' , help = " Forces to specify a Serial Number for all moves containing this product and going to a Customer Location " ) ,
' track_all ' : fields . boolean ( ' Full Lots Traceability ' , help = " Forces to specify a Serial Number on each and every operation related to this product " ) ,
# sum of product variant qty
# 'reception_count': fields.function(_product_available, multi='qty_available',
# fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
# 'delivery_count': fields.function(_product_available, multi='qty_available',
# fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
2015-03-02 14:04:06 +00:00
' qty_available ' : fields . function ( _product_available , multi = ' qty_available ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2014-05-27 07:42:52 +00:00
fnct_search = _search_product_quantity , type = ' float ' , string = ' Quantity On Hand ' ) ,
2015-03-02 14:04:06 +00:00
' virtual_available ' : fields . function ( _product_available , multi = ' qty_available ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2014-05-27 07:42:52 +00:00
fnct_search = _search_product_quantity , type = ' float ' , string = ' Quantity Available ' ) ,
2015-03-02 14:04:06 +00:00
' incoming_qty ' : fields . function ( _product_available , multi = ' qty_available ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2014-05-27 07:42:52 +00:00
fnct_search = _search_product_quantity , type = ' float ' , string = ' Incoming ' ) ,
2015-03-02 14:04:06 +00:00
' outgoing_qty ' : fields . function ( _product_available , multi = ' qty_available ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
2014-05-27 07:42:52 +00:00
fnct_search = _search_product_quantity , type = ' float ' , string = ' Outgoing ' ) ,
' route_ids ' : fields . many2many ( ' stock.location.route ' , ' stock_route_product ' , ' product_id ' , ' route_id ' , ' Routes ' , domain = " [( ' product_selectable ' , ' = ' , True)] " ,
help = " Depending on the modules installed, this will allow you to define the route of the product: whether it will be bought, manufactured, MTO/MTS,... " ) ,
2008-07-22 15:11:28 +00:00
}
2010-07-06 11:56:32 +00:00
2012-10-03 06:48:40 +00:00
_defaults = {
2012-10-03 11:42:08 +00:00
' sale_delay ' : 7 ,
2012-10-03 06:48:40 +00:00
}
2014-04-11 17:19:01 +00:00
2014-05-27 07:42:52 +00:00
def action_view_routes ( self , cr , uid , ids , context = None ) :
route_obj = self . pool . get ( " stock.location.route " )
act_obj = self . pool . get ( ' ir.actions.act_window ' )
mod_obj = self . pool . get ( ' ir.model.data ' )
product_route_ids = set ( )
for product in self . browse ( cr , uid , ids , context = context ) :
product_route_ids | = set ( [ r . id for r in product . route_ids ] )
product_route_ids | = set ( [ r . id for r in product . categ_id . total_route_ids ] )
route_ids = route_obj . search ( cr , uid , [ ' | ' , ( ' id ' , ' in ' , list ( product_route_ids ) ) , ( ' warehouse_selectable ' , ' = ' , True ) ] , context = context )
2014-07-16 09:58:22 +00:00
result = mod_obj . xmlid_to_res_id ( cr , uid , ' stock.action_routes_form ' , raise_if_not_found = True )
result = act_obj . read ( cr , uid , [ result ] , context = context ) [ 0 ]
2014-05-27 07:42:52 +00:00
result [ ' domain ' ] = " [( ' id ' , ' in ' ,[ " + ' , ' . join ( map ( str , route_ids ) ) + " ])] "
return result
2014-04-11 17:19:01 +00:00
2014-07-07 19:45:11 +00:00
def _get_products ( self , cr , uid , ids , context = None ) :
products = [ ]
for prodtmpl in self . browse ( cr , uid , ids , context = None ) :
products + = [ x . id for x in prodtmpl . product_variant_ids ]
return products
2014-07-16 09:58:22 +00:00
def _get_act_window_dict ( self , cr , uid , name , context = None ) :
2014-07-07 19:45:11 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
act_obj = self . pool . get ( ' ir.actions.act_window ' )
2014-07-16 09:58:22 +00:00
result = mod_obj . xmlid_to_res_id ( cr , uid , name , raise_if_not_found = True )
result = act_obj . read ( cr , uid , [ result ] , context = context ) [ 0 ]
2014-07-07 19:45:11 +00:00
return result
def action_open_quants ( self , cr , uid , ids , context = None ) :
products = self . _get_products ( cr , uid , ids , context = context )
2014-07-16 09:58:22 +00:00
result = self . _get_act_window_dict ( cr , uid , ' stock.product_open_quants ' , context = context )
2014-07-07 19:45:11 +00:00
result [ ' domain ' ] = " [( ' product_id ' , ' in ' ,[ " + ' , ' . join ( map ( str , products ) ) + " ])] "
2014-07-07 21:33:47 +00:00
result [ ' context ' ] = " { ' search_default_locationgroup ' : 1, ' search_default_internal_loc ' : 1} "
2014-07-07 19:45:11 +00:00
return result
def action_view_orderpoints ( self , cr , uid , ids , context = None ) :
products = self . _get_products ( cr , uid , ids , context = context )
2014-07-16 09:58:22 +00:00
result = self . _get_act_window_dict ( cr , uid , ' stock.product_open_orderpoint ' , context = context )
2014-08-06 12:44:45 +00:00
if len ( ids ) == 1 and len ( products ) == 1 :
result [ ' context ' ] = " { ' default_product_id ' : " + str ( products [ 0 ] ) + " , ' search_default_product_id ' : " + str ( products [ 0 ] ) + " } "
else :
result [ ' domain ' ] = " [( ' product_id ' , ' in ' ,[ " + ' , ' . join ( map ( str , products ) ) + " ])] "
result [ ' context ' ] = " {} "
2014-07-07 19:45:11 +00:00
return result
def action_view_stock_moves ( self , cr , uid , ids , context = None ) :
products = self . _get_products ( cr , uid , ids , context = context )
2014-07-16 09:58:22 +00:00
result = self . _get_act_window_dict ( cr , uid , ' stock.act_product_stock_move_open ' , context = context )
2014-08-06 12:44:45 +00:00
if len ( ids ) == 1 and len ( products ) == 1 :
2014-08-26 22:07:24 +00:00
ctx = " { ' tree_view_ref ' : ' stock.view_move_tree ' , \
' default_product_id ' : % s , ' search_default_product_id ' : % s } " \
% ( products [ 0 ] , products [ 0 ] )
result [ ' context ' ] = ctx
2014-08-06 12:44:45 +00:00
else :
result [ ' domain ' ] = " [( ' product_id ' , ' in ' ,[ " + ' , ' . join ( map ( str , products ) ) + " ])] "
2014-08-26 22:07:24 +00:00
result [ ' context ' ] = " { ' tree_view_ref ' : ' stock.view_move_tree ' } "
2014-07-07 19:45:11 +00:00
return result
2015-06-23 09:55:30 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
if ' uom_id ' in vals :
new_uom = self . pool . get ( ' product.uom ' ) . browse ( cr , uid , vals [ ' uom_id ' ] , context = context )
for product in self . browse ( cr , uid , ids , context = context ) :
old_uom = product . uom_id
if old_uom != new_uom :
if self . pool . get ( ' stock.move ' ) . search ( cr , uid , [ ( ' product_id ' , ' in ' , [ x . id for x in product . product_variant_ids ] ) , ( ' state ' , ' = ' , ' done ' ) ] , limit = 1 , context = context ) :
raise except_orm ( _ ( ' Warning ' ) , _ ( " You can not change the unit of measure of a product that has already been used in a done stock move. If you need to change the unit of measure, you may deactivate this product. " ) )
return super ( product_template , self ) . write ( cr , uid , ids , vals , context = context )
2014-07-07 19:45:11 +00:00
2013-10-04 14:56:59 +00:00
class product_removal_strategy ( osv . osv ) :
_name = ' product.removal '
_description = ' Removal Strategy '
2014-04-11 17:19:01 +00:00
2013-10-04 14:56:59 +00:00
_columns = {
2014-04-11 17:19:01 +00:00
' name ' : fields . char ( ' Name ' , required = True ) ,
' method ' : fields . char ( " Method " , required = True , help = " FIFO, LIFO... " ) ,
2013-10-04 14:56:59 +00:00
}
2013-10-07 13:04:47 +00:00
class product_putaway_strategy ( osv . osv ) :
_name = ' product.putaway '
_description = ' Put Away Strategy '
2014-04-11 17:19:01 +00:00
def _get_putaway_options ( self , cr , uid , context = None ) :
return [ ( ' fixed ' , ' Fixed Location ' ) ]
2013-10-07 13:04:47 +00:00
_columns = {
2014-04-11 17:19:01 +00:00
' name ' : fields . char ( ' Name ' , required = True ) ,
' method ' : fields . selection ( _get_putaway_options , " Method " , required = True ) ,
2014-07-06 14:44:26 +00:00
' fixed_location_ids ' : fields . one2many ( ' stock.fixed.putaway.strat ' , ' putaway_id ' , ' Fixed Locations Per Product Category ' , help = " When the method is fixed, this location will be used to store the products " , copy = True ) ,
2014-04-11 17:19:01 +00:00
}
_defaults = {
' method ' : ' fixed ' ,
}
def putaway_apply ( self , cr , uid , putaway_strat , product , context = None ) :
if putaway_strat . method == ' fixed ' :
for strat in putaway_strat . fixed_location_ids :
2014-04-14 09:47:43 +00:00
categ = product . categ_id
while categ :
if strat . category_id . id == categ . id :
return strat . fixed_location_id . id
categ = categ . parent_id
2014-04-11 17:19:01 +00:00
class fixed_putaway_strat ( osv . osv ) :
_name = ' stock.fixed.putaway.strat '
_order = ' sequence '
_columns = {
' putaway_id ' : fields . many2one ( ' product.putaway ' , ' Put Away Method ' , required = True ) ,
' category_id ' : fields . many2one ( ' product.category ' , ' Product Category ' , required = True ) ,
' fixed_location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True ) ,
' sequence ' : fields . integer ( ' Priority ' , help = " Give to the more specialized category, a higher priority to have them in top of the list. " ) ,
2013-10-07 13:04:47 +00:00
}
2013-10-04 14:56:59 +00:00
class product_category ( osv . osv ) :
_inherit = ' product.category '
2014-04-11 17:19:01 +00:00
2013-10-04 14:56:59 +00:00
def calculate_total_routes ( self , cr , uid , ids , name , args , context = None ) :
res = { }
for categ in self . browse ( cr , uid , ids , context = context ) :
categ2 = categ
routes = [ x . id for x in categ . route_ids ]
while categ2 . parent_id :
categ2 = categ2 . parent_id
routes + = [ x . id for x in categ2 . route_ids ]
res [ categ . id ] = routes
return res
2014-04-11 17:19:01 +00:00
2013-10-04 14:56:59 +00:00
_columns = {
' route_ids ' : fields . many2many ( ' stock.location.route ' , ' stock_location_route_categ ' , ' categ_id ' , ' route_id ' , ' Routes ' , domain = " [( ' product_categ_selectable ' , ' = ' , True)] " ) ,
2014-04-11 17:19:01 +00:00
' removal_strategy_id ' : fields . many2one ( ' product.removal ' , ' Force Removal Strategy ' , help = " Set a specific removal strategy that will be used regardless of the source location for this product category " ) ,
2013-10-04 14:56:59 +00:00
' total_route_ids ' : fields . function ( calculate_total_routes , relation = ' stock.location.route ' , type = ' many2many ' , string = ' Total routes ' , readonly = True ) ,
}
2006-12-07 13:41:40 +00:00
2010-07-06 11:56:32 +00:00
2010-09-21 05:58:33 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: