2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2008-09-07 23:24:39 +00:00
##############################################################################
2010-06-15 08:14:32 +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>).
2008-09-07 23:24:39 +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.
2008-09-07 23:24:39 +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.
2008-09-07 23:24:39 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-06-15 08:14:32 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2008-09-07 23:24:39 +00:00
#
##############################################################################
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
2013-07-07 20:42:48 +00:00
from datetime import *
2013-07-09 12:06:59 +00:00
from dateutil . relativedelta import relativedelta
from openerp . tools . translate import _
2013-07-01 06:30:34 +00:00
2013-06-29 22:17:03 +00:00
class stock_location_route ( osv . osv ) :
_name = ' stock.location.route '
_description = " Inventory Routes "
_order = ' sequence '
_columns = {
' name ' : fields . char ( ' Route Name ' , required = True ) ,
' sequence ' : fields . integer ( ' Sequence ' ) ,
2013-07-01 06:30:34 +00:00
' pull_ids ' : fields . one2many ( ' procurement.rule ' , ' route_id ' , ' Pull Rules ' ) ,
2013-06-29 22:17:03 +00:00
' push_ids ' : fields . one2many ( ' stock.location.path ' , ' route_id ' , ' Push Rules ' ) ,
}
_defaults = {
' sequence ' : lambda self , cr , uid , ctx : 0 ,
}
2008-09-07 23:24:39 +00:00
class stock_location_path ( osv . osv ) :
_name = " stock.location.path "
2010-10-11 21:57:06 +00:00
_description = " Pushed Flows "
2008-09-07 23:24:39 +00:00
_columns = {
' name ' : fields . char ( ' Operation ' , size = 64 ) ,
2010-05-28 09:55:05 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2013-06-29 22:17:03 +00:00
' route_id ' : fields . many2one ( ' stock.location.route ' , ' Route ' ) ,
2008-09-07 23:24:39 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Products ' , ondelete = ' cascade ' , select = 1 ) ,
2013-06-29 22:17:03 +00:00
2010-10-11 21:57:06 +00:00
' location_from_id ' : fields . many2one ( ' stock.location ' , ' Source Location ' , ondelete = ' cascade ' , select = 1 , required = True ) ,
' location_dest_id ' : fields . many2one ( ' stock.location ' , ' Destination Location ' , ondelete = ' cascade ' , select = 1 , required = True ) ,
2008-09-07 23:24:39 +00:00
' delay ' : fields . integer ( ' Delay (days) ' , help = " Number of days to do this transition " ) ,
2010-06-10 13:00:36 +00:00
' invoice_state ' : fields . selection ( [
( " invoiced " , " Invoiced " ) ,
( " 2binvoiced " , " To Be Invoiced " ) ,
2010-10-28 07:20:11 +00:00
( " none " , " Not Applicable " ) ] , " Invoice Status " ,
2010-06-10 13:00:36 +00:00
required = True , ) ,
2010-10-11 21:57:06 +00:00
' picking_type ' : fields . selection ( [ ( ' out ' , ' Sending Goods ' ) , ( ' in ' , ' Getting Goods ' ) , ( ' internal ' , ' Internal ' ) ] , ' Shipping Type ' , required = True , select = True , help = " Depending on the company, choose whatever you want to receive or send products " ) ,
2008-09-07 23:24:39 +00:00
' auto ' : fields . selection (
[ ( ' auto ' , ' Automatic Move ' ) , ( ' manual ' , ' Manual Operation ' ) , ( ' transparent ' , ' Automatic No Step Added ' ) ] ,
' Automatic Move ' ,
required = True , select = 1 ,
help = " This is used to define paths the product has to follow within the location tree. \n " \
" The ' Automatic Move ' value will create a stock move after the current one that will be " \
" validated automatically. With ' Manual Operation ' , the stock move has to be validated " \
" by a worker. With ' Automatic No Step Added ' , the location is replaced in the original move. "
2013-06-29 22:17:03 +00:00
) ,
2008-09-07 23:24:39 +00:00
}
_defaults = {
2010-11-27 20:16:16 +00:00
' auto ' : ' auto ' ,
' delay ' : 1 ,
' invoice_state ' : ' none ' ,
' picking_type ' : ' internal ' ,
2008-09-07 23:24:39 +00:00
}
2013-06-29 22:17:03 +00:00
def _apply ( self , cr , uid , rule , move , context = None ) :
move_obj = self . pool . get ( ' stock.move ' )
2013-07-09 12:06:59 +00:00
newdate = ( datetime . strptime ( move . date , ' % Y- % m- %d % H: % M: % S ' ) + relativedelta ( days = rule . delay or 0 ) ) . strftime ( ' % Y- % m- %d ' )
2013-06-29 22:17:03 +00:00
if rule . auto == ' transparent ' :
self . write ( cr , uid , [ move . id ] , {
' date ' : newdate ,
' location_dest_id ' : rule . location_dest_id . id
} )
vals = { }
2013-07-07 20:42:48 +00:00
# TODO journal_id was to be removed?
# if route.journal_id:
# vals['stock_journal_id'] = route.journal_id.id
2013-06-29 22:17:03 +00:00
vals [ ' type ' ] = rule . picking_type
if rule . location_dest_id . id < > move . location_dest_id . id :
2013-07-01 19:13:08 +00:00
move_obj . _push_apply ( self , cr , uid , move . id , context )
2013-06-29 22:17:03 +00:00
return move . id
else :
move_id = move_obj . copy ( cr , uid , move . id , {
' location_id ' : move . location_dest_id . id ,
' location_dest_id ' : rule . location_dest_id . id ,
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
' company_id ' : rule . company_id . id ,
' date_expected ' : newdate ,
2013-07-01 19:13:08 +00:00
} )
2013-06-29 22:17:03 +00:00
move_obj . write ( cr , uid , [ move . id ] , {
' move_dest_id ' : move_id ,
} )
move_obj . action_confirm ( self , cr , uid , [ move_id ] , context = None )
return move_id
2008-09-07 23:24:39 +00:00
2013-07-01 19:13:08 +00:00
class procurement_rule ( osv . osv ) :
2013-07-01 06:30:34 +00:00
_inherit = ' procurement.rule '
2010-05-28 09:55:05 +00:00
_columns = {
2013-07-01 06:30:34 +00:00
' route_id ' : fields . many2one ( ' stock.location.route ' , ' Route ' ,
2013-07-09 15:15:28 +00:00
help = " If route_id is False, the rule is global " ) ,
2013-06-29 22:17:03 +00:00
' delay ' : fields . integer ( ' Number of Hours ' ) ,
2010-05-28 09:55:05 +00:00
' procure_method ' : fields . selection ( [ ( ' make_to_stock ' , ' Make to Stock ' ) , ( ' make_to_order ' , ' Make to Order ' ) ] , ' Procure Method ' , required = True , help = " ' Make to Stock ' : When needed, take from the stock or wait until re-supplying. ' Make to Order ' : When needed, purchase or produce for the procurement request. " ) ,
' type_proc ' : fields . selection ( [ ( ' produce ' , ' Produce ' ) , ( ' buy ' , ' Buy ' ) , ( ' move ' , ' Move ' ) ] , ' Type of Procurement ' , required = True ) ,
2012-03-21 12:07:42 +00:00
' partner_address_id ' : fields . many2one ( ' res.partner ' , ' Partner Address ' ) ,
2010-06-10 13:00:36 +00:00
' invoice_state ' : fields . selection ( [
( " invoiced " , " Invoiced " ) ,
( " 2binvoiced " , " To Be Invoiced " ) ,
2010-10-28 07:20:11 +00:00
( " none " , " Not Applicable " ) ] , " Invoice Status " ,
2010-06-10 13:00:36 +00:00
required = True , ) ,
2010-05-28 09:55:05 +00:00
}
_defaults = {
2010-06-21 10:44:15 +00:00
' procure_method ' : ' make_to_stock ' ,
' type_proc ' : ' move ' ,
' invoice_state ' : ' none ' ,
2010-05-28 09:55:05 +00:00
}
2013-07-09 13:25:06 +00:00
2013-07-09 12:06:59 +00:00
2013-06-29 22:17:03 +00:00
2010-05-28 09:55:05 +00:00
2013-07-01 06:30:34 +00:00
class procurement_order ( osv . osv ) :
2013-07-01 19:13:08 +00:00
_inherit = ' procurement.order '
2013-07-09 12:06:59 +00:00
def _run_move_create ( self , cr , uid , procurement , context = None ) :
d = super ( procurement_order , self ) . _run_move_create ( cr , uid , procurement , context = context )
if procurement . move_dest_id :
date = procurement . move_dest_id . date
else :
date = procurement . date_planned
newdate = ( datetime . strptime ( date , ' % Y- % m- %d % H: % M: % S ' ) - relativedelta ( days = procurement . rule_id . delay or 0 ) ) . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2013-07-01 06:30:34 +00:00
d . update ( {
2013-07-10 15:02:07 +00:00
' date ' : newdate ,
2013-06-29 22:17:03 +00:00
} )
2013-07-01 06:30:34 +00:00
return d
# TODO: implement using routes on products
def _assign ( self , cr , uid , procurement , context = None ) :
if procurement . location_id :
rule_obj = self . pool . get ( ' procurement.rule ' )
2013-07-10 10:31:56 +00:00
route_ids = [ x . id for x in procurement . product_id . route_ids ]
res = rule_obj . search ( cr , uid , [ ( ' location_id ' , ' = ' , procurement . location_id . id ) , ( ' route_id ' , ' in ' , route_ids ) , ] , context = context )
2013-07-01 06:30:34 +00:00
if not res :
2013-07-10 10:31:56 +00:00
return super ( procurement_order , self ) . _assign ( cr , uid , procurement , context = context )
2013-07-09 12:06:59 +00:00
return res [ 0 ]
2013-07-01 06:30:34 +00:00
return super ( procurement_order , self ) . _assign ( cr , uid , procurement , context = context )
2013-06-24 15:28:50 +00:00
class product_putaway_strategy ( osv . osv ) :
_name = ' product.putaway '
_description = ' Put Away Strategy '
_columns = {
2013-06-27 14:46:29 +00:00
' product_categ_id ' : fields . many2one ( ' product.category ' , ' Product Category ' , required = True ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Parent Location ' , help = " Parent Destination Location from which a child bin location needs to be chosen " , required = True ) , #domain=[('type', '=', 'parent')],
' method ' : fields . selection ( [ ( ' empty ' , ' Empty ' ) , ( ' fixed ' , ' Fixed Location ' ) ] , " Method " , required = True ) ,
2013-06-29 22:17:03 +00:00
}
2013-06-24 15:28:50 +00:00
2013-06-29 22:17:03 +00:00
# TODO: move this on stock module
2013-06-24 15:28:50 +00:00
class product_removal_strategy ( osv . osv ) :
2013-07-01 19:13:08 +00:00
_name = ' product.removal '
_description = ' Removal Strategy '
2013-06-29 22:17:03 +00:00
_order = ' sequence '
2013-06-24 15:28:50 +00:00
_columns = {
2013-07-01 19:13:08 +00:00
' product_categ_id ' : fields . many2one ( ' product.removal ' , ' Category ' , required = True ) ,
2013-06-29 22:17:03 +00:00
' sequence ' : fields . integer ( ' Sequence ' ) ,
2013-07-01 19:13:08 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Locations ' , required = True ) ,
2013-06-29 22:17:03 +00:00
}
2013-06-24 15:28:50 +00:00
2008-09-07 23:24:39 +00:00
class product_product ( osv . osv ) :
_inherit = ' product.product '
_columns = {
2013-07-03 20:36:22 +00:00
' route_ids ' : fields . many2many ( ' stock.location.route ' , ' stock_location_route_product ' , ' product_id ' , ' route_id ' , ' Routes ' ) ,
2008-09-07 23:24:39 +00:00
}
2013-06-24 15:28:50 +00:00
class product_category ( osv . osv ) :
_inherit = ' product.category '
_columns = {
2013-07-03 20:36:22 +00:00
' route_ids ' : fields . many2many ( ' stock.location.route ' , ' stock_location_route_categ ' , ' categ_id ' , ' route_id ' , ' Routes ' ) ,
#'removal_strategy_ids': fields.one2many('product.removal', 'product_categ_id', 'Removal Strategies'),
#'putaway_strategy_ids': fields.one2many('product.putaway', 'product_categ_id', 'Put Away Strategies'),
2013-06-24 15:28:50 +00:00
}
2013-07-07 20:42:48 +00:00
class stock_move_putaway ( osv . osv ) :
2013-06-29 22:17:03 +00:00
_name = ' stock.move.putaway '
_description = ' Proposed Destination '
_columns = {
' move_id ' : fields . many2one ( ' stock.move ' , required = True ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True ) ,
' lot_id ' : fields . many2one ( ' stock.production.lot ' , ' Lot ' ) ,
' quantity ' : fields . float ( ' Quantity ' , required = True ) ,
}
2013-06-24 11:24:34 +00:00
2010-05-28 09:55:05 +00:00
class stock_move ( osv . osv ) :
_inherit = ' stock.move '
_columns = {
2013-06-29 22:17:03 +00:00
' cancel_cascade ' : fields . boolean ( ' Cancel Cascade ' , help = ' If checked, when this move is cancelled, cancel the linked move too ' ) ,
2013-07-09 12:06:59 +00:00
' putaway_ids ' : fields . one2many ( ' stock.move.putaway ' , ' move_id ' , ' Put Away Suggestions ' ) ,
2010-05-28 09:55:05 +00:00
}
2013-07-09 13:25:06 +00:00
2013-07-01 09:09:23 +00:00
# TODO: reimplement this
2013-07-09 13:25:06 +00:00
# def _pull_apply(self, cr, uid, moves, context):
# # Create a procurement is MTO on stock.move
# # Call _assign on procurement
# for move in moves:
# #If move is MTO, then you should create a procurement
# #Search for original rule
# #Then change procurement to stock
# for route in move.product_id.route_ids:
# found = False
# for rule in route.pull_ids:
# if rule.location_id.id == move.location_dest_id.id and move.procure_method == "make_to_order":
# self._create_procurement(cr, uid, rule, move, context=context)
# found = True
# break
# if found: break
# return True
2013-06-29 22:17:03 +00:00
def _push_apply ( self , cr , uid , moves , context ) :
for move in moves :
for route in move . product_id . route_ids :
found = False
for rule in route . push_ids :
if rule . location_from_id . id == move . location_dest_id . id :
self . pool . get ( ' stock.location.path ' ) . _apply ( cr , uid , rule , move , context = context )
found = True
break
if found : break
2013-06-25 14:40:59 +00:00
return True
2013-06-29 22:17:03 +00:00
# Create the stock.move.putaway records
def _putaway_apply ( self , cr , uid , ids , context = None ) :
for move in self . browse ( cr , uid , ids , context = context ) :
res = self . pool . get ( ' stock.location ' ) . get_putaway_strategy ( cr , uid , move . location_dest_id . id , move . product_id . id , context = context )
if res :
raise ' put away strategies not implemented yet! '
return True
2013-06-25 14:40:59 +00:00
2013-06-29 22:17:03 +00:00
def action_assign ( self , cr , uid , ids , context = None ) :
result = super ( stock_move , self ) . action_assign ( cr , uid , ids , context = context )
self . _putaway_apply ( cr , uid , ids , context = context )
return result
2013-06-25 14:40:59 +00:00
2013-06-29 22:17:03 +00:00
def action_confirm ( self , cr , uid , ids , context = None ) :
result = super ( stock_move , self ) . action_confirm ( cr , uid , ids , context )
2013-07-09 12:06:59 +00:00
moves = self . browse ( cr , uid , ids , context = context )
self . _push_apply ( cr , uid , moves , context = context )
2013-06-29 22:17:03 +00:00
return result
2010-05-28 09:55:05 +00:00
2008-09-07 23:24:39 +00:00
class stock_location ( osv . osv ) :
_inherit = ' stock.location '
2013-06-24 15:28:50 +00:00
_columns = {
2013-06-25 14:40:59 +00:00
' removal_strategy_ids ' : fields . one2many ( ' product.removal ' , ' location_id ' , ' Removal Strategies ' ) ,
' putaway_strategy_ids ' : fields . one2many ( ' product.putaway ' , ' location_id ' , ' Put Away Strategies ' ) ,
2013-06-29 22:17:03 +00:00
}
2013-06-24 15:28:50 +00:00
def get_putaway_strategy ( self , cr , uid , id , product_id , context = None ) :
product = self . pool . get ( " product.product " ) . browse ( cr , uid , product_id , context = context )
2013-06-29 22:17:03 +00:00
strats = self . pool . get ( ' product.removal ' ) . search ( cr , uid , [ ( ' location_id ' , ' = ' , id ) , ( ' product_categ_id ' , ' child_of ' , product . categ_id . id ) ] , context = context )
return strats and strats [ 0 ] or None
2013-06-24 15:28:50 +00:00
2013-06-30 10:20:28 +00:00
def get_removal_strategy ( self , cr , uid , location , product , context = None ) :
2013-06-29 22:17:03 +00:00
pr = self . pool . get ( ' product.removal ' )
categ = product . categ_id
categs = [ categ . id , False ]
while categ . parent_id :
categ = categ . parent_id
categs . append ( categ . id )
2013-06-27 14:46:29 +00:00
2013-06-29 22:17:03 +00:00
result = pr . search ( cr , uid , [
2013-06-30 10:20:28 +00:00
( ' location_id ' , ' = ' , location . id ) ,
2013-07-09 12:06:59 +00:00
( ' product_categ_id ' , ' in ' , categs )
2013-06-29 22:17:03 +00:00
] , context = context )
if result :
return pr . browse ( cr , uid , result [ 0 ] , context = context )
2013-07-07 20:42:48 +00:00
return super ( stock_location , self ) . get_removal_strategy ( cr , uid , id , product , context = context )
2013-06-27 14:46:29 +00:00