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/>.
#
##############################################################################
2013-01-28 10:00:38 +00:00
import time
2013-06-30 22:26:46 +00:00
from datetime import datetime
from dateutil . relativedelta import relativedelta
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2013-07-08 13:06:18 +00:00
from openerp . tools . translate import _
import openerp . registry as openerp_registry
2010-04-29 13:30:07 +00:00
2013-06-29 09:13:28 +00:00
class procurement_group ( osv . osv ) :
2013-06-27 14:46:29 +00:00
'''
2013-06-29 09:13:28 +00:00
The procurement requirement class is used to group products together
when computing procurements . ( tasks , physical products , . . . )
The goal is that when you have one sale order of several products
and the products are pulled from the same or several location ( s ) , to keep
having the moves grouped into pickings that represent the sale order .
Used in : sales order ( to group delivery order lines like the so ) , pull / push
rules ( to pack like the delivery order ) , on orderpoints ( e . g . for wave picking
all the similar products together ) .
Grouping is made only if the source and the destination is the same .
Suppose you have 4 lines on a picking from Output where 2 lines will need
to come from Input ( crossdock ) and 2 lines coming from Stock - > Output As
the four procurement orders will have the same group ids from the SO , the
move from input will have a stock . picking with 2 grouped lines and the move
from stock will have 2 grouped lines also .
The name is usually the name of the original document ( sale order ) or a
sequence computed if created manually .
2013-06-27 14:46:29 +00:00
'''
2013-06-29 09:13:28 +00:00
_name = ' procurement.group '
_description = ' Procurement Requisition '
_order = " id desc "
2013-06-27 14:46:29 +00:00
_columns = {
2013-06-29 09:13:28 +00:00
' name ' : fields . char ( ' Reference ' ) ,
}
_defaults = {
' name ' : lambda self , cr , uid , c : self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' procurement.group ' ) or ' '
}
2013-06-27 14:46:29 +00:00
2013-06-30 22:26:46 +00:00
class procurement_rule ( osv . osv ) :
'''
A rule describe what a procurement should do ; produce , buy , move , . . .
'''
_name = ' procurement.rule '
_description = " Procurement Rule "
2013-07-08 13:06:18 +00:00
def _get_action ( self , cr , uid , context = None ) :
return [ ( ' move ' , ' Move ' ) ]
2013-06-30 22:26:46 +00:00
_columns = {
' name ' : fields . char ( ' Name ' , required = True ,
help = " This field will fill the packing origin and the name of its moves " ) ,
' group_id ' : fields . many2one ( ' procurement.group ' , ' Procurement Group ' ) ,
2013-07-08 13:06:18 +00:00
' action ' : fields . selection ( selection = lambda s , cr , uid , context = None : s . _get_action ( cr , uid , context = context ) ,
2013-06-30 22:26:46 +00:00
string = ' Action ' , required = True )
}
2013-06-27 14:46:29 +00:00
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 = {
2012-12-18 15:48:55 +00:00
' name ' : fields . text ( ' Description ' , required = True ) ,
2013-06-30 22:26:46 +00:00
2010-04-29 13:30:07 +00:00
' 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. " ) ,
2013-06-30 22:26:46 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
# These two fields are used for shceduling
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 ) ,
2013-06-30 22:26:46 +00:00
' group_id ' : fields . many2one ( ' procurement.group ' , ' Procurement Requisition ' ) ,
2013-07-01 09:09:23 +00:00
' rule_id ' : fields . many2one ( ' procurement.rule ' , ' Rule ' ) ,
2013-06-30 22:26:46 +00:00
2010-04-29 13:30:07 +00:00
' 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 ) ,
2013-06-30 22:26:46 +00:00
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 ) ,
2013-06-30 22:26:46 +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. " ) ,
2013-06-30 22:26:46 +00:00
2010-04-29 13:30:07 +00:00
' state ' : fields . selection ( [
2012-05-04 11:03:05 +00:00
( ' cancel ' , ' Cancelled ' ) ,
2010-04-29 13:30:07 +00:00
( ' confirmed ' , ' Confirmed ' ) ,
( ' exception ' , ' Exception ' ) ,
( ' running ' , ' Running ' ) ,
2013-06-30 22:26:46 +00:00
( ' done ' , ' Done ' )
] , ' Status ' , required = True , track_visibility = ' onchange ' ) ,
2010-04-29 13:30:07 +00:00
}
_defaults = {
2013-06-30 22:26:46 +00:00
' state ' : ' confirmed ' ,
2010-12-07 17:53:42 +00:00
' 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
' 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
}
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 { }
2013-06-30 22:26:46 +00:00
def run ( self , cr , uid , ids , context = None ) :
for procurement in self . browse ( cr , uid , ids , context = context or { } ) :
if procurement . procure_method == ' make_to_order ' :
2013-07-01 06:30:34 +00:00
rule = self . _assign ( cr , uid , procurement , context = context )
if rule :
self . write ( cr , uid , [ procurement . id ] , { ' rule_id ' , rule . id } , context = context )
procurement . refresh ( )
self . _run ( cr , uid , procurement , context = context or { } )
2013-07-01 09:09:23 +00:00
result = True
2013-07-01 06:30:34 +00:00
else :
self . message_post ( cr , uid , [ procurement . id ] , body = _ ( ' No rule matching this procurement ' ) , context = context )
2013-07-01 09:09:23 +00:00
result = False
2010-04-29 13:30:07 +00:00
else :
2013-06-30 22:26:46 +00:00
result = True
if result :
self . write ( cr , uid , [ procurement . id ] , { ' state ' : ' running ' } , context = context )
2010-04-29 13:30:07 +00:00
else :
2013-06-30 22:26:46 +00:00
self . write ( cr , uid , [ procurement . id ] , { ' state ' : ' exception ' } , context = context )
2010-04-29 13:30:07 +00:00
return True
2013-06-30 22:26:46 +00:00
def check ( self , cr , uid , ids , context = None ) :
done = [ ]
for procurement in self . browse ( cr , uid , ids , context = context or { } ) :
result = self . _check ( cr , uid , procurement . id , context = context or { } )
if result :
self . write ( cr , uid , [ procurement . id ] , { ' state ' : ' done ' } , context = context )
done . append ( procurement . id )
return done
2013-02-04 16:31:51 +00:00
2013-06-30 22:26:46 +00:00
#
# Method to overwrite in different procurement modules
#
2013-07-01 06:30:34 +00:00
def _assign ( self , cr , uid , procurement , context = None ) :
return False
2013-06-30 22:26:46 +00:00
def _run ( self , cr , uid , procurement , context = None ) :
2010-04-29 13:30:07 +00:00
return True
2013-06-30 22:26:46 +00:00
def _check ( self , cr , uid , procurement , context = None ) :
2010-04-29 13:30:07 +00:00
return True
2013-06-30 22:26:46 +00:00
#
# Scheduler
#
def run_scheduler ( self , cr , uid , use_new_cursor = False , context = None ) :
2012-05-23 11:04:39 +00:00
'''
2013-06-30 22:26:46 +00:00
Call the scheduler to check the procurement order
@param self : The object pointer
@param cr : The current row , from the database cursor ,
@param uid : The current user ID for security checks
@param ids : List of selected IDs
@param use_new_cursor : False or the dbname
@param context : A standard dictionary for contextual values
@return : Dictionary of values
2012-05-23 11:04:39 +00:00
'''
2013-06-30 22:26:46 +00:00
if context is None :
2012-05-23 11:04:39 +00:00
context = { }
2013-06-30 22:26:46 +00:00
try :
if use_new_cursor :
2013-07-08 13:06:18 +00:00
cr = openerp_registry ( use_new_cursor ) . db . cursor ( )
2013-06-30 22:26:46 +00:00
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id
maxdate = ( datetime . today ( ) + relativedelta ( days = company . schedule_range ) ) . strftime ( ' % Y- % m- %d % H: % M: % S ' )
# Run confirmed procurements
while True :
ids = self . search ( cr , uid , [ ( ' state ' , ' = ' , ' confirmed ' ) , ( ' date_planned ' , ' <= ' , maxdate ) ] , context = context )
if not ids : break
self . run ( cr , uid , ids , context = context )
if use_new_cursor :
cr . commit ( )
# Check if running procurements are done
offset = 0
while True :
ids = self . search ( cr , uid , [ ( ' state ' , ' = ' , ' running ' ) , ( ' date_planned ' , ' <= ' , maxdate ) ] , offset = offset , context = context )
if not ids : break
done = self . check ( cr , uid , ids , context = context )
offset + = len ( ids ) - len ( done )
if use_new_cursor and len ( done ) :
cr . commit ( )
finally :
if use_new_cursor :
try :
cr . close ( )
except Exception :
pass
2010-04-29 13:30:07 +00:00
return { }
2010-11-15 09:36:28 +00:00