2010-07-28 12:42:15 +00:00
# -*- coding: utf-8 -*-
2010-03-11 13:05:02 +00:00
##############################################################################
#
2010-07-01 10:28:21 +00:00
# OpenERP, Open Source Management Solution
2010-07-28 12:42:15 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2010-03-11 13:05:02 +00:00
#
# This program is free software: you can redistribute it and/or modify
2010-07-28 12:42:15 +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.
2010-03-11 13:05:02 +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
2010-07-28 12:42:15 +00:00
# GNU Affero General Public License for more details.
2010-03-11 13:05:02 +00:00
#
2010-07-28 12:42:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-03-11 13:05:02 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
2010-09-29 08:05:01 +00:00
from datetime import datetime
from dateutil . relativedelta import relativedelta
2010-06-23 10:39:01 +00:00
2010-07-01 10:28:21 +00:00
from osv import osv , fields
2010-06-23 10:39:01 +00:00
import netsvc
2010-03-11 13:05:02 +00:00
from tools . translate import _
2011-01-21 09:13:05 +00:00
import logging
2011-10-31 11:38:30 +00:00
import decimal_precision as dp
2011-01-21 09:13:05 +00:00
2011-09-16 20:32:30 +00:00
_logger = logging . getLogger ( ' mps ' )
2010-03-11 13:05:02 +00:00
2010-03-15 08:58:28 +00:00
def rounding ( fl , round_value ) :
if not round_value :
return fl
return round ( fl / round_value ) * round_value
# Periods have no company_id field as they can be shared across similar companies.
# If somone thinks different it can be improved.
2010-03-11 13:05:02 +00:00
class stock_period ( osv . osv ) :
_name = " stock.period "
2010-07-01 10:28:21 +00:00
_description = " stock period "
2011-09-16 20:32:30 +00:00
_order = " date_start "
2010-03-11 13:05:02 +00:00
_columns = {
2010-07-01 10:28:21 +00:00
' name ' : fields . char ( ' Period Name ' , size = 64 , required = True ) ,
2010-03-11 13:05:02 +00:00
' date_start ' : fields . datetime ( ' Start Date ' , required = True ) ,
2010-07-01 10:28:21 +00:00
' date_stop ' : fields . datetime ( ' End Date ' , required = True ) ,
2011-08-29 12:16:44 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' open ' , ' Open ' ) , ( ' close ' , ' Close ' ) ] , ' State ' ) ,
2010-03-11 13:05:02 +00:00
}
_defaults = {
2010-07-01 10:28:21 +00:00
' state ' : ' draft '
2010-03-11 13:05:02 +00:00
}
2010-07-30 05:41:12 +00:00
2010-11-22 10:37:53 +00:00
def button_open ( self , cr , uid , ids , context = None ) :
2010-07-30 05:41:12 +00:00
self . write ( cr , uid , ids , { ' state ' : ' open ' } )
return True
2010-11-22 10:37:53 +00:00
def button_close ( self , cr , uid , ids , context = None ) :
2010-07-30 05:41:12 +00:00
self . write ( cr , uid , ids , { ' state ' : ' close ' } )
return True
2010-07-01 10:28:21 +00:00
2010-03-11 13:05:02 +00:00
stock_period ( )
2010-03-15 08:58:28 +00:00
# Stock and Sales Forecast object. Previously stock_planning_sale_prevision.
# A lot of changes in 1.1
class stock_sale_forecast ( osv . osv ) :
_name = " stock.sale.forecast "
2010-07-01 10:28:21 +00:00
2010-03-11 13:05:02 +00:00
_columns = {
2010-07-01 10:28:21 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
' create_uid ' : fields . many2one ( ' res.users ' , ' Responsible ' ) ,
' name ' : fields . char ( ' Name ' , size = 64 , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-07-30 07:19:20 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Created/Validated by ' , readonly = True , \
2010-03-15 08:58:28 +00:00
help = ' Shows who created this forecast, or who validated. ' ) ,
2010-07-30 07:19:20 +00:00
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Warehouse ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , \
2010-03-15 08:58:28 +00:00
help = ' Shows which warehouse this forecast concerns. ' \
' If during stock planning you will need sales forecast for all warehouses choose any warehouse now. ' ) ,
2010-07-30 07:19:20 +00:00
' period_id ' : fields . many2one ( ' stock.period ' , ' Period ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , \
2010-03-15 08:58:28 +00:00
help = ' Shows which period this forecast concerns. ' ) ,
2010-07-30 07:19:20 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , \
2010-03-15 08:58:28 +00:00
help = ' Shows which product this forecast concerns. ' ) ,
2012-04-25 12:09:08 +00:00
' product_qty ' : fields . float ( ' Forecast Quantity ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) , required = True , readonly = True , \
2011-10-31 11:38:30 +00:00
states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = ' Forecast Product quantity. ' ) ,
2010-07-01 10:28:21 +00:00
' product_amt ' : fields . float ( ' Product Amount ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , \
2010-03-15 08:58:28 +00:00
help = ' Forecast value which will be converted to Product Quantity according to prices. ' ) ,
2012-04-25 12:09:08 +00:00
' product_uom_categ ' : fields . many2one ( ' product.uom.categ ' , ' Product Unit of Measure Category ' ) , # Invisible field for product_uom domain
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product Unit of Measure ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , \
2011-01-21 09:13:05 +00:00
help = " Unit of Measure used to show the quantities of stock calculation. " \
2010-03-15 08:58:28 +00:00
" You can use units form default category or from second category (UoS category). " ) ,
' product_uos_categ ' : fields . many2one ( ' product.uom.categ ' , ' Product UoS Category ' ) , # Invisible field for product_uos domain
2011-01-21 09:13:05 +00:00
# Field used in onchange_uom to check what uom was before change and recalculate quantities according to old uom (active_uom) and new uom.
2012-04-25 12:09:08 +00:00
' active_uom ' : fields . many2one ( ' product.uom ' , string = " Active Unit of Measure " ) ,
2010-07-01 10:28:21 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' validated ' , ' Validated ' ) ] , ' State ' , readonly = True ) ,
2010-07-30 07:19:20 +00:00
' analyzed_period1_id ' : fields . many2one ( ' stock.period ' , ' Period1 ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
' analyzed_period2_id ' : fields . many2one ( ' stock.period ' , ' Period2 ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
' analyzed_period3_id ' : fields . many2one ( ' stock.period ' , ' Period3 ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
' analyzed_period4_id ' : fields . many2one ( ' stock.period ' , ' Period4 ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
2010-07-01 10:28:21 +00:00
' analyzed_period5_id ' : fields . many2one ( ' stock.period ' , ' Period5 ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
2010-07-30 07:19:20 +00:00
' analyzed_user_id ' : fields . many2one ( ' res.users ' , ' This User ' , required = False , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
2011-09-16 20:32:30 +00:00
' analyzed_team_id ' : fields . many2one ( ' crm.case.section ' , ' Sales Team ' , required = False , \
2010-03-15 08:58:28 +00:00
readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ) ,
2010-07-01 10:28:21 +00:00
' analyzed_warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' This Warehouse ' , required = False , \
2010-03-15 08:58:28 +00:00
readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-07-01 10:28:21 +00:00
' analyze_company ' : fields . boolean ( ' Per Company ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , \
2010-03-15 08:58:28 +00:00
help = " Check this box to see the sales for whole company. " ) ,
2010-07-01 10:28:21 +00:00
' analyzed_period1_per_user ' : fields . float ( ' This User Period1 ' , readonly = True ) ,
' analyzed_period2_per_user ' : fields . float ( ' This User Period2 ' , readonly = True ) ,
' analyzed_period3_per_user ' : fields . float ( ' This User Period3 ' , readonly = True ) ,
' analyzed_period4_per_user ' : fields . float ( ' This User Period4 ' , readonly = True ) ,
' analyzed_period5_per_user ' : fields . float ( ' This User Period5 ' , readonly = True ) ,
' analyzed_period1_per_dept ' : fields . float ( ' This Dept Period1 ' , readonly = True ) ,
' analyzed_period2_per_dept ' : fields . float ( ' This Dept Period2 ' , readonly = True ) ,
' analyzed_period3_per_dept ' : fields . float ( ' This Dept Period3 ' , readonly = True ) ,
' analyzed_period4_per_dept ' : fields . float ( ' This Dept Period4 ' , readonly = True ) ,
' analyzed_period5_per_dept ' : fields . float ( ' This Dept Period5 ' , readonly = True ) ,
' analyzed_period1_per_warehouse ' : fields . float ( ' This Warehouse Period1 ' , readonly = True ) ,
' analyzed_period2_per_warehouse ' : fields . float ( ' This Warehouse Period2 ' , readonly = True ) ,
' analyzed_period3_per_warehouse ' : fields . float ( ' This Warehouse Period3 ' , readonly = True ) ,
' analyzed_period4_per_warehouse ' : fields . float ( ' This Warehouse Period4 ' , readonly = True ) ,
' analyzed_period5_per_warehouse ' : fields . float ( ' This Warehouse Period5 ' , readonly = True ) ,
2011-01-21 09:13:05 +00:00
' analyzed_period1_per_company ' : fields . float ( ' This Company Period1 ' , readonly = True ) ,
2010-07-01 10:28:21 +00:00
' analyzed_period2_per_company ' : fields . float ( ' This Company Period2 ' , readonly = True ) ,
' analyzed_period3_per_company ' : fields . float ( ' This Company Period3 ' , readonly = True ) ,
' analyzed_period4_per_company ' : fields . float ( ' This Company Period4 ' , readonly = True ) ,
' analyzed_period5_per_company ' : fields . float ( ' This Company Period5 ' , readonly = True ) ,
2010-03-11 13:05:02 +00:00
}
_defaults = {
2010-03-15 08:58:28 +00:00
' user_id ' : lambda obj , cr , uid , context : uid ,
2010-07-01 10:28:21 +00:00
' state ' : ' draft ' ,
2010-03-15 08:58:28 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.sale.forecast ' , context = c ) ,
2010-03-11 13:05:02 +00:00
}
2010-03-15 08:58:28 +00:00
2010-03-11 13:05:02 +00:00
def action_validate ( self , cr , uid , ids , * args ) :
2010-08-04 10:18:41 +00:00
self . write ( cr , uid , ids , { ' state ' : ' validated ' , ' user_id ' : uid } )
2010-03-11 13:05:02 +00:00
return True
2010-07-01 10:28:21 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-03-15 08:58:28 +00:00
forecasts = self . read ( cr , uid , ids , [ ' state ' ] )
2010-03-11 13:05:02 +00:00
unlink_ids = [ ]
2010-03-15 08:58:28 +00:00
for t in forecasts :
2010-03-11 13:05:02 +00:00
if t [ ' state ' ] in ( ' draft ' ) :
unlink_ids . append ( t [ ' id ' ] )
else :
2011-09-18 13:41:16 +00:00
raise osv . except_osv ( _ ( ' Invalid action ! ' ) , _ ( ' Cannot delete a validated sales forecast! ' ) )
2010-08-04 10:18:41 +00:00
osv . osv . unlink ( self , cr , uid , unlink_ids , context = context )
2010-03-11 13:05:02 +00:00
return True
2010-07-01 10:28:21 +00:00
def onchange_company ( self , cr , uid , ids , company_id = False ) :
2010-03-11 13:05:02 +00:00
result = { }
2010-07-01 10:28:21 +00:00
if not company_id :
return result
result [ ' warehouse_id ' ] = False
result [ ' analyzed_user_id ' ] = False
2011-09-16 20:32:30 +00:00
result [ ' analyzed_team_id ' ] = False
2010-07-01 10:28:21 +00:00
result [ ' analyzed_warehouse_id ' ] = False
2010-03-11 13:05:02 +00:00
return { ' value ' : result }
2010-03-15 08:58:28 +00:00
2010-07-01 10:28:21 +00:00
def product_id_change ( self , cr , uid , ids , product_id = False ) :
ret = { }
2010-03-15 08:58:28 +00:00
if product_id :
product_rec = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id )
ret [ ' product_uom ' ] = product_rec . uom_id . id
ret [ ' product_uom_categ ' ] = product_rec . uom_id . category_id . id
ret [ ' product_uos_categ ' ] = product_rec . uos_id and product_rec . uos_id . category_id . id or False
ret [ ' active_uom ' ] = product_rec . uom_id . id
else :
ret [ ' product_uom ' ] = False
ret [ ' product_uom_categ ' ] = False
ret [ ' product_uos_categ ' ] = False
res = { ' value ' : ret }
return res
2010-08-04 09:48:20 +00:00
def onchange_uom ( self , cr , uid , ids , product_uom = False , product_qty = 0.0 ,
active_uom = False , product_id = False ) :
2010-07-01 10:28:21 +00:00
ret = { }
2010-08-04 09:48:20 +00:00
if product_uom and product_id :
coeff_uom2def = self . _to_default_uom_factor ( cr , uid , product_id , active_uom , { } )
coeff_def2uom , round_value = self . _from_default_uom_factor ( cr , uid , product_id , product_uom , { } )
2010-07-01 10:28:21 +00:00
coeff = coeff_uom2def * coeff_def2uom
ret [ ' product_qty ' ] = rounding ( coeff * product_qty , round_value )
ret [ ' active_uom ' ] = product_uom
2010-03-15 08:58:28 +00:00
return { ' value ' : ret }
2010-08-04 09:48:20 +00:00
def product_amt_change ( self , cr , uid , ids , product_amt = 0.0 , product_uom = False , product_id = False ) :
2010-06-18 11:19:59 +00:00
round_value = 1
2010-08-04 09:48:20 +00:00
qty = 0.0
if product_amt and product_id :
product = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id )
2010-03-15 08:58:28 +00:00
coeff_def2uom = 1
2010-08-04 09:48:20 +00:00
if ( product_uom != product . uom_id . id ) :
coeff_def2uom , round_value = self . _from_default_uom_factor ( cr , uid , product_id , product_uom , { } )
qty = rounding ( coeff_def2uom * product_amt / ( product . product_tmpl_id . list_price ) , round_value )
res = { ' value ' : { ' product_qty ' : qty } }
2010-03-15 08:58:28 +00:00
return res
2010-08-04 09:48:20 +00:00
def _to_default_uom_factor ( self , cr , uid , product_id , uom_id , context = None ) :
2010-03-15 08:58:28 +00:00
uom_obj = self . pool . get ( ' product.uom ' )
2010-08-04 09:48:20 +00:00
product_obj = self . pool . get ( ' product.product ' )
2010-11-22 10:37:53 +00:00
product = product_obj . browse ( cr , uid , product_id , context = context )
2010-03-15 08:58:28 +00:00
uom = uom_obj . browse ( cr , uid , uom_id , context = context )
2010-07-30 07:19:20 +00:00
coef = uom . factor
2010-08-04 09:48:20 +00:00
if uom . category_id . id < > product . uom_id . category_id . id :
2011-01-21 09:13:05 +00:00
coef = coef * product . uos_coeff
2010-08-04 09:48:20 +00:00
return product . uom_id . factor / coef
2010-03-15 08:58:28 +00:00
2010-11-22 10:37:53 +00:00
def _from_default_uom_factor ( self , cr , uid , product_id , uom_id , context = None ) :
2010-03-15 08:58:28 +00:00
uom_obj = self . pool . get ( ' product.uom ' )
2010-08-04 09:48:20 +00:00
product_obj = self . pool . get ( ' product.product ' )
2010-11-22 10:37:53 +00:00
product = product_obj . browse ( cr , uid , product_id , context = context )
2010-03-15 08:58:28 +00:00
uom = uom_obj . browse ( cr , uid , uom_id , context = context )
res = uom . factor
2010-08-04 09:48:20 +00:00
if uom . category_id . id < > product . uom_id . category_id . id :
2011-01-21 09:13:05 +00:00
res = res * product . uos_coeff
2010-08-04 09:48:20 +00:00
return res / product . uom_id . factor , uom . rounding
2010-03-15 08:58:28 +00:00
def _sales_per_users ( self , cr , uid , so , so_line , company , users ) :
cr . execute ( " SELECT sum(sol.product_uom_qty) FROM sale_order_line AS sol LEFT JOIN sale_order AS s ON (s.id = sol.order_id) " \
2010-06-16 11:51:39 +00:00
" WHERE (sol.id IN %s ) AND (s.state NOT IN ( \' draft \' , \' cancel \' )) AND (s.id IN %s ) AND (s.company_id= %s ) " \
2010-06-23 09:27:20 +00:00
" AND (s.user_id IN %s ) " , ( tuple ( so_line ) , tuple ( so ) , company , tuple ( users ) ) )
2010-03-15 08:58:28 +00:00
ret = cr . fetchone ( ) [ 0 ] or 0.0
return ret
2010-07-01 10:28:21 +00:00
def _sales_per_warehouse ( self , cr , uid , so , so_line , company , shops ) :
2010-03-15 08:58:28 +00:00
cr . execute ( " SELECT sum(sol.product_uom_qty) FROM sale_order_line AS sol LEFT JOIN sale_order AS s ON (s.id = sol.order_id) " \
2010-06-16 11:51:39 +00:00
" WHERE (sol.id IN %s ) AND (s.state NOT IN ( \' draft \' , \' cancel \' )) AND (s.id IN %s )AND (s.company_id= %s ) " \
2010-06-23 09:27:20 +00:00
" AND (s.shop_id IN %s ) " , ( tuple ( so_line ) , tuple ( so ) , company , tuple ( shops ) ) )
2010-03-15 08:58:28 +00:00
ret = cr . fetchone ( ) [ 0 ] or 0.0
return ret
2010-07-01 10:28:21 +00:00
def _sales_per_company ( self , cr , uid , so , so_line , company ) :
2010-03-15 08:58:28 +00:00
cr . execute ( " SELECT sum(sol.product_uom_qty) FROM sale_order_line AS sol LEFT JOIN sale_order AS s ON (s.id = sol.order_id) " \
2010-07-01 10:28:21 +00:00
" WHERE (sol.id IN %s ) AND (s.state NOT IN ( \' draft \' , \' cancel \' )) AND (s.id IN %s ) AND (s.company_id= %s ) " , ( tuple ( so_line ) , tuple ( so ) , company ) )
2010-03-15 08:58:28 +00:00
ret = cr . fetchone ( ) [ 0 ] or 0.0
return ret
2010-07-01 10:28:21 +00:00
2010-03-15 08:58:28 +00:00
def calculate_sales_history ( self , cr , uid , ids , context , * args ) :
2010-07-01 10:28:21 +00:00
sales = [ [ 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 ] , ]
2010-12-13 06:43:09 +00:00
for obj in self . browse ( cr , uid , ids , context = context ) :
2010-07-01 10:28:21 +00:00
periods = obj . analyzed_period1_id , obj . analyzed_period2_id , obj . analyzed_period3_id , obj . analyzed_period4_id , obj . analyzed_period5_id
2010-03-15 08:58:28 +00:00
so_obj = self . pool . get ( ' sale.order ' )
2010-07-01 10:28:21 +00:00
so_line_obj = self . pool . get ( ' sale.order.line ' )
2010-03-15 08:58:28 +00:00
so_line_product_ids = so_line_obj . search ( cr , uid , [ ( ' product_id ' , ' = ' , obj . product_id . id ) ] , context = context )
if so_line_product_ids :
2011-09-16 20:32:30 +00:00
shops = users = None
2010-03-15 08:58:28 +00:00
if obj . analyzed_warehouse_id :
shops = self . pool . get ( ' sale.shop ' ) . search ( cr , uid , [ ( ' warehouse_id ' , ' = ' , obj . analyzed_warehouse_id . id ) ] , context = context )
2011-09-16 20:32:30 +00:00
if obj . analyzed_team_id :
users = [ u . id for u in obj . analyzed_team_id . member_ids ]
factor , _ = self . _from_default_uom_factor ( cr , uid , obj . product_id . id , obj . product_uom . id , context = context )
2010-03-15 08:58:28 +00:00
for i , period in enumerate ( periods ) :
if period :
2010-06-23 09:40:44 +00:00
so_period_ids = so_obj . search ( cr , uid , [ ( ' date_order ' , ' >= ' , period . date_start ) , ( ' date_order ' , ' <= ' , period . date_stop ) ] , context = context )
2010-03-15 08:58:28 +00:00
if so_period_ids :
if obj . analyzed_user_id :
2011-09-16 20:32:30 +00:00
sales [ i ] [ 0 ] = self . _sales_per_users ( cr , uid , so_period_ids , so_line_product_ids , obj . company_id . id , [ obj . analyzed_user_id . id ] )
2010-07-01 10:28:21 +00:00
sales [ i ] [ 0 ] * = factor
2011-09-16 20:32:30 +00:00
if users :
sales [ i ] [ 1 ] = self . _sales_per_users ( cr , uid , so_period_ids , so_line_product_ids , obj . company_id . id , users )
2010-07-01 10:28:21 +00:00
sales [ i ] [ 1 ] * = factor
2010-03-15 08:58:28 +00:00
if shops :
2011-09-16 20:32:30 +00:00
sales [ i ] [ 2 ] = self . _sales_per_warehouse ( cr , uid , so_period_ids , so_line_product_ids , obj . company_id . id , shops )
2010-07-01 10:28:21 +00:00
sales [ i ] [ 2 ] * = factor
2010-03-15 08:58:28 +00:00
if obj . analyze_company :
2010-07-01 10:28:21 +00:00
sales [ i ] [ 3 ] = self . _sales_per_company ( cr , uid , so_period_ids , so_line_product_ids , obj . company_id . id , )
sales [ i ] [ 3 ] * = factor
2010-03-15 08:58:28 +00:00
self . write ( cr , uid , ids , {
2010-07-01 10:28:21 +00:00
' analyzed_period1_per_user ' : sales [ 0 ] [ 0 ] ,
' analyzed_period2_per_user ' : sales [ 1 ] [ 0 ] ,
' analyzed_period3_per_user ' : sales [ 2 ] [ 0 ] ,
' analyzed_period4_per_user ' : sales [ 3 ] [ 0 ] ,
' analyzed_period5_per_user ' : sales [ 4 ] [ 0 ] ,
' analyzed_period1_per_dept ' : sales [ 0 ] [ 1 ] ,
' analyzed_period2_per_dept ' : sales [ 1 ] [ 1 ] ,
' analyzed_period3_per_dept ' : sales [ 2 ] [ 1 ] ,
' analyzed_period4_per_dept ' : sales [ 3 ] [ 1 ] ,
' analyzed_period5_per_dept ' : sales [ 4 ] [ 1 ] ,
' analyzed_period1_per_warehouse ' : sales [ 0 ] [ 2 ] ,
' analyzed_period2_per_warehouse ' : sales [ 1 ] [ 2 ] ,
' analyzed_period3_per_warehouse ' : sales [ 2 ] [ 2 ] ,
' analyzed_period4_per_warehouse ' : sales [ 3 ] [ 2 ] ,
' analyzed_period5_per_warehouse ' : sales [ 4 ] [ 2 ] ,
' analyzed_period1_per_company ' : sales [ 0 ] [ 3 ] ,
' analyzed_period2_per_company ' : sales [ 1 ] [ 3 ] ,
' analyzed_period3_per_company ' : sales [ 2 ] [ 3 ] ,
' analyzed_period4_per_company ' : sales [ 3 ] [ 3 ] ,
' analyzed_period5_per_company ' : sales [ 4 ] [ 3 ] ,
2010-03-15 08:58:28 +00:00
} )
return True
stock_sale_forecast ( )
# The main Stock Planning object
# A lot of changes by contributor in ver 1.1
2010-03-11 13:05:02 +00:00
class stock_planning ( osv . osv ) :
_name = " stock.planning "
2010-07-01 10:28:21 +00:00
def _get_in_out ( self , cr , uid , val , date_start , date_stop , direction , done , context = None ) :
2010-11-23 07:05:05 +00:00
if context is None :
2010-07-01 10:28:21 +00:00
context = { }
product_obj = self . pool . get ( ' product.product ' )
2010-03-15 08:58:28 +00:00
mapping = { ' in ' : {
' field ' : " incoming_qty " ,
' adapter ' : lambda x : x ,
} ,
' out ' : {
' field ' : " outgoing_qty " ,
' adapter ' : lambda x : - x ,
} ,
2010-03-11 13:05:02 +00:00
}
2010-03-15 08:58:28 +00:00
context [ ' from_date ' ] = date_start
context [ ' to_date ' ] = date_stop
locations = [ val . warehouse_id . lot_stock_id . id , ]
if not val . stock_only :
locations . extend ( [ val . warehouse_id . lot_input_id . id , val . warehouse_id . lot_output_id . id ] )
context [ ' location ' ] = locations
context [ ' compute_child ' ] = True
prod_id = val . product_id . id
if done :
2010-08-04 10:18:41 +00:00
context . update ( { ' states ' : ( ' done ' , ) , ' what ' : ( direction , ) } )
2010-03-15 08:58:28 +00:00
prod_ids = [ prod_id ]
2010-08-04 10:18:41 +00:00
st = product_obj . get_product_available ( cr , uid , prod_ids , context = context )
2010-03-15 08:58:28 +00:00
res = mapping [ direction ] [ ' adapter ' ] ( st . get ( prod_id , 0.0 ) )
else :
2010-07-01 10:28:21 +00:00
product = product_obj . read ( cr , uid , prod_id , [ ] , context )
2010-03-15 08:58:28 +00:00
product_qty = product [ mapping [ direction ] [ ' field ' ] ]
res = mapping [ direction ] [ ' adapter ' ] ( product_qty )
2010-03-11 13:05:02 +00:00
return res
2010-03-15 08:58:28 +00:00
2010-07-01 10:28:21 +00:00
def _get_outgoing_before ( self , cr , uid , val , date_start , date_stop , context = None ) :
2010-03-15 08:58:28 +00:00
cr . execute ( " SELECT sum(planning.planned_outgoing), planning.product_uom \
FROM stock_planning AS planning \
LEFT JOIN stock_period AS period \
ON ( planning . period_id = period . id ) \
WHERE ( period . date_stop > = % s ) AND ( period . date_stop < = % s ) \
AND ( planning . product_id = % s ) AND ( planning . company_id = % s ) \
GROUP BY planning . product_uom " , \
( date_start , date_stop , val . product_id . id , val . company_id . id , ) )
planning_qtys = cr . fetchall ( )
2011-01-21 09:13:05 +00:00
res = self . _to_default_uom ( cr , uid , val , planning_qtys , context )
2010-03-11 13:05:02 +00:00
return res
2010-03-15 08:58:28 +00:00
2011-01-21 09:13:05 +00:00
def _to_default_uom ( self , cr , uid , val , qtys , context = None ) :
res_qty = 0
if qtys :
for qty , prod_uom in qtys :
coef = self . _to_default_uom_factor ( cr , uid , val . product_id . id , prod_uom , context = context )
res_qty + = qty * coef
return res_qty
def _to_form_uom ( self , cr , uid , val , qtys , context = None ) :
2010-03-15 08:58:28 +00:00
res_qty = 0
if qtys :
for qty , prod_uom in qtys :
2010-08-04 09:48:20 +00:00
coef = self . _to_default_uom_factor ( cr , uid , val . product_id . id , prod_uom , context = context )
res_coef , round_value = self . _from_default_uom_factor ( cr , uid , val . product_id . id , val . product_uom . id , context = context )
2010-03-15 08:58:28 +00:00
coef = coef * res_coef
res_qty + = rounding ( qty * coef , round_value )
return res_qty
2010-07-01 10:28:21 +00:00
def _get_forecast ( self , cr , uid , ids , field_names , arg , context = None ) :
2010-03-11 13:05:02 +00:00
res = { }
2010-11-22 10:37:53 +00:00
for val in self . browse ( cr , uid , ids , context = context ) :
2010-07-01 10:28:21 +00:00
res [ val . id ] = { }
2010-03-15 08:58:28 +00:00
valid_part = val . confirmed_forecasts_only and " AND state = ' validated ' " or " "
cr . execute ( ' SELECT sum(product_qty), product_uom \
FROM stock_sale_forecast \
WHERE product_id = % s AND period_id = % s AND company_id = % s ' +valid_part+ \
' GROUP BY product_uom ' , \
( val . product_id . id , val . period_id . id , val . company_id . id ) )
company_qtys = cr . fetchall ( )
2011-01-21 09:13:05 +00:00
res [ val . id ] [ ' company_forecast ' ] = self . _to_form_uom ( cr , uid , val , company_qtys , context )
2010-03-15 08:58:28 +00:00
cr . execute ( ' SELECT sum(product_qty), product_uom \
FROM stock_sale_forecast \
WHERE product_id = % s and period_id = % s AND warehouse_id = % s ' + valid_part + \
' GROUP BY product_uom ' , \
( val . product_id . id , val . period_id . id , val . warehouse_id . id ) )
warehouse_qtys = cr . fetchall ( )
2011-01-21 09:13:05 +00:00
res [ val . id ] [ ' warehouse_forecast ' ] = self . _to_form_uom ( cr , uid , val , warehouse_qtys , context )
# res[val.id]['warehouse_forecast'] = rounding(res[val.id]['warehouse_forecast'], val.product_id.uom_id.rounding)
2010-03-11 13:05:02 +00:00
return res
2010-03-15 08:58:28 +00:00
2010-07-01 10:28:21 +00:00
def _get_stock_start ( self , cr , uid , val , date , context = None ) :
2010-11-23 07:05:05 +00:00
if context is None :
2010-11-22 10:37:53 +00:00
context = { }
2010-03-15 08:58:28 +00:00
context [ ' from_date ' ] = None
context [ ' to_date ' ] = date
locations = [ val . warehouse_id . lot_stock_id . id , ]
if not val . stock_only :
locations . extend ( [ val . warehouse_id . lot_input_id . id , val . warehouse_id . lot_output_id . id ] )
context [ ' location ' ] = locations
context [ ' compute_child ' ] = True
product_obj = self . pool . get ( ' product.product ' ) . read ( cr , uid , val . product_id . id , [ ] , context )
res = product_obj [ ' qty_available ' ] # value for stock_start
2010-03-11 13:05:02 +00:00
return res
2010-07-01 10:28:21 +00:00
2010-11-22 10:37:53 +00:00
def _get_past_future ( self , cr , uid , ids , field_names , arg , context = None ) :
2010-03-11 13:05:02 +00:00
res = { }
2010-03-15 08:58:28 +00:00
for val in self . browse ( cr , uid , ids , context = context ) :
2010-03-11 13:05:02 +00:00
if val . period_id . date_stop < time . strftime ( ' % Y- % m- %d ' ) :
res [ val . id ] = ' Past '
else :
res [ val . id ] = ' Future '
return res
2010-03-15 08:58:28 +00:00
2010-07-01 10:28:21 +00:00
def _get_op ( self , cr , uid , ids , field_names , arg , context = None ) : # op = OrderPoint
2010-03-11 13:05:02 +00:00
res = { }
2010-03-15 08:58:28 +00:00
for val in self . browse ( cr , uid , ids , context = context ) :
res [ val . id ] = { }
cr . execute ( " SELECT product_min_qty, product_max_qty, product_uom \
FROM stock_warehouse_orderpoint \
WHERE warehouse_id = % s AND product_id = % s AND active = ' TRUE ' " , (val.warehouse_id.id, val.product_id.id))
2010-07-01 10:28:21 +00:00
ret = cr . fetchone ( ) or [ 0.0 , 0.0 , False ]
2010-03-15 08:58:28 +00:00
coef = 1
round_value = 1
if ret [ 2 ] :
2010-08-04 09:48:20 +00:00
coef = self . _to_default_uom_factor ( cr , uid , val . product_id . id , ret [ 2 ] , context )
res_coef , round_value = self . _from_default_uom_factor ( cr , uid , val . product_id . id , val . product_uom . id , context = context )
2010-03-15 08:58:28 +00:00
coef = coef * res_coef
res [ val . id ] [ ' minimum_op ' ] = rounding ( ret [ 0 ] * coef , round_value )
2011-01-21 09:13:05 +00:00
res [ val . id ] [ ' maximum_op ' ] = rounding ( ret [ 1 ] * coef , round_value )
2010-03-15 08:58:28 +00:00
return res
2010-07-01 10:28:21 +00:00
def onchange_company ( self , cr , uid , ids , company_id = False ) :
2010-03-15 08:58:28 +00:00
result = { }
if company_id :
result [ ' warehouse_id ' ] = False
return { ' value ' : result }
2010-03-11 13:05:02 +00:00
2010-08-04 09:48:20 +00:00
def onchange_uom ( self , cr , uid , ids , product_uom = False , product_id = False , active_uom = False ,
planned_outgoing = 0.0 , to_procure = 0.0 ) :
2010-07-01 10:28:21 +00:00
ret = { }
2010-03-15 08:58:28 +00:00
if not product_uom :
return { }
2010-08-04 09:48:20 +00:00
if active_uom :
coeff_uom2def = self . _to_default_uom_factor ( cr , uid , product_id , active_uom , { } )
coeff_def2uom , round_value = self . _from_default_uom_factor ( cr , uid , product_id , product_uom , { } )
2010-07-01 10:28:21 +00:00
coeff = coeff_uom2def * coeff_def2uom
2010-08-04 09:48:20 +00:00
ret [ ' planned_outgoing ' ] = rounding ( coeff * planned_outgoing , round_value )
ret [ ' to_procure ' ] = rounding ( coeff * to_procure , round_value )
2010-03-15 08:58:28 +00:00
ret [ ' active_uom ' ] = product_uom
return { ' value ' : ret }
2010-03-11 13:05:02 +00:00
_columns = {
2010-03-15 08:58:28 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
2010-07-01 10:28:21 +00:00
' history ' : fields . text ( ' Procurement History ' , readonly = True , help = " History of procurement or internal supply of this planning line. " ) ,
2010-03-11 13:05:02 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' done ' , ' Done ' ) ] , ' State ' , readonly = True ) ,
2010-03-15 08:58:28 +00:00
' period_id ' : fields . many2one ( ' stock.period ' , ' Period ' , required = True , \
2011-12-09 06:03:08 +00:00
help = ' Period for this planning. Requisition will be created for beginning of the period. ' , select = True ) ,
2010-07-01 10:28:21 +00:00
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Warehouse ' , required = True ) ,
2010-03-15 08:58:28 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True , help = ' Product which this planning is created for. ' ) ,
2012-04-25 12:09:08 +00:00
' product_uom_categ ' : fields . many2one ( ' product.uom.categ ' , ' Product Unit of Measure Category ' ) , # Invisible field for product_uom domain
' product_uom ' : fields . many2one ( ' product.uom ' , ' Unit of Measure ' , required = True , help = " Unit of Measure used to show the quantities of stock calculation. " \
2011-01-21 09:13:05 +00:00
" You can use units from default category or from second category (UoS category). " ) ,
2012-04-25 12:09:08 +00:00
' product_uos_categ ' : fields . many2one ( ' product.uom.categ ' , ' Product Unit of Measure Category ' ) , # Invisible field for product_uos domain
2011-01-21 09:13:05 +00:00
# Field used in onchange_uom to check what uom was before change to recalculate quantities according to old uom (active_uom) and new uom.
2012-04-25 12:09:08 +00:00
' active_uom ' : fields . many2one ( ' product.uom ' , string = " Active Unit of Measure " ) , # It works only in Forecast
2010-07-01 10:28:21 +00:00
' planned_outgoing ' : fields . float ( ' Planned Out ' , required = True , \
2010-03-15 08:58:28 +00:00
help = ' Enter planned outgoing quantity from selected Warehouse during the selected Period of selected Product. ' \
' To plan this value look at Confirmed Out or Sales Forecasts. This value should be equal or greater than Confirmed Out. ' ) ,
2011-07-01 23:41:24 +00:00
' company_forecast ' : fields . function ( _get_forecast , string = ' Company Forecast ' , multi = ' company ' , \
2010-03-15 08:58:28 +00:00
help = ' All sales forecasts for whole company (for all Warehouses) of selected Product during selected Period. ' ) ,
2011-07-01 23:41:24 +00:00
' warehouse_forecast ' : fields . function ( _get_forecast , string = ' Warehouse Forecast ' , multi = ' warehouse ' , \
2010-03-15 08:58:28 +00:00
help = ' All sales forecasts for selected Warehouse of selected Product during selected Period. ' ) ,
' stock_simulation ' : fields . float ( ' Stock Simulation ' , readonly = True , \
help = ' Stock simulation at the end of selected Period. \n For current period it is: \n ' \
' Initial Stock - Already Out + Already In - Expected Out + Incoming Left. \n ' \
' For periods ahead it is: \n Initial Stock - Planned Out Before + Incoming Before - Planned Out + Planned In. ' ) ,
' incoming ' : fields . float ( ' Confirmed In ' , readonly = True , \
help = ' Quantity of all confirmed incoming moves in calculated Period. ' ) ,
' outgoing ' : fields . float ( ' Confirmed Out ' , readonly = True , \
help = ' Quantity of all confirmed outgoing moves in calculated Period. ' ) ,
' incoming_left ' : fields . float ( ' Incoming Left ' , readonly = True , \
help = ' Quantity left to Planned incoming quantity. This is calculated difference between Planned In and Confirmed In. ' \
' For current period Already In is also calculated. This value is used to create procurement for lacking quantity. ' ) ,
' outgoing_left ' : fields . float ( ' Expected Out ' , readonly = True , \
2011-01-21 09:13:05 +00:00
help = ' Quantity expected to go out in selected period besides Confirmed Out. As a difference between Planned Out and Confirmed Out. ' \
2010-03-15 08:58:28 +00:00
' For current period Already Out is also calculated ' ) ,
' to_procure ' : fields . float ( string = ' Planned In ' , required = True , \
help = ' Enter quantity which (by your plan) should come in. Change this value and observe Stock simulation. ' \
' This value should be equal or greater than Confirmed In. ' ) ,
2011-09-16 20:32:30 +00:00
' line_time ' : fields . function ( _get_past_future , type = ' char ' , string = ' Past/Future ' ) ,
2011-07-01 23:41:24 +00:00
' minimum_op ' : fields . function ( _get_op , type = ' float ' , string = ' Minimum Rule ' , multi = ' minimum ' , \
2011-01-21 09:13:05 +00:00
help = ' Minimum quantity set in Minimum Stock Rules for this Warehouse ' ) ,
2011-07-01 23:41:24 +00:00
' maximum_op ' : fields . function ( _get_op , type = ' float ' , string = ' Maximum Rule ' , multi = ' maximum ' , \
2011-01-21 09:13:05 +00:00
help = ' Maximum quantity set in Minimum Stock Rules for this Warehouse ' ) ,
2010-07-01 10:28:21 +00:00
' outgoing_before ' : fields . float ( ' Planned Out Before ' , readonly = True , \
2010-03-15 08:58:28 +00:00
help = ' Planned Out in periods before calculated. ' \
' Between start date of current period and one day before start of calculated period. ' ) ,
' incoming_before ' : fields . float ( ' Incoming Before ' , readonly = True , \
help = ' Confirmed incoming in periods before calculated (Including Already In). ' \
' Between start date of current period and one day before start of calculated period. ' ) ,
2010-07-01 10:28:21 +00:00
' stock_start ' : fields . float ( ' Initial Stock ' , readonly = True , \
2010-03-15 08:58:28 +00:00
help = ' Stock quantity one day before current period. ' ) ,
2010-07-01 10:28:21 +00:00
' already_out ' : fields . float ( ' Already Out ' , readonly = True , \
2010-03-15 08:58:28 +00:00
help = ' Quantity which is already dispatched out of this warehouse in current period. ' ) ,
2010-07-01 10:28:21 +00:00
' already_in ' : fields . float ( ' Already In ' , readonly = True , \
2010-03-15 08:58:28 +00:00
help = ' Quantity which is already picked up to this warehouse in current period. ' ) ,
2010-07-01 10:28:21 +00:00
' stock_only ' : fields . boolean ( " Stock Location Only " , help = " Check to calculate stock location of selected warehouse only. " \
2010-03-15 08:58:28 +00:00
" If not selected calculation is made for input, stock and output location of warehouse. " ) ,
2011-01-21 09:13:05 +00:00
" procure_to_stock " : fields . boolean ( " Procure To Stock Location " , help = " Check to make procurement to stock location of selected warehouse. " \
2010-03-15 08:58:28 +00:00
" If not selected procurement will be made into input location of warehouse. " ) ,
2010-07-01 10:28:21 +00:00
" confirmed_forecasts_only " : fields . boolean ( " Validated Forecasts " , help = " Check to take validated forecasts only. " \
2010-03-15 08:58:28 +00:00
" If not checked system takes validated and draft forecasts. " ) ,
2011-01-21 09:13:05 +00:00
' supply_warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Source Warehouse ' , help = " Warehouse used as source in supply pick move created by ' Supply from Another Warehouse ' . " ) ,
2010-07-01 10:28:21 +00:00
" stock_supply_location " : fields . boolean ( " Stock Supply Location " , help = " Check to supply from Stock location of Supply Warehouse. " \
2011-01-21 09:13:05 +00:00
" If not checked supply will be made from Output location of Supply Warehouse. Used in ' Supply from Another Warehouse ' with Supply Warehouse. " ) ,
2010-03-15 08:58:28 +00:00
2010-03-11 13:05:02 +00:00
}
2010-03-15 08:58:28 +00:00
2010-03-11 13:05:02 +00:00
_defaults = {
2011-09-16 20:32:30 +00:00
' state ' : ' draft ' ,
2010-07-01 10:28:21 +00:00
' to_procure ' : 0.0 ,
' planned_outgoing ' : 0.0 ,
2010-03-15 08:58:28 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.planning ' , context = c ) ,
2010-07-01 10:28:21 +00:00
}
2010-03-11 13:05:02 +00:00
_order = ' period_id '
2010-03-15 08:58:28 +00:00
2010-08-04 09:48:20 +00:00
def _to_default_uom_factor ( self , cr , uid , product_id , uom_id , context = None ) :
2010-03-15 08:58:28 +00:00
uom_obj = self . pool . get ( ' product.uom ' )
2010-08-04 09:48:20 +00:00
product_obj = self . pool . get ( ' product.product ' )
2010-11-22 10:37:53 +00:00
product = product_obj . browse ( cr , uid , product_id , context = context )
2010-03-15 08:58:28 +00:00
uom = uom_obj . browse ( cr , uid , uom_id , context = context )
2010-07-01 10:28:21 +00:00
coef = uom . factor
2010-08-04 09:48:20 +00:00
if uom . category_id . id != product . uom_id . category_id . id :
2011-01-21 09:13:05 +00:00
coef = coef * product . uos_coeff
2010-08-04 09:48:20 +00:00
return product . uom_id . factor / coef
2010-03-15 08:58:28 +00:00
2010-08-04 09:48:20 +00:00
def _from_default_uom_factor ( self , cr , uid , product_id , uom_id , context = None ) :
2010-03-15 08:58:28 +00:00
uom_obj = self . pool . get ( ' product.uom ' )
2010-08-04 09:48:20 +00:00
product_obj = self . pool . get ( ' product.product ' )
2010-11-22 10:37:53 +00:00
product = product_obj . browse ( cr , uid , product_id , context = context )
2010-03-15 08:58:28 +00:00
uom = uom_obj . browse ( cr , uid , uom_id , context = context )
res = uom . factor
2010-08-04 09:48:20 +00:00
if uom . category_id . id != product . uom_id . category_id . id :
2011-01-21 09:13:05 +00:00
res = res * product . uos_coeff
2010-08-04 09:48:20 +00:00
return res / product . uom_id . factor , uom . rounding
2010-03-15 08:58:28 +00:00
def calculate_planning ( self , cr , uid , ids , context , * args ) :
2011-02-01 08:23:15 +00:00
one_second = relativedelta ( seconds = 1 )
today = datetime . today ( )
current_date_beginning_c = datetime ( today . year , today . month , today . day )
current_date_end_c = current_date_beginning_c + relativedelta ( days = 1 , seconds = - 1 ) # to get hour 23:59:59
2010-03-15 08:58:28 +00:00
current_date_beginning = current_date_beginning_c . strftime ( ' % Y- % m- %d % H: % M: % S ' )
current_date_end = current_date_end_c . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2011-09-16 20:32:30 +00:00
_logger . debug ( " Calculate Planning: current date beg: %s and end: %s " , current_date_beginning , current_date_end )
2010-03-15 08:58:28 +00:00
for val in self . browse ( cr , uid , ids , context = context ) :
2010-09-29 08:05:01 +00:00
day = datetime . strptime ( val . period_id . date_start , ' % Y- % m- %d % H: % M: % S ' )
2011-02-01 08:23:15 +00:00
dbefore = datetime ( day . year , day . month , day . day ) - one_second
2010-03-15 08:58:28 +00:00
day_before_calculated_period = dbefore . strftime ( ' % Y- % m- %d % H: % M: % S ' ) # one day before start of calculated period
2011-09-16 20:32:30 +00:00
_logger . debug ( " Day before calculated period: %s " , day_before_calculated_period )
2010-03-15 08:58:28 +00:00
cr . execute ( " SELECT date_start \
FROM stock_period AS period \
LEFT JOIN stock_planning AS planning \
ON ( planning . period_id = period . id ) \
WHERE ( period . date_stop > = % s ) AND ( period . date_start < = % s ) AND \
planning . product_id = % s " , (current_date_end, current_date_end, val.product_id.id,)) #
date = cr . fetchone ( )
start_date_current_period = date and date [ 0 ] or False
start_date_current_period = start_date_current_period or current_date_beginning
2010-09-29 08:05:01 +00:00
day = datetime . strptime ( start_date_current_period , ' % Y- % m- %d % H: % M: % S ' )
2011-02-01 08:23:15 +00:00
dbefore = datetime ( day . year , day . month , day . day ) - one_second
2010-03-15 08:58:28 +00:00
date_for_start = dbefore . strftime ( ' % Y- % m- %d % H: % M: % S ' ) # one day before current period
2011-09-16 20:32:30 +00:00
_logger . debug ( " Date for start: %s " , date_for_start )
2010-08-04 10:18:41 +00:00
already_out = self . _get_in_out ( cr , uid , val , start_date_current_period , current_date_end , direction = ' out ' , done = True , context = context ) ,
already_in = self . _get_in_out ( cr , uid , val , start_date_current_period , current_date_end , direction = ' in ' , done = True , context = context ) ,
outgoing = self . _get_in_out ( cr , uid , val , val . period_id . date_start , val . period_id . date_stop , direction = ' out ' , done = False , context = context ) ,
incoming = self . _get_in_out ( cr , uid , val , val . period_id . date_start , val . period_id . date_stop , direction = ' in ' , done = False , context = context ) ,
2010-03-15 08:58:28 +00:00
outgoing_before = self . _get_outgoing_before ( cr , uid , val , start_date_current_period , day_before_calculated_period , context = context ) ,
2010-08-04 10:18:41 +00:00
incoming_before = self . _get_in_out ( cr , uid , val , start_date_current_period , day_before_calculated_period , direction = ' in ' , done = False , context = context ) ,
2010-03-15 08:58:28 +00:00
stock_start = self . _get_stock_start ( cr , uid , val , date_for_start , context = context ) ,
if start_date_current_period == val . period_id . date_start : # current period is calculated
2010-07-01 10:28:21 +00:00
current = True
2010-03-15 08:58:28 +00:00
else :
2010-07-01 10:28:21 +00:00
current = False
2010-08-04 09:48:20 +00:00
factor , round_value = self . _from_default_uom_factor ( cr , uid , val . product_id . id , val . product_uom . id , context = context )
2010-03-15 08:58:28 +00:00
self . write ( cr , uid , ids , {
' already_out ' : rounding ( already_out [ 0 ] * factor , round_value ) ,
' already_in ' : rounding ( already_in [ 0 ] * factor , round_value ) ,
' outgoing ' : rounding ( outgoing [ 0 ] * factor , round_value ) ,
' incoming ' : rounding ( incoming [ 0 ] * factor , round_value ) ,
' outgoing_before ' : rounding ( outgoing_before [ 0 ] * factor , round_value ) ,
' incoming_before ' : rounding ( ( incoming_before [ 0 ] + ( not current and already_in [ 0 ] ) ) * factor , round_value ) ,
' outgoing_left ' : rounding ( val . planned_outgoing - ( outgoing [ 0 ] + ( current and already_out [ 0 ] ) ) * factor , round_value ) ,
' incoming_left ' : rounding ( val . to_procure - ( incoming [ 0 ] + ( current and already_in [ 0 ] ) ) * factor , round_value ) ,
2010-07-01 10:28:21 +00:00
' stock_start ' : rounding ( stock_start [ 0 ] * factor , round_value ) ,
2010-03-15 08:58:28 +00:00
' stock_simulation ' : rounding ( val . to_procure - val . planned_outgoing + ( stock_start [ 0 ] + incoming_before [ 0 ] - outgoing_before [ 0 ] \
+ ( not current and already_in [ 0 ] ) ) * factor , round_value ) ,
} )
return True
# method below converts quantities and uoms to general OpenERP standard with UoM Qty, UoM, UoS Qty, UoS.
2010-07-01 10:28:21 +00:00
# from stock_planning standard where you have one Qty and one UoM (any from UoM or UoS category)
2010-03-15 08:58:28 +00:00
# so if UoM is from UoM category it is used as UoM in standard and if product has UoS the UoS will be calcualated.
2010-07-01 10:28:21 +00:00
# If UoM is from UoS category it is recalculated to basic UoS from product (in planning you can use any UoS from UoS category)
2010-03-15 08:58:28 +00:00
# and basic UoM is calculated.
2010-11-22 10:37:53 +00:00
def _qty_to_standard ( self , cr , uid , val , context = None ) :
2010-03-15 08:58:28 +00:00
uos = False
uos_qty = 0.0
if val . product_uom . category_id . id == val . product_id . uom_id . category_id . id :
uom_qty = val . incoming_left
uom = val . product_uom . id
if val . product_id . uos_id :
uos = val . product_id . uos_id . id
2010-08-04 09:48:20 +00:00
coeff_uom2def = self . _to_default_uom_factor ( cr , uid , val . product_id . id , val . product_uom . id , { } )
coeff_def2uom , round_value = self . _from_default_uom_factor ( cr , uid , val . product_id . id , uos , { } )
2010-03-15 08:58:28 +00:00
uos_qty = rounding ( val . incoming_left * coeff_uom2def * coeff_def2uom , round_value )
elif val . product_uom . category_id . id == val . product_id . uos_id . category_id . id :
2010-08-04 09:48:20 +00:00
coeff_uom2def = self . _to_default_uom_factor ( cr , uid , val . product_id . id , val . product_uom . id , { } )
2010-03-15 08:58:28 +00:00
uos = val . product_id . uos_id . id
2010-08-04 09:48:20 +00:00
coeff_def2uom , round_value = self . _from_default_uom_factor ( cr , uid , val . product_id . id , uos , { } )
2010-03-15 08:58:28 +00:00
uos_qty = rounding ( val . incoming_left * coeff_uom2def * coeff_def2uom , round_value )
uom = val . product_id . uom_id . id
2010-08-04 09:48:20 +00:00
coeff_def2uom , round_value = self . _from_default_uom_factor ( cr , uid , val . product_id . id , uom , { } )
2010-03-15 08:58:28 +00:00
uom_qty = rounding ( val . incoming_left * coeff_uom2def * coeff_def2uom , round_value )
return uom_qty , uom , uos_qty , uos
2010-07-01 10:28:21 +00:00
2010-03-11 13:05:02 +00:00
def procure_incomming_left ( self , cr , uid , ids , context , * args ) :
2010-12-13 06:43:09 +00:00
for obj in self . browse ( cr , uid , ids , context = context ) :
2010-03-15 08:58:28 +00:00
if obj . incoming_left < = 0 :
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' Incoming Left must be greater than 0 ! ' ) )
uom_qty , uom , uos_qty , uos = self . _qty_to_standard ( cr , uid , obj , context )
2010-12-13 06:43:09 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
2010-05-27 12:47:06 +00:00
proc_id = self . pool . get ( ' procurement.order ' ) . create ( cr , uid , {
2010-03-15 08:58:28 +00:00
' company_id ' : obj . company_id . id ,
2011-09-16 20:32:30 +00:00
' name ' : _ ( ' MPS planning for %s ' ) % ( obj . period_id . name ) ,
2011-03-03 13:27:14 +00:00
' origin ' : _ ( ' MPS( %s ) %s ' ) % ( user . login , obj . period_id . name ) ,
2010-03-15 08:58:28 +00:00
' date_planned ' : obj . period_id . date_start ,
' product_id ' : obj . product_id . id ,
' product_qty ' : uom_qty ,
' product_uom ' : uom ,
' product_uos_qty ' : uos_qty ,
' product_uos ' : uos ,
2010-07-01 10:28:21 +00:00
' location_id ' : obj . procure_to_stock and obj . warehouse_id . lot_stock_id . id or obj . warehouse_id . lot_input_id . id ,
2010-03-15 08:58:28 +00:00
' procure_method ' : ' make_to_order ' ,
2011-09-16 20:32:30 +00:00
' note ' : _ ( ' Procurement created by MPS for user: %s Creation Date: %s \
2011-03-03 13:27:14 +00:00
\n For period : % s \
\n according to state : \
\n Warehouse Forecast : % s \
\n Initial Stock : % s \
\n Planned Out : % s Planned In : % s \
\n Already Out : % s Already In : % s \
\n Confirmed Out : % s Confirmed In : % s \
\n Planned Out Before : % s Confirmed In Before : % s \
\n Expected Out : % s Incoming Left : % s \
\n Stock Simulation : % s Minimum stock : % s ' ) % (user.login, time.strftime( ' % Y - % m - % d % H : % M : % S ' ),
obj . period_id . name , obj . warehouse_forecast , obj . planned_outgoing , obj . stock_start , obj . to_procure ,
obj . already_out , obj . already_in , obj . outgoing , obj . incoming , obj . outgoing_before , obj . incoming_before ,
obj . outgoing_left , obj . incoming_left , obj . stock_simulation , obj . minimum_op )
2010-03-15 08:58:28 +00:00
} , context = context )
wf_service = netsvc . LocalService ( " workflow " )
2010-05-27 12:47:06 +00:00
wf_service . trg_validate ( uid , ' procurement.order ' , proc_id , ' button_confirm ' , cr )
2010-03-15 08:58:28 +00:00
self . calculate_planning ( cr , uid , ids , context )
prev_text = obj . history or " "
self . write ( cr , uid , ids , {
2011-09-16 20:32:30 +00:00
' history ' : _ ( ' %s Procurement ( %s , %s ) %s %s \n ' ) % ( prev_text , user . login , time . strftime ( ' % Y. % m. %d % H: % M ' ) ,
2011-03-03 13:27:14 +00:00
obj . incoming_left , obj . product_uom . name )
2010-03-15 08:58:28 +00:00
} )
2011-03-03 13:27:14 +00:00
2010-03-11 13:05:02 +00:00
return True
2010-03-15 08:58:28 +00:00
def internal_supply ( self , cr , uid , ids , context , * args ) :
2010-12-13 06:43:09 +00:00
for obj in self . browse ( cr , uid , ids , context = context ) :
2010-03-15 08:58:28 +00:00
if obj . incoming_left < = 0 :
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' Incoming Left must be greater than 0 ! ' ) )
if not obj . supply_warehouse_id :
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' You must specify a Source Warehouse ! ' ) )
if obj . supply_warehouse_id . id == obj . warehouse_id . id :
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' You must specify a Source Warehouse different than calculated (destination) Warehouse ! ' ) )
uom_qty , uom , uos_qty , uos = self . _qty_to_standard ( cr , uid , obj , context )
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context )
picking_id = self . pool . get ( ' stock.picking ' ) . create ( cr , uid , {
2011-03-03 13:27:14 +00:00
' origin ' : _ ( ' MPS( %s ) %s ' ) % ( user . login , obj . period_id . name ) ,
' type ' : ' internal ' ,
' state ' : ' auto ' ,
' date ' : obj . period_id . date_start ,
' move_type ' : ' direct ' ,
' invoice_state ' : ' none ' ,
' company_id ' : obj . company_id . id ,
' note ' : _ ( ' Pick created from MPS by user: %s Creation Date: %s \
\nFor period : % s according to state : \
\n Warehouse Forecast : % s \
\n Initial Stock : % s \
\n Planned Out : % s Planned In : % s \
\n Already Out : % s Already In : % s \
\n Confirmed Out : % s Confirmed In : % s \
\n Planned Out Before : % s Confirmed In Before : % s \
\n Expected Out : % s Incoming Left : % s \
\n Stock Simulation : % s Minimum stock : % s ' )
% ( user . login , time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) , obj . period_id . name , obj . warehouse_forecast ,
obj . stock_start , obj . planned_outgoing , obj . to_procure , obj . already_out , obj . already_in ,
obj . outgoing , obj . incoming , obj . outgoing_before , obj . incoming_before ,
obj . outgoing_left , obj . incoming_left , obj . stock_simulation , obj . minimum_op )
2010-03-15 08:58:28 +00:00
} )
move_id = self . pool . get ( ' stock.move ' ) . create ( cr , uid , {
2011-03-03 13:27:14 +00:00
' name ' : _ ( ' MPS( %s ) %s ' ) % ( user . login , obj . period_id . name ) ,
2010-03-15 08:58:28 +00:00
' picking_id ' : picking_id ,
' product_id ' : obj . product_id . id ,
2010-09-21 12:50:29 +00:00
' date ' : obj . period_id . date_start ,
2010-03-15 08:58:28 +00:00
' product_qty ' : uom_qty ,
' product_uom ' : uom ,
' product_uos_qty ' : uos_qty ,
' product_uos ' : uos ,
' location_id ' : obj . stock_supply_location and obj . supply_warehouse_id . lot_stock_id . id or \
obj . supply_warehouse_id . lot_output_id . id ,
' location_dest_id ' : obj . procure_to_stock and obj . warehouse_id . lot_stock_id . id or \
obj . warehouse_id . lot_input_id . id ,
' tracking_id ' : False ,
' company_id ' : obj . company_id . id ,
} )
wf_service = netsvc . LocalService ( " workflow " )
wf_service . trg_validate ( uid , ' stock.picking ' , picking_id , ' button_confirm ' , cr )
self . calculate_planning ( cr , uid , ids , context )
prev_text = obj . history or " "
pick_name = self . pool . get ( ' stock.picking ' ) . browse ( cr , uid , picking_id ) . name
self . write ( cr , uid , ids , {
2011-03-03 13:27:14 +00:00
' history ' : _ ( ' %s Pick List %s ( %s , %s ) %s %s \n ' ) % ( prev_text , pick_name , user . login , time . strftime ( ' % Y. % m. %d % H: % M ' ) ,
obj . incoming_left , obj . product_uom . name )
2010-03-15 08:58:28 +00:00
} )
return True
2010-07-01 10:28:21 +00:00
2010-03-15 08:58:28 +00:00
def product_id_change ( self , cr , uid , ids , product_id ) :
2010-07-01 10:28:21 +00:00
ret = { }
2010-03-15 08:58:28 +00:00
if product_id :
product_rec = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id )
ret [ ' product_uom ' ] = product_rec . uom_id . id
ret [ ' active_uom ' ] = product_rec . uom_id . id
ret [ ' product_uom_categ ' ] = product_rec . uom_id . category_id . id
ret [ ' product_uos_categ ' ] = product_rec . uos_id and product_rec . uos_id . category_id . id or False
else :
ret [ ' product_uom ' ] = False
ret [ ' product_uom_categ ' ] = False
ret [ ' product_uos_categ ' ] = False
res = { ' value ' : ret }
return res
2010-03-11 13:05:02 +00:00
stock_planning ( )
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: