2010-04-29 13:30:07 +00:00
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# 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.
#
# 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import osv , fields
from tools . translate import _
import netsvc
import time
2011-10-31 11:38:30 +00:00
import decimal_precision as dp
2010-04-29 13:30:07 +00:00
# Procurement
# ------------------------------------------------------------------
#
# Produce, Buy or Find products and place a move
# then wizard for picking lists & move
#
2010-07-07 11:45:16 +00:00
class mrp_property_group ( osv . osv ) :
"""
Group of mrp properties .
"""
_name = ' mrp.property.group '
_description = ' Property Group '
_columns = {
' name ' : fields . char ( ' Property Group ' , size = 64 , required = True ) ,
' description ' : fields . text ( ' Description ' ) ,
}
mrp_property_group ( )
class mrp_property ( osv . osv ) :
"""
Properties of mrp .
"""
_name = ' mrp.property '
_description = ' Property '
_columns = {
' name ' : fields . char ( ' Name ' , size = 64 , required = True ) ,
' composition ' : fields . selection ( [ ( ' min ' , ' min ' ) , ( ' max ' , ' max ' ) , ( ' plus ' , ' plus ' ) ] , ' Properties composition ' , required = True , help = " Not used in computations, for information purpose only. " ) ,
' group_id ' : fields . many2one ( ' mrp.property.group ' , ' Property Group ' , required = True ) ,
' description ' : fields . text ( ' Description ' ) ,
}
_defaults = {
' composition ' : lambda * a : ' min ' ,
}
mrp_property ( )
2010-09-01 09:13:08 +00:00
class StockMove ( osv . osv ) :
_inherit = ' stock.move '
_columns = {
' procurements ' : fields . one2many ( ' procurement.order ' , ' move_id ' , ' Procurements ' ) ,
}
2010-11-15 09:36:28 +00:00
2010-09-01 09:13:08 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
default = default or { }
default [ ' procurements ' ] = [ ]
2010-11-22 10:37:53 +00:00
return super ( StockMove , self ) . copy ( cr , uid , id , default , context = context )
2010-09-01 09:13:08 +00:00
StockMove ( )
2010-05-27 12:47:06 +00:00
class procurement_order ( osv . osv ) :
2010-04-29 13:30:07 +00:00
"""
Procurement Orders
"""
2010-05-27 12:47:06 +00:00
_name = " procurement.order "
2010-04-29 13:30:07 +00:00
_description = " Procurement "
2012-09-11 14:01:33 +00:00
_order = ' priority desc,date_planned '
2012-05-07 10:43:44 +00:00
_inherit = [ ' mail.thread ' ]
2010-05-19 20:02:36 +00:00
_log_create = False
2010-04-29 13:30:07 +00:00
_columns = {
' name ' : fields . char ( ' Reason ' , size = 64 , required = True , help = ' Procurement name. ' ) ,
' origin ' : fields . char ( ' Source Document ' , size = 64 ,
help = " Reference of the document that created this Procurement. \n "
2010-08-10 11:35:06 +00:00
" This is automatically completed by OpenERP. " ) ,
2011-12-09 06:03:08 +00:00
' priority ' : fields . selection ( [ ( ' 0 ' , ' Not urgent ' ) , ( ' 1 ' , ' Normal ' ) , ( ' 2 ' , ' Urgent ' ) , ( ' 3 ' , ' Very Urgent ' ) ] , ' Priority ' , required = True , select = True ) ,
' date_planned ' : fields . datetime ( ' Scheduled date ' , required = True , select = True ) ,
2010-04-29 13:30:07 +00:00
' date_close ' : fields . datetime ( ' Date Closed ' ) ,
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
2012-04-25 11:29:50 +00:00
' product_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product Unit of Measure ' , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
2010-04-29 13:30:07 +00:00
' product_uos_qty ' : fields . float ( ' UoS Quantity ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
' product_uos ' : fields . many2one ( ' product.uom ' , ' Product UoS ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
' move_id ' : fields . many2one ( ' stock.move ' , ' Reservation ' , ondelete = ' set null ' ) ,
2012-09-28 13:44:07 +00:00
' close_move ' : fields . boolean ( ' Close Move at end ' ) ,
2010-04-29 13:30:07 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , readonly = True ) ,
2012-02-22 13:12:16 +00:00
' procure_method ' : fields . selection ( [ ( ' make_to_stock ' , ' Make to Stock ' ) , ( ' make_to_order ' , ' Make to Order ' ) ] , ' Procurement Method ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' confirmed ' : [ ( ' readonly ' , False ) ] } ,
2010-04-29 13:30:07 +00:00
readonly = True , required = True , help = " If you encode manually a Procurement, you probably want to use " \
" a make to order method. " ) ,
' note ' : fields . text ( ' Note ' ) ,
2012-05-15 05:52:22 +00:00
' message ' : fields . char ( ' Latest error ' , size = 124 , help = " Exception occurred while computing procurement orders. " ) ,
2010-04-29 13:30:07 +00:00
' state ' : fields . selection ( [
( ' draft ' , ' Draft ' ) ,
2012-05-04 11:03:05 +00:00
( ' cancel ' , ' Cancelled ' ) ,
2010-04-29 13:30:07 +00:00
( ' confirmed ' , ' Confirmed ' ) ,
( ' exception ' , ' Exception ' ) ,
( ' running ' , ' Running ' ) ,
( ' ready ' , ' Ready ' ) ,
( ' done ' , ' Done ' ) ,
2012-05-04 11:57:48 +00:00
( ' waiting ' , ' Waiting ' ) ] , ' Status ' , required = True ,
2012-10-12 11:42:58 +00:00
help = ' When a procurement is created the status is set to \' Draft \' . \n If the procurement is confirmed, the status is set to \' Confirmed \' . \
\nAfter confirming the status is set to \' Running \' . \n If any exception arises in the order then the status is set to \' Exception \' . \n Once the exception is removed the status becomes \' Ready \' . \n It is in \' Waiting \' . status when the procurement is waiting for another one to finish. ' ) ,
2010-04-29 13:30:07 +00:00
' note ' : fields . text ( ' Note ' ) ,
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
}
_defaults = {
2010-12-07 17:53:42 +00:00
' state ' : ' draft ' ,
' priority ' : ' 1 ' ,
2010-04-29 13:30:07 +00:00
' date_planned ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2010-12-07 17:53:42 +00:00
' close_move ' : 0 ,
' procure_method ' : ' make_to_order ' ,
2010-05-27 12:47:06 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' procurement.order ' , context = c )
2010-04-29 13:30:07 +00:00
}
def unlink ( self , cr , uid , ids , context = None ) :
2010-12-13 06:43:09 +00:00
procurements = self . read ( cr , uid , ids , [ ' state ' ] , context = context )
2010-04-29 13:30:07 +00:00
unlink_ids = [ ]
for s in procurements :
if s [ ' state ' ] in [ ' draft ' , ' cancel ' ] :
unlink_ids . append ( s [ ' id ' ] )
else :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) ,
_ ( ' Cannot delete Procurement Order(s) which are in %s state. ' ) % \
2010-09-07 07:49:46 +00:00
s [ ' state ' ] )
2010-04-29 13:30:07 +00:00
return osv . osv . unlink ( self , cr , uid , unlink_ids , context = context )
2010-11-19 13:48:01 +00:00
def onchange_product_id ( self , cr , uid , ids , product_id , context = None ) :
2010-04-29 13:30:07 +00:00
""" Finds UoM and UoS of changed product.
@param product_id : Changed id of product .
@return : Dictionary of values .
"""
if product_id :
2010-11-19 13:48:01 +00:00
w = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id , context = context )
2010-04-29 13:30:07 +00:00
v = {
' product_uom ' : w . uom_id . id ,
' product_uos ' : w . uos_id and w . uos_id . id or w . uom_id . id
}
return { ' value ' : v }
return { }
2012-12-14 14:23:05 +00:00
def is_product ( self , cr , uid , ids , context = None ) :
""" Checks product type to decide which transition of the workflow to follow.
@return : True if all product ids received in argument are of type ' product ' or ' consummable ' . False if any is of type ' service '
2010-04-29 13:30:07 +00:00
"""
2011-12-16 10:31:25 +00:00
return all ( proc . product_id . type in ( ' product ' , ' consu ' ) for proc in self . browse ( cr , uid , ids , context = context ) )
2010-04-29 13:30:07 +00:00
2010-11-19 13:48:01 +00:00
def check_move_cancel ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Checks if move is cancelled or not.
2010-05-14 13:24:15 +00:00
@return : True or False .
2010-04-29 13:30:07 +00:00
"""
2010-11-19 13:48:01 +00:00
return all ( procurement . move_id . state == ' cancel ' for procurement in self . browse ( cr , uid , ids , context = context ) )
2010-04-29 13:30:07 +00:00
2012-08-07 10:23:26 +00:00
#This Function is create to avoid a server side Error Like 'ERROR:tests.mrp:name 'check_move' is not defined'
2011-08-04 13:17:05 +00:00
def check_move ( self , cr , uid , ids , context = None ) :
pass
2010-11-22 12:38:00 +00:00
def check_move_done ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Checks if move is done or not.
2010-05-14 13:24:15 +00:00
@return : True or False .
2010-04-29 13:30:07 +00:00
"""
2011-12-16 10:31:25 +00:00
return all ( proc . product_id . type == ' service ' or ( proc . move_id and proc . move_id . state == ' done ' ) \
for proc in self . browse ( cr , uid , ids , context = context ) )
2010-04-29 13:30:07 +00:00
#
2010-05-27 12:47:06 +00:00
# This method may be overrided by objects that override procurement.order
2010-04-29 13:30:07 +00:00
# for computing their own purpose
#
2010-11-19 13:48:01 +00:00
def _quantity_compute_get ( self , cr , uid , proc , context = None ) :
2010-04-29 13:30:07 +00:00
""" Finds sold quantity of product.
@param proc : Current procurement .
@return : Quantity or False .
"""
2010-08-31 10:20:45 +00:00
if proc . product_id . type == ' product ' and proc . move_id :
2010-04-29 13:30:07 +00:00
if proc . move_id . product_uos :
return proc . move_id . product_uos_qty
return False
2010-11-19 13:48:01 +00:00
def _uom_compute_get ( self , cr , uid , proc , context = None ) :
2010-05-14 13:24:15 +00:00
""" Finds UoS if product is Stockable Product.
2010-04-29 13:30:07 +00:00
@param proc : Current procurement .
@return : UoS or False .
"""
2010-08-31 10:20:45 +00:00
if proc . product_id . type == ' product ' and proc . move_id :
2010-04-29 13:30:07 +00:00
if proc . move_id . product_uos :
return proc . move_id . product_uos . id
return False
#
2010-06-02 07:13:39 +00:00
# Return the quantity of product shipped/produced/served, which may be
2010-04-29 13:30:07 +00:00
# different from the planned quantity
#
2010-11-19 13:48:01 +00:00
def quantity_get ( self , cr , uid , id , context = None ) :
2010-04-29 13:30:07 +00:00
""" Finds quantity of product used in procurement.
2010-05-14 13:24:15 +00:00
@return : Quantity of product .
2010-04-29 13:30:07 +00:00
"""
2010-11-19 13:48:01 +00:00
proc = self . browse ( cr , uid , id , context = context )
result = self . _quantity_compute_get ( cr , uid , proc , context = context )
2010-04-29 13:30:07 +00:00
if not result :
result = proc . product_qty
return result
def uom_get ( self , cr , uid , id , context = None ) :
""" Finds UoM of product used in procurement.
2010-05-14 13:24:15 +00:00
@return : UoM of product .
2010-04-29 13:30:07 +00:00
"""
2010-11-22 10:37:53 +00:00
proc = self . browse ( cr , uid , id , context = context )
result = self . _uom_compute_get ( cr , uid , proc , context = context )
2010-04-29 13:30:07 +00:00
if not result :
result = proc . product_uom . id
return result
2010-11-23 07:05:05 +00:00
def check_waiting ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Checks state of move.
2010-05-14 13:24:15 +00:00
@return : True or False
2010-04-29 13:30:07 +00:00
"""
for procurement in self . browse ( cr , uid , ids , context = context ) :
if procurement . move_id and procurement . move_id . state == ' auto ' :
return True
return False
2010-11-22 10:37:53 +00:00
def check_produce_service ( self , cr , uid , procurement , context = None ) :
2010-07-10 05:16:19 +00:00
return False
2010-04-29 13:30:07 +00:00
2010-11-22 10:37:53 +00:00
def check_produce_product ( self , cr , uid , procurement , context = None ) :
2010-04-29 13:30:07 +00:00
""" Finds BoM of a product if not found writes exception message.
@param procurement : Current procurement .
@return : True or False .
"""
2012-12-14 14:58:33 +00:00
return False
2010-04-29 13:30:07 +00:00
2010-11-19 13:48:01 +00:00
def check_make_to_stock ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Checks product type.
2010-05-14 13:24:15 +00:00
@return : True or False
2010-04-29 13:30:07 +00:00
"""
ok = True
for procurement in self . browse ( cr , uid , ids , context = context ) :
if procurement . product_id . type == ' service ' :
ok = ok and self . _check_make_to_stock_service ( cr , uid , procurement , context )
else :
ok = ok and self . _check_make_to_stock_product ( cr , uid , procurement , context )
return ok
2010-11-19 13:48:01 +00:00
def check_produce ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Checks product type.
2011-12-16 10:31:25 +00:00
@return : True or False
2010-04-29 13:30:07 +00:00
"""
2010-11-19 13:48:01 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
for procurement in self . browse ( cr , uid , ids , context = context ) :
2011-12-13 13:29:04 +00:00
product = procurement . product_id
#TOFIX: if product type is 'service' but supply_method is 'buy'.
if product . supply_method < > ' produce ' :
2010-04-29 13:30:07 +00:00
return False
2011-12-14 06:32:09 +00:00
if product . type == ' service ' :
2011-12-16 10:31:25 +00:00
res = self . check_produce_service ( cr , uid , procurement , context )
2010-04-29 13:30:07 +00:00
else :
2011-12-16 10:31:25 +00:00
res = self . check_produce_product ( cr , uid , procurement , context )
2010-04-29 13:30:07 +00:00
if not res :
return False
2011-12-16 10:31:25 +00:00
return True
2012-08-07 10:23:26 +00:00
2010-04-29 13:30:07 +00:00
def check_buy ( self , cr , uid , ids ) :
2012-12-14 16:39:21 +00:00
""" ability of the workflow to manage the supply_method == ' buy ' .
2010-04-29 13:30:07 +00:00
"""
2012-12-14 16:39:21 +00:00
return False
2012-06-01 14:40:59 +00:00
2010-04-29 13:30:07 +00:00
def test_cancel ( self , cr , uid , ids ) :
2010-05-14 13:24:15 +00:00
""" Tests whether state of move is cancelled or not.
2010-04-29 13:30:07 +00:00
@return : True or False
"""
for record in self . browse ( cr , uid , ids ) :
if record . move_id and record . move_id . state == ' cancel ' :
return True
return False
2012-07-26 06:08:36 +00:00
#Initialize get_phantom_bom_id method as it is raising an error from yml of mrp_jit
#when one install first mrp and after that, mrp_jit. get_phantom_bom_id defined in mrp module
#which is not dependent for mrp_jit.
def get_phantom_bom_id ( self , cr , uid , ids , context = None ) :
return False
2012-09-11 14:05:07 +00:00
2010-11-19 13:48:01 +00:00
def action_confirm ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Confirms procurement and writes exception message if any.
@return : True
"""
move_obj = self . pool . get ( ' stock.move ' )
2010-11-19 13:48:01 +00:00
for procurement in self . browse ( cr , uid , ids , context = context ) :
2010-04-29 13:30:07 +00:00
if procurement . product_qty < = 0.00 :
2010-11-15 09:36:28 +00:00
raise osv . except_osv ( _ ( ' Data Insufficient ! ' ) ,
2012-05-30 06:05:48 +00:00
_ ( ' Please check the quantity in procurement order(s) for the product " %s " , it should not be 0 or less! ' % procurement . product_id . name ) )
2010-04-29 13:30:07 +00:00
if procurement . product_id . type in ( ' product ' , ' consu ' ) :
if not procurement . move_id :
source = procurement . location_id . id
if procurement . procure_method == ' make_to_order ' :
source = procurement . product_id . product_tmpl_id . property_stock_procurement . id
id = move_obj . create ( cr , uid , {
2010-10-11 21:57:06 +00:00
' name ' : procurement . name ,
2010-04-29 13:30:07 +00:00
' location_id ' : source ,
' location_dest_id ' : procurement . location_id . id ,
' product_id ' : procurement . product_id . id ,
' product_qty ' : procurement . product_qty ,
' product_uom ' : procurement . product_uom . id ,
2010-10-05 12:50:28 +00:00
' date_expected ' : procurement . date_planned ,
2010-05-19 20:54:46 +00:00
' state ' : ' draft ' ,
2010-04-29 13:30:07 +00:00
' company_id ' : procurement . company_id . id ,
2010-10-11 19:16:13 +00:00
' auto_validate ' : True ,
2010-04-29 13:30:07 +00:00
} )
2010-05-19 20:54:46 +00:00
move_obj . action_confirm ( cr , uid , [ id ] , context = context )
2010-04-29 13:30:07 +00:00
self . write ( cr , uid , [ procurement . id ] , { ' move_id ' : id , ' close_move ' : 1 } )
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' , ' message ' : ' ' } )
2012-04-27 08:54:40 +00:00
self . confirm_send_note ( cr , uid , ids , context )
2010-04-29 13:30:07 +00:00
return True
2010-11-19 13:48:01 +00:00
def action_move_assigned ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Changes procurement state to Running and writes message.
2010-05-14 13:24:15 +00:00
@return : True
2010-04-29 13:30:07 +00:00
"""
2012-10-04 13:38:29 +00:00
message = _ ( ' Products reserved from stock. ' )
2010-11-15 09:36:28 +00:00
self . write ( cr , uid , ids , { ' state ' : ' running ' ,
2012-06-01 14:40:59 +00:00
' message ' : message } , context = context )
2012-08-17 10:26:50 +00:00
self . message_post ( cr , uid , ids , body = message , context = context )
2010-04-29 13:30:07 +00:00
return True
2010-11-19 13:48:01 +00:00
def _check_make_to_stock_service ( self , cr , uid , procurement , context = None ) :
2010-07-10 05:16:19 +00:00
"""
This method may be overrided by objects that override procurement . order
for computing their own purpose
@return : True """
2010-04-29 13:30:07 +00:00
return True
2010-11-19 13:48:01 +00:00
def _check_make_to_stock_product ( self , cr , uid , procurement , context = None ) :
2010-04-29 13:30:07 +00:00
""" Checks procurement move state.
@param procurement : Current procurement .
@return : True or move id .
"""
ok = True
if procurement . move_id :
2011-01-05 06:20:12 +00:00
message = False
2010-04-29 13:30:07 +00:00
id = procurement . move_id . id
if not ( procurement . move_id . state in ( ' done ' , ' assigned ' , ' cancel ' ) ) :
ok = ok and self . pool . get ( ' stock.move ' ) . action_assign ( cr , uid , [ id ] )
2011-09-16 13:24:42 +00:00
order_point_id = self . pool . get ( ' stock.warehouse.orderpoint ' ) . search ( cr , uid , [ ( ' product_id ' , ' = ' , procurement . product_id . id ) ] , context = context )
2011-09-16 13:01:50 +00:00
if not order_point_id and not ok :
2012-08-22 11:34:39 +00:00
message = _ ( " Not enough stock and no minimum orderpoint rule defined. " )
2011-01-05 06:20:12 +00:00
elif not ok :
2011-01-05 06:49:55 +00:00
message = _ ( " Not enough stock. " )
2011-01-05 06:20:12 +00:00
if message :
2012-05-09 05:46:21 +00:00
message = _ ( " Procurement ' %s ' is in exception: " ) % ( procurement . name ) + message
2011-01-05 06:20:12 +00:00
cr . execute ( ' update procurement_order set message= %s where id= %s ' , ( message , procurement . id ) )
2012-08-17 10:26:50 +00:00
self . message_post ( cr , uid , [ procurement . id ] , body = message , context = context )
2010-04-29 13:30:07 +00:00
return ok
2010-11-19 13:48:01 +00:00
def action_produce_assign_service ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Changes procurement state to Running.
2010-05-14 13:24:15 +00:00
@return : True
2010-04-29 13:30:07 +00:00
"""
2010-11-19 13:48:01 +00:00
for procurement in self . browse ( cr , uid , ids , context = context ) :
2010-04-29 13:30:07 +00:00
self . write ( cr , uid , [ procurement . id ] , { ' state ' : ' running ' } )
return True
2010-11-19 13:48:01 +00:00
def action_produce_assign_product ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" This is action which call from workflow to assign production order to procurements
@return : True
"""
return 0
2010-05-14 13:24:15 +00:00
2010-11-19 13:48:01 +00:00
def action_po_assign ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" This is action which call from workflow to assign purchase order to procurements
@return : True
"""
return 0
2012-03-08 08:58:29 +00:00
# XXX action_cancel() should accept a context argument
2010-04-29 13:30:07 +00:00
def action_cancel ( self , cr , uid , ids ) :
2012-03-08 08:31:39 +00:00
""" Cancel Procurements and either cancel or assign the related Stock Moves, depending on the procurement configuration.
2010-05-14 13:24:15 +00:00
@return : True
2010-04-29 13:30:07 +00:00
"""
2012-03-08 08:31:39 +00:00
to_assign = [ ]
to_cancel = [ ]
2010-04-29 13:30:07 +00:00
move_obj = self . pool . get ( ' stock.move ' )
for proc in self . browse ( cr , uid , ids ) :
2010-08-31 10:20:45 +00:00
if proc . close_move and proc . move_id :
2010-04-29 13:30:07 +00:00
if proc . move_id . state not in ( ' done ' , ' cancel ' ) :
2012-03-08 08:31:39 +00:00
to_cancel . append ( proc . move_id . id )
2010-04-29 13:30:07 +00:00
else :
if proc . move_id and proc . move_id . state == ' waiting ' :
2012-03-08 08:31:39 +00:00
to_assign . append ( proc . move_id . id )
if len ( to_cancel ) :
move_obj . action_cancel ( cr , uid , to_cancel )
if len ( to_assign ) :
move_obj . write ( cr , uid , to_assign , { ' state ' : ' assigned ' } )
2010-04-29 13:30:07 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } )
2012-04-27 08:54:40 +00:00
self . cancel_send_note ( cr , uid , ids , context = None )
2010-04-29 13:30:07 +00:00
wf_service = netsvc . LocalService ( " workflow " )
for id in ids :
2010-05-27 12:47:06 +00:00
wf_service . trg_trigger ( uid , ' procurement.order ' , id , cr )
2010-04-29 13:30:07 +00:00
return True
2010-09-15 11:58:12 +00:00
def action_check_finished ( self , cr , uid , ids ) :
2010-04-29 13:30:07 +00:00
return self . check_move_done ( cr , uid , ids )
def action_check ( self , cr , uid , ids ) :
2010-05-14 13:24:15 +00:00
""" Checks procurement move state whether assigned or done.
@return : True
2010-04-29 13:30:07 +00:00
"""
ok = False
for procurement in self . browse ( cr , uid , ids ) :
2010-08-31 10:20:45 +00:00
if procurement . move_id and procurement . move_id . state == ' assigned ' or procurement . move_id . state == ' done ' :
2010-04-29 13:30:07 +00:00
self . action_done ( cr , uid , [ procurement . id ] )
ok = True
return ok
def action_ready ( self , cr , uid , ids ) :
""" Changes procurement state to Ready.
2010-05-14 13:24:15 +00:00
@return : True
2010-04-29 13:30:07 +00:00
"""
res = self . write ( cr , uid , ids , { ' state ' : ' ready ' } )
return res
def action_done ( self , cr , uid , ids ) :
""" Changes procurement state to Done and writes Closed date.
2010-05-14 13:24:15 +00:00
@return : True
2010-04-29 13:30:07 +00:00
"""
move_obj = self . pool . get ( ' stock.move ' )
for procurement in self . browse ( cr , uid , ids ) :
if procurement . move_id :
if procurement . close_move and ( procurement . move_id . state < > ' done ' ) :
move_obj . action_done ( cr , uid , [ procurement . move_id . id ] )
res = self . write ( cr , uid , ids , { ' state ' : ' done ' , ' date_close ' : time . strftime ( ' % Y- % m- %d ' ) } )
2012-04-27 08:54:40 +00:00
self . done_send_note ( cr , uid , ids , context = None )
2010-04-29 13:30:07 +00:00
wf_service = netsvc . LocalService ( " workflow " )
for id in ids :
2010-05-27 12:47:06 +00:00
wf_service . trg_trigger ( uid , ' procurement.order ' , id , cr )
2010-04-29 13:30:07 +00:00
return res
2012-04-20 10:29:12 +00:00
# ----------------------------------------
# OpenChatter methods and notifications
# ----------------------------------------
def create ( self , cr , uid , vals , context = None ) :
obj_id = super ( procurement_order , self ) . create ( cr , uid , vals , context )
2012-04-27 08:54:40 +00:00
self . create_send_note ( cr , uid , [ obj_id ] , context = context )
2012-04-20 10:29:12 +00:00
return obj_id
2012-04-27 08:54:40 +00:00
def create_send_note ( self , cr , uid , ids , context = None ) :
2012-10-02 20:40:23 +00:00
self . message_post ( cr , uid , ids , body = _ ( " Procurement <b>created</b>. " ) , context = context )
2012-04-20 10:29:12 +00:00
2012-04-27 08:54:40 +00:00
def confirm_send_note ( self , cr , uid , ids , context = None ) :
2012-10-02 20:40:23 +00:00
self . message_post ( cr , uid , ids , body = _ ( " Procurement <b>confirmed</b>. " ) , context = context )
2012-04-27 08:54:40 +00:00
def cancel_send_note ( self , cr , uid , ids , context = None ) :
2012-10-02 20:40:23 +00:00
self . message_post ( cr , uid , ids , body = _ ( " Procurement <b>cancelled</b>. " ) , context = context )
2012-04-27 08:54:40 +00:00
def done_send_note ( self , cr , uid , ids , context = None ) :
2012-10-02 20:40:23 +00:00
self . message_post ( cr , uid , ids , body = _ ( " Procurement <b>done</b>. " ) , context = context )
2012-04-20 10:29:12 +00:00
2010-05-27 12:47:06 +00:00
procurement_order ( )
2010-04-29 13:30:07 +00:00
2010-09-01 09:13:08 +00:00
class StockPicking ( osv . osv ) :
_inherit = ' stock.picking '
2010-09-15 11:58:12 +00:00
def test_finished ( self , cursor , user , ids ) :
2010-09-01 09:13:08 +00:00
wf_service = netsvc . LocalService ( " workflow " )
2010-09-15 11:58:12 +00:00
res = super ( StockPicking , self ) . test_finished ( cursor , user , ids )
2010-09-01 09:13:08 +00:00
for picking in self . browse ( cursor , user , ids ) :
for move in picking . move_lines :
if move . state == ' done ' and move . procurements :
for procurement in move . procurements :
wf_service . trg_validate ( user , ' procurement.order ' ,
2011-12-16 09:58:11 +00:00
procurement . id , ' button_check ' , cursor )
2010-09-01 09:13:08 +00:00
return res
2010-11-15 09:36:28 +00:00
2010-09-01 09:13:08 +00:00
StockPicking ( )
2010-04-29 13:30:07 +00:00
class stock_warehouse_orderpoint ( osv . osv ) :
"""
Defines Minimum stock rules .
"""
_name = " stock.warehouse.orderpoint "
2010-05-27 12:47:06 +00:00
_description = " Minimum Inventory Rule "
2010-08-10 11:35:06 +00:00
2010-12-07 10:48:11 +00:00
def _get_draft_procurements ( self , cr , uid , ids , field_name , arg , context = None ) :
2010-11-24 12:33:32 +00:00
if context is None :
context = { }
result = { }
procurement_obj = self . pool . get ( ' procurement.order ' )
2010-12-07 10:48:11 +00:00
for orderpoint in self . browse ( cr , uid , ids , context = context ) :
procurement_ids = procurement_obj . search ( cr , uid , [ ( ' state ' , ' = ' , ' draft ' ) , ( ' product_id ' , ' = ' , orderpoint . product_id . id ) , ( ' location_id ' , ' = ' , orderpoint . location_id . id ) ] )
result [ orderpoint . id ] = procurement_ids
2010-11-24 12:33:32 +00:00
return result
2012-05-23 11:04:39 +00:00
def _check_product_uom ( self , cr , uid , ids , context = None ) :
'''
Check if the UoM has the same category as the product standard UoM
'''
if not context :
context = { }
for rule in self . browse ( cr , uid , ids , context = context ) :
if rule . product_id . uom_id . category_id . id != rule . product_uom . category_id . id :
return False
return True
2010-04-29 13:30:07 +00:00
_columns = {
' name ' : fields . char ( ' Name ' , size = 32 , required = True ) ,
2010-11-15 13:15:55 +00:00
' active ' : fields . boolean ( ' Active ' , help = " If the active field is set to False, it will allow you to hide the orderpoint without removing it. " ) ,
2010-04-29 13:30:07 +00:00
' logic ' : fields . selection ( [ ( ' max ' , ' Order to Max ' ) , ( ' price ' , ' Best price (not yet active!) ' ) ] , ' Reordering Mode ' , required = True ) ,
2010-05-27 12:47:06 +00:00
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Warehouse ' , required = True , ondelete = " cascade " ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True , ondelete = " cascade " ) ,
2012-08-28 05:41:11 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True , ondelete = ' cascade ' , domain = [ ( ' type ' , ' != ' , ' service ' ) ] ) ,
2012-04-25 11:29:50 +00:00
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product Unit of Measure ' , required = True ) ,
2012-06-11 09:59:14 +00:00
' product_min_qty ' : fields . float ( ' Minimum Quantity ' , required = True ,
2011-04-20 06:13:48 +00:00
help = " When the virtual stock goes below the Min Quantity specified for this field, OpenERP generates " \
2012-11-02 09:37:44 +00:00
" a procurement to bring the forecasted quantity to the Max Quantity. " ) ,
2012-06-11 09:59:14 +00:00
' product_max_qty ' : fields . float ( ' Maximum Quantity ' , required = True ,
2011-04-20 06:13:48 +00:00
help = " When the virtual stock goes below the Min Quantity, OpenERP generates " \
2012-11-02 09:37:44 +00:00
" a procurement to bring the forecasted quantity to the Quantity specified as Max Quantity. " ) ,
2010-04-29 13:30:07 +00:00
' qty_multiple ' : fields . integer ( ' Qty Multiple ' , required = True ,
2011-04-15 08:43:11 +00:00
help = " The procurement quantity will be rounded up to this multiple. " ) ,
2010-05-27 12:47:06 +00:00
' procurement_id ' : fields . many2one ( ' procurement.order ' , ' Latest procurement ' , ondelete = " set null " ) ,
2010-04-29 13:30:07 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
2011-07-01 23:41:24 +00:00
' procurement_draft_ids ' : fields . function ( _get_draft_procurements , type = ' many2many ' , relation = " procurement.order " , \
2010-11-24 12:33:32 +00:00
string = " Related Procurement Orders " , help = " Draft procurement of the product and location of that orderpoint " ) ,
2010-04-29 13:30:07 +00:00
}
_defaults = {
' active ' : lambda * a : 1 ,
' logic ' : lambda * a : ' max ' ,
' qty_multiple ' : lambda * a : 1 ,
2010-05-28 10:44:30 +00:00
' name ' : lambda x , y , z , c : x . pool . get ( ' ir.sequence ' ) . get ( y , z , ' stock.orderpoint ' ) or ' ' ,
2010-04-29 13:30:07 +00:00
' product_uom ' : lambda sel , cr , uid , context : context . get ( ' product_uom ' , False ) ,
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.warehouse.orderpoint ' , context = c )
}
2010-05-27 12:47:06 +00:00
_sql_constraints = [
2010-12-15 16:03:04 +00:00
( ' qty_multiple_check ' , ' CHECK( qty_multiple > 0 ) ' , ' Qty Multiple must be greater than zero. ' ) ,
2010-08-10 11:35:06 +00:00
]
2012-05-23 11:04:39 +00:00
_constraints = [
2012-09-29 13:43:52 +00:00
( _check_product_uom , ' You have to select a product unit of measure in the same category than the default unit of measure of the product ' , [ ' product_id ' , ' product_uom ' ] ) ,
2012-05-23 11:04:39 +00:00
]
2010-08-10 11:35:06 +00:00
2012-05-14 14:40:41 +00:00
def default_get ( self , cr , uid , fields , context = None ) :
res = super ( stock_warehouse_orderpoint , self ) . default_get ( cr , uid , fields , context )
# default 'warehouse_id' and 'location_id'
if ' warehouse_id ' not in res :
warehouse = self . pool . get ( ' ir.model.data ' ) . get_object ( cr , uid , ' stock ' , ' warehouse0 ' , context )
res [ ' warehouse_id ' ] = warehouse . id
if ' location_id ' not in res :
warehouse = self . pool . get ( ' stock.warehouse ' ) . browse ( cr , uid , res [ ' warehouse_id ' ] , context )
res [ ' location_id ' ] = warehouse . lot_stock_id . id
return res
2010-11-19 13:48:01 +00:00
def onchange_warehouse_id ( self , cr , uid , ids , warehouse_id , context = None ) :
2010-04-29 13:30:07 +00:00
""" Finds location id for changed warehouse.
@param warehouse_id : Changed id of warehouse .
@return : Dictionary of values .
"""
if warehouse_id :
2010-11-22 10:37:53 +00:00
w = self . pool . get ( ' stock.warehouse ' ) . browse ( cr , uid , warehouse_id , context = context )
2010-04-29 13:30:07 +00:00
v = { ' location_id ' : w . lot_stock_id . id }
return { ' value ' : v }
return { }
2010-11-15 09:36:28 +00:00
2010-11-19 13:48:01 +00:00
def onchange_product_id ( self , cr , uid , ids , product_id , context = None ) :
2010-04-29 13:30:07 +00:00
""" Finds UoM for changed product.
@param product_id : Changed id of product .
@return : Dictionary of values .
"""
if product_id :
2010-11-19 13:48:01 +00:00
prod = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id , context = context )
2012-05-23 11:04:39 +00:00
d = { ' product_uom ' : [ ( ' category_id ' , ' = ' , prod . uom_id . category_id . id ) ] }
2010-04-29 13:30:07 +00:00
v = { ' product_uom ' : prod . uom_id . id }
2012-05-23 11:04:39 +00:00
return { ' value ' : v , ' domain ' : d }
return { ' domain ' : { ' product_uom ' : [ ] } }
2012-08-07 10:23:26 +00:00
2010-11-19 13:48:01 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2010-04-29 13:30:07 +00:00
if not default :
default = { }
default . update ( {
2010-05-28 10:44:30 +00:00
' name ' : self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.orderpoint ' ) or ' ' ,
2010-04-29 13:30:07 +00:00
} )
2010-11-22 10:37:53 +00:00
return super ( stock_warehouse_orderpoint , self ) . copy ( cr , uid , id , default , context = context )
2012-08-07 10:23:26 +00:00
2012-10-01 12:42:42 +00:00
class product_template ( osv . osv ) :
_inherit = " product.template "
2012-10-05 07:33:00 +00:00
2012-10-01 12:42:42 +00:00
_columns = {
2012-10-30 13:14:01 +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. " ) ,
2012-10-31 07:27:58 +00:00
' procure_method ' : fields . selection ( [ ( ' make_to_stock ' , ' Make to Stock ' ) , ( ' make_to_order ' , ' Make to Order ' ) ] , ' Procurement Method ' , required = True , help = " Make to Stock: When needed, the product is taken from the stock or we wait for replenishment. \n Make to Order: When needed, the product is purchased or produced. " ) ,
' supply_method ' : fields . selection ( [ ( ' produce ' , ' Manufacture ' ) , ( ' buy ' , ' Buy ' ) ] , ' Supply Method ' , required = True , help = " Manufacture: When procuring the product, a manufacturing order or a task will be generated, depending on the product type. \n Buy: When procuring the product, a purchase order will be generated. " ) ,
2012-10-01 12:42:42 +00:00
}
_defaults = {
2012-10-03 11:38:46 +00:00
' procure_method ' : ' make_to_stock ' ,
' supply_method ' : ' buy ' ,
2012-10-01 12:42:42 +00:00
}
2012-01-04 06:38:07 +00:00
class product_product ( osv . osv ) :
_inherit = " product.product "
_columns = {
2012-05-14 08:36:57 +00:00
' orderpoint_ids ' : fields . one2many ( ' stock.warehouse.orderpoint ' , ' product_id ' , ' Minimum Stock Rules ' ) ,
2012-01-04 06:38:07 +00:00
}
2012-05-14 08:36:57 +00:00
2012-01-04 06:38:07 +00:00
2010-04-29 13:30:07 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: