2012-01-16 14:18:24 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2009-11-13 05:41:16 +00:00
#
2008-11-04 05:29:09 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 11:15:34 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-10-14 11:15:34 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2009-11-13 05:41:16 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2010-03-01 05:05:14 +00:00
from datetime import datetime
from dateutil . relativedelta import relativedelta
2010-07-06 08:59:22 +00:00
import time
2010-09-28 09:24:45 +00:00
from operator import itemgetter
from itertools import groupby
2010-03-01 05:05:14 +00:00
2013-07-29 10:29:23 +00:00
from openerp . osv import fields , osv , orm
2012-12-06 14:56:32 +00:00
from openerp . tools . translate import _
2013-11-21 12:06:11 +00:00
from openerp import workflow
2012-12-06 14:56:32 +00:00
from openerp import tools
2013-05-14 09:19:14 +00:00
from openerp . tools import float_compare , DEFAULT_SERVER_DATETIME_FORMAT
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2010-09-03 10:01:28 +00:00
import logging
2012-06-22 06:48:54 +00:00
_logger = logging . getLogger ( __name__ )
2006-12-07 13:41:40 +00:00
#----------------------------------------------------------
# Incoterms
#----------------------------------------------------------
class stock_incoterms ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.incoterms "
_description = " Incoterms "
_columns = {
2010-07-06 11:21:27 +00:00
' name ' : fields . char ( ' Name ' , size = 64 , required = True , help = " Incoterms are series of sales terms.They are used to divide transaction costs and responsibilities between buyer and seller and reflect state-of-the-art transportation practices. " ) ,
' code ' : fields . char ( ' Code ' , size = 3 , required = True , help = " Code for Incoterms " ) ,
2010-09-06 13:51:23 +00:00
' active ' : fields . boolean ( ' Active ' , help = " By unchecking the active field, you may hide an INCOTERM without deleting it. " ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-07-06 11:21:27 +00:00
' active ' : True ,
2008-07-22 15:11:28 +00:00
}
2009-08-28 09:40:49 +00:00
2006-12-07 13:41:40 +00:00
2010-06-15 08:14:32 +00:00
class stock_journal ( osv . osv ) :
_name = " stock.journal "
_description = " Stock Journal "
_columns = {
' name ' : fields . char ( ' Stock Journal ' , size = 32 , required = True ) ,
2010-07-06 11:21:27 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Responsible ' ) ,
2010-06-15 08:14:32 +00:00
}
_defaults = {
2010-07-06 11:21:27 +00:00
' user_id ' : lambda s , c , u , ctx : u
2010-06-15 08:14:32 +00:00
}
2010-07-06 11:21:27 +00:00
2010-12-29 14:11:22 +00:00
2006-12-07 13:41:40 +00:00
#----------------------------------------------------------
# Stock Location
#----------------------------------------------------------
class stock_location ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.location "
_description = " Location "
_parent_name = " location_id "
2008-09-17 07:49:51 +00:00
_parent_store = True
2010-12-29 14:11:22 +00:00
_parent_order = ' posz,name '
2008-09-17 07:49:51 +00:00
_order = ' parent_left '
2013-10-04 10:52:18 +00:00
# TODO: implement name_search() in a way that matches the results of name_get!
2010-07-06 11:21:27 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2012-01-13 15:15:48 +00:00
# always return the full hierarchical name
res = self . _complete_name ( cr , uid , ids , ' complete_name ' , None , context = context )
return res . items ( )
2009-09-24 10:46:21 +00:00
2010-07-06 11:21:27 +00:00
def _complete_name ( self , cr , uid , ids , name , args , context = None ) :
2010-05-26 12:59:30 +00:00
""" Forms complete name of location from parent location to child location.
@return : Dictionary of values
"""
2008-09-17 07:49:51 +00:00
res = { }
for m in self . browse ( cr , uid , ids , context = context ) :
2012-01-13 15:15:48 +00:00
names = [ m . name ]
parent = m . location_id
while parent :
names . append ( parent . name )
parent = parent . location_id
res [ m . id ] = ' / ' . join ( reversed ( names ) )
2008-09-17 07:49:51 +00:00
return res
2012-01-27 13:55:37 +00:00
def _get_sublocations ( self , cr , uid , ids , context = None ) :
""" return all sublocations of the given stock locations (included) """
return self . search ( cr , uid , [ ( ' id ' , ' child_of ' , ids ) ] , context = context )
2009-02-11 13:32:54 +00:00
2010-07-06 11:21:27 +00:00
def _product_value ( self , cr , uid , ids , field_names , arg , context = None ) :
2010-09-28 09:24:45 +00:00
""" Computes stock value (real and virtual) for a product, as well as stock qty (real and virtual).
2010-06-16 08:40:57 +00:00
@param field_names : Name of field
2010-05-26 12:59:30 +00:00
@return : Dictionary of values
"""
2010-11-24 11:32:47 +00:00
prod_id = context and context . get ( ' product_id ' , False )
2010-09-28 09:24:45 +00:00
2011-05-09 09:57:10 +00:00
if not prod_id :
return dict ( [ ( i , { } . fromkeys ( field_names , 0.0 ) ) for i in ids ] )
2010-09-28 09:24:45 +00:00
product_product_obj = self . pool . get ( ' product.product ' )
2010-12-08 09:32:58 +00:00
cr . execute ( ' select distinct product_id, location_id from stock_move where location_id in %s ' , ( tuple ( ids ) , ) )
dict1 = cr . dictfetchall ( )
cr . execute ( ' select distinct product_id, location_dest_id as location_id from stock_move where location_dest_id in %s ' , ( tuple ( ids ) , ) )
dict2 = cr . dictfetchall ( )
res_products_by_location = sorted ( dict1 + dict2 , key = itemgetter ( ' location_id ' ) )
2010-09-28 09:24:45 +00:00
products_by_location = dict ( ( k , [ v [ ' product_id ' ] for v in itr ] ) for k , itr in groupby ( res_products_by_location , itemgetter ( ' location_id ' ) ) )
2010-10-04 08:43:28 +00:00
result = dict ( [ ( i , { } . fromkeys ( field_names , 0.0 ) ) for i in ids ] )
result . update ( dict ( [ ( i , { } . fromkeys ( field_names , 0.0 ) ) for i in list ( set ( [ aaa [ ' location_id ' ] for aaa in res_products_by_location ] ) ) ] ) )
2010-09-28 09:24:45 +00:00
currency_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id . currency_id . id
currency_obj = self . pool . get ( ' res.currency ' )
2010-12-08 07:17:22 +00:00
currency = currency_obj . browse ( cr , uid , currency_id , context = context )
2010-09-28 09:24:45 +00:00
for loc_id , product_ids in products_by_location . items ( ) :
2010-11-24 11:32:47 +00:00
if prod_id :
product_ids = [ prod_id ]
2010-09-28 09:24:45 +00:00
c = ( context or { } ) . copy ( )
c [ ' location ' ] = loc_id
for prod in product_product_obj . browse ( cr , uid , product_ids , context = c ) :
for f in field_names :
if f == ' stock_real ' :
2010-10-04 08:43:28 +00:00
if loc_id not in result :
result [ loc_id ] = { }
2010-09-28 09:24:45 +00:00
result [ loc_id ] [ f ] + = prod . qty_available
elif f == ' stock_virtual ' :
result [ loc_id ] [ f ] + = prod . virtual_available
elif f == ' stock_real_value ' :
amount = prod . qty_available * prod . standard_price
amount = currency_obj . round ( cr , uid , currency , amount )
result [ loc_id ] [ f ] + = amount
elif f == ' stock_virtual_value ' :
amount = prod . virtual_available * prod . standard_price
amount = currency_obj . round ( cr , uid , currency , amount )
result [ loc_id ] [ f ] + = amount
2009-01-21 12:38:28 +00:00
return result
2009-02-11 13:32:54 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2008-08-26 22:18:32 +00:00
' name ' : fields . char ( ' Location Name ' , size = 64 , required = True , translate = True ) ,
2010-09-06 13:51:23 +00:00
' active ' : fields . boolean ( ' Active ' , help = " By unchecking the active field, you may hide a location without deleting it. " ) ,
2010-08-30 12:03:58 +00:00
' usage ' : fields . selection ( [ ( ' supplier ' , ' Supplier Location ' ) , ( ' view ' , ' View ' ) , ( ' internal ' , ' Internal Location ' ) , ( ' customer ' , ' Customer Location ' ) , ( ' inventory ' , ' Inventory ' ) , ( ' procurement ' , ' Procurement ' ) , ( ' production ' , ' Production ' ) , ( ' transit ' , ' Transit Location for Inter-Companies Transfers ' ) ] , ' Location Type ' , required = True ,
2010-09-06 13:51:23 +00:00
help = """ * Supplier Location: Virtual location representing the source location for products coming from your suppliers
\n * View : Virtual location used to create a hierarchical structures for your warehouse , aggregating its child locations ; can ' t directly contain products
\n * Internal Location : Physical locations inside your own warehouses ,
\n * Customer Location : Virtual location representing the destination location for products sent to your customers
\n * Inventory : Virtual location serving as counterpart for inventory operations used to correct stock levels ( Physical inventories )
\n * Procurement : Virtual location serving as temporary counterpart for procurement operations when the source ( supplier or production ) is not known yet . This location should be empty when the procurement scheduler has finished running .
\n * Production : Virtual counterpart location for production operations : this location consumes the raw material and produces finished products
""" , select = True),
# temporarily removed, as it's unused: 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True),
2013-10-23 16:32:47 +00:00
# as discussed on bug 765559, the main purpose of this field is to allow sorting the list of locations
# according to the displayed names, and reversing that sort by clicking on a column. It does not work for
# translated values though - so it needs fixing.
2012-01-27 13:55:37 +00:00
' complete_name ' : fields . function ( _complete_name , type = ' char ' , size = 256 , string = " Location Name " ,
store = { ' stock.location ' : ( _get_sublocations , [ ' name ' , ' location_id ' ] , 10 ) } ) ,
2008-09-17 08:59:36 +00:00
2011-07-01 23:41:24 +00:00
' stock_real ' : fields . function ( _product_value , type = ' float ' , string = ' Real Stock ' , multi = " stock " ) ,
' stock_virtual ' : fields . function ( _product_value , type = ' float ' , string = ' Virtual Stock ' , multi = " stock " ) ,
2008-09-17 07:49:51 +00:00
2008-09-17 08:59:36 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Parent Location ' , select = True , ondelete = ' cascade ' ) ,
2008-07-22 15:11:28 +00:00
' child_ids ' : fields . one2many ( ' stock.location ' , ' location_id ' , ' Contains ' ) ,
2010-08-30 12:55:05 +00:00
' chained_journal_id ' : fields . many2one ( ' stock.journal ' , ' Chaining Journal ' , help = " Inventory Journal in which the chained move will be written, if the Chaining Type is not Transparent (no journal is used if left empty) " ) ,
2008-07-22 15:11:28 +00:00
' chained_location_id ' : fields . many2one ( ' stock.location ' , ' Chained Location If Fixed ' ) ,
2009-08-28 09:40:49 +00:00
' chained_location_type ' : fields . selection ( [ ( ' none ' , ' None ' ) , ( ' customer ' , ' Customer ' ) , ( ' fixed ' , ' Fixed Location ' ) ] ,
2010-08-19 07:25:01 +00:00
' Chained Location Type ' , required = True ,
2010-08-31 11:26:24 +00:00
help = " Determines whether this location is chained to another location, i.e. any incoming product in this location \n " \
" should next go to the chained location. The chained location is determined according to the type : " \
" \n * None: No chaining at all " \
" \n * Customer: The chained location will be taken from the Customer Location field on the Partner form of the Partner that is specified in the Picking list of the incoming products. " \
" \n * Fixed Location: The chained location is taken from the next field: Chained Location if Fixed. " \
) ,
2008-09-07 23:24:39 +00:00
' chained_auto_packing ' : fields . selection (
2009-08-28 09:40:49 +00:00
[ ( ' auto ' , ' Automatic Move ' ) , ( ' manual ' , ' Manual Operation ' ) , ( ' transparent ' , ' Automatic No Step Added ' ) ] ,
2010-08-30 12:55:05 +00:00
' Chaining Type ' ,
2008-09-17 09:10:14 +00:00
required = True ,
2009-12-21 09:50:54 +00:00
help = " This is used only if you select a chained location type. \n " \
2008-09-07 23:24:39 +00:00
" The ' Automatic Move ' value will create a stock move after the current one that will be " \
" validated automatically. With ' Manual Operation ' , the stock move has to be validated " \
" by a worker. With ' Automatic No Step Added ' , the location is replaced in the original move. "
) ,
2010-10-11 21:57:06 +00:00
' chained_picking_type ' : fields . selection ( [ ( ' out ' , ' Sending Goods ' ) , ( ' in ' , ' Getting Goods ' ) , ( ' internal ' , ' Internal ' ) ] , ' Shipping Type ' , help = " Shipping Type of the Picking List that will contain the chained move (leave empty to automatically detect the type based on the source and destination locations). " ) ,
2010-08-30 12:55:05 +00:00
' chained_company_id ' : fields . many2one ( ' res.company ' , ' Chained Company ' , help = ' The company the Picking List containing the chained move will belong to (leave empty to use the default company determination rules ' ) ,
' chained_delay ' : fields . integer ( ' Chaining Lead Time ' , help = " Delay between original move and chained move in days " ) ,
2012-03-30 07:49:01 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Location Address ' , help = " Address of customer or supplier. " ) ,
2010-08-30 12:55:05 +00:00
' icon ' : fields . selection ( tools . icons , ' Icon ' , size = 64 , help = " Icon show in hierarchical tree view " ) ,
2008-07-22 15:11:28 +00:00
' comment ' : fields . text ( ' Additional Information ' ) ,
2010-08-31 11:29:56 +00:00
' posx ' : fields . integer ( ' Corridor (X) ' , help = " Optional localization details, for information purpose only " ) ,
' posy ' : fields . integer ( ' Shelves (Y) ' , help = " Optional localization details, for information purpose only " ) ,
' posz ' : fields . integer ( ' Height (Z) ' , help = " Optional localization details, for information purpose only " ) ,
2008-09-17 08:59:36 +00:00
' parent_left ' : fields . integer ( ' Left Parent ' , select = 1 ) ,
' parent_right ' : fields . integer ( ' Right Parent ' , select = 1 ) ,
2011-07-01 23:41:24 +00:00
' stock_real_value ' : fields . function ( _product_value , type = ' float ' , string = ' Real Stock Value ' , multi = " stock " , digits_compute = dp . get_precision ( ' Account ' ) ) ,
' stock_virtual_value ' : fields . function ( _product_value , type = ' float ' , string = ' Virtual Stock Value ' , multi = " stock " , digits_compute = dp . get_precision ( ' Account ' ) ) ,
2010-09-06 13:51:23 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , select = 1 , help = ' Let this field empty if this location is shared between all companies ' ) ,
2010-08-30 11:33:00 +00:00
' scrap_location ' : fields . boolean ( ' Scrap Location ' , help = ' Check this box to allow using this location to put scrapped/damaged goods. ' ) ,
2011-12-01 00:37:55 +00:00
' valuation_in_account_id ' : fields . many2one ( ' account.account ' , ' Stock Valuation Account (Incoming) ' , domain = [ ( ' type ' , ' = ' , ' other ' ) ] ,
help = " Used for real-time inventory valuation. When set on a virtual location (non internal type), "
" this account will be used to hold the value of products being moved from an internal location "
" into this location, instead of the generic Stock Output Account set on the product. "
" This has no effect for internal locations. " ) ,
' valuation_out_account_id ' : fields . many2one ( ' account.account ' , ' Stock Valuation Account (Outgoing) ' , domain = [ ( ' type ' , ' = ' , ' other ' ) ] ,
help = " Used for real-time inventory valuation. When set on a virtual location (non internal type), "
" this account will be used to hold the value of products being moved out of this location "
" and into an internal location, instead of the generic Stock Output Account set on the product. "
" This has no effect for internal locations. " ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-07-06 11:44:51 +00:00
' active ' : True ,
2010-07-06 11:21:27 +00:00
' usage ' : ' internal ' ,
' chained_location_type ' : ' none ' ,
' chained_auto_packing ' : ' manual ' ,
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.location ' , context = c ) ,
' posx ' : 0 ,
' posy ' : 0 ,
' posz ' : 0 ,
' icon ' : False ,
' scrap_location ' : False ,
2008-07-22 15:11:28 +00:00
}
2010-07-06 11:21:27 +00:00
def chained_location_get ( self , cr , uid , location , partner = None , product = None , context = None ) :
2010-05-26 12:59:30 +00:00
""" Finds chained location
@param location : Location id
@param partner : Partner id
@param product : Product id
@return : List of values
"""
2008-07-22 15:11:28 +00:00
result = None
2009-08-28 09:40:49 +00:00
if location . chained_location_type == ' customer ' :
2008-09-07 23:24:39 +00:00
if partner :
result = partner . property_stock_customer
2013-11-07 10:53:02 +00:00
else :
loc_id = self . pool [ ' res.partner ' ] . default_get ( cr , uid , [ ' property_stock_customer ' ] , context = context ) [ ' property_stock_customer ' ]
result = self . pool [ ' stock.location ' ] . browse ( cr , uid , loc_id , context = context )
2009-08-28 09:40:49 +00:00
elif location . chained_location_type == ' fixed ' :
2008-07-22 15:11:28 +00:00
result = location . chained_location_id
2008-09-07 23:24:39 +00:00
if result :
2012-12-09 10:55:27 +00:00
return result , location . chained_auto_packing , location . chained_delay , location . chained_journal_id and location . chained_journal_id . id or False , location . chained_company_id and location . chained_company_id . id or False , location . chained_picking_type , False
2008-07-22 15:11:28 +00:00
return result
2010-07-06 11:21:27 +00:00
def picking_type_get ( self , cr , uid , from_location , to_location , context = None ) :
2010-05-26 12:59:30 +00:00
""" Gets type of picking.
@param from_location : Source location
@param to_location : Destination location
@return : Location type
"""
2008-07-22 15:11:28 +00:00
result = ' internal '
2009-08-28 09:40:49 +00:00
if ( from_location . usage == ' internal ' ) and ( to_location and to_location . usage in ( ' customer ' , ' supplier ' ) ) :
2010-10-11 21:57:06 +00:00
result = ' out '
2010-07-06 11:21:27 +00:00
elif ( from_location . usage in ( ' supplier ' , ' customer ' ) ) and ( to_location . usage == ' internal ' ) :
2008-07-22 15:11:28 +00:00
result = ' in '
return result
2010-07-06 11:21:27 +00:00
def _product_get_all_report ( self , cr , uid , ids , product_ids = False , context = None ) :
return self . _product_get_report ( cr , uid , ids , product_ids , context , recursive = True )
2008-07-22 15:11:28 +00:00
def _product_get_report ( self , cr , uid , ids , product_ids = False ,
context = None , recursive = False ) :
2010-05-26 12:59:30 +00:00
""" Finds the product quantity and price for particular location.
@param product_ids : Ids of product
@param recursive : True or False
@return : Dictionary of values
"""
2008-07-22 15:11:28 +00:00
if context is None :
context = { }
product_obj = self . pool . get ( ' product.product ' )
2009-12-23 16:34:53 +00:00
# Take the user company and pricetype
2010-12-13 06:43:09 +00:00
context [ ' currency_id ' ] = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . currency_id . id
2010-03-18 11:25:02 +00:00
2010-09-06 13:51:23 +00:00
# To be able to offer recursive or non-recursive reports we need to prevent recursive quantities by default
context [ ' compute_child ' ] = False
2008-07-22 15:11:28 +00:00
if not product_ids :
2011-01-17 08:15:42 +00:00
product_ids = product_obj . search ( cr , uid , [ ] , context = { ' active_test ' : False } )
2008-07-22 15:11:28 +00:00
products = product_obj . browse ( cr , uid , product_ids , context = context )
products_by_uom = { }
products_by_id = { }
for product in products :
products_by_uom . setdefault ( product . uom_id . id , [ ] )
products_by_uom [ product . uom_id . id ] . append ( product )
products_by_id . setdefault ( product . id , [ ] )
products_by_id [ product . id ] = product
2009-05-28 13:11:45 +00:00
result = { }
result [ ' product ' ] = [ ]
2008-07-22 15:11:28 +00:00
for id in ids :
2009-05-28 13:11:45 +00:00
quantity_total = 0.0
total_price = 0.0
2008-07-22 15:11:28 +00:00
for uom_id in products_by_uom . keys ( ) :
fnc = self . _product_get
if recursive :
fnc = self . _product_all_get
ctx = context . copy ( )
ctx [ ' uom ' ] = uom_id
qty = fnc ( cr , uid , id , [ x . id for x in products_by_uom [ uom_id ] ] ,
context = ctx )
for product_id in qty . keys ( ) :
if not qty [ product_id ] :
continue
product = products_by_id [ product_id ]
2009-05-28 13:11:45 +00:00
quantity_total + = qty [ product_id ]
2010-03-18 11:25:02 +00:00
2009-12-23 16:34:53 +00:00
# Compute based on pricetype
# Choose the right filed standard_price to read
2011-09-13 13:45:51 +00:00
amount_unit = product . price_get ( ' standard_price ' , context = context ) [ product . id ]
2009-12-23 16:34:53 +00:00
price = qty [ product_id ] * amount_unit
2009-05-28 13:11:45 +00:00
total_price + = price
result [ ' product ' ] . append ( {
2009-12-23 16:34:53 +00:00
' price ' : amount_unit ,
2009-05-28 13:11:45 +00:00
' prod_name ' : product . name ,
2008-07-22 15:11:28 +00:00
' code ' : product . default_code , # used by lot_overview_all report!
' variants ' : product . variants or ' ' ,
' uom ' : product . uom_id . name ,
2009-05-28 13:11:45 +00:00
' prod_qty ' : qty [ product_id ] ,
2009-08-28 09:40:49 +00:00
' price_value ' : price ,
2008-07-22 15:11:28 +00:00
} )
2009-05-28 13:11:45 +00:00
result [ ' total ' ] = quantity_total
result [ ' total_price ' ] = total_price
2008-07-22 15:11:28 +00:00
return result
2010-07-06 11:44:51 +00:00
def _product_get_multi_location ( self , cr , uid , ids , product_ids = False , context = None ,
2010-05-26 12:59:30 +00:00
states = [ ' done ' ] , what = ( ' in ' , ' out ' ) ) :
2010-06-16 08:40:57 +00:00
"""
2010-05-26 12:59:30 +00:00
@param product_ids : Ids of product
@param states : List of states
@param what : Tuple of
2010-06-16 08:40:57 +00:00
@return :
2010-05-26 12:59:30 +00:00
"""
2008-11-03 14:40:49 +00:00
product_obj = self . pool . get ( ' product.product ' )
2010-07-06 11:44:51 +00:00
if context is None :
context = { }
2008-09-23 13:24:11 +00:00
context . update ( {
2009-08-28 09:40:49 +00:00
' states ' : states ,
' what ' : what ,
' location ' : ids
2008-11-03 14:40:49 +00:00
} )
2009-08-28 09:40:49 +00:00
return product_obj . get_product_available ( cr , uid , product_ids , context = context )
2008-07-22 15:11:28 +00:00
2012-03-05 17:53:10 +00:00
def _product_get ( self , cr , uid , id , product_ids = False , context = None , states = None ) :
2010-05-26 12:59:30 +00:00
"""
@param product_ids :
@param states :
2010-06-16 08:40:57 +00:00
@return :
2010-05-26 12:59:30 +00:00
"""
2012-03-05 17:53:10 +00:00
if states is None :
states = [ ' done ' ]
2008-07-22 15:11:28 +00:00
ids = id and [ id ] or [ ]
2010-07-06 11:44:51 +00:00
return self . _product_get_multi_location ( cr , uid , ids , product_ids , context = context , states = states )
2008-07-22 15:11:28 +00:00
2012-03-05 17:53:10 +00:00
def _product_all_get ( self , cr , uid , id , product_ids = False , context = None , states = None ) :
if states is None :
states = [ ' done ' ]
2008-07-22 15:11:28 +00:00
# build the list of ids of children of the location given by id
ids = id and [ id ] or [ ]
location_ids = self . search ( cr , uid , [ ( ' location_id ' , ' child_of ' , ids ) ] )
return self . _product_get_multi_location ( cr , uid , location_ids , product_ids , context , states )
2012-03-05 17:53:10 +00:00
def _product_virtual_get ( self , cr , uid , id , product_ids = False , context = None , states = None ) :
if states is None :
states = [ ' done ' ]
2009-08-28 09:40:49 +00:00
return self . _product_all_get ( cr , uid , id , product_ids , context , [ ' confirmed ' , ' waiting ' , ' assigned ' , ' done ' ] )
2008-07-22 15:11:28 +00:00
2010-09-03 10:01:28 +00:00
def _product_reserve ( self , cr , uid , ids , product_id , product_qty , context = None , lock = False ) :
2010-06-16 08:40:57 +00:00
"""
2010-09-03 10:01:28 +00:00
Attempt to find a quantity ` ` product_qty ` ` ( in the product ' s default uom or the uom passed in ``context``) of product ``product_id``
in locations with id ` ` ids ` ` and their child locations . If ` ` lock ` ` is True , the stock . move lines
of product with id ` ` product_id ` ` in the searched location will be write - locked using Postgres ' s
2010-10-26 13:56:22 +00:00
" FOR UPDATE NOWAIT " option until the transaction is committed or rolled back , to prevent reservin
2010-09-03 10:01:28 +00:00
twice the same products .
If ` ` lock ` ` is True and the lock cannot be obtained ( because another transaction has locked some of
the same stock . move lines ) , a log line will be output and False will be returned , as if there was
not enough stock .
: param product_id : Id of product to reserve
: param product_qty : Quantity of product to reserve ( in the product ' s default uom or the uom passed in ``context``)
: param lock : if True , the stock . move lines of product with id ` ` product_id ` ` in all locations ( and children locations ) with ` ` ids ` ` will
be write - locked using postgres ' s " FOR UPDATE NOWAIT " option until the transaction is committed or rolled back. This is
to prevent reserving twice the same products .
2012-02-15 17:12:15 +00:00
: param context : optional context dictionary : if a ' uom ' key is present it will be used instead of the default product uom to
2010-09-03 10:01:28 +00:00
compute the ` ` product_qty ` ` and in the return value .
: return : List of tuples in the form ( qty , location_id ) with the ( partial ) quantities that can be taken in each location to
reach the requested product_qty ( ` ` qty ` ` is expressed in the default uom of the product ) , of False if enough
products could not be found , or the lock could not be obtained ( and ` ` lock ` ` was True ) .
2010-05-26 12:59:30 +00:00
"""
2008-07-22 15:11:28 +00:00
result = [ ]
amount = 0.0
2010-07-06 11:44:51 +00:00
if context is None :
context = { }
2012-02-15 17:12:15 +00:00
uom_obj = self . pool . get ( ' product.uom ' )
uom_rounding = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id , context = context ) . uom_id . rounding
if context . get ( ' uom ' ) :
uom_rounding = uom_obj . browse ( cr , uid , context . get ( ' uom ' ) , context = context ) . rounding
2013-09-20 11:24:37 +00:00
2013-04-04 13:54:46 +00:00
locations_ids = self . search ( cr , uid , [ ( ' location_id ' , ' child_of ' , ids ) ] )
2013-10-15 11:20:16 +00:00
if locations_ids :
# Fetch only the locations in which this product has ever been processed (in or out)
cr . execute ( """ SELECT l.id FROM stock_location l WHERE l.id in %s AND
EXISTS ( SELECT 1 FROM stock_move m WHERE m . product_id = % s
AND ( ( state = ' done ' AND m . location_dest_id = l . id )
OR ( state in ( ' done ' , ' assigned ' ) AND m . location_id = l . id ) ) )
""" , (tuple(locations_ids), product_id,))
locations_ids = [ i for ( i , ) in cr . fetchall ( ) ]
for id in locations_ids :
2010-09-03 10:01:28 +00:00
if lock :
try :
# Must lock with a separate select query because FOR UPDATE can't be used with
# aggregation/group by's (when individual rows aren't identifiable).
# We use a SAVEPOINT to be able to rollback this part of the transaction without
# failing the whole transaction in case the LOCK cannot be acquired.
cr . execute ( " SAVEPOINT stock_location_product_reserve " )
cr . execute ( """ SELECT id FROM stock_move
WHERE product_id = % s AND
(
( location_dest_id = % s AND
location_id < > % s AND
state = ' done ' )
OR
( location_id = % s AND
location_dest_id < > % s AND
state in ( ' done ' , ' assigned ' ) )
)
FOR UPDATE of stock_move NOWAIT """ , (product_id, id, id, id, id), log_exceptions=False)
except Exception :
# Here it's likely that the FOR UPDATE NOWAIT failed to get the LOCK,
# so we ROLLBACK to the SAVEPOINT to restore the transaction to its earlier
# state, we return False as if the products were not available, and log it:
cr . execute ( " ROLLBACK TO stock_location_product_reserve " )
2012-01-25 09:44:51 +00:00
_logger . warning ( " Failed attempt to reserve %s x product %s , likely due to another transaction already in progress. Next attempt is likely to work. Detailed error available at DEBUG level. " , product_qty , product_id )
2012-06-22 06:48:54 +00:00
_logger . debug ( " Trace of the failed product reservation attempt: " , exc_info = True )
2010-09-03 10:01:28 +00:00
return False
# XXX TODO: rewrite this with one single query, possibly even the quantity conversion
cr . execute ( """ SELECT product_uom, sum(product_qty) AS product_qty
FROM stock_move
WHERE location_dest_id = % s AND
location_id < > % s AND
product_id = % s AND
state = ' done '
GROUP BY product_uom
""" ,
( id , id , product_id ) )
2008-07-22 15:11:28 +00:00
results = cr . dictfetchall ( )
2010-09-03 10:01:28 +00:00
cr . execute ( """ SELECT product_uom,-sum(product_qty) AS product_qty
FROM stock_move
WHERE location_id = % s AND
location_dest_id < > % s AND
product_id = % s AND
state in ( ' done ' , ' assigned ' )
GROUP BY product_uom
""" ,
( id , id , product_id ) )
2008-07-22 15:11:28 +00:00
results + = cr . dictfetchall ( )
total = 0.0
results2 = 0.0
for r in results :
2012-02-15 17:12:15 +00:00
amount = uom_obj . _compute_qty ( cr , uid , r [ ' product_uom ' ] , r [ ' product_qty ' ] , context . get ( ' uom ' , False ) )
2008-07-22 15:11:28 +00:00
results2 + = amount
total + = amount
2009-08-28 09:40:49 +00:00
if total < = 0.0 :
2008-07-22 15:11:28 +00:00
continue
amount = results2
2012-02-15 17:12:15 +00:00
compare_qty = float_compare ( amount , 0 , precision_rounding = uom_rounding )
if compare_qty == 1 :
2009-08-28 09:40:49 +00:00
if amount > min ( total , product_qty ) :
amount = min ( product_qty , total )
result . append ( ( amount , id ) )
2008-07-22 15:11:28 +00:00
product_qty - = amount
total - = amount
2009-08-28 09:40:49 +00:00
if product_qty < = 0.0 :
2008-07-22 15:11:28 +00:00
return result
2009-08-28 09:40:49 +00:00
if total < = 0.0 :
2008-07-22 15:11:28 +00:00
continue
return False
2009-08-28 09:40:49 +00:00
2006-12-07 13:41:40 +00:00
2009-08-28 09:40:49 +00:00
2006-12-07 13:41:40 +00:00
class stock_tracking ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.tracking "
2010-06-26 15:15:23 +00:00
_description = " Packs "
2010-05-28 05:18:58 +00:00
2008-07-22 15:11:28 +00:00
def checksum ( sscc ) :
salt = ' 31 ' * 8 + ' 3 '
sum = 0
for sscc_part , salt_part in zip ( sscc , salt ) :
sum + = int ( sscc_part ) * int ( salt_part )
return ( 10 - ( sum % 10 ) ) % 10
checksum = staticmethod ( checksum )
2010-07-06 11:21:27 +00:00
def make_sscc ( self , cr , uid , context = None ) :
2008-07-22 15:11:28 +00:00
sequence = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.lot.tracking ' )
2010-12-30 11:32:46 +00:00
try :
return sequence + str ( self . checksum ( sequence ) )
2010-12-30 14:29:13 +00:00
except Exception :
2010-12-30 11:32:46 +00:00
return sequence
2008-07-22 15:11:28 +00:00
_columns = {
2011-07-11 16:11:22 +00:00
' name ' : fields . char ( ' Pack Reference ' , size = 64 , required = True , select = True , help = " By default, the pack reference is generated following the sscc standard. (Serial number + 1 check digit) " ) ,
2010-09-06 13:51:23 +00:00
' active ' : fields . boolean ( ' Active ' , help = " By unchecking the active field, you may hide a pack without deleting it. " ) ,
2010-09-06 14:33:47 +00:00
' serial ' : fields . char ( ' Additional Reference ' , size = 64 , select = True , help = " Other reference or serial number " ) ,
2010-09-06 11:47:40 +00:00
' move_ids ' : fields . one2many ( ' stock.move ' , ' tracking_id ' , ' Moves for this pack ' , readonly = True ) ,
' date ' : fields . datetime ( ' Creation Date ' , required = True ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-07-06 11:21:27 +00:00
' active ' : 1 ,
2009-08-28 09:40:49 +00:00
' name ' : make_sscc ,
2011-02-23 06:15:04 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2008-07-22 15:11:28 +00:00
}
2009-12-09 11:43:34 +00:00
def name_search ( self , cr , user , name , args = None , operator = ' ilike ' , context = None , limit = 100 ) :
2008-07-22 15:11:28 +00:00
if not args :
2009-08-28 09:40:49 +00:00
args = [ ]
ids = self . search ( cr , user , [ ( ' serial ' , ' = ' , name ) ] + args , limit = limit , context = context )
ids + = self . search ( cr , user , [ ( ' name ' , operator , name ) ] + args , limit = limit , context = context )
2008-07-22 15:11:28 +00:00
return self . name_get ( cr , user , ids , context )
2010-07-06 11:21:27 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2012-02-10 15:07:25 +00:00
""" Append the serial to the name """
2008-07-22 15:11:28 +00:00
if not len ( ids ) :
return [ ]
2012-02-10 15:07:25 +00:00
res = [ ( r [ ' id ' ] , r [ ' serial ' ] and ' %s [ %s ] ' % ( r [ ' name ' ] , r [ ' serial ' ] )
or r [ ' name ' ] )
for r in self . read ( cr , uid , ids , [ ' name ' , ' serial ' ] ,
context = context ) ]
2008-07-22 15:11:28 +00:00
return res
2009-08-28 09:40:49 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot remove a lot line. ' ) )
2010-10-26 13:56:22 +00:00
2012-03-05 17:53:10 +00:00
def action_traceability ( self , cr , uid , ids , context = None ) :
2010-09-06 09:51:52 +00:00
""" It traces the information of a product
@param self : The object pointer .
@param cr : A database cursor
@param uid : ID of the user currently logged in
2010-10-26 13:56:22 +00:00
@param ids : List of IDs selected
@param context : A standard dictionary
2010-09-06 09:51:52 +00:00
@return : A dictionary of values
"""
2012-01-11 15:09:42 +00:00
return self . pool . get ( ' action.traceability ' ) . action_traceability ( cr , uid , ids , context )
2006-12-07 13:41:40 +00:00
#----------------------------------------------------------
# Stock Picking
#----------------------------------------------------------
class stock_picking ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.picking "
2012-04-03 12:10:37 +00:00
_inherit = [ ' mail.thread ' ]
2009-11-13 05:41:16 +00:00
_description = " Picking List "
2013-01-16 10:55:05 +00:00
_order = " id desc "
2009-08-28 09:40:49 +00:00
2010-07-06 11:21:27 +00:00
def _set_maximum_date ( self , cr , uid , ids , name , value , arg , context = None ) :
2010-05-26 12:59:30 +00:00
""" Calculates planned date if it is greater than ' value ' .
@param name : Name of field
@param value : Value of field
2010-06-16 08:40:57 +00:00
@param arg : User defined argument
2010-05-26 12:59:30 +00:00
@return : True or False
"""
2009-08-28 09:40:49 +00:00
if not value :
return False
2008-10-13 06:22:33 +00:00
if isinstance ( ids , ( int , long ) ) :
2009-08-28 09:40:49 +00:00
ids = [ ids ]
2010-11-22 10:37:53 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
2009-08-28 09:40:49 +00:00
sql_str = """ update stock_move set
2013-07-02 11:40:49 +00:00
date_expected = ' %s '
2008-09-16 10:37:53 +00:00
where
2009-08-28 09:40:49 +00:00
picking_id = % d """ % (value, pick.id)
2008-10-14 13:23:55 +00:00
if pick . max_date :
2013-07-02 11:40:49 +00:00
sql_str + = " and (date_expected= ' " + pick . max_date + " ' ) "
2008-10-14 13:23:55 +00:00
cr . execute ( sql_str )
2008-09-16 10:37:53 +00:00
return True
2008-09-05 07:12:28 +00:00
2010-07-06 11:21:27 +00:00
def _set_minimum_date ( self , cr , uid , ids , name , value , arg , context = None ) :
2010-05-26 12:59:30 +00:00
""" Calculates planned date if it is less than ' value ' .
@param name : Name of field
@param value : Value of field
2010-06-16 08:40:57 +00:00
@param arg : User defined argument
2010-05-26 12:59:30 +00:00
@return : True or False
"""
2009-08-28 09:40:49 +00:00
if not value :
return False
2008-10-13 06:22:33 +00:00
if isinstance ( ids , ( int , long ) ) :
2009-08-28 09:40:49 +00:00
ids = [ ids ]
2010-07-06 11:21:27 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
2009-08-28 09:40:49 +00:00
sql_str = """ update stock_move set
2013-07-02 11:40:49 +00:00
date_expected = ' %s '
2008-09-16 10:37:53 +00:00
where
2009-08-28 09:40:49 +00:00
picking_id = % s """ % (value, pick.id)
2008-10-14 13:23:55 +00:00
if pick . min_date :
2013-07-02 11:40:49 +00:00
sql_str + = " and (date_expected= ' " + pick . min_date + " ' ) "
2008-10-14 13:23:55 +00:00
cr . execute ( sql_str )
2008-09-16 10:37:53 +00:00
return True
2008-09-05 07:12:28 +00:00
2010-07-06 11:21:27 +00:00
def get_min_max_date ( self , cr , uid , ids , field_name , arg , context = None ) :
2010-05-26 12:59:30 +00:00
""" Finds minimum and maximum dates for picking.
@return : Dictionary of values
"""
2008-09-05 07:12:28 +00:00
res = { }
2008-09-16 10:37:53 +00:00
for id in ids :
2009-08-28 09:40:49 +00:00
res [ id ] = { ' min_date ' : False , ' max_date ' : False }
2008-09-16 10:37:53 +00:00
if not ids :
return res
cr . execute ( """ select
picking_id ,
2010-11-15 08:36:42 +00:00
min ( date_expected ) ,
max ( date_expected )
2008-09-16 10:37:53 +00:00
from
2008-11-03 14:40:49 +00:00
stock_move
2008-09-16 10:37:53 +00:00
where
2010-06-16 11:51:39 +00:00
picking_id IN % s
2008-09-16 10:37:53 +00:00
group by
2010-06-16 11:51:39 +00:00
picking_id """ ,(tuple(ids),))
2009-08-28 09:40:49 +00:00
for pick , dt1 , dt2 in cr . fetchall ( ) :
2010-11-24 17:21:57 +00:00
res [ pick ] [ ' min_date ' ] = dt1
res [ pick ] [ ' max_date ' ] = dt2
2008-09-05 07:12:28 +00:00
return res
2012-08-07 11:06:16 +00:00
2009-02-27 07:04:59 +00:00
def create ( self , cr , user , vals , context = None ) :
2013-03-24 09:44:45 +00:00
if ( ' name ' not in vals ) or ( vals . get ( ' name ' ) == ' / ' ) or ( vals . get ( ' name ' ) == False ) :
2012-10-24 08:33:24 +00:00
seq_obj_name = self . _name
2010-05-24 09:32:50 +00:00
vals [ ' name ' ] = self . pool . get ( ' ir.sequence ' ) . get ( cr , user , seq_obj_name )
2010-06-10 09:32:15 +00:00
new_id = super ( stock_picking , self ) . create ( cr , user , vals , context )
return new_id
2009-01-09 13:48:17 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2012-03-20 09:47:17 +00:00
' name ' : fields . char ( ' Reference ' , size = 64 , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2012-10-08 10:41:51 +00:00
' origin ' : fields . char ( ' Source Document ' , size = 64 , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " Reference of the document " , select = True ) ,
2012-03-21 11:14:45 +00:00
' backorder_id ' : fields . many2one ( ' stock.picking ' , ' Back Order of ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " If this shipment was split, then this field links to the shipment which contains the already processed part. " , select = True ) ,
2012-10-22 09:41:42 +00:00
' type ' : fields . selection ( [ ( ' out ' , ' Sending Goods ' ) , ( ' in ' , ' Getting Goods ' ) , ( ' internal ' , ' Internal ' ) ] , ' Shipping Type ' , required = True , select = True , help = " Shipping type specify, goods coming in or going out. " ) ,
2012-03-20 09:47:17 +00:00
' note ' : fields . text ( ' Notes ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
' stock_journal_id ' : fields . many2one ( ' stock.journal ' , ' Stock Journal ' , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " Keep empty if you produce at the location where the finished products are needed. " \
2009-09-24 10:46:21 +00:00
" Set a location if you produce at a fixed location. This can be a partner location " \
2010-09-06 13:51:23 +00:00
" if you subcontract the manufacturing operations. " , select = True ) ,
2012-03-20 09:47:17 +00:00
' location_dest_id ' : fields . many2one ( ' stock.location ' , ' Dest. Location ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " Location where the system will stock the finished products. " , select = True ) ,
2012-03-21 11:43:30 +00:00
' move_type ' : fields . selection ( [ ( ' direct ' , ' Partial ' ) , ( ' one ' , ' All at once ' ) ] , ' Delivery Method ' , required = True , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " It specifies goods to be deliver partially or all at once " ) ,
2012-05-03 10:29:10 +00:00
' state ' : fields . selection ( [
2012-05-04 15:36:13 +00:00
( ' draft ' , ' Draft ' ) ,
2012-05-01 12:34:46 +00:00
( ' cancel ' , ' Cancelled ' ) ,
2012-05-03 10:29:10 +00:00
( ' auto ' , ' Waiting Another Operation ' ) ,
( ' confirmed ' , ' Waiting Availability ' ) ,
2012-05-04 15:36:13 +00:00
( ' assigned ' , ' Ready to Transfer ' ) ,
( ' done ' , ' Transferred ' ) ,
2012-12-20 11:47:30 +00:00
] , ' Status ' , readonly = True , select = True , track_visibility = ' onchange ' , help = """
2012-05-04 15:36:13 +00:00
* Draft : not confirmed yet and will not be scheduled until confirmed \n
* Waiting Another Operation : waiting for another move to proceed before it becomes automatically available ( e . g . in Make - To - Order flows ) \n
* Waiting Availability : still waiting for the availability of products \n
* Ready to Transfer : products reserved , simply waiting for confirmation . \n
* Transferred : has been processed , can ' t be modified or cancelled anymore \n
* Cancelled : has been cancelled , can ' t be confirmed anymore " " "
) ,
2008-09-16 10:37:53 +00:00
' min_date ' : fields . function ( get_min_max_date , fnct_inv = _set_minimum_date , multi = " min_max_date " ,
2012-10-12 10:08:37 +00:00
store = True , type = ' datetime ' , string = ' Scheduled Time ' , select = 1 , help = " Scheduled time for the shipment to be processed " ) ,
2013-05-13 14:53:18 +00:00
' date ' : fields . datetime ( ' Creation Date ' , help = " Creation date, usually the time of the order. " , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2012-11-21 07:17:18 +00:00
' date_done ' : fields . datetime ( ' Date of Transfer ' , help = " Date of Completion " , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2008-09-16 10:37:53 +00:00
' max_date ' : fields . function ( get_min_max_date , fnct_inv = _set_maximum_date , multi = " min_max_date " ,
2011-07-05 12:28:57 +00:00
store = True , type = ' datetime ' , string = ' Max. Expected Date ' , select = 2 ) ,
2010-05-10 05:46:20 +00:00
' move_lines ' : fields . one2many ( ' stock.move ' , ' picking_id ' , ' Internal Moves ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2012-05-04 15:00:52 +00:00
' product_id ' : fields . related ( ' move_lines ' , ' product_id ' , type = ' many2one ' , relation = ' product.product ' , string = ' Product ' ) ,
2012-03-20 09:47:17 +00:00
' auto_picking ' : fields . boolean ( ' Auto-Picking ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2012-04-02 10:24:12 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Partner ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2009-08-28 09:40:49 +00:00
' invoice_state ' : fields . selection ( [
( " invoiced " , " Invoiced " ) ,
( " 2binvoiced " , " To Be Invoiced " ) ,
2010-10-28 07:20:11 +00:00
( " none " , " Not Applicable " ) ] , " Invoice Control " ,
2012-12-20 11:47:30 +00:00
select = True , required = True , readonly = True , track_visibility = ' onchange ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2012-03-20 09:47:17 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2012-09-27 09:03:52 +00:00
' name ' : lambda self , cr , uid , context : ' / ' ,
2010-07-06 11:21:27 +00:00
' state ' : ' draft ' ,
' move_type ' : ' direct ' ,
2012-05-07 15:43:56 +00:00
' type ' : ' internal ' ,
2010-07-06 11:21:27 +00:00
' invoice_state ' : ' none ' ,
2011-02-23 06:15:04 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2010-07-06 11:21:27 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.picking ' , context = c )
2008-07-22 15:11:28 +00:00
}
2011-11-09 06:30:48 +00:00
_sql_constraints = [
2011-11-17 06:44:09 +00:00
( ' name_uniq ' , ' unique(name, company_id) ' , ' Reference must be unique per Company! ' ) ,
2011-11-09 06:30:48 +00:00
]
2011-05-11 06:57:46 +00:00
2010-11-22 10:37:53 +00:00
def action_process ( self , cr , uid , ids , context = None ) :
2012-09-20 05:05:24 +00:00
if context is None :
context = { }
2012-02-27 10:10:20 +00:00
""" Open the partial picking wizard """
2012-09-20 05:05:24 +00:00
context . update ( {
' active_model ' : self . _name ,
' active_ids ' : ids ,
' active_id ' : len ( ids ) and ids [ 0 ] or False
} )
2010-10-18 18:36:42 +00:00
return {
2012-05-03 10:29:10 +00:00
' view_type ' : ' form ' ,
2012-03-26 08:43:13 +00:00
' view_mode ' : ' form ' ,
2010-10-18 18:36:42 +00:00
' res_model ' : ' stock.partial.picking ' ,
' type ' : ' ir.actions.act_window ' ,
2012-05-03 10:29:10 +00:00
' target ' : ' new ' ,
' context ' : context ,
2012-03-26 08:43:13 +00:00
' nodestroy ' : True ,
2010-10-18 18:36:42 +00:00
}
2009-08-28 09:40:49 +00:00
2010-07-06 11:21:27 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2009-02-05 16:41:33 +00:00
if default is None :
default = { }
default = default . copy ( )
2010-11-23 11:31:52 +00:00
picking_obj = self . browse ( cr , uid , id , context = context )
2013-03-19 12:52:44 +00:00
if ( ' name ' not in default ) or ( picking_obj . name == ' / ' ) :
seq_obj_name = ' stock.picking. ' + picking_obj . type
2010-06-16 08:40:57 +00:00
default [ ' name ' ] = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , seq_obj_name )
2010-08-30 06:49:19 +00:00
default [ ' origin ' ] = ' '
2010-09-01 08:40:10 +00:00
default [ ' backorder_id ' ] = False
2012-11-26 11:18:11 +00:00
if ' invoice_state ' not in default and picking_obj . invoice_state == ' invoiced ' :
2011-11-22 06:24:37 +00:00
default [ ' invoice_state ' ] = ' 2binvoiced '
2013-03-19 12:52:44 +00:00
res = super ( stock_picking , self ) . copy ( cr , uid , id , default , context )
2010-10-26 13:56:22 +00:00
return res
2013-03-19 12:52:44 +00:00
2012-09-21 11:00:47 +00:00
def fields_view_get ( self , cr , uid , view_id = None , view_type = False , context = None , toolbar = False , submenu = False ) :
2012-09-21 11:46:00 +00:00
if view_type == ' form ' and not view_id :
2012-09-21 11:00:47 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
if self . _name == " stock.picking.in " :
2013-03-19 12:52:44 +00:00
model , view_id = mod_obj . get_object_reference ( cr , uid , ' stock ' , ' view_picking_in_form ' )
2012-09-21 11:00:47 +00:00
if self . _name == " stock.picking.out " :
2013-03-19 12:52:44 +00:00
model , view_id = mod_obj . get_object_reference ( cr , uid , ' stock ' , ' view_picking_out_form ' )
return super ( stock_picking , self ) . fields_view_get ( cr , uid , view_id = view_id , view_type = view_type , context = context , toolbar = toolbar , submenu = submenu )
2008-09-07 23:24:39 +00:00
2012-11-06 09:31:33 +00:00
def onchange_partner_in ( self , cr , uid , ids , partner_id = None , context = None ) :
2009-08-28 09:40:49 +00:00
return { }
2008-07-22 15:11:28 +00:00
2010-07-06 11:21:27 +00:00
def action_explode ( self , cr , uid , moves , context = None ) :
2012-02-28 16:04:00 +00:00
""" Hook to allow other modules to split the moves of a picking. """
2008-09-07 23:24:39 +00:00
return moves
2008-07-22 15:11:28 +00:00
2010-07-06 11:21:27 +00:00
def action_confirm ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Confirms picking.
2010-06-16 08:40:57 +00:00
@return : True
2010-05-26 12:59:30 +00:00
"""
2012-09-26 08:16:13 +00:00
pickings = self . browse ( cr , uid , ids , context = context )
2009-02-05 17:11:04 +00:00
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
2008-07-22 15:11:28 +00:00
todo = [ ]
2012-09-26 08:16:13 +00:00
for picking in pickings :
2008-07-22 15:11:28 +00:00
for r in picking . move_lines :
2009-08-28 09:40:49 +00:00
if r . state == ' draft ' :
2008-11-05 09:25:04 +00:00
todo . append ( r . id )
2008-09-07 23:24:39 +00:00
todo = self . action_explode ( cr , uid , todo , context )
2008-07-22 15:11:28 +00:00
if len ( todo ) :
2010-07-06 11:21:27 +00:00
self . pool . get ( ' stock.move ' ) . action_confirm ( cr , uid , todo , context = context )
2008-07-22 15:11:28 +00:00
return True
def test_auto_picking ( self , cr , uid , ids ) :
# TODO: Check locations to see if in the same location ?
return True
def action_assign ( self , cr , uid , ids , * args ) :
2010-05-26 12:59:30 +00:00
""" Changes state of picking to available if all moves are confirmed.
@return : True
"""
2008-07-22 15:11:28 +00:00
for pick in self . browse ( cr , uid , ids ) :
2012-02-29 12:06:38 +00:00
if pick . state == ' draft ' :
2013-01-28 15:29:29 +00:00
self . signal_button_confirm ( cr , uid , [ pick . id ] )
2009-08-28 09:40:49 +00:00
move_ids = [ x . id for x in pick . move_lines if x . state == ' confirmed ' ]
2010-03-18 07:12:23 +00:00
if not move_ids :
2012-08-07 11:34:14 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' Not enough stock, unable to reserve the products. ' ) )
2008-07-22 15:11:28 +00:00
self . pool . get ( ' stock.move ' ) . action_assign ( cr , uid , move_ids )
return True
def force_assign ( self , cr , uid , ids , * args ) :
2010-05-26 12:59:30 +00:00
""" Changes state of picking to available if moves are confirmed or waiting.
@return : True
"""
2008-07-22 15:11:28 +00:00
for pick in self . browse ( cr , uid , ids ) :
2009-12-10 11:55:52 +00:00
move_ids = [ x . id for x in pick . move_lines if x . state in [ ' confirmed ' , ' waiting ' ] ]
2008-07-22 15:11:28 +00:00
self . pool . get ( ' stock.move ' ) . force_assign ( cr , uid , move_ids )
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , pick . id , cr )
2008-07-22 15:11:28 +00:00
return True
2008-11-03 14:40:49 +00:00
2008-09-17 11:54:26 +00:00
def draft_force_assign ( self , cr , uid , ids , * args ) :
2010-05-26 12:59:30 +00:00
""" Confirms picking directly from draft state.
@return : True
"""
2008-09-17 11:54:26 +00:00
for pick in self . browse ( cr , uid , ids ) :
2010-06-21 08:53:40 +00:00
if not pick . move_lines :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot process picking without stock moves. ' ) )
2013-01-28 15:29:29 +00:00
self . signal_button_confirm ( cr , uid , [ pick . id ] )
2008-09-17 11:54:26 +00:00
return True
2008-11-03 14:40:49 +00:00
2010-10-13 06:30:46 +00:00
def draft_validate ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Validates picking directly from draft state.
@return : True
"""
2008-09-17 11:54:26 +00:00
self . draft_force_assign ( cr , uid , ids )
2010-11-22 10:37:53 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
2008-12-14 17:23:47 +00:00
move_ids = [ x . id for x in pick . move_lines ]
self . pool . get ( ' stock.move ' ) . force_assign ( cr , uid , move_ids )
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , pick . id , cr )
2011-01-14 12:49:50 +00:00
return self . action_process (
cr , uid , ids , context = context )
2008-07-22 15:11:28 +00:00
def cancel_assign ( self , cr , uid , ids , * args ) :
2010-06-16 08:40:57 +00:00
""" Cancels picking and moves.
2010-05-26 12:59:30 +00:00
@return : True
"""
2008-07-22 15:11:28 +00:00
for pick in self . browse ( cr , uid , ids ) :
move_ids = [ x . id for x in pick . move_lines ]
self . pool . get ( ' stock.move ' ) . cancel_assign ( cr , uid , move_ids )
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , pick . id , cr )
2008-07-22 15:11:28 +00:00
return True
2010-05-19 20:02:36 +00:00
def action_assign_wkf ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Changes picking state to assigned.
@return : True
"""
2009-08-28 09:40:49 +00:00
self . write ( cr , uid , ids , { ' state ' : ' assigned ' } )
2008-07-22 15:11:28 +00:00
return True
2010-09-15 11:58:12 +00:00
def test_finished ( self , cr , uid , ids ) :
2010-05-26 12:59:30 +00:00
""" Tests whether the move is in done or cancel state or not.
@return : True or False
"""
2009-08-28 09:40:49 +00:00
move_ids = self . pool . get ( ' stock.move ' ) . search ( cr , uid , [ ( ' picking_id ' , ' in ' , ids ) ] )
for move in self . pool . get ( ' stock.move ' ) . browse ( cr , uid , move_ids ) :
if move . state not in ( ' done ' , ' cancel ' ) :
2010-12-17 12:14:59 +00:00
2008-07-22 15:11:28 +00:00
if move . product_qty != 0.0 :
return False
else :
2010-12-17 12:14:59 +00:00
move . write ( { ' state ' : ' done ' } )
2008-07-22 15:11:28 +00:00
return True
def test_assigned ( self , cr , uid , ids ) :
2010-05-26 12:59:30 +00:00
""" Tests whether the move is in assigned state or not.
@return : True or False
"""
2011-12-22 09:53:38 +00:00
#TOFIX: assignment of move lines should be call before testing assigment otherwise picking never gone in assign state
2008-07-22 15:11:28 +00:00
ok = True
for pick in self . browse ( cr , uid , ids ) :
mt = pick . move_type
2012-05-03 12:12:53 +00:00
# incomming shipments are always set as available if they aren't chained
if pick . type == ' in ' :
if all ( [ x . state != ' waiting ' for x in pick . move_lines ] ) :
2012-05-02 16:56:39 +00:00
return True
2008-07-22 15:11:28 +00:00
for move in pick . move_lines :
2010-05-26 12:59:30 +00:00
if ( move . state in ( ' confirmed ' , ' draft ' ) ) and ( mt == ' one ' ) :
2008-07-22 15:11:28 +00:00
return False
2010-05-26 12:59:30 +00:00
if ( mt == ' direct ' ) and ( move . state == ' assigned ' ) and ( move . product_qty ) :
2008-07-22 15:11:28 +00:00
return True
2009-08-28 09:40:49 +00:00
ok = ok and ( move . state in ( ' cancel ' , ' done ' , ' assigned ' ) )
2008-07-22 15:11:28 +00:00
return ok
2010-07-06 11:44:51 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Changes picking state to cancel.
@return : True
"""
2010-11-22 10:37:53 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
ids2 = [ move . id for move in pick . move_lines ]
self . pool . get ( ' stock.move ' ) . action_cancel ( cr , uid , ids2 , context )
2009-08-28 09:40:49 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' , ' invoice_state ' : ' none ' } )
2008-07-22 15:11:28 +00:00
return True
#
# TODO: change and create a move if not parents
#
def action_done ( self , cr , uid , ids , context = None ) :
2012-02-27 10:10:20 +00:00
""" Changes picking state to done.
This method is called at the end of the workflow by the activity " done " .
2010-05-26 12:59:30 +00:00
@return : True
"""
2009-08-28 09:40:49 +00:00
self . write ( cr , uid , ids , { ' state ' : ' done ' , ' date_done ' : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) } )
2008-07-22 15:11:28 +00:00
return True
2010-07-06 11:44:51 +00:00
def action_move ( self , cr , uid , ids , context = None ) :
2012-02-27 10:10:20 +00:00
""" Process the Stock Moves of the Picking
This method is called by the workflow by the activity " move " .
Normally that happens when the signal button_done is received ( button
" Done " pressed on a Picking view ) .
2010-05-26 12:59:30 +00:00
@return : True
"""
2010-12-13 06:43:09 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
todo = [ ]
for move in pick . move_lines :
2011-09-25 23:20:56 +00:00
if move . state == ' draft ' :
self . pool . get ( ' stock.move ' ) . action_confirm ( cr , uid , [ move . id ] ,
context = context )
todo . append ( move . id )
elif move . state in ( ' assigned ' , ' confirmed ' ) :
2008-07-22 15:11:28 +00:00
todo . append ( move . id )
if len ( todo ) :
self . pool . get ( ' stock.move ' ) . action_done ( cr , uid , todo ,
context = context )
return True
2010-03-29 14:29:43 +00:00
def get_currency_id ( self , cr , uid , picking ) :
2008-11-05 16:36:08 +00:00
return False
2012-01-05 16:08:45 +00:00
def _get_partner_to_invoice ( self , cr , uid , picking , context = None ) :
""" Gets the partner that will be invoiced
2012-05-08 16:32:04 +00:00
Note that this function is inherited in the sale and purchase modules
2012-01-31 10:53:22 +00:00
@param picking : object of the picking for which we are selecting the partner to invoice
@return : object of the partner to invoice
2010-05-26 12:59:30 +00:00
"""
2012-03-30 07:49:01 +00:00
return picking . partner_id and picking . partner_id . id
2008-07-22 15:11:28 +00:00
2010-03-29 14:29:43 +00:00
def _get_comment_invoice ( self , cr , uid , picking ) :
2010-05-26 12:59:30 +00:00
"""
@return : comment string for invoice
"""
2008-07-22 15:11:28 +00:00
return picking . note or ' '
2010-03-29 14:29:43 +00:00
def _get_price_unit_invoice ( self , cr , uid , move_line , type , context = None ) :
2010-05-26 12:59:30 +00:00
""" Gets price unit for invoice
@param move_line : Stock move lines
@param type : Type of invoice
@return : The price unit for the move line
"""
2010-03-29 14:29:43 +00:00
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
if type in ( ' in_invoice ' , ' in_refund ' ) :
2009-12-23 16:34:53 +00:00
# Take the user company and pricetype
2010-03-29 14:29:43 +00:00
context [ ' currency_id ' ] = move_line . company_id . currency_id . id
2011-09-13 13:45:51 +00:00
amount_unit = move_line . product_id . price_get ( ' standard_price ' , context = context ) [ move_line . product_id . id ]
2009-12-23 16:34:53 +00:00
return amount_unit
2008-07-22 15:11:28 +00:00
else :
return move_line . product_id . list_price
2008-11-03 14:40:49 +00:00
2010-03-29 14:29:43 +00:00
def _get_discount_invoice ( self , cr , uid , move_line ) :
2008-07-22 15:11:28 +00:00
''' Return the discount for the move line '''
return 0.0
2010-03-29 14:29:43 +00:00
def _get_taxes_invoice ( self , cr , uid , move_line , type ) :
2010-05-26 12:59:30 +00:00
""" Gets taxes on invoice
@param move_line : Stock move lines
2010-06-16 08:40:57 +00:00
@param type : Type of invoice
2010-05-26 12:59:30 +00:00
@return : Taxes Ids for the move line
"""
2008-07-22 15:11:28 +00:00
if type in ( ' in_invoice ' , ' in_refund ' ) :
2008-10-21 16:28:19 +00:00
taxes = move_line . product_id . supplier_taxes_id
else :
taxes = move_line . product_id . taxes_id
2012-03-30 07:49:01 +00:00
if move_line . picking_id and move_line . picking_id . partner_id and move_line . picking_id . partner_id . id :
2008-10-21 16:28:19 +00:00
return self . pool . get ( ' account.fiscal.position ' ) . map_tax (
2010-03-29 14:29:43 +00:00
cr ,
uid ,
2012-03-30 07:49:01 +00:00
move_line . picking_id . partner_id . property_account_position ,
2008-10-21 16:28:19 +00:00
taxes
)
2008-07-22 15:11:28 +00:00
else :
2008-10-21 16:28:19 +00:00
return map ( lambda x : x . id , taxes )
2008-07-22 15:11:28 +00:00
2010-03-29 14:29:43 +00:00
def _get_account_analytic_invoice ( self , cr , uid , picking , move_line ) :
2008-07-22 15:11:28 +00:00
return False
2010-03-29 14:29:43 +00:00
def _invoice_line_hook ( self , cr , uid , move_line , invoice_line_id ) :
2008-07-22 15:11:28 +00:00
''' Call after the creation of the invoice line '''
return
2010-03-29 14:29:43 +00:00
def _invoice_hook ( self , cr , uid , picking , invoice_id ) :
2008-07-22 15:11:28 +00:00
''' Call after the creation of the invoice '''
return
2010-11-12 12:54:47 +00:00
def _get_invoice_type ( self , pick ) :
src_usage = dest_usage = None
inv_type = None
if pick . invoice_state == ' 2binvoiced ' :
if pick . move_lines :
src_usage = pick . move_lines [ 0 ] . location_id . usage
dest_usage = pick . move_lines [ 0 ] . location_dest_id . usage
if pick . type == ' out ' and dest_usage == ' supplier ' :
inv_type = ' in_refund '
elif pick . type == ' out ' and dest_usage == ' customer ' :
inv_type = ' out_invoice '
elif pick . type == ' in ' and src_usage == ' supplier ' :
inv_type = ' in_invoice '
elif pick . type == ' in ' and src_usage == ' customer ' :
inv_type = ' out_refund '
else :
inv_type = ' out_invoice '
return inv_type
2012-01-05 16:06:35 +00:00
def _prepare_invoice_group ( self , cr , uid , picking , partner , invoice , context = None ) :
2012-01-31 10:53:22 +00:00
""" Builds the dict for grouped invoices
@param picking : picking object
@param partner : object of the partner to invoice ( not used here , but may be usefull if this function is inherited )
@param invoice : object of the invoice that we are updating
@return : dict that will be used to update the invoice
2012-01-19 21:40:53 +00:00
"""
2012-01-05 16:06:35 +00:00
comment = self . _get_comment_invoice ( cr , uid , picking )
return {
' name ' : ( invoice . name or ' ' ) + ' , ' + ( picking . name or ' ' ) ,
' origin ' : ( invoice . origin or ' ' ) + ' , ' + ( picking . name or ' ' ) + ( picking . origin and ( ' : ' + picking . origin ) or ' ' ) ,
' comment ' : ( comment and ( invoice . comment and invoice . comment + " \n " + comment or comment ) ) or ( invoice . comment and invoice . comment or ' ' ) ,
' date_invoice ' : context . get ( ' date_inv ' , False ) ,
}
def _prepare_invoice ( self , cr , uid , picking , partner , inv_type , journal_id , context = None ) :
2012-01-31 10:53:22 +00:00
""" Builds the dict containing the values for the invoice
@param picking : picking object
@param partner : object of the partner to invoice
@param inv_type : type of the invoice ( ' out_invoice ' , ' in_invoice ' , . . . )
@param journal_id : ID of the accounting journal
@return : dict that will be used to create the invoice object
2012-01-19 21:40:53 +00:00
"""
2012-03-09 04:52:22 +00:00
if isinstance ( partner , int ) :
partner = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner , context = context )
2012-01-05 16:06:35 +00:00
if inv_type in ( ' out_invoice ' , ' out_refund ' ) :
account_id = partner . property_account_receivable . id
2012-07-11 13:18:24 +00:00
payment_term = partner . property_payment_term . id or False
2012-01-05 16:06:35 +00:00
else :
account_id = partner . property_account_payable . id
2012-07-11 13:18:24 +00:00
payment_term = partner . property_supplier_payment_term . id or False
2012-01-05 16:06:35 +00:00
comment = self . _get_comment_invoice ( cr , uid , picking )
invoice_vals = {
' name ' : picking . name ,
' origin ' : ( picking . name or ' ' ) + ( picking . origin and ( ' : ' + picking . origin ) or ' ' ) ,
' type ' : inv_type ,
' account_id ' : account_id ,
' partner_id ' : partner . id ,
' comment ' : comment ,
2012-07-11 11:14:34 +00:00
' payment_term ' : payment_term ,
2012-01-05 16:06:35 +00:00
' fiscal_position ' : partner . property_account_position . id ,
' date_invoice ' : context . get ( ' date_inv ' , False ) ,
' company_id ' : picking . company_id . id ,
2012-01-31 10:53:22 +00:00
' user_id ' : uid ,
2012-01-05 16:06:35 +00:00
}
cur_id = self . get_currency_id ( cr , uid , picking )
if cur_id :
invoice_vals [ ' currency_id ' ] = cur_id
if journal_id :
invoice_vals [ ' journal_id ' ] = journal_id
return invoice_vals
2012-01-16 14:18:24 +00:00
def _prepare_invoice_line ( self , cr , uid , group , picking , move_line , invoice_id ,
invoice_vals , context = None ) :
2012-01-31 10:53:22 +00:00
""" Builds the dict containing the values for the invoice line
@param group : True or False
@param picking : picking object
@param : move_line : move_line object
@param : invoice_id : ID of the related invoice
@param : invoice_vals : dict used to created the invoice
@return : dict that will be used to create the invoice line
2012-01-19 21:40:53 +00:00
"""
2012-01-16 14:18:24 +00:00
if group :
name = ( picking . name or ' ' ) + ' - ' + move_line . name
else :
name = move_line . name
origin = move_line . picking_id . name or ' '
if move_line . picking_id . origin :
origin + = ' : ' + move_line . picking_id . origin
if invoice_vals [ ' type ' ] in ( ' out_invoice ' , ' out_refund ' ) :
2012-07-27 01:52:02 +00:00
account_id = move_line . product_id . property_account_income . id
2012-01-16 14:18:24 +00:00
if not account_id :
account_id = move_line . product_id . categ_id . \
property_account_income_categ . id
else :
2012-07-27 01:52:02 +00:00
account_id = move_line . product_id . property_account_expense . id
2012-01-16 14:18:24 +00:00
if not account_id :
account_id = move_line . product_id . categ_id . \
property_account_expense_categ . id
if invoice_vals [ ' fiscal_position ' ] :
fp_obj = self . pool . get ( ' account.fiscal.position ' )
fiscal_position = fp_obj . browse ( cr , uid , invoice_vals [ ' fiscal_position ' ] , context = context )
account_id = fp_obj . map_account ( cr , uid , fiscal_position , account_id )
# set UoS if it's a sale and the picking doesn't have one
uos_id = move_line . product_uos and move_line . product_uos . id or False
if not uos_id and invoice_vals [ ' type ' ] in ( ' out_invoice ' , ' out_refund ' ) :
uos_id = move_line . product_uom . id
return {
' name ' : name ,
' origin ' : origin ,
' invoice_id ' : invoice_id ,
' uos_id ' : uos_id ,
' product_id ' : move_line . product_id . id ,
' account_id ' : account_id ,
' price_unit ' : self . _get_price_unit_invoice ( cr , uid , move_line , invoice_vals [ ' type ' ] ) ,
' discount ' : self . _get_discount_invoice ( cr , uid , move_line ) ,
' quantity ' : move_line . product_uos_qty or move_line . product_qty ,
2012-01-31 10:53:22 +00:00
' invoice_line_tax_id ' : [ ( 6 , 0 , self . _get_taxes_invoice ( cr , uid , move_line , invoice_vals [ ' type ' ] ) ) ] ,
2012-01-16 14:18:24 +00:00
' account_analytic_id ' : self . _get_account_analytic_invoice ( cr , uid , picking , move_line ) ,
2012-01-31 10:53:22 +00:00
}
2012-01-16 14:18:24 +00:00
2010-03-29 14:29:43 +00:00
def action_invoice_create ( self , cr , uid , ids , journal_id = False ,
2008-07-22 15:11:28 +00:00
group = False , type = ' out_invoice ' , context = None ) :
2010-05-26 12:59:30 +00:00
""" Creates invoice based on the invoice state selected for picking.
@param journal_id : Id of journal
@param group : Whether to create a group invoice or not
@param type : Type invoice to be created
@return : Ids of created invoices for the pickings
"""
2010-03-29 14:29:43 +00:00
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
invoice_obj = self . pool . get ( ' account.invoice ' )
invoice_line_obj = self . pool . get ( ' account.invoice.line ' )
2012-03-30 09:08:37 +00:00
partner_obj = self . pool . get ( ' res.partner ' )
2008-07-22 15:11:28 +00:00
invoices_group = { }
res = { }
2010-11-12 12:54:47 +00:00
inv_type = type
2010-03-29 14:29:43 +00:00
for picking in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
if picking . invoice_state != ' 2binvoiced ' :
continue
2012-01-05 16:08:45 +00:00
partner = self . _get_partner_to_invoice ( cr , uid , picking , context = context )
2012-03-27 09:58:27 +00:00
if isinstance ( partner , int ) :
2012-03-30 09:08:37 +00:00
partner = partner_obj . browse ( cr , uid , [ partner ] , context = context ) [ 0 ]
2008-10-28 12:10:01 +00:00
if not partner :
2013-06-07 11:38:29 +00:00
raise osv . except_osv ( _ ( ' Error, no partner! ' ) ,
2008-10-27 07:23:22 +00:00
_ ( ' Please put a partner on the picking list if you want to generate invoice. ' ) )
2010-11-15 08:36:42 +00:00
2010-11-12 12:54:47 +00:00
if not inv_type :
inv_type = self . _get_invoice_type ( picking )
2008-10-27 07:23:22 +00:00
2014-06-04 08:42:43 +00:00
invoice_vals = self . _prepare_invoice ( cr , uid , picking , partner , inv_type , journal_id , context = context )
2008-07-22 15:11:28 +00:00
if group and partner . id in invoices_group :
invoice_id = invoices_group [ partner . id ]
2010-03-29 14:29:43 +00:00
invoice = invoice_obj . browse ( cr , uid , invoice_id )
2012-01-16 14:18:24 +00:00
invoice_vals_group = self . _prepare_invoice_group ( cr , uid , picking , partner , invoice , context = context )
invoice_obj . write ( cr , uid , [ invoice_id ] , invoice_vals_group , context = context )
2008-07-22 15:11:28 +00:00
else :
2012-01-16 14:18:24 +00:00
invoice_id = invoice_obj . create ( cr , uid , invoice_vals , context = context )
2012-03-30 09:08:37 +00:00
invoices_group [ partner . id ] = invoice_id
2008-07-22 15:11:28 +00:00
res [ picking . id ] = invoice_id
for move_line in picking . move_lines :
2010-10-12 20:26:56 +00:00
if move_line . state == ' cancel ' :
continue
2012-03-06 16:28:21 +00:00
if move_line . scrapped :
# do no invoice scrapped products
continue
2012-01-27 04:50:32 +00:00
vals = self . _prepare_invoice_line ( cr , uid , group , picking , move_line ,
2012-01-27 05:37:26 +00:00
invoice_id , invoice_vals , context = context )
2012-01-27 04:50:32 +00:00
if vals :
invoice_line_id = invoice_line_obj . create ( cr , uid , vals , context = context )
self . _invoice_line_hook ( cr , uid , move_line , invoice_line_id )
2008-07-22 15:11:28 +00:00
2010-03-29 14:29:43 +00:00
invoice_obj . button_compute ( cr , uid , [ invoice_id ] , context = context ,
2010-11-12 12:54:47 +00:00
set_total = ( inv_type in ( ' in_invoice ' , ' in_refund ' ) ) )
2010-03-29 14:29:43 +00:00
self . write ( cr , uid , [ picking . id ] , {
2008-07-22 15:11:28 +00:00
' invoice_state ' : ' invoiced ' ,
} , context = context )
2010-03-29 14:29:43 +00:00
self . _invoice_hook ( cr , uid , picking , invoice_id )
self . write ( cr , uid , res . keys ( ) , {
2008-07-22 15:11:28 +00:00
' invoice_state ' : ' invoiced ' ,
} , context = context )
return res
2007-09-26 05:20:30 +00:00
2010-07-06 11:44:51 +00:00
def test_done ( self , cr , uid , ids , context = None ) :
2010-07-07 09:05:22 +00:00
""" Test whether the move lines are done or not.
@return : True or False
"""
2010-06-25 22:34:40 +00:00
ok = False
for pick in self . browse ( cr , uid , ids , context = context ) :
if not pick . move_lines :
return True
for move in pick . move_lines :
if move . state not in ( ' cancel ' , ' done ' ) :
return False
if move . state == ' done ' :
ok = True
return ok
2010-07-06 11:44:51 +00:00
def test_cancel ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Test whether the move lines are canceled or not.
@return : True or False
"""
2009-06-24 08:55:52 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
for move in pick . move_lines :
if move . state not in ( ' cancel ' , ) :
return False
return True
2010-10-26 13:56:22 +00:00
2010-11-22 10:37:53 +00:00
def allow_cancel ( self , cr , uid , ids , context = None ) :
2010-09-16 12:44:12 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
if not pick . move_lines :
return True
for move in pick . move_lines :
if move . state == ' done ' :
2012-09-26 07:41:21 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot cancel the picking as some moves have been done. You should cancel the picking lines. ' ) )
2010-09-16 12:44:12 +00:00
return True
2012-09-26 07:41:21 +00:00
2009-12-02 07:15:24 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-05-11 09:17:05 +00:00
move_obj = self . pool . get ( ' stock.move ' )
2010-08-31 10:20:45 +00:00
if context is None :
2010-05-11 09:17:05 +00:00
context = { }
2009-12-02 07:15:24 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
if pick . state in [ ' done ' , ' cancel ' ] :
2014-09-19 09:34:41 +00:00
# retrieve the string value of field in user's language
state = dict ( self . fields_get ( cr , uid , context = context ) [ ' state ' ] [ ' selection ' ] ) . get ( pick . state , pick . state )
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot remove the picking which is in %s state! ' ) % ( state , ) )
2011-05-10 06:00:08 +00:00
else :
2009-12-02 07:15:24 +00:00
ids2 = [ move . id for move in pick . move_lines ]
2010-05-11 09:17:05 +00:00
ctx = context . copy ( )
ctx . update ( { ' call_unlink ' : True } )
if pick . state != ' draft ' :
#Cancelling the move in order to affect Virtual stock of product
move_obj . action_cancel ( cr , uid , ids2 , ctx )
#Removing the move
move_obj . unlink ( cr , uid , ids2 , ctx )
2010-05-28 05:18:58 +00:00
2009-12-02 07:15:24 +00:00
return super ( stock_picking , self ) . unlink ( cr , uid , ids , context = context )
2009-08-28 09:40:49 +00:00
2010-09-15 18:01:00 +00:00
# FIXME: needs refactoring, this code is partially duplicated in stock_move.do_partial()!
2010-07-06 11:44:51 +00:00
def do_partial ( self , cr , uid , ids , partial_datas , context = None ) :
2010-05-26 12:59:30 +00:00
""" Makes partial picking and moves done.
@param partial_datas : Dictionary containing details of partial picking
2012-03-30 07:49:01 +00:00
like partner_id , partner_id , delivery_date ,
2010-05-26 12:59:30 +00:00
delivery moves with product_id , product_qty , uom
@return : Dictionary of values
2010-03-25 12:08:31 +00:00
"""
2010-09-15 18:01:00 +00:00
if context is None :
context = { }
else :
context = dict ( context )
2010-03-25 12:08:31 +00:00
res = { }
move_obj = self . pool . get ( ' stock.move ' )
product_obj = self . pool . get ( ' product.product ' )
currency_obj = self . pool . get ( ' res.currency ' )
uom_obj = self . pool . get ( ' product.uom ' )
2010-03-26 09:25:02 +00:00
sequence_obj = self . pool . get ( ' ir.sequence ' )
2010-03-25 12:08:31 +00:00
for pick in self . browse ( cr , uid , ids , context = context ) :
new_picking = None
2010-03-26 09:25:02 +00:00
complete , too_many , too_few = [ ] , [ ] , [ ]
2014-10-21 19:12:58 +00:00
move_product_qty , prodlot_ids , product_avail , partial_qty , uos_qty , product_uoms = { } , { } , { } , { } , { } , { }
2010-03-25 12:08:31 +00:00
for move in pick . move_lines :
2010-03-26 14:52:01 +00:00
if move . state in ( ' done ' , ' cancel ' ) :
continue
2011-02-03 07:11:44 +00:00
partial_data = partial_datas . get ( ' move %s ' % ( move . id ) , { } )
2010-03-25 12:08:31 +00:00
product_qty = partial_data . get ( ' product_qty ' , 0.0 )
2010-03-26 09:25:02 +00:00
move_product_qty [ move . id ] = product_qty
2010-03-29 11:37:53 +00:00
product_uom = partial_data . get ( ' product_uom ' , False )
2010-03-25 12:08:31 +00:00
product_price = partial_data . get ( ' product_price ' , 0.0 )
product_currency = partial_data . get ( ' product_currency ' , False )
2010-09-28 15:28:30 +00:00
prodlot_id = partial_data . get ( ' prodlot_id ' )
prodlot_ids [ move . id ] = prodlot_id
2011-04-11 12:42:04 +00:00
product_uoms [ move . id ] = product_uom
2011-04-12 09:32:36 +00:00
partial_qty [ move . id ] = uom_obj . _compute_qty ( cr , uid , product_uoms [ move . id ] , product_qty , move . product_uom . id )
2014-10-21 19:12:58 +00:00
uos_qty [ move . id ] = move . product_id . _compute_uos_qty ( product_uom , product_qty , move . product_uos ) if product_qty else 0.0
2011-04-12 09:32:36 +00:00
if move . product_qty == partial_qty [ move . id ] :
2010-03-25 12:08:31 +00:00
complete . append ( move )
2011-11-16 12:32:51 +00:00
elif move . product_qty > partial_qty [ move . id ] :
2010-03-25 12:08:31 +00:00
too_few . append ( move )
else :
too_many . append ( move )
# Average price computation
if ( pick . type == ' in ' ) and ( move . product_id . cost_method == ' average ' ) :
product = product_obj . browse ( cr , uid , move . product_id . id )
2010-09-15 18:01:00 +00:00
move_currency_id = move . company_id . currency_id . id
context [ ' currency_id ' ] = move_currency_id
2010-03-25 12:08:31 +00:00
qty = uom_obj . _compute_qty ( cr , uid , product_uom , product_qty , product . uom_id . id )
2011-01-07 13:02:46 +00:00
2013-11-04 17:12:45 +00:00
if product . id not in product_avail :
# keep track of stock on hand including processed lines not yet marked as done
2011-01-07 13:02:46 +00:00
product_avail [ product . id ] = product . qty_available
2010-09-28 15:28:30 +00:00
if qty > 0 :
2010-03-26 11:27:31 +00:00
new_price = currency_obj . compute ( cr , uid , product_currency ,
2013-11-04 17:32:32 +00:00
move_currency_id , product_price , round = False )
2010-03-25 12:08:31 +00:00
new_price = uom_obj . _compute_price ( cr , uid , product_uom , new_price ,
2010-03-29 11:37:53 +00:00
product . uom_id . id )
2013-11-04 17:12:45 +00:00
if product_avail [ product . id ] < = 0 :
product_avail [ product . id ] = 0
2010-03-25 12:08:31 +00:00
new_std_price = new_price
else :
# Get the standard price
2011-09-13 13:45:51 +00:00
amount_unit = product . price_get ( ' standard_price ' , context = context ) [ product . id ]
2011-01-07 13:02:46 +00:00
new_std_price = ( ( amount_unit * product_avail [ product . id ] ) \
+ ( new_price * qty ) ) / ( product_avail [ product . id ] + qty )
2010-03-25 12:08:31 +00:00
# Write the field according to price type field
2010-09-28 15:28:30 +00:00
product_obj . write ( cr , uid , [ product . id ] , { ' standard_price ' : new_std_price } )
2010-09-15 18:01:00 +00:00
# Record the values that were chosen in the wizard, so they can be
# used for inventory valuation if real-time valuation is enabled.
2010-10-26 13:56:22 +00:00
move_obj . write ( cr , uid , [ move . id ] ,
2010-09-15 18:01:00 +00:00
{ ' price_unit ' : product_price ,
' price_currency_id ' : product_currency } )
2010-03-25 12:08:31 +00:00
2013-11-04 17:12:45 +00:00
product_avail [ product . id ] + = qty
2014-09-24 12:34:14 +00:00
# every line of the picking is empty, do not generate anything
empty_picking = not any ( q for q in move_product_qty . values ( ) if q > 0 )
2010-03-29 11:37:53 +00:00
2010-03-25 12:08:31 +00:00
for move in too_few :
2010-03-26 09:25:02 +00:00
product_qty = move_product_qty [ move . id ]
2014-09-24 12:34:14 +00:00
if not new_picking and not empty_picking :
2012-02-06 16:57:39 +00:00
new_picking_name = pick . name
self . write ( cr , uid , [ pick . id ] ,
{ ' name ' : sequence_obj . get ( cr , uid ,
' stock.picking. %s ' % ( pick . type ) ) ,
} )
2014-07-25 09:54:36 +00:00
pick . refresh ( )
2010-03-25 12:08:31 +00:00
new_picking = self . copy ( cr , uid , pick . id ,
{
2012-02-06 16:57:39 +00:00
' name ' : new_picking_name ,
2010-03-25 12:08:31 +00:00
' move_lines ' : [ ] ,
' state ' : ' draft ' ,
} )
if product_qty != 0 :
2010-09-28 15:28:30 +00:00
defaults = {
2010-03-25 12:08:31 +00:00
' product_qty ' : product_qty ,
2014-10-21 19:12:58 +00:00
' product_uos_qty ' : uos_qty [ move . id ] ,
2010-03-29 11:37:53 +00:00
' picking_id ' : new_picking ,
2010-03-25 12:08:31 +00:00
' state ' : ' assigned ' ,
2010-03-29 11:37:53 +00:00
' move_dest_id ' : False ,
2010-03-25 12:08:31 +00:00
' price_unit ' : move . price_unit ,
2011-11-10 06:47:23 +00:00
' product_uom ' : product_uoms [ move . id ]
2010-09-28 15:28:30 +00:00
}
prodlot_id = prodlot_ids [ move . id ]
if prodlot_id :
defaults . update ( prodlot_id = prodlot_id )
2010-10-13 06:40:57 +00:00
move_obj . copy ( cr , uid , move . id , defaults )
2010-03-25 12:08:31 +00:00
move_obj . write ( cr , uid , [ move . id ] ,
{
2012-02-07 10:58:08 +00:00
' product_qty ' : move . product_qty - partial_qty [ move . id ] ,
2014-10-21 19:12:58 +00:00
' product_uos_qty ' : move . product_uos_qty - uos_qty [ move . id ] ,
2012-02-07 10:58:08 +00:00
' prodlot_id ' : False ,
' tracking_id ' : False ,
2010-03-25 12:08:31 +00:00
} )
if new_picking :
move_obj . write ( cr , uid , [ c . id for c in complete ] , { ' picking_id ' : new_picking } )
2011-02-08 10:17:54 +00:00
for move in complete :
2011-11-10 06:47:23 +00:00
defaults = { ' product_uom ' : product_uoms [ move . id ] , ' product_qty ' : move_product_qty [ move . id ] }
2011-02-08 10:17:54 +00:00
if prodlot_ids . get ( move . id ) :
2011-04-11 06:50:10 +00:00
defaults . update ( { ' prodlot_id ' : prodlot_ids [ move . id ] } )
2011-11-10 06:16:39 +00:00
move_obj . write ( cr , uid , [ move . id ] , defaults )
2010-09-28 15:28:30 +00:00
for move in too_many :
product_qty = move_product_qty [ move . id ]
defaults = {
' product_qty ' : product_qty ,
2014-10-21 19:12:58 +00:00
' product_uos_qty ' : uos_qty [ move . id ] ,
2011-11-16 12:32:51 +00:00
' product_uom ' : product_uoms [ move . id ]
2010-09-28 15:28:30 +00:00
}
prodlot_id = prodlot_ids . get ( move . id )
if prodlot_ids . get ( move . id ) :
defaults . update ( prodlot_id = prodlot_id )
if new_picking :
defaults . update ( picking_id = new_picking )
move_obj . write ( cr , uid , [ move . id ] , defaults )
2010-03-29 11:37:53 +00:00
# At first we confirm the new picking (if necessary)
2010-03-25 12:08:31 +00:00
if new_picking :
2013-01-28 15:29:29 +00:00
self . signal_button_confirm ( cr , uid , [ new_picking ] )
2010-09-28 15:28:30 +00:00
# Then we finish the good picking
2010-03-25 12:08:31 +00:00
self . write ( cr , uid , [ pick . id ] , { ' backorder_id ' : new_picking } )
2012-11-06 11:28:56 +00:00
self . action_move ( cr , uid , [ new_picking ] , context = context )
2013-01-28 15:29:29 +00:00
self . signal_button_done ( cr , uid , [ new_picking ] )
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , pick . id , cr )
2014-07-25 09:54:36 +00:00
delivered_pack_id = new_picking
self . message_post ( cr , uid , new_picking , body = _ ( " Back order <em> %s </em> has been <b>created</b>. " ) % ( pick . name ) , context = context )
2014-09-24 12:34:14 +00:00
elif empty_picking :
delivered_pack_id = pick . id
2010-03-25 12:08:31 +00:00
else :
2012-11-06 11:28:56 +00:00
self . action_move ( cr , uid , [ pick . id ] , context = context )
2013-01-28 15:29:29 +00:00
self . signal_button_done ( cr , uid , [ pick . id ] )
2010-03-26 09:25:02 +00:00
delivered_pack_id = pick . id
2010-03-29 11:37:53 +00:00
delivered_pack = self . browse ( cr , uid , delivered_pack_id , context = context )
2010-03-26 09:25:02 +00:00
res [ pick . id ] = { ' delivered_picking ' : delivered_pack . id or False }
2012-08-07 11:06:16 +00:00
2010-03-25 12:08:31 +00:00
return res
2012-04-06 13:04:08 +00:00
# views associated to each picking type
_VIEW_LIST = {
' out ' : ' view_picking_out_form ' ,
' in ' : ' view_picking_in_form ' ,
' internal ' : ' view_picking_form ' ,
}
def _get_view_id ( self , cr , uid , type ) :
""" Get the view id suiting the given type
@param type : the picking type as a string
@return : view i , or False if no view found
"""
res = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid ,
' stock ' , self . _VIEW_LIST . get ( type , ' view_picking_form ' ) )
return res and res [ 1 ] or False
2010-03-25 12:08:31 +00:00
2006-12-07 13:41:40 +00:00
class stock_production_lot ( osv . osv ) :
2010-07-06 11:44:51 +00:00
2010-07-06 11:21:27 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2008-07-22 15:11:28 +00:00
if not ids :
return [ ]
2010-02-23 05:34:00 +00:00
reads = self . read ( cr , uid , ids , [ ' name ' , ' prefix ' , ' ref ' ] , context )
2009-08-28 09:40:49 +00:00
res = [ ]
2008-07-22 15:11:28 +00:00
for record in reads :
2009-08-28 09:40:49 +00:00
name = record [ ' name ' ]
2010-02-23 05:34:00 +00:00
prefix = record [ ' prefix ' ]
if prefix :
name = prefix + ' / ' + name
2008-07-22 15:11:28 +00:00
if record [ ' ref ' ] :
2010-02-23 05:34:00 +00:00
name = ' %s [ %s ] ' % ( name , record [ ' ref ' ] )
2008-07-22 15:11:28 +00:00
res . append ( ( record [ ' id ' ] , name ) )
return res
2012-03-30 07:49:01 +00:00
2011-12-16 06:10:09 +00:00
def name_search ( self , cr , uid , name , args = None , operator = ' ilike ' , context = None , limit = 100 ) :
args = args or [ ]
ids = [ ]
if name :
2011-12-21 22:27:17 +00:00
ids = self . search ( cr , uid , [ ( ' prefix ' , ' = ' , name ) ] + args , limit = limit , context = context )
2011-12-16 06:10:09 +00:00
if not ids :
2011-12-21 12:43:41 +00:00
ids = self . search ( cr , uid , [ ( ' name ' , operator , name ) ] + args , limit = limit , context = context )
2011-12-16 06:10:09 +00:00
else :
2011-12-21 12:43:41 +00:00
ids = self . search ( cr , uid , args , limit = limit , context = context )
return self . name_get ( cr , uid , ids , context )
2008-11-03 14:40:49 +00:00
2008-07-22 15:11:28 +00:00
_name = ' stock.production.lot '
2012-04-24 13:08:05 +00:00
_description = ' Serial Number '
2008-07-22 15:11:28 +00:00
2010-07-06 11:21:27 +00:00
def _get_stock ( self , cr , uid , ids , field_name , arg , context = None ) :
2010-05-26 12:59:30 +00:00
""" Gets stock of products for locations
@return : Dictionary of values
"""
2010-11-23 07:05:05 +00:00
if context is None :
2010-11-22 10:37:53 +00:00
context = { }
2008-09-16 10:37:53 +00:00
if ' location_id ' not in context :
2009-08-28 09:40:49 +00:00
locations = self . pool . get ( ' stock.location ' ) . search ( cr , uid , [ ( ' usage ' , ' = ' , ' internal ' ) ] , context = context )
2008-09-16 10:37:53 +00:00
else :
2009-06-03 09:26:56 +00:00
locations = context [ ' location_id ' ] and [ context [ ' location_id ' ] ] or [ ]
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2009-07-31 11:30:07 +00:00
2008-09-16 10:37:53 +00:00
res = { } . fromkeys ( ids , 0.0 )
2009-06-03 09:26:56 +00:00
if locations :
cr . execute ( ''' select
prodlot_id ,
2010-10-13 06:40:57 +00:00
sum ( qty )
2009-06-03 09:26:56 +00:00
from
stock_report_prodlots
where
2010-06-10 13:34:19 +00:00
location_id IN % s and prodlot_id IN % s group by prodlot_id ''' ,(tuple(locations),tuple(ids),))
2009-06-03 09:26:56 +00:00
res . update ( dict ( cr . fetchall ( ) ) )
2010-07-06 11:44:51 +00:00
2008-09-16 10:37:53 +00:00
return res
2009-05-27 13:42:15 +00:00
2010-07-06 11:21:27 +00:00
def _stock_search ( self , cr , uid , obj , name , args , context = None ) :
2010-05-26 12:59:30 +00:00
""" Searches Ids of products
@return : Ids of locations
"""
2009-08-28 09:40:49 +00:00
locations = self . pool . get ( ' stock.location ' ) . search ( cr , uid , [ ( ' usage ' , ' = ' , ' internal ' ) ] )
2009-03-17 08:18:08 +00:00
cr . execute ( ''' select
prodlot_id ,
2010-10-13 06:40:57 +00:00
sum ( qty )
2009-03-17 08:18:08 +00:00
from
stock_report_prodlots
where
2010-06-10 13:34:19 +00:00
location_id IN % s group by prodlot_id
2010-10-13 06:40:57 +00:00
having sum ( qty ) ''' + str(args[0][1]) + str(args[0][2]),(tuple(locations),))
2009-03-17 08:18:08 +00:00
res = cr . fetchall ( )
2009-08-28 09:40:49 +00:00
ids = [ ( ' id ' , ' in ' , map ( lambda x : x [ 0 ] , res ) ) ]
2009-03-17 08:18:08 +00:00
return ids
2009-05-27 13:42:15 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2012-04-24 13:08:05 +00:00
' name ' : fields . char ( ' Serial Number ' , size = 64 , required = True , help = " Unique Serial Number, will be displayed as: PREFIX/SERIAL [INT_REF] " ) ,
2010-09-06 13:51:23 +00:00
' ref ' : fields . char ( ' Internal Reference ' , size = 256 , help = " Internal reference number in case it differs from the manufacturer ' s serial number " ) ,
2010-09-06 14:33:47 +00:00
' prefix ' : fields . char ( ' Prefix ' , size = 64 , help = " Optional prefix to prepend when displaying this serial number: PREFIX/SERIAL [INT_REF] " ) ,
2010-11-10 12:13:19 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True , domain = [ ( ' type ' , ' <> ' , ' service ' ) ] ) ,
2010-09-06 13:51:23 +00:00
' date ' : fields . datetime ( ' Creation Date ' , required = True ) ,
2011-07-01 23:41:24 +00:00
' stock_available ' : fields . function ( _get_stock , fnct_search = _stock_search , type = " float " , string = " Available " , select = True ,
2012-09-19 12:08:18 +00:00
help = " Current quantity of products with this Serial Number available in company warehouses " ,
2012-04-25 12:09:08 +00:00
digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ) ,
2009-08-28 09:40:49 +00:00
' revisions ' : fields . one2many ( ' stock.production.lot.revision ' , ' lot_id ' , ' Revisions ' ) ,
2010-09-06 13:51:23 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , select = True ) ,
2012-05-09 13:24:01 +00:00
' move_ids ' : fields . one2many ( ' stock.move ' , ' prodlot_id ' , ' Moves for this serial number ' , readonly = True ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2011-02-23 06:15:04 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2009-08-28 09:40:49 +00:00
' name ' : lambda x , y , z , c : x . pool . get ( ' ir.sequence ' ) . get ( y , z , ' stock.lot.serial ' ) ,
' product_id ' : lambda x , y , z , c : c . get ( ' product_id ' , False ) ,
2008-07-22 15:11:28 +00:00
}
_sql_constraints = [
2014-07-23 08:03:40 +00:00
( ' name_ref_uniq ' , ' unique (name, ref, product_id, company_id) ' , ' The combination of Serial Number, internal reference, Product and Company must be unique ! ' ) ,
2008-07-22 15:11:28 +00:00
]
2010-11-22 10:37:53 +00:00
def action_traceability ( self , cr , uid , ids , context = None ) :
2010-09-06 09:51:52 +00:00
""" It traces the information of a product
@param self : The object pointer .
@param cr : A database cursor
@param uid : ID of the user currently logged in
2010-10-26 13:56:22 +00:00
@param ids : List of IDs selected
@param context : A standard dictionary
2010-09-06 09:51:52 +00:00
@return : A dictionary of values
"""
2010-09-06 10:47:40 +00:00
value = self . pool . get ( ' action.traceability ' ) . action_traceability ( cr , uid , ids , context )
2010-09-06 09:51:52 +00:00
return value
2012-11-27 10:20:02 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2012-12-15 17:47:43 +00:00
context = context or { }
default = default and default . copy ( ) or { }
default . update ( date = time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) , move_ids = [ ] )
2012-11-27 10:20:02 +00:00
return super ( stock_production_lot , self ) . copy ( cr , uid , id , default = default , context = context )
2006-12-07 13:41:40 +00:00
class stock_production_lot_revision ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = ' stock.production.lot.revision '
2012-05-09 13:24:01 +00:00
_description = ' Serial Number Revision '
2010-06-16 08:40:57 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2009-01-27 11:15:46 +00:00
' name ' : fields . char ( ' Revision Name ' , size = 64 , required = True ) ,
2008-07-22 15:11:28 +00:00
' description ' : fields . text ( ' Description ' ) ,
2009-01-27 11:15:46 +00:00
' date ' : fields . date ( ' Revision Date ' ) ,
2010-09-06 13:51:23 +00:00
' indice ' : fields . char ( ' Revision Number ' , size = 16 ) ,
2008-07-22 15:11:28 +00:00
' author_id ' : fields . many2one ( ' res.users ' , ' Author ' ) ,
2012-04-24 13:08:05 +00:00
' lot_id ' : fields . many2one ( ' stock.production.lot ' , ' Serial Number ' , select = True , ondelete = ' cascade ' ) ,
2011-01-06 11:32:21 +00:00
' company_id ' : fields . related ( ' lot_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2009-08-28 09:40:49 +00:00
' author_id ' : lambda x , y , z , c : z ,
2012-02-13 18:07:41 +00:00
' date ' : fields . date . context_today ,
2008-07-22 15:11:28 +00:00
}
2009-08-28 09:40:49 +00:00
2006-12-07 13:41:40 +00:00
2010-04-09 08:22:51 +00:00
# ----------------------------------------------------
# Move
# ----------------------------------------------------
2006-12-07 13:41:40 +00:00
#
# Fields:
2008-07-22 15:11:28 +00:00
# location_dest_id is only used for predicting futur stocks
2006-12-07 13:41:40 +00:00
#
class stock_move ( osv . osv ) :
2010-06-15 19:19:08 +00:00
2010-07-06 11:21:27 +00:00
def _getSSCC ( self , cr , uid , context = None ) :
2008-12-10 14:29:55 +00:00
cr . execute ( ' select id from stock_tracking where create_uid= %s order by id desc limit 1 ' , ( uid , ) )
2008-07-22 15:11:28 +00:00
res = cr . fetchone ( )
return ( res and res [ 0 ] ) or False
2012-08-03 07:56:33 +00:00
2008-07-22 15:11:28 +00:00
_name = " stock.move "
_description = " Stock Move "
2012-12-06 10:14:45 +00:00
_order = ' date_expected desc, id '
2010-05-19 20:02:36 +00:00
_log_create = False
2011-01-20 10:04:53 +00:00
2011-01-14 15:01:33 +00:00
def action_partial_move ( self , cr , uid , ids , context = None ) :
if context is None : context = { }
2011-09-26 00:36:12 +00:00
if context . get ( ' active_model ' ) != self . _name :
context . update ( active_ids = ids , active_model = self . _name )
2011-01-14 15:01:33 +00:00
partial_id = self . pool . get ( " stock.partial.move " ) . create (
cr , uid , { } , context = context )
return {
' name ' : _ ( " Products to Process " ) ,
' view_mode ' : ' form ' ,
' view_id ' : False ,
' view_type ' : ' form ' ,
' res_model ' : ' stock.partial.move ' ,
' res_id ' : partial_id ,
' type ' : ' ir.actions.act_window ' ,
' nodestroy ' : True ,
' target ' : ' new ' ,
' domain ' : ' [] ' ,
' context ' : context
}
2011-01-20 10:04:53 +00:00
2009-08-28 09:40:49 +00:00
2010-07-06 11:21:27 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2008-10-10 08:59:17 +00:00
res = [ ]
2010-11-22 10:37:53 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
2012-09-11 16:14:35 +00:00
name = line . location_id . name + ' > ' + line . location_dest_id . name
# optional prefixes
if line . product_id . code :
name = line . product_id . code + ' : ' + name
if line . picking_id . origin :
name = line . picking_id . origin + ' / ' + name
res . append ( ( line . id , name ) )
2008-10-10 08:59:17 +00:00
return res
2010-12-13 06:43:09 +00:00
def _check_tracking ( self , cr , uid , ids , context = None ) :
2012-05-09 13:24:01 +00:00
""" Checks if serial number is assigned to stock move or not.
2010-05-26 12:59:30 +00:00
@return : True or False
"""
2010-12-13 06:43:09 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2009-08-28 09:40:49 +00:00
if not move . prodlot_id and \
( move . state == ' done ' and \
( \
2010-09-03 17:52:12 +00:00
( move . product_id . track_production and move . location_id . usage == ' production ' ) or \
( move . product_id . track_production and move . location_dest_id . usage == ' production ' ) or \
( move . product_id . track_incoming and move . location_id . usage == ' supplier ' ) or \
2012-07-30 07:18:23 +00:00
( move . product_id . track_outgoing and move . location_dest_id . usage == ' customer ' ) or \
( move . product_id . track_incoming and move . location_id . usage == ' inventory ' ) \
2009-08-28 09:40:49 +00:00
) ) :
return False
return True
2008-09-20 13:56:44 +00:00
2010-12-13 06:43:09 +00:00
def _check_product_lot ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Checks whether move is done or not and production lot is assigned to that move.
@return : True or False
"""
2010-12-13 06:43:09 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2010-05-24 10:40:14 +00:00
if move . prodlot_id and move . state == ' done ' and ( move . prodlot_id . product_id . id != move . product_id . id ) :
2008-11-03 14:40:49 +00:00
return False
2009-08-28 09:40:49 +00:00
return True
2008-11-03 14:40:49 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2012-12-17 22:28:27 +00:00
' name ' : fields . char ( ' Description ' , required = True , select = True ) ,
2009-08-28 09:40:49 +00:00
' priority ' : fields . selection ( [ ( ' 0 ' , ' Not urgent ' ) , ( ' 1 ' , ' Urgent ' ) ] , ' Priority ' ) ,
2011-01-17 06:22:31 +00:00
' create_date ' : fields . datetime ( ' Creation Date ' , readonly = True , select = True ) ,
2011-05-03 07:25:13 +00:00
' date ' : fields . datetime ( ' Date ' , required = True , select = True , help = " Move date: scheduled date until move is done, then date of actual move processing " , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2011-01-17 06:22:31 +00:00
' date_expected ' : fields . datetime ( ' Scheduled Date ' , states = { ' done ' : [ ( ' readonly ' , True ) ] } , required = True , select = True , help = " Scheduled date for the processing of this move " ) ,
2010-10-11 09:52:55 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True , select = True , domain = [ ( ' type ' , ' <> ' , ' service ' ) ] , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2008-07-22 15:11:28 +00:00
2012-09-29 10:30:13 +00:00
' product_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ,
required = True , states = { ' done ' : [ ( ' readonly ' , True ) ] } ,
help = " This is the quantity of products from an inventory "
" point of view. For moves in the state ' done ' , this is the "
" quantity of products that were actually moved. For other "
" moves, this is the quantity of product that is planned to "
" be moved. Lowering this quantity does not generate a "
" backorder. Changing this quantity on assigned moves affects "
" the product reservation, and should be done with care. "
) ,
2010-10-11 09:52:55 +00:00
' product_uom ' : fields . many2one ( ' product.uom ' , ' Unit of Measure ' , required = True , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2012-04-25 12:09:08 +00:00
' product_uos_qty ' : fields . float ( ' Quantity (UOS) ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2011-07-15 12:00:59 +00:00
' product_uos ' : fields . many2one ( ' product.uom ' , ' Product UOS ' , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2009-09-24 10:46:21 +00:00
' product_packaging ' : fields . many2one ( ' product.packaging ' , ' Packaging ' , help = " It specifies attributes of packaging like type, quantity of packaging,etc. " ) ,
2008-07-22 15:11:28 +00:00
2010-10-11 09:52:55 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Source Location ' , required = True , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] } , help = " Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations. " ) ,
' location_dest_id ' : fields . many2one ( ' stock.location ' , ' Destination Location ' , required = True , states = { ' done ' : [ ( ' readonly ' , True ) ] } , select = True , help = " Location where the system will stock the finished products. " ) ,
2012-03-30 07:49:01 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Destination Address ' , states = { ' done ' : [ ( ' readonly ' , True ) ] } , help = " Optional address where goods are to be delivered, specifically used for allotment " ) ,
2008-07-22 15:11:28 +00:00
2012-05-09 13:24:01 +00:00
' prodlot_id ' : fields . many2one ( ' stock.production.lot ' , ' Serial Number ' , states = { ' done ' : [ ( ' readonly ' , True ) ] } , help = " Serial number is used to put a serial number on the production " , select = True ) ,
2010-12-08 07:17:22 +00:00
' tracking_id ' : fields . many2one ( ' stock.tracking ' , ' Pack ' , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] } , help = " Logistical shipping unit: pallet, box, pack ... " ) ,
2008-07-22 15:11:28 +00:00
' auto_validate ' : fields . boolean ( ' Auto Validate ' ) ,
2010-09-06 13:51:23 +00:00
' move_dest_id ' : fields . many2one ( ' stock.move ' , ' Destination Move ' , help = " Optional: next stock move when chaining them " , select = True ) ,
2010-09-01 08:21:28 +00:00
' move_history_ids ' : fields . many2many ( ' stock.move ' , ' stock_move_history_ids ' , ' parent_id ' , ' child_id ' , ' Move History (child moves) ' ) ,
' move_history_ids2 ' : fields . many2many ( ' stock.move ' , ' stock_move_history_ids ' , ' child_id ' , ' parent_id ' , ' Move History (parent moves) ' ) ,
2010-11-10 05:37:49 +00:00
' picking_id ' : fields . many2one ( ' stock.picking ' , ' Reference ' , select = True , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2008-07-22 15:11:28 +00:00
' note ' : fields . text ( ' Notes ' ) ,
2012-05-07 15:43:56 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' New ' ) ,
2012-05-17 10:15:17 +00:00
( ' cancel ' , ' Cancelled ' ) ,
2012-05-07 15:43:56 +00:00
( ' waiting ' , ' Waiting Another Move ' ) ,
( ' confirmed ' , ' Waiting Availability ' ) ,
( ' assigned ' , ' Available ' ) ,
( ' done ' , ' Done ' ) ,
2012-05-22 16:10:55 +00:00
] , ' Status ' , readonly = True , select = True ,
2012-05-07 15:43:56 +00:00
help = " * New: When the stock move is created and not yet confirmed. \n " \
" * Waiting Another Move: This state can be seen when a move is waiting for another one, for example in a chained flow. \n " \
" * Waiting Availability: This state is reached when the procurement resolution is not straight forward. It may need the scheduler to run, a component to me manufactured... \n " \
" * Available: When products are reserved, it is set to \' Available \' . \n " \
" * Done: When the shipment is processed, the state is \' Done \' . " ) ,
2013-02-01 13:26:48 +00:00
' price_unit ' : fields . float ( ' Unit Price ' , digits_compute = dp . get_precision ( ' Product Price ' ) , help = " Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used) " ) ,
2010-09-15 18:01:00 +00:00
' price_currency_id ' : fields . many2one ( ' res.currency ' , ' Currency for average price ' , help = " Technical field used to record the currency chosen by the user during a picking confirmation (when average price costing method is used) " ) ,
2010-09-06 13:51:23 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , select = True ) ,
2012-02-24 10:58:49 +00:00
' backorder_id ' : fields . related ( ' picking_id ' , ' backorder_id ' , type = ' many2one ' , relation = " stock.picking " , string = " Back Order of " , select = True ) ,
2012-05-04 12:32:02 +00:00
' origin ' : fields . related ( ' picking_id ' , ' origin ' , type = ' char ' , size = 64 , relation = " stock.picking " , string = " Source " , store = True ) ,
2010-09-06 13:51:23 +00:00
# used for colors in tree views:
2010-12-17 10:24:47 +00:00
' scrapped ' : fields . related ( ' location_dest_id ' , ' scrap_location ' , type = ' boolean ' , relation = ' stock.location ' , string = ' Scrapped ' , readonly = True ) ,
2012-09-18 17:10:20 +00:00
' type ' : fields . related ( ' picking_id ' , ' type ' , type = ' selection ' , selection = [ ( ' out ' , ' Sending Goods ' ) , ( ' in ' , ' Getting Goods ' ) , ( ' internal ' , ' Internal ' ) ] , string = ' Shipping Type ' ) ,
2008-07-22 15:11:28 +00:00
}
2012-09-21 15:02:06 +00:00
2011-09-17 12:49:53 +00:00
def _check_location ( self , cr , uid , ids , context = None ) :
2011-09-19 11:28:37 +00:00
for record in self . browse ( cr , uid , ids , context = context ) :
2012-08-07 06:10:11 +00:00
if ( record . state == ' done ' ) and ( record . location_id . usage == ' view ' ) :
2012-09-21 15:02:06 +00:00
raise osv . except_osv ( _ ( ' Error ' ) , _ ( ' You cannot move product %s from a location of type view %s . ' ) % ( record . product_id . name , record . location_id . name ) )
2012-08-07 06:10:11 +00:00
if ( record . state == ' done ' ) and ( record . location_dest_id . usage == ' view ' ) :
2012-09-21 15:02:06 +00:00
raise osv . except_osv ( _ ( ' Error ' ) , _ ( ' You cannot move product %s to a location of type view %s . ' ) % ( record . product_id . name , record . location_dest_id . name ) )
2011-09-17 12:49:53 +00:00
return True
2008-09-20 11:54:38 +00:00
_constraints = [
2008-09-20 13:56:44 +00:00
( _check_tracking ,
2012-07-25 10:14:28 +00:00
' You must assign a serial number for this product. ' ,
2008-09-20 13:56:44 +00:00
[ ' prodlot_id ' ] ) ,
2012-07-25 10:14:28 +00:00
( _check_location , ' You cannot move products from or to a location of the type view. ' ,
2011-09-17 12:49:53 +00:00
[ ' location_id ' , ' location_dest_id ' ] ) ,
2008-09-20 13:56:44 +00:00
( _check_product_lot ,
2012-07-25 10:14:28 +00:00
' You try to assign a lot which is not from the same product. ' ,
2008-09-20 11:54:38 +00:00
[ ' prodlot_id ' ] ) ]
2009-08-28 09:40:49 +00:00
2010-07-06 11:21:27 +00:00
def _default_location_destination ( self , cr , uid , context = None ) :
2010-05-26 12:59:30 +00:00
""" Gets default address of partner for destination location
@return : Address id or False
"""
2011-11-11 06:16:52 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
picking_type = context . get ( ' picking_type ' )
location_id = False
2010-11-23 07:05:05 +00:00
if context is None :
2010-11-22 10:37:53 +00:00
context = { }
2008-09-07 23:24:39 +00:00
if context . get ( ' move_line ' , [ ] ) :
2009-07-22 06:44:55 +00:00
if context [ ' move_line ' ] [ 0 ] :
2009-08-28 09:40:49 +00:00
if isinstance ( context [ ' move_line ' ] [ 0 ] , ( tuple , list ) ) :
2011-11-11 06:16:52 +00:00
location_id = context [ ' move_line ' ] [ 0 ] [ 2 ] and context [ ' move_line ' ] [ 0 ] [ 2 ] . get ( ' location_dest_id ' , False )
2009-07-22 06:44:55 +00:00
else :
2009-08-28 09:40:49 +00:00
move_list = self . pool . get ( ' stock.move ' ) . read ( cr , uid , context [ ' move_line ' ] [ 0 ] , [ ' location_dest_id ' ] )
2011-11-11 06:16:52 +00:00
location_id = move_list and move_list [ ' location_dest_id ' ] [ 0 ] or False
elif context . get ( ' address_out_id ' , False ) :
2012-03-06 12:53:56 +00:00
property_out = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , context [ ' address_out_id ' ] , context ) . property_stock_customer
2011-11-11 06:16:52 +00:00
location_id = property_out and property_out . id or False
else :
location_xml_id = False
2012-09-27 12:15:03 +00:00
if picking_type in ( ' in ' , ' internal ' ) :
2011-11-11 06:16:52 +00:00
location_xml_id = ' stock_location_stock '
elif picking_type == ' out ' :
location_xml_id = ' stock_location_customers '
if location_xml_id :
2013-07-29 10:29:23 +00:00
try :
2013-07-31 09:27:32 +00:00
location_model , location_id = mod_obj . get_object_reference ( cr , uid , ' stock ' , location_xml_id )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ location_id ] , ' read ' , context = context )
2013-07-29 10:29:23 +00:00
except ( orm . except_orm , ValueError ) :
location_id = False
2011-11-11 06:16:52 +00:00
return location_id
2008-09-07 23:24:39 +00:00
2010-07-06 11:21:27 +00:00
def _default_location_source ( self , cr , uid , context = None ) :
2010-05-26 12:59:30 +00:00
""" Gets default address of partner for source location
@return : Address id or False
"""
2011-11-11 06:16:52 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
picking_type = context . get ( ' picking_type ' )
location_id = False
2010-11-23 07:05:05 +00:00
if context is None :
2010-11-22 10:37:53 +00:00
context = { }
2008-09-07 23:24:39 +00:00
if context . get ( ' move_line ' , [ ] ) :
2008-09-09 12:46:37 +00:00
try :
2011-11-11 06:16:52 +00:00
location_id = context [ ' move_line ' ] [ 0 ] [ 2 ] [ ' location_id ' ]
2008-09-09 12:46:37 +00:00
except :
pass
2011-11-11 06:16:52 +00:00
elif context . get ( ' address_in_id ' , False ) :
2012-03-06 09:06:45 +00:00
part_obj_add = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , context [ ' address_in_id ' ] , context = context )
if part_obj_add :
location_id = part_obj_add . property_stock_supplier . id
2011-11-11 06:16:52 +00:00
else :
location_xml_id = False
if picking_type == ' in ' :
location_xml_id = ' stock_location_suppliers '
2012-09-27 12:15:03 +00:00
elif picking_type in ( ' out ' , ' internal ' ) :
2012-09-27 10:14:36 +00:00
location_xml_id = ' stock_location_stock '
2011-11-11 06:16:52 +00:00
if location_xml_id :
2013-07-31 09:27:32 +00:00
try :
location_model , location_id = mod_obj . get_object_reference ( cr , uid , ' stock ' , location_xml_id )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ location_id ] , ' read ' , context = context )
2013-07-31 09:27:32 +00:00
except ( orm . except_orm , ValueError ) :
location_id = False
2011-11-11 06:16:52 +00:00
return location_id
2008-09-07 23:24:39 +00:00
2012-06-29 12:23:47 +00:00
def _default_destination_address ( self , cr , uid , context = None ) :
2012-07-25 13:51:27 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
return user . company_id . partner_id . id
2008-09-07 23:24:39 +00:00
2012-08-30 06:52:55 +00:00
def _default_move_type ( self , cr , uid , context = None ) :
2012-09-21 15:02:06 +00:00
""" Gets default type of move
2012-08-30 06:52:55 +00:00
@return : type
"""
if context is None :
context = { }
2012-08-31 06:26:37 +00:00
picking_type = context . get ( ' picking_type ' )
type = ' internal '
2012-08-30 06:52:55 +00:00
if picking_type == ' in ' :
type = ' in '
elif picking_type == ' out ' :
type = ' out '
return type
2008-07-22 15:11:28 +00:00
_defaults = {
2008-09-07 23:24:39 +00:00
' location_id ' : _default_location_source ,
' location_dest_id ' : _default_location_destination ,
2012-06-29 12:23:47 +00:00
' partner_id ' : _default_destination_address ,
2012-08-30 06:52:55 +00:00
' type ' : _default_move_type ,
2010-07-06 11:21:27 +00:00
' state ' : ' draft ' ,
' priority ' : ' 1 ' ,
' product_qty ' : 1.0 ,
2010-08-31 06:54:27 +00:00
' scrapped ' : False ,
2011-02-23 06:15:04 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2010-05-18 12:22:13 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.move ' , context = c ) ,
2011-02-23 06:15:04 +00:00
' date_expected ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2008-07-22 15:11:28 +00:00
}
2010-02-01 12:50:00 +00:00
2010-08-31 06:39:28 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2011-03-29 12:27:12 +00:00
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2010-09-01 08:21:28 +00:00
if uid != 1 :
2010-09-28 12:29:49 +00:00
frozen_fields = set ( [ ' product_qty ' , ' product_uom ' , ' product_uos_qty ' , ' product_uos ' , ' location_id ' , ' location_dest_id ' , ' product_id ' ] )
2010-11-22 10:37:53 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2010-09-01 08:21:28 +00:00
if move . state == ' done ' :
if frozen_fields . intersection ( vals ) :
2013-06-07 11:38:29 +00:00
raise osv . except_osv ( _ ( ' Operation Forbidden! ' ) ,
2012-11-19 12:08:10 +00:00
_ ( ' Quantities, Units of Measure, Products and Locations cannot be modified on stock moves that have already been processed (except by the Administrator). ' ) )
2010-08-31 06:39:28 +00:00
return super ( stock_move , self ) . write ( cr , uid , ids , vals , context = context )
2010-09-01 08:21:28 +00:00
2014-02-18 12:32:11 +00:00
def copy_data ( self , cr , uid , id , default = None , context = None ) :
2010-01-07 15:39:43 +00:00
if default is None :
default = { }
default = default . copy ( )
2014-02-24 09:03:54 +00:00
default . setdefault ( ' tracking_id ' , False )
default . setdefault ( ' prodlot_id ' , False )
default . setdefault ( ' move_history_ids ' , [ ] )
default . setdefault ( ' move_history_ids2 ' , [ ] )
2014-02-18 12:32:11 +00:00
return super ( stock_move , self ) . copy_data ( cr , uid , id , default , context = context )
2010-02-01 12:50:00 +00:00
2010-07-06 11:21:27 +00:00
def _auto_init ( self , cursor , context = None ) :
2010-07-06 12:11:43 +00:00
res = super ( stock_move , self ) . _auto_init ( cursor , context = context )
2008-07-22 15:11:28 +00:00
cursor . execute ( ' SELECT indexname \
FROM pg_indexes \
WHERE indexname = \' stock_move_location_id_location_dest_id_product_id_state \' ' )
if not cursor . fetchone ( ) :
cursor . execute ( ' CREATE INDEX stock_move_location_id_location_dest_id_product_id_state \
2012-02-14 18:43:31 +00:00
ON stock_move ( product_id , state , location_id , location_dest_id ) ' )
2009-07-31 11:30:07 +00:00
return res
2008-07-22 15:11:28 +00:00
2010-06-16 08:40:57 +00:00
def onchange_lot_id ( self , cr , uid , ids , prodlot_id = False , product_qty = False ,
2010-12-15 10:29:07 +00:00
loc_id = False , product_id = False , uom_id = False , context = None ) :
2010-05-26 12:59:30 +00:00
""" On change of production lot gives a warning message.
@param prodlot_id : Changed production lot id
@param product_qty : Quantity of product
@param loc_id : Location id
@param product_id : Product id
@return : Warning message
"""
2008-09-20 13:56:44 +00:00
if not prodlot_id or not loc_id :
return { }
2009-06-09 15:37:28 +00:00
ctx = context and context . copy ( ) or { }
2009-07-31 11:30:07 +00:00
ctx [ ' location_id ' ] = loc_id
2010-12-15 10:29:07 +00:00
ctx . update ( { ' raise-exception ' : True } )
uom_obj = self . pool . get ( ' product.uom ' )
product_obj = self . pool . get ( ' product.product ' )
2010-12-20 13:00:56 +00:00
product_uom = product_obj . browse ( cr , uid , product_id , context = ctx ) . uom_id
prodlot = self . pool . get ( ' stock.production.lot ' ) . browse ( cr , uid , prodlot_id , context = ctx )
location = self . pool . get ( ' stock.location ' ) . browse ( cr , uid , loc_id , context = ctx )
uom = uom_obj . browse ( cr , uid , uom_id , context = ctx )
amount_actual = uom_obj . _compute_qty_obj ( cr , uid , product_uom , prodlot . stock_available , uom , context = ctx )
2009-08-28 09:40:49 +00:00
warning = { }
2010-12-20 13:00:56 +00:00
if ( location . usage == ' internal ' ) and ( product_qty > ( amount_actual or 0.0 ) ) :
2009-08-28 09:40:49 +00:00
warning = {
2012-09-21 10:08:50 +00:00
' title ' : _ ( ' Insufficient Stock for Serial Number ! ' ) ,
2012-09-25 06:26:57 +00:00
' message ' : _ ( ' You are moving %.2f %s but only %.2f %s available for this serial number. ' ) % ( product_qty , uom . name , amount_actual , uom . name )
2008-09-20 19:06:25 +00:00
}
2009-08-28 09:40:49 +00:00
return { ' warning ' : warning }
2009-12-08 12:58:25 +00:00
2010-06-16 08:40:57 +00:00
def onchange_quantity ( self , cr , uid , ids , product_id , product_qty ,
2010-05-26 12:59:30 +00:00
product_uom , product_uos ) :
""" On change of product quantity finds UoM and UoS quantities
@param product_id : Product id
@param product_qty : Changed Quantity of product
@param product_uom : Unit of measure of product
2010-06-16 08:40:57 +00:00
@param product_uos : Unit of sale of product
2010-05-26 12:59:30 +00:00
@return : Dictionary of values
"""
2009-11-20 11:20:04 +00:00
result = {
' product_uos_qty ' : 0.00
}
2012-02-29 11:08:20 +00:00
warning = { }
2009-12-08 12:58:25 +00:00
2009-11-20 11:20:04 +00:00
if ( not product_id ) or ( product_qty < = 0.0 ) :
2012-02-28 15:05:27 +00:00
result [ ' product_qty ' ] = 0.0
2009-11-20 11:20:04 +00:00
return { ' value ' : result }
2009-12-08 12:58:25 +00:00
2009-11-20 11:20:04 +00:00
product_obj = self . pool . get ( ' product.product ' )
uos_coeff = product_obj . read ( cr , uid , product_id , [ ' uos_coeff ' ] )
2012-02-29 11:08:20 +00:00
# Warn if the quantity was decreased
2012-09-29 10:30:13 +00:00
if ids :
for move in self . read ( cr , uid , ids , [ ' product_qty ' ] ) :
if product_qty < move [ ' product_qty ' ] :
warning . update ( {
' title ' : _ ( ' Information ' ) ,
' message ' : _ ( " By changing this quantity here, you accept the "
2012-02-29 11:08:20 +00:00
" new quantity as complete: OpenERP will not "
2012-09-29 10:30:13 +00:00
" automatically generate a back order. " ) } )
2012-02-29 11:08:20 +00:00
break
2009-12-08 12:58:25 +00:00
2009-11-20 11:20:04 +00:00
if product_uos and product_uom and ( product_uom != product_uos ) :
result [ ' product_uos_qty ' ] = product_qty * uos_coeff [ ' uos_coeff ' ]
else :
result [ ' product_uos_qty ' ] = product_qty
2009-12-08 12:58:25 +00:00
2012-02-29 11:08:20 +00:00
return { ' value ' : result , ' warning ' : warning }
2009-12-08 12:58:25 +00:00
2010-11-25 07:00:10 +00:00
def onchange_uos_quantity ( self , cr , uid , ids , product_id , product_uos_qty ,
product_uos , product_uom ) :
""" On change of product quantity finds UoM and UoS quantities
@param product_id : Product id
@param product_uos_qty : Changed UoS Quantity of product
@param product_uom : Unit of measure of product
@param product_uos : Unit of sale of product
@return : Dictionary of values
"""
result = {
' product_qty ' : 0.00
}
if ( not product_id ) or ( product_uos_qty < = 0.0 ) :
2012-02-28 15:05:27 +00:00
result [ ' product_uos_qty ' ] = 0.0
2010-11-25 07:00:10 +00:00
return { ' value ' : result }
product_obj = self . pool . get ( ' product.product ' )
uos_coeff = product_obj . read ( cr , uid , product_id , [ ' uos_coeff ' ] )
2014-05-31 17:00:34 +00:00
# No warning if the quantity was decreased to avoid double warnings:
# The clients should call onchange_quantity too anyway
2010-11-25 07:00:10 +00:00
if product_uos and product_uom and ( product_uom != product_uos ) :
result [ ' product_qty ' ] = product_uos_qty / uos_coeff [ ' uos_coeff ' ]
else :
result [ ' product_qty ' ] = product_uos_qty
2014-05-31 17:00:34 +00:00
return { ' value ' : result }
2010-12-15 10:29:07 +00:00
2010-06-16 08:40:57 +00:00
def onchange_product_id ( self , cr , uid , ids , prod_id = False , loc_id = False ,
2012-03-30 07:49:01 +00:00
loc_dest_id = False , partner_id = False ) :
2010-05-26 12:59:30 +00:00
""" On change of product id, if finds UoM, UoS, quantity and UoS quantity.
@param prod_id : Changed Product id
@param loc_id : Source location id
2011-11-11 06:16:52 +00:00
@param loc_dest_id : Destination location id
2012-03-30 07:49:01 +00:00
@param partner_id : Address id of partner
2010-05-26 12:59:30 +00:00
@return : Dictionary of values
"""
2008-07-22 15:11:28 +00:00
if not prod_id :
return { }
2013-06-26 13:08:20 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid )
lang = user and user . lang or False
2012-03-30 07:49:01 +00:00
if partner_id :
addr_rec = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_id )
2010-02-10 11:48:08 +00:00
if addr_rec :
2012-03-06 09:06:45 +00:00
lang = addr_rec and addr_rec . lang or False
2010-02-10 11:48:08 +00:00
ctx = { ' lang ' : lang }
product = self . pool . get ( ' product.product ' ) . browse ( cr , uid , [ prod_id ] , context = ctx ) [ 0 ]
2009-11-20 11:20:04 +00:00
uos_id = product . uos_id and product . uos_id . id or False
2008-07-22 15:11:28 +00:00
result = {
2014-08-05 15:15:06 +00:00
' name ' : product . partner_ref ,
2008-07-22 15:11:28 +00:00
' product_uom ' : product . uom_id . id ,
2009-11-20 11:20:04 +00:00
' product_uos ' : uos_id ,
' product_qty ' : 1.00 ,
2012-04-07 09:03:58 +00:00
' product_uos_qty ' : self . pool . get ( ' stock.move ' ) . onchange_quantity ( cr , uid , ids , prod_id , 1.00 , product . uom_id . id , uos_id ) [ ' value ' ] [ ' product_uos_qty ' ] ,
' prodlot_id ' : False ,
2008-07-22 15:11:28 +00:00
}
if loc_id :
result [ ' location_id ' ] = loc_id
if loc_dest_id :
result [ ' location_dest_id ' ] = loc_dest_id
2009-08-28 09:40:49 +00:00
return { ' value ' : result }
2008-07-22 15:11:28 +00:00
2012-08-30 06:52:55 +00:00
def onchange_move_type ( self , cr , uid , ids , type , context = None ) :
""" On change of move type gives sorce and destination location.
@param type : Move Type
@return : Dictionary of values
"""
mod_obj = self . pool . get ( ' ir.model.data ' )
2012-09-27 09:55:39 +00:00
location_source_id = ' stock_location_stock '
location_dest_id = ' stock_location_stock '
2012-08-30 06:52:55 +00:00
if type == ' in ' :
location_source_id = ' stock_location_suppliers '
2012-09-21 15:02:06 +00:00
location_dest_id = ' stock_location_stock '
2012-08-30 06:52:55 +00:00
elif type == ' out ' :
2012-09-21 15:02:06 +00:00
location_source_id = ' stock_location_stock '
2012-08-30 06:52:55 +00:00
location_dest_id = ' stock_location_customers '
2013-07-29 10:29:23 +00:00
try :
source_location = mod_obj . get_object_reference ( cr , uid , ' stock ' , location_source_id )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ source_location [ 1 ] ] , ' read ' , context = context )
2013-07-29 10:29:23 +00:00
except ( orm . except_orm , ValueError ) :
source_location = False
try :
dest_location = mod_obj . get_object_reference ( cr , uid , ' stock ' , location_dest_id )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ dest_location [ 1 ] ] , ' read ' , context = context )
2013-07-29 10:29:23 +00:00
except ( orm . except_orm , ValueError ) :
dest_location = False
2012-09-27 09:55:39 +00:00
return { ' value ' : { ' location_id ' : source_location and source_location [ 1 ] or False , ' location_dest_id ' : dest_location and dest_location [ 1 ] or False } }
2012-08-30 06:52:55 +00:00
2011-05-03 07:25:13 +00:00
def onchange_date ( self , cr , uid , ids , date , date_expected , context = None ) :
""" On change of Scheduled Date gives a Move date.
2011-12-01 21:32:12 +00:00
@param date_expected : Scheduled Date
2011-05-03 07:25:13 +00:00
@param date : Move Date
@return : Move Date
"""
if not date_expected :
2011-05-04 05:11:34 +00:00
date_expected = time . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2011-05-03 07:25:13 +00:00
return { ' value ' : { ' date ' : date_expected } }
2010-07-06 11:21:27 +00:00
def _chain_compute ( self , cr , uid , moves , context = None ) :
2010-05-26 12:59:30 +00:00
""" Finds whether the location has chained location type or not.
@param moves : Stock moves
@return : Dictionary containing destination location with chained location type .
"""
2008-09-07 23:24:39 +00:00
result = { }
for m in moves :
2008-09-20 19:06:25 +00:00
dest = self . pool . get ( ' stock.location ' ) . chained_location_get (
2008-11-03 14:40:49 +00:00
cr ,
uid ,
m . location_dest_id ,
2012-03-30 07:49:01 +00:00
m . picking_id and m . picking_id . partner_id and m . picking_id . partner_id ,
2008-11-03 14:40:49 +00:00
m . product_id ,
2008-09-20 19:06:25 +00:00
context
)
2008-09-07 23:24:39 +00:00
if dest :
2009-08-28 09:40:49 +00:00
if dest [ 1 ] == ' transparent ' :
2010-10-11 21:57:06 +00:00
newdate = ( datetime . strptime ( m . date , ' % Y- % m- %d % H: % M: % S ' ) + relativedelta ( days = dest [ 2 ] or 0 ) ) . strftime ( ' % Y- % m- %d ' )
2008-09-07 23:24:39 +00:00
self . write ( cr , uid , [ m . id ] , {
2010-10-11 21:57:06 +00:00
' date ' : newdate ,
2008-09-07 23:24:39 +00:00
' location_dest_id ' : dest [ 0 ] . id } )
2010-10-11 22:20:15 +00:00
if m . picking_id and ( dest [ 3 ] or dest [ 5 ] ) :
self . pool . get ( ' stock.picking ' ) . write ( cr , uid , [ m . picking_id . id ] , {
' stock_journal_id ' : dest [ 3 ] or m . picking_id . stock_journal_id . id ,
' type ' : dest [ 5 ] or m . picking_id . type
} , context = context )
2010-10-11 21:57:06 +00:00
m . location_dest_id = dest [ 0 ]
res2 = self . _chain_compute ( cr , uid , [ m ] , context = context )
for pick_id in res2 . keys ( ) :
result . setdefault ( pick_id , [ ] )
result [ pick_id ] + = res2 [ pick_id ]
2008-09-07 23:24:39 +00:00
else :
result . setdefault ( m . picking_id , [ ] )
result [ m . picking_id ] . append ( ( m , dest ) )
return result
2010-12-16 06:31:49 +00:00
2011-12-15 18:49:54 +00:00
def _prepare_chained_picking ( self , cr , uid , picking_name , picking , picking_type , moves_todo , context = None ) :
""" Prepare the definition (values) to create a new chained picking.
: param str picking_name : desired new picking name
: param browse_record picking : source picking ( being chained to )
: param str picking_type : desired new picking type
: param list moves_todo : specification of the stock moves to be later included in this
picking , in the form : :
[ [ move , ( dest_location , auto_packing , chained_delay , chained_journal ,
chained_company_id , chained_picking_type ) ] ,
. . .
]
See also : meth : ` stock_location . chained_location_get ` .
"""
res_company = self . pool . get ( ' res.company ' )
2011-12-03 11:53:20 +00:00
return {
2011-12-15 18:49:54 +00:00
' name ' : picking_name ,
2011-12-03 11:53:20 +00:00
' origin ' : tools . ustr ( picking . origin or ' ' ) ,
2011-12-15 18:49:54 +00:00
' type ' : picking_type ,
2011-12-03 11:53:20 +00:00
' note ' : picking . note ,
' move_type ' : picking . move_type ,
2011-12-15 18:49:54 +00:00
' auto_picking ' : moves_todo [ 0 ] [ 1 ] [ 1 ] == ' auto ' ,
' stock_journal_id ' : moves_todo [ 0 ] [ 1 ] [ 3 ] ,
' company_id ' : moves_todo [ 0 ] [ 1 ] [ 4 ] or res_company . _company_default_get ( cr , uid , ' stock.company ' , context = context ) ,
2012-03-30 07:49:01 +00:00
' partner_id ' : picking . partner_id . id ,
2011-12-03 11:53:20 +00:00
' invoice_state ' : ' none ' ,
' date ' : picking . date ,
}
2011-12-15 18:49:54 +00:00
def _create_chained_picking ( self , cr , uid , picking_name , picking , picking_type , moves_todo , context = None ) :
2010-09-16 09:07:22 +00:00
picking_obj = self . pool . get ( ' stock.picking ' )
2011-12-15 18:49:54 +00:00
return picking_obj . create ( cr , uid , self . _prepare_chained_picking ( cr , uid , picking_name , picking , picking_type , moves_todo , context = context ) )
2011-12-03 11:53:20 +00:00
2011-01-12 16:44:40 +00:00
def create_chained_picking ( self , cr , uid , moves , context = None ) :
res_obj = self . pool . get ( ' res.company ' )
location_obj = self . pool . get ( ' stock.location ' )
move_obj = self . pool . get ( ' stock.move ' )
new_moves = [ ]
if context is None :
context = { }
seq_obj = self . pool . get ( ' ir.sequence ' )
2014-04-15 16:03:31 +00:00
for picking , chained_moves in self . _chain_compute ( cr , uid , moves , context = context ) . items ( ) :
# We group the moves by automatic move type, so it creates different pickings for different types
moves_by_type = { }
for move in chained_moves :
moves_by_type . setdefault ( move [ 1 ] [ 1 ] , [ ] ) . append ( move )
for todo in moves_by_type . values ( ) :
ptype = todo [ 0 ] [ 1 ] [ 5 ] and todo [ 0 ] [ 1 ] [ 5 ] or location_obj . picking_type_get ( cr , uid , todo [ 0 ] [ 0 ] . location_dest_id , todo [ 0 ] [ 1 ] [ 0 ] )
if picking :
# name of new picking according to its type
if ptype == ' internal ' :
new_pick_name = seq_obj . get ( cr , uid , ' stock.picking ' )
else :
new_pick_name = seq_obj . get ( cr , uid , ' stock.picking. ' + ptype )
pickid = self . _create_chained_picking ( cr , uid , new_pick_name , picking , ptype , todo , context = context )
# Need to check name of old picking because it always considers picking as "OUT" when created from Sales Order
old_ptype = location_obj . picking_type_get ( cr , uid , picking . move_lines [ 0 ] . location_id , picking . move_lines [ 0 ] . location_dest_id )
if old_ptype != picking . type :
old_pick_name = seq_obj . get ( cr , uid , ' stock.picking. ' + old_ptype )
self . pool . get ( ' stock.picking ' ) . write ( cr , uid , [ picking . id ] , { ' name ' : old_pick_name , ' type ' : old_ptype } , context = context )
else :
pickid = False
for move , ( loc , dummy , delay , dummy , company_id , ptype , invoice_state ) in todo :
new_id = move_obj . copy ( cr , uid , move . id , {
' location_id ' : move . location_dest_id . id ,
' location_dest_id ' : loc . id ,
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
' picking_id ' : pickid ,
' state ' : ' waiting ' ,
' company_id ' : company_id or res_obj . _company_default_get ( cr , uid , ' stock.company ' , context = context ) ,
' move_history_ids ' : [ ] ,
' date_expected ' : ( datetime . strptime ( move . date , ' % Y- % m- %d % H: % M: % S ' ) + relativedelta ( days = delay or 0 ) ) . strftime ( ' % Y- % m- %d ' ) ,
' move_history_ids2 ' : [ ] }
)
move_obj . write ( cr , uid , [ move . id ] , {
' move_dest_id ' : new_id ,
' move_history_ids ' : [ ( 4 , new_id ) ]
} )
new_moves . append ( self . browse ( cr , uid , [ new_id ] ) [ 0 ] )
if pickid :
2014-04-17 11:26:55 +00:00
self . pool . get ( ' stock.picking ' ) . signal_button_confirm ( cr , uid , [ pickid ] )
2011-01-12 16:44:40 +00:00
if new_moves :
new_moves + = self . create_chained_picking ( cr , uid , new_moves , context )
return new_moves
2010-07-06 11:21:27 +00:00
def action_confirm ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Confirms stock move.
@return : List of ids .
"""
2010-11-22 10:37:53 +00:00
moves = self . browse ( cr , uid , ids , context = context )
2009-08-28 09:40:49 +00:00
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
2011-01-12 16:44:40 +00:00
self . create_chained_picking ( cr , uid , moves , context )
2010-12-31 14:12:42 +00:00
return [ ]
2008-07-22 15:11:28 +00:00
def action_assign ( self , cr , uid , ids , * args ) :
2010-05-26 12:59:30 +00:00
""" Changes state to confirmed or waiting.
@return : List of values
"""
2008-07-22 15:11:28 +00:00
todo = [ ]
for move in self . browse ( cr , uid , ids ) :
2009-08-28 09:40:49 +00:00
if move . state in ( ' confirmed ' , ' waiting ' ) :
2008-07-22 15:11:28 +00:00
todo . append ( move . id )
res = self . check_assign ( cr , uid , todo )
return res
2010-11-22 10:37:53 +00:00
def force_assign ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Changes the state to assigned.
@return : True
"""
2009-08-28 09:40:49 +00:00
self . write ( cr , uid , ids , { ' state ' : ' assigned ' } )
2012-02-27 09:58:35 +00:00
for move in self . browse ( cr , uid , ids , context ) :
if move . picking_id :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , move . picking_id . id , cr )
2008-07-22 15:11:28 +00:00
return True
2010-11-22 10:37:53 +00:00
def cancel_assign ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Changes the state to confirmed.
@return : True
"""
2008-07-22 15:11:28 +00:00
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
2011-02-02 12:17:37 +00:00
# fix for bug lp:707031
# called write of related picking because changing move availability does
# not trigger workflow of picking in order to change the state of picking
for move in self . browse ( cr , uid , ids , context ) :
if move . picking_id :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , move . picking_id . id , cr )
2008-07-22 15:11:28 +00:00
return True
#
# Duplicate stock.move
#
2010-07-06 11:21:27 +00:00
def check_assign ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Checks the product type and accordingly writes the state.
@return : No . of moves done
"""
2008-07-22 15:11:28 +00:00
done = [ ]
2009-08-28 09:40:49 +00:00
count = 0
2008-07-22 15:11:28 +00:00
pickings = { }
2010-07-06 11:21:27 +00:00
if context is None :
context = { }
2010-06-21 18:40:58 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2010-09-03 16:56:58 +00:00
if move . product_id . type == ' consu ' or move . location_id . usage == ' supplier ' :
2008-07-22 15:11:28 +00:00
if move . state in ( ' confirmed ' , ' waiting ' ) :
done . append ( move . id )
pickings [ move . picking_id . id ] = 1
continue
2009-08-28 09:40:49 +00:00
if move . state in ( ' confirmed ' , ' waiting ' ) :
2010-09-03 10:01:28 +00:00
# Important: we must pass lock=True to _product_reserve() to avoid race conditions and double reservations
res = self . pool . get ( ' stock.location ' ) . _product_reserve ( cr , uid , [ move . location_id . id ] , move . product_id . id , move . product_qty , { ' uom ' : move . product_uom . id } , lock = True )
2008-07-22 15:11:28 +00:00
if res :
2009-09-10 09:12:54 +00:00
#_product_available_test depends on the next status for correct functioning
#the test does not work correctly if the same product occurs multiple times
2009-11-13 05:41:16 +00:00
#in the same order. This is e.g. the case when using the button 'split in two' of
#the stock outgoing form
2010-08-31 09:37:34 +00:00
self . write ( cr , uid , [ move . id ] , { ' state ' : ' assigned ' } )
2008-07-22 15:11:28 +00:00
done . append ( move . id )
pickings [ move . picking_id . id ] = 1
r = res . pop ( 0 )
2014-06-05 16:38:19 +00:00
product_uos_qty = self . pool . get ( ' stock.move ' ) . onchange_quantity ( cr , uid , [ move . id ] , move . product_id . id , r [ 0 ] , move . product_id . uom_id . id , move . product_id . uos_id . id ) [ ' value ' ] [ ' product_uos_qty ' ]
2012-08-23 13:11:16 +00:00
cr . execute ( ' update stock_move set location_id= %s , product_qty= %s , product_uos_qty= %s where id= %s ' , ( r [ 1 ] , r [ 0 ] , product_uos_qty , move . id ) )
2008-07-22 15:11:28 +00:00
while res :
r = res . pop ( 0 )
2014-06-05 16:38:19 +00:00
product_uos_qty = self . pool . get ( ' stock.move ' ) . onchange_quantity ( cr , uid , [ move . id ] , move . product_id . id , r [ 0 ] , move . product_id . uom_id . id , move . product_id . uos_id . id ) [ ' value ' ] [ ' product_uos_qty ' ]
2012-06-21 09:10:01 +00:00
move_id = self . copy ( cr , uid , move . id , { ' product_uos_qty ' : product_uos_qty , ' product_qty ' : r [ 0 ] , ' location_id ' : r [ 1 ] } )
2008-07-22 15:11:28 +00:00
done . append ( move_id )
if done :
count + = len ( done )
2009-08-28 09:40:49 +00:00
self . write ( cr , uid , done , { ' state ' : ' assigned ' } )
2008-07-22 15:11:28 +00:00
if count :
for pick_id in pickings :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , pick_id , cr )
2008-07-22 15:11:28 +00:00
return count
2010-06-15 19:19:08 +00:00
def setlast_tracking ( self , cr , uid , ids , context = None ) :
2014-09-16 13:21:07 +00:00
assert len ( ids ) == 1 , " 1 ID expected, got %s " % ( ids , )
tracking_obj = self . pool [ ' stock.tracking ' ]
move = self . browse ( cr , uid , ids [ 0 ] , context = context )
picking_id = move . picking_id . id
if picking_id :
move_ids = self . search ( cr , uid , [
( ' picking_id ' , ' = ' , picking_id ) ,
( ' tracking_id ' , ' != ' , False )
] , limit = 1 , order = ' tracking_id DESC ' , context = context )
if move_ids :
tracking_move = self . browse ( cr , uid , move_ids [ 0 ] ,
context = context )
tracking_id = tracking_move . tracking_id . id
2010-08-30 13:31:38 +00:00
else :
2014-09-16 13:21:07 +00:00
tracking_id = tracking_obj . create ( cr , uid , { } , context = context )
self . write ( cr , uid , move . id ,
{ ' tracking_id ' : tracking_id } ,
context = context )
2010-06-15 19:19:08 +00:00
return True
2010-06-16 08:40:57 +00:00
2008-07-22 15:11:28 +00:00
#
# Cancel move => cancel others move and pickings
#
2010-07-06 11:21:27 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Cancels the moves and if all moves are cancelled it cancels the picking.
@return : True
"""
2008-07-22 15:11:28 +00:00
if not len ( ids ) :
return True
2010-07-06 11:21:27 +00:00
if context is None :
context = { }
2012-05-15 08:21:43 +00:00
pickings = set ( )
2010-12-13 06:43:09 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2009-08-28 09:40:49 +00:00
if move . state in ( ' confirmed ' , ' waiting ' , ' assigned ' , ' draft ' ) :
2008-07-22 15:11:28 +00:00
if move . picking_id :
2012-05-15 08:21:43 +00:00
pickings . add ( move . picking_id . id )
2009-08-28 09:40:49 +00:00
if move . move_dest_id and move . move_dest_id . state == ' waiting ' :
2014-03-13 09:50:54 +00:00
self . write ( cr , uid , [ move . move_dest_id . id ] , { ' state ' : ' confirmed ' } , context = context )
2010-01-11 13:35:13 +00:00
if context . get ( ' call_unlink ' , False ) and move . move_dest_id . picking_id :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , move . move_dest_id . picking_id . id , cr )
2013-06-11 12:43:38 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' , ' move_dest_id ' : False } , context = context )
2010-01-11 13:35:13 +00:00
if not context . get ( ' call_unlink ' , False ) :
2012-05-15 08:21:43 +00:00
for pick in self . pool . get ( ' stock.picking ' ) . browse ( cr , uid , list ( pickings ) , context = context ) :
2010-01-11 13:35:13 +00:00
if all ( move . state == ' cancel ' for move in pick . move_lines ) :
2013-06-11 12:43:38 +00:00
self . pool . get ( ' stock.picking ' ) . write ( cr , uid , [ pick . id ] , { ' state ' : ' cancel ' } , context = context )
2008-07-22 15:11:28 +00:00
for id in ids :
2013-11-21 12:06:11 +00:00
workflow . trg_trigger ( uid , ' stock.move ' , id , cr )
2008-07-22 15:11:28 +00:00
return True
2010-09-15 18:01:00 +00:00
def _get_accounting_data_for_valuation ( self , cr , uid , move , context = None ) :
"""
Return the accounts and journal to use to post Journal Entries for the real - time
valuation of the move .
2010-10-26 13:56:22 +00:00
2010-09-15 18:01:00 +00:00
: param context : context dictionary that can explicitly mention the company to consider via the ' force_company ' key
: raise : osv . except_osv ( ) is any mandatory account or journal is not defined .
"""
2010-06-10 12:44:52 +00:00
product_obj = self . pool . get ( ' product.product ' )
2010-09-15 18:01:00 +00:00
accounts = product_obj . get_product_accounts ( cr , uid , move . product_id . id , context )
2011-01-17 13:13:39 +00:00
if move . location_id . valuation_out_account_id :
acc_src = move . location_id . valuation_out_account_id . id
else :
acc_src = accounts [ ' stock_account_input ' ]
if move . location_dest_id . valuation_in_account_id :
acc_dest = move . location_dest_id . valuation_in_account_id . id
else :
acc_dest = accounts [ ' stock_account_output ' ]
2011-10-10 11:33:46 +00:00
acc_valuation = accounts . get ( ' property_stock_valuation_account_id ' , False )
2010-06-10 12:44:52 +00:00
journal_id = accounts [ ' stock_journal ' ]
2011-10-10 11:33:46 +00:00
if acc_dest == acc_valuation :
2012-07-17 13:21:35 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Cannot create Journal Entry, Output Account of this product and Valuation account on category of this product are same. ' ) )
2010-11-23 13:24:34 +00:00
2011-10-10 11:33:46 +00:00
if acc_src == acc_valuation :
2012-07-17 13:21:35 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Cannot create Journal Entry, Input Account of this product and Valuation account on category of this product are same. ' ) )
2010-11-23 13:24:34 +00:00
2010-06-10 12:44:52 +00:00
if not acc_src :
2012-07-25 10:30:41 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please define stock input account for this product or its category: " %s " (id: %d ) ' ) % \
2010-06-10 12:44:52 +00:00
( move . product_id . name , move . product_id . id , ) )
if not acc_dest :
2012-07-25 10:30:41 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please define stock output account for this product or its category: " %s " (id: %d ) ' ) % \
2010-06-10 12:44:52 +00:00
( move . product_id . name , move . product_id . id , ) )
if not journal_id :
2012-07-25 10:30:41 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please define journal on the product category: " %s " (id: %d ) ' ) % \
2010-06-10 12:44:52 +00:00
( move . product_id . categ_id . name , move . product_id . categ_id . id , ) )
2011-10-10 11:33:46 +00:00
if not acc_valuation :
2012-07-25 10:30:41 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please define inventory valuation account on the product category: " %s " (id: %d ) ' ) % \
2010-06-10 12:44:52 +00:00
( move . product_id . categ_id . name , move . product_id . categ_id . id , ) )
2011-10-10 11:33:46 +00:00
return journal_id , acc_src , acc_dest , acc_valuation
2010-09-15 18:01:00 +00:00
def _get_reference_accounting_values_for_valuation ( self , cr , uid , move , context = None ) :
"""
Return the reference amount and reference currency representing the inventory valuation for this move .
These reference values should possibly be converted before being posted in Journals to adapt to the primary
and secondary currencies of the relevant accounts .
"""
product_uom_obj = self . pool . get ( ' product.uom ' )
# by default the reference currency is that of the move's company
reference_currency_id = move . company_id . currency_id . id
default_uom = move . product_id . uom_id . id
qty = product_uom_obj . _compute_qty ( cr , uid , move . product_uom . id , move . product_qty , default_uom )
2010-10-26 13:56:22 +00:00
# if product is set to average price and a specific value was entered in the picking wizard,
2010-09-15 18:01:00 +00:00
# we use it
2014-04-29 14:53:32 +00:00
if move . location_dest_id . usage != ' internal ' and move . product_id . cost_method == ' average ' :
reference_amount = qty * move . product_id . standard_price
elif move . product_id . cost_method == ' average ' and move . price_unit :
2010-09-15 18:01:00 +00:00
reference_amount = qty * move . price_unit
reference_currency_id = move . price_currency_id . id or reference_currency_id
2010-10-26 13:56:22 +00:00
# Otherwise we default to the company's valuation price type, considering that the values of the
2010-09-15 18:01:00 +00:00
# valuation field are expressed in the default currency of the move's company.
else :
if context is None :
context = { }
currency_ctx = dict ( context , currency_id = move . company_id . currency_id . id )
2011-09-13 13:45:51 +00:00
amount_unit = move . product_id . price_get ( ' standard_price ' , context = currency_ctx ) [ move . product_id . id ]
2012-03-02 13:22:20 +00:00
reference_amount = amount_unit * qty
2010-09-15 18:01:00 +00:00
return reference_amount , reference_currency_id
def _create_product_valuation_moves ( self , cr , uid , move , context = None ) :
"""
Generate the appropriate accounting moves if the product being moves is subject
to real_time valuation tracking , and the source or destination location is
a transit location or is outside of the company .
"""
if move . product_id . valuation == ' real_time ' : # FIXME: product valuation should perhaps be a property?
if context is None :
context = { }
src_company_ctx = dict ( context , force_company = move . location_id . company_id . id )
dest_company_ctx = dict ( context , force_company = move . location_dest_id . company_id . id )
account_moves = [ ]
# Outgoing moves (or cross-company output part)
if move . location_id . company_id \
and ( move . location_id . usage == ' internal ' and move . location_dest_id . usage != ' internal ' \
or move . location_id . company_id != move . location_dest_id . company_id ) :
2011-10-10 11:33:46 +00:00
journal_id , acc_src , acc_dest , acc_valuation = self . _get_accounting_data_for_valuation ( cr , uid , move , src_company_ctx )
2010-09-15 18:01:00 +00:00
reference_amount , reference_currency_id = self . _get_reference_accounting_values_for_valuation ( cr , uid , move , src_company_ctx )
2012-09-24 07:40:36 +00:00
#returning goods to supplier
if move . location_dest_id . usage == ' supplier ' :
account_moves + = [ ( journal_id , self . _create_account_move_line ( cr , uid , move , acc_valuation , acc_src , reference_amount , reference_currency_id , context ) ) ]
else :
account_moves + = [ ( journal_id , self . _create_account_move_line ( cr , uid , move , acc_valuation , acc_dest , reference_amount , reference_currency_id , context ) ) ]
2010-09-15 18:01:00 +00:00
# Incoming moves (or cross-company input part)
if move . location_dest_id . company_id \
and ( move . location_id . usage != ' internal ' and move . location_dest_id . usage == ' internal ' \
or move . location_id . company_id != move . location_dest_id . company_id ) :
2011-10-10 11:33:46 +00:00
journal_id , acc_src , acc_dest , acc_valuation = self . _get_accounting_data_for_valuation ( cr , uid , move , dest_company_ctx )
2010-09-15 18:01:00 +00:00
reference_amount , reference_currency_id = self . _get_reference_accounting_values_for_valuation ( cr , uid , move , src_company_ctx )
2012-09-24 07:40:36 +00:00
#goods return from customer
if move . location_id . usage == ' customer ' :
account_moves + = [ ( journal_id , self . _create_account_move_line ( cr , uid , move , acc_dest , acc_valuation , reference_amount , reference_currency_id , context ) ) ]
else :
account_moves + = [ ( journal_id , self . _create_account_move_line ( cr , uid , move , acc_src , acc_valuation , reference_amount , reference_currency_id , context ) ) ]
2010-09-15 18:01:00 +00:00
move_obj = self . pool . get ( ' account.move ' )
for j_id , move_lines in account_moves :
2010-10-26 13:56:22 +00:00
move_obj . create ( cr , uid ,
2011-03-23 09:39:28 +00:00
{
2010-09-15 18:01:00 +00:00
' journal_id ' : j_id ,
' line_id ' : move_lines ,
2013-05-27 13:30:44 +00:00
' ref ' : move . picking_id and move . picking_id . name } , context = context )
2010-09-15 18:01:00 +00:00
2010-07-06 11:21:27 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2010-05-26 12:59:30 +00:00
""" Makes the move done and if all moves are done, it will finish the picking.
2010-06-16 08:40:57 +00:00
@return :
2010-05-26 12:59:30 +00:00
"""
2010-01-17 07:39:14 +00:00
picking_ids = [ ]
2010-12-07 11:39:29 +00:00
move_ids = [ ]
2010-07-06 11:21:27 +00:00
if context is None :
context = { }
2010-10-10 17:32:39 +00:00
todo = [ ]
2010-11-22 10:37:53 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2010-10-10 17:32:39 +00:00
if move . state == " draft " :
todo . append ( move . id )
if todo :
self . action_confirm ( cr , uid , todo , context = context )
2011-02-22 14:09:36 +00:00
todo = [ ]
2010-10-10 17:32:39 +00:00
2010-11-22 10:37:53 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2010-12-07 11:39:29 +00:00
if move . state in [ ' done ' , ' cancel ' ] :
continue
move_ids . append ( move . id )
2010-06-21 18:40:58 +00:00
if move . picking_id :
picking_ids . append ( move . picking_id . id )
2008-07-22 15:11:28 +00:00
if move . move_dest_id . id and ( move . state != ' done ' ) :
2012-11-01 10:39:34 +00:00
# Downstream move should only be triggered if this move is the last pending upstream move
2014-05-27 15:56:30 +00:00
other_upstream_move_ids = self . search ( cr , uid , [ ( ' id ' , ' not in ' , move_ids ) , ( ' state ' , ' not in ' , [ ' done ' , ' cancel ' ] ) ,
2012-11-01 10:39:34 +00:00
( ' move_dest_id ' , ' = ' , move . move_dest_id . id ) ] , context = context )
if not other_upstream_move_ids :
self . write ( cr , uid , [ move . id ] , { ' move_history_ids ' : [ ( 4 , move . move_dest_id . id ) ] } )
if move . move_dest_id . state in ( ' waiting ' , ' confirmed ' ) :
self . force_assign ( cr , uid , [ move . move_dest_id . id ] , context = context )
if move . move_dest_id . picking_id :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , move . move_dest_id . picking_id . id , cr )
2012-11-01 10:39:34 +00:00
if move . move_dest_id . auto_validate :
self . action_done ( cr , uid , [ move . move_dest_id . id ] , context = context )
2008-07-22 15:11:28 +00:00
2010-09-15 18:01:00 +00:00
self . _create_product_valuation_moves ( cr , uid , move , context = context )
2011-04-11 10:29:52 +00:00
if move . state not in ( ' confirmed ' , ' done ' , ' assigned ' ) :
2011-02-22 14:09:36 +00:00
todo . append ( move . id )
if todo :
self . action_confirm ( cr , uid , todo , context = context )
2010-12-13 04:56:14 +00:00
2013-05-13 14:53:18 +00:00
self . write ( cr , uid , move_ids , { ' state ' : ' done ' , ' date ' : time . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) } , context = context )
2010-12-13 04:56:14 +00:00
for id in move_ids :
2013-11-21 12:06:11 +00:00
workflow . trg_trigger ( uid , ' stock.move ' , id , cr )
2010-06-16 08:40:57 +00:00
2010-06-25 22:34:40 +00:00
for pick_id in picking_ids :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' stock.picking ' , pick_id , cr )
2010-06-21 18:58:08 +00:00
2008-07-22 15:11:28 +00:00
return True
2010-06-16 08:40:57 +00:00
2010-09-15 18:01:00 +00:00
def _create_account_move_line ( self , cr , uid , move , src_account_id , dest_account_id , reference_amount , reference_currency_id , context = None ) :
"""
2010-10-26 13:56:22 +00:00
Generate the account . move . line values to post to track the stock valuation difference due to the
2010-09-15 18:01:00 +00:00
processing of the given stock move .
"""
2010-10-26 13:56:22 +00:00
# prepare default values considering that the destination accounts have the reference_currency_id as their main currency
2013-03-06 15:01:42 +00:00
partner_id = ( move . picking_id . partner_id and self . pool . get ( ' res.partner ' ) . _find_accounting_partner ( move . picking_id . partner_id ) . id ) or False
2010-09-15 18:01:00 +00:00
debit_line_vals = {
' name ' : move . name ,
' product_id ' : move . product_id and move . product_id . id or False ,
' quantity ' : move . product_qty ,
' ref ' : move . picking_id and move . picking_id . name or False ,
2010-10-05 12:49:37 +00:00
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
2010-09-15 18:01:00 +00:00
' partner_id ' : partner_id ,
' debit ' : reference_amount ,
' account_id ' : dest_account_id ,
}
credit_line_vals = {
' name ' : move . name ,
' product_id ' : move . product_id and move . product_id . id or False ,
' quantity ' : move . product_qty ,
' ref ' : move . picking_id and move . picking_id . name or False ,
2010-10-05 12:49:37 +00:00
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
2010-09-15 18:01:00 +00:00
' partner_id ' : partner_id ,
' credit ' : reference_amount ,
' account_id ' : src_account_id ,
}
# if we are posting to accounts in a different currency, provide correct values in both currencies correctly
2010-10-26 13:56:22 +00:00
# when compatible with the optional secondary currency on the account.
2010-09-15 18:01:00 +00:00
# Financial Accounts only accept amounts in secondary currencies if there's no secondary currency on the account
# or if it's the same as that of the secondary amount being posted.
account_obj = self . pool . get ( ' account.account ' )
src_acct , dest_acct = account_obj . browse ( cr , uid , [ src_account_id , dest_account_id ] , context = context )
2013-05-30 15:04:41 +00:00
src_main_currency_id = src_acct . company_id . currency_id . id
dest_main_currency_id = dest_acct . company_id . currency_id . id
2010-09-15 18:01:00 +00:00
cur_obj = self . pool . get ( ' res.currency ' )
if reference_currency_id != src_main_currency_id :
# fix credit line:
credit_line_vals [ ' credit ' ] = cur_obj . compute ( cr , uid , reference_currency_id , src_main_currency_id , reference_amount , context = context )
if ( not src_acct . currency_id ) or src_acct . currency_id . id == reference_currency_id :
2013-04-25 12:49:33 +00:00
credit_line_vals . update ( currency_id = reference_currency_id , amount_currency = - reference_amount )
2010-09-15 18:01:00 +00:00
if reference_currency_id != dest_main_currency_id :
# fix debit line:
debit_line_vals [ ' debit ' ] = cur_obj . compute ( cr , uid , reference_currency_id , dest_main_currency_id , reference_amount , context = context )
2010-09-16 15:27:53 +00:00
if ( not dest_acct . currency_id ) or dest_acct . currency_id . id == reference_currency_id :
2010-09-15 18:01:00 +00:00
debit_line_vals . update ( currency_id = reference_currency_id , amount_currency = reference_amount )
return [ ( 0 , 0 , debit_line_vals ) , ( 0 , 0 , credit_line_vals ) ]
2010-06-21 21:20:55 +00:00
2008-07-22 15:11:28 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-05-12 12:22:04 +00:00
if context is None :
2010-05-28 05:18:58 +00:00
context = { }
2010-08-31 10:20:45 +00:00
ctx = context . copy ( )
2008-07-22 15:11:28 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2013-03-19 12:52:44 +00:00
if move . state != ' draft ' and not ctx . get ( ' call_unlink ' , False ) :
raise osv . except_osv ( _ ( ' User Error! ' ) , _ ( ' You can only delete draft moves. ' ) )
2008-07-22 15:11:28 +00:00
return super ( stock_move , self ) . unlink (
2010-08-31 10:20:45 +00:00
cr , uid , ids , context = ctx )
2007-12-31 13:31:32 +00:00
2011-12-09 08:32:35 +00:00
# _create_lot function is not used anywhere
2010-02-24 10:55:03 +00:00
def _create_lot ( self , cr , uid , ids , product_id , prefix = False ) :
2010-05-26 12:59:30 +00:00
""" Creates production lot
@return : Production lot id
"""
2010-02-19 10:51:45 +00:00
prodlot_obj = self . pool . get ( ' stock.production.lot ' )
2010-06-21 18:40:58 +00:00
prodlot_id = prodlot_obj . create ( cr , uid , { ' prefix ' : prefix , ' product_id ' : product_id } )
2010-02-24 10:55:03 +00:00
return prodlot_id
2010-03-18 06:31:22 +00:00
def action_scrap ( self , cr , uid , ids , quantity , location_id , context = None ) :
2010-05-26 12:59:30 +00:00
""" Move the scrap/damaged product into scrap location
@param cr : the database cursor
@param uid : the user id
2010-08-31 06:54:27 +00:00
@param ids : ids of stock move object to be scrapped
2010-05-26 12:59:30 +00:00
@param quantity : specify scrap qty
@param location_id : specify scrap location
@param context : context arguments
@return : Scraped lines
"""
2011-12-21 11:38:22 +00:00
#quantity should in MOVE UOM
2010-03-18 06:31:22 +00:00
if quantity < = 0 :
2012-07-17 13:21:35 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' Please provide a positive quantity to scrap. ' ) )
2010-03-19 08:46:14 +00:00
res = [ ]
for move in self . browse ( cr , uid , ids , context = context ) :
2013-03-19 12:52:16 +00:00
source_location = move . location_id
if move . state == ' done ' :
source_location = move . location_dest_id
if source_location . usage != ' internal ' :
#restrict to scrap from a virtual location because it's meaningless and it may introduce errors in stock ('creating' new products from nowhere)
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Forbidden operation: it is not allowed to scrap products from a virtual location. ' ) )
2010-03-18 06:31:22 +00:00
move_qty = move . product_qty
uos_qty = quantity / move_qty * move . product_uos_qty
default_val = {
2013-03-19 12:52:16 +00:00
' location_id ' : source_location . id ,
2010-08-13 12:20:05 +00:00
' product_qty ' : quantity ,
' product_uos_qty ' : uos_qty ,
' state ' : move . state ,
2013-03-19 12:52:16 +00:00
' scrapped ' : True ,
2010-08-30 07:37:18 +00:00
' location_dest_id ' : location_id ,
2010-10-18 12:27:07 +00:00
' tracking_id ' : move . tracking_id . id ,
2010-10-12 10:41:12 +00:00
' prodlot_id ' : move . prodlot_id . id ,
2010-08-13 12:20:05 +00:00
}
2010-03-18 06:31:22 +00:00
new_move = self . copy ( cr , uid , move . id , default_val )
2010-09-02 12:00:21 +00:00
2011-04-07 11:32:08 +00:00
res + = [ new_move ]
2010-07-23 10:11:19 +00:00
product_obj = self . pool . get ( ' product.product ' )
2012-04-03 09:46:42 +00:00
for product in product_obj . browse ( cr , uid , [ move . product_id . id ] , context = context ) :
2012-04-03 13:52:37 +00:00
if move . picking_id :
uom = product . uom_id . name if product . uom_id else ' '
2012-12-19 20:55:13 +00:00
message = _ ( " %s %s %s has been <b>moved to</b> scrap. " ) % ( quantity , uom , product . name )
move . picking_id . message_post ( body = message )
2010-07-23 10:11:19 +00:00
2011-12-09 08:32:35 +00:00
self . action_done ( cr , uid , res , context = context )
2010-03-18 06:31:22 +00:00
return res
2011-12-09 08:32:35 +00:00
# action_split function is not used anywhere
2012-02-27 10:10:20 +00:00
# FIXME: deprecate this method
2010-03-18 06:31:22 +00:00
def action_split ( self , cr , uid , ids , quantity , split_by_qty = 1 , prefix = False , with_lot = True , context = None ) :
2010-05-26 12:59:30 +00:00
""" Split Stock Move lines into production lot which specified split by quantity.
@param cr : the database cursor
@param uid : the user id
@param ids : ids of stock move object to be splited
@param split_by_qty : specify split by qty
@param prefix : specify prefix of production lot
@param with_lot : if true , prodcution lot will assign for split line otherwise not .
@param context : context arguments
@return : Splited move lines
"""
2010-02-23 05:34:00 +00:00
2010-07-06 11:21:27 +00:00
if context is None :
context = { }
2010-02-24 09:57:17 +00:00
if quantity < = 0 :
2012-07-17 13:21:35 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' Please provide proper quantity. ' ) )
2010-02-24 09:57:17 +00:00
2010-02-24 10:55:03 +00:00
res = [ ]
2010-02-23 05:34:00 +00:00
2010-12-13 06:43:09 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
2010-02-23 05:34:00 +00:00
if split_by_qty < = 0 or quantity == 0 :
return res
uos_qty = split_by_qty / move . product_qty * move . product_uos_qty
quantity_rest = quantity % split_by_qty
uos_qty_rest = split_by_qty / move . product_qty * move . product_uos_qty
update_val = {
' product_qty ' : split_by_qty ,
' product_uos_qty ' : uos_qty ,
2010-03-18 11:25:02 +00:00
}
for idx in range ( int ( quantity / / split_by_qty ) ) :
2010-02-24 10:55:03 +00:00
if not idx and move . product_qty < = quantity :
current_move = move . id
else :
current_move = self . copy ( cr , uid , move . id , { ' state ' : move . state } )
2010-02-24 09:57:17 +00:00
res . append ( current_move )
if with_lot :
2010-02-24 10:55:03 +00:00
update_val [ ' prodlot_id ' ] = self . _create_lot ( cr , uid , [ current_move ] , move . product_id . id )
2010-02-22 07:23:42 +00:00
2010-02-23 05:34:00 +00:00
self . write ( cr , uid , [ current_move ] , update_val )
2010-03-18 11:25:02 +00:00
if quantity_rest > 0 :
idx = int ( quantity / / split_by_qty )
2010-02-23 05:34:00 +00:00
update_val [ ' product_qty ' ] = quantity_rest
2010-03-18 11:25:02 +00:00
update_val [ ' product_uos_qty ' ] = uos_qty_rest
if not idx and move . product_qty < = quantity :
current_move = move . id
2010-02-24 10:55:03 +00:00
else :
2010-03-18 11:25:02 +00:00
current_move = self . copy ( cr , uid , move . id , { ' state ' : move . state } )
2010-02-24 09:57:17 +00:00
res . append ( current_move )
2010-03-18 11:25:02 +00:00
if with_lot :
2010-02-24 10:55:03 +00:00
update_val [ ' prodlot_id ' ] = self . _create_lot ( cr , uid , [ current_move ] , move . product_id . id )
2010-02-24 09:57:17 +00:00
2010-02-23 05:34:00 +00:00
self . write ( cr , uid , [ current_move ] , update_val )
2010-03-18 11:25:02 +00:00
return res
2010-02-22 07:23:42 +00:00
2010-12-31 14:12:42 +00:00
def action_consume ( self , cr , uid , ids , quantity , location_id = False , context = None ) :
2010-05-26 12:59:30 +00:00
""" Consumed product with specific quatity from specific source location
@param cr : the database cursor
@param uid : the user id
@param ids : ids of stock move object to be consumed
@param quantity : specify consume quantity
@param location_id : specify source location
@param context : context arguments
@return : Consumed lines
"""
2011-12-21 11:38:22 +00:00
#quantity should in MOVE UOM
2010-05-18 14:26:51 +00:00
if context is None :
2010-02-24 09:57:17 +00:00
context = { }
2010-02-23 05:34:00 +00:00
if quantity < = 0 :
2012-07-17 13:21:35 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' Please provide proper quantity. ' ) )
2010-03-18 11:25:02 +00:00
res = [ ]
for move in self . browse ( cr , uid , ids , context = context ) :
2010-02-23 05:34:00 +00:00
move_qty = move . product_qty
2010-12-21 07:16:53 +00:00
if move_qty < = 0 :
2012-07-17 13:21:35 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Cannot consume a move with negative or zero quantity. ' ) )
2010-02-23 05:34:00 +00:00
quantity_rest = move . product_qty
2010-03-18 11:25:02 +00:00
quantity_rest - = quantity
2010-02-19 13:44:55 +00:00
uos_qty_rest = quantity_rest / move_qty * move . product_uos_qty
2010-02-22 07:23:42 +00:00
if quantity_rest < = 0 :
2010-03-18 11:25:02 +00:00
quantity_rest = 0
2010-02-24 08:17:06 +00:00
uos_qty_rest = 0
quantity = move . product_qty
2010-03-18 11:25:02 +00:00
2010-02-24 08:17:06 +00:00
uos_qty = quantity / move_qty * move . product_uos_qty
2014-01-22 13:38:22 +00:00
if float_compare ( quantity_rest , 0 , precision_rounding = move . product_id . uom_id . rounding ) :
2010-02-24 08:17:06 +00:00
default_val = {
2010-03-18 11:25:02 +00:00
' product_qty ' : quantity ,
' product_uos_qty ' : uos_qty ,
2010-12-31 14:12:42 +00:00
' state ' : move . state ,
2010-10-20 10:58:16 +00:00
' location_id ' : location_id or move . location_id . id ,
2010-02-24 08:17:06 +00:00
}
2011-03-31 10:07:25 +00:00
current_move = self . copy ( cr , uid , move . id , default_val )
res + = [ current_move ]
2010-03-18 11:25:02 +00:00
update_val = { }
2010-02-23 05:34:00 +00:00
update_val [ ' product_qty ' ] = quantity_rest
2010-03-18 11:25:02 +00:00
update_val [ ' product_uos_qty ' ] = uos_qty_rest
self . write ( cr , uid , [ move . id ] , update_val )
2010-02-24 08:17:06 +00:00
2010-03-18 11:25:02 +00:00
else :
quantity_rest = quantity
2010-02-24 10:55:03 +00:00
uos_qty_rest = uos_qty
2011-03-31 10:07:25 +00:00
res + = [ move . id ]
update_val = {
2010-03-18 06:31:22 +00:00
' product_qty ' : quantity_rest ,
' product_uos_qty ' : uos_qty_rest ,
2012-02-06 13:26:12 +00:00
' location_id ' : location_id or move . location_id . id ,
2011-03-31 10:07:25 +00:00
}
self . write ( cr , uid , [ move . id ] , update_val )
2012-04-03 12:50:53 +00:00
2011-12-09 08:32:35 +00:00
self . action_done ( cr , uid , res , context = context )
2010-07-06 11:44:51 +00:00
2010-02-24 08:17:06 +00:00
return res
2010-03-29 11:37:53 +00:00
2010-09-15 18:01:00 +00:00
# FIXME: needs refactoring, this code is partially duplicated in stock_picking.do_partial()!
2010-07-06 11:21:27 +00:00
def do_partial ( self , cr , uid , ids , partial_datas , context = None ) :
2010-05-26 12:59:30 +00:00
""" Makes partial pickings and moves done.
@param partial_datas : Dictionary containing details of partial picking
2012-03-30 07:49:01 +00:00
like partner_id , delivery_date , delivery
2010-05-26 12:59:30 +00:00
moves with product_id , product_qty , uom
2010-03-26 14:52:01 +00:00
"""
res = { }
picking_obj = self . pool . get ( ' stock.picking ' )
product_obj = self . pool . get ( ' product.product ' )
currency_obj = self . pool . get ( ' res.currency ' )
uom_obj = self . pool . get ( ' product.uom ' )
2010-12-13 06:43:09 +00:00
if context is None :
2010-07-06 11:21:27 +00:00
context = { }
2010-03-26 14:52:01 +00:00
complete , too_many , too_few = [ ] , [ ] , [ ]
move_product_qty = { }
2010-09-28 15:28:30 +00:00
prodlot_ids = { }
2010-03-26 14:52:01 +00:00
for move in self . browse ( cr , uid , ids , context = context ) :
if move . state in ( ' done ' , ' cancel ' ) :
continue
2010-03-29 11:37:53 +00:00
partial_data = partial_datas . get ( ' move %s ' % ( move . id ) , False )
2012-07-25 10:14:28 +00:00
assert partial_data , _ ( ' Missing partial picking data for move # %s . ' ) % ( move . id )
2010-03-26 14:52:01 +00:00
product_qty = partial_data . get ( ' product_qty ' , 0.0 )
move_product_qty [ move . id ] = product_qty
2010-03-29 11:37:53 +00:00
product_uom = partial_data . get ( ' product_uom ' , False )
2010-03-26 14:52:01 +00:00
product_price = partial_data . get ( ' product_price ' , 0.0 )
product_currency = partial_data . get ( ' product_currency ' , False )
2010-09-28 15:28:30 +00:00
prodlot_ids [ move . id ] = partial_data . get ( ' prodlot_id ' )
2010-03-26 14:52:01 +00:00
if move . product_qty == product_qty :
complete . append ( move )
elif move . product_qty > product_qty :
too_few . append ( move )
else :
too_many . append ( move )
# Average price computation
if ( move . picking_id . type == ' in ' ) and ( move . product_id . cost_method == ' average ' ) :
product = product_obj . browse ( cr , uid , move . product_id . id )
2010-09-28 15:28:30 +00:00
move_currency_id = move . company_id . currency_id . id
context [ ' currency_id ' ] = move_currency_id
2010-03-26 14:52:01 +00:00
qty = uom_obj . _compute_qty ( cr , uid , product_uom , product_qty , product . uom_id . id )
2010-09-28 12:29:49 +00:00
if qty > 0 :
2010-03-26 14:52:01 +00:00
new_price = currency_obj . compute ( cr , uid , product_currency ,
2013-11-04 17:32:32 +00:00
move_currency_id , product_price , round = False )
2010-03-26 14:52:01 +00:00
new_price = uom_obj . _compute_price ( cr , uid , product_uom , new_price ,
2010-03-29 11:37:53 +00:00
product . uom_id . id )
2010-03-26 14:52:01 +00:00
if product . qty_available < = 0 :
new_std_price = new_price
else :
# Get the standard price
2011-09-13 13:45:51 +00:00
amount_unit = product . price_get ( ' standard_price ' , context = context ) [ product . id ]
2010-03-26 14:52:01 +00:00
new_std_price = ( ( amount_unit * product . qty_available ) \
+ ( new_price * qty ) ) / ( product . qty_available + qty )
2010-03-29 11:37:53 +00:00
2010-09-28 12:29:49 +00:00
product_obj . write ( cr , uid , [ product . id ] , { ' standard_price ' : new_std_price } )
2010-09-15 18:01:00 +00:00
# Record the values that were chosen in the wizard, so they can be
# used for inventory valuation if real-time valuation is enabled.
2010-10-26 13:56:22 +00:00
self . write ( cr , uid , [ move . id ] ,
2010-09-15 18:01:00 +00:00
{ ' price_unit ' : product_price ,
' price_currency_id ' : product_currency ,
} )
2010-03-26 14:52:01 +00:00
2010-03-29 11:37:53 +00:00
for move in too_few :
product_qty = move_product_qty [ move . id ]
2010-03-26 14:52:01 +00:00
if product_qty != 0 :
2010-09-28 15:28:30 +00:00
defaults = {
' product_qty ' : product_qty ,
' product_uos_qty ' : product_qty ,
' picking_id ' : move . picking_id . id ,
' state ' : ' assigned ' ,
' move_dest_id ' : False ,
' price_unit ' : move . price_unit ,
}
prodlot_id = prodlot_ids [ move . id ]
if prodlot_id :
defaults . update ( prodlot_id = prodlot_id )
new_move = self . copy ( cr , uid , move . id , defaults )
2010-03-26 14:52:01 +00:00
complete . append ( self . browse ( cr , uid , new_move ) )
2011-03-07 09:52:46 +00:00
self . write ( cr , uid , [ move . id ] ,
2010-03-26 14:52:01 +00:00
{
2012-02-07 10:58:08 +00:00
' product_qty ' : move . product_qty - product_qty ,
' product_uos_qty ' : move . product_qty - product_qty ,
' prodlot_id ' : False ,
' tracking_id ' : False ,
2010-03-26 14:52:01 +00:00
} )
2010-03-29 11:37:53 +00:00
2010-03-26 14:52:01 +00:00
for move in too_many :
2011-03-07 09:52:46 +00:00
self . write ( cr , uid , [ move . id ] ,
2010-03-26 14:52:01 +00:00
{
2010-05-05 10:27:11 +00:00
' product_qty ' : move . product_qty ,
2010-05-11 11:53:03 +00:00
' product_uos_qty ' : move . product_qty ,
2010-03-26 14:52:01 +00:00
} )
2010-03-29 11:37:53 +00:00
complete . append ( move )
2010-03-26 14:52:01 +00:00
for move in complete :
2010-09-28 15:28:30 +00:00
if prodlot_ids . get ( move . id ) :
self . write ( cr , uid , [ move . id ] , { ' prodlot_id ' : prodlot_ids . get ( move . id ) } )
2010-07-06 11:21:27 +00:00
self . action_done ( cr , uid , [ move . id ] , context = context )
2010-06-18 09:41:42 +00:00
if move . picking_id . id :
# TOCHECK : Done picking if all moves are done
cr . execute ( """
SELECT move . id FROM stock_picking pick
RIGHT JOIN stock_move move ON move . picking_id = pick . id AND move . state = % s
WHERE pick . id = % s """ ,
( ' done ' , move . picking_id . id ) )
res = cr . fetchall ( )
if len ( res ) == len ( move . picking_id . move_lines ) :
picking_obj . action_move ( cr , uid , [ move . picking_id . id ] )
2013-01-28 15:29:29 +00:00
picking_obj . signal_button_done ( cr , uid , [ move . picking_id . id ] )
2010-03-29 11:37:53 +00:00
2010-09-28 15:28:30 +00:00
return [ move . id for move in complete ]
2007-12-31 13:31:32 +00:00
2006-12-07 13:41:40 +00:00
class stock_inventory ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.inventory "
_description = " Inventory "
_columns = {
2010-09-06 13:51:23 +00:00
' name ' : fields . char ( ' Inventory Reference ' , size = 64 , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' date ' : fields . datetime ( ' Creation Date ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
' date_done ' : fields . datetime ( ' Date done ' ) ,
2012-08-29 10:43:06 +00:00
' inventory_line_id ' : fields . one2many ( ' stock.inventory.line ' , ' inventory_id ' , ' Inventories ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
' move_ids ' : fields . many2many ( ' stock.move ' , ' stock_inventory_move_rel ' , ' inventory_id ' , ' move_id ' , ' Created Moves ' ) ,
2012-05-22 16:10:55 +00:00
' state ' : fields . selection ( ( ( ' draft ' , ' Draft ' ) , ( ' cancel ' , ' Cancelled ' ) , ( ' confirm ' , ' Confirmed ' ) , ( ' done ' , ' Done ' ) ) , ' Status ' , readonly = True , select = True ) ,
2010-12-15 06:26:29 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , select = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2011-02-23 06:15:04 +00:00
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2010-07-06 11:44:51 +00:00
' state ' : ' draft ' ,
2009-12-23 17:16:39 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.inventory ' , context = c )
2008-07-22 15:11:28 +00:00
}
2010-10-26 13:56:22 +00:00
2012-01-06 12:59:14 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
if default is None :
default = { }
default = default . copy ( )
default . update ( { ' move_ids ' : [ ] , ' date_done ' : False } )
return super ( stock_inventory , self ) . copy ( cr , uid , id , default , context = context )
2010-02-28 03:18:01 +00:00
def _inventory_line_hook ( self , cr , uid , inventory_line , move_vals ) :
2010-05-26 12:59:30 +00:00
""" Creates a stock move from an inventory line
@param inventory_line :
@param move_vals :
2010-06-16 08:40:57 +00:00
@return :
2010-05-26 12:59:30 +00:00
"""
2010-02-28 03:18:01 +00:00
return self . pool . get ( ' stock.move ' ) . create ( cr , uid , move_vals )
2009-08-28 09:40:49 +00:00
2008-07-22 15:11:28 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2010-12-20 14:30:49 +00:00
""" Finish the inventory
2010-11-23 13:24:34 +00:00
@return : True
"""
if context is None :
context = { }
2010-11-18 06:47:12 +00:00
move_obj = self . pool . get ( ' stock.move ' )
2010-11-18 07:08:23 +00:00
for inv in self . browse ( cr , uid , ids , context = context ) :
move_obj . action_done ( cr , uid , [ x . id for x in inv . move_ids ] , context = context )
2010-11-24 10:42:05 +00:00
self . write ( cr , uid , [ inv . id ] , { ' state ' : ' done ' , ' date_done ' : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) } , context = context )
2010-11-11 10:46:37 +00:00
return True
def action_confirm ( self , cr , uid , ids , context = None ) :
2010-11-23 13:24:34 +00:00
""" Confirm the inventory and writes its finished date
2010-05-26 12:59:30 +00:00
@return : True
"""
2010-07-06 11:44:51 +00:00
if context is None :
context = { }
2010-09-09 12:46:41 +00:00
# to perform the correct inventory corrections we need analyze stock location by
# location, never recursively, so we use a special context
product_context = dict ( context , compute_child = False )
location_obj = self . pool . get ( ' stock.location ' )
2010-11-23 13:24:34 +00:00
for inv in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
move_ids = [ ]
for line in inv . inventory_line_id :
2009-08-28 09:40:49 +00:00
pid = line . product_id . id
2012-03-27 08:34:40 +00:00
product_context . update ( uom = line . product_uom . id , to_date = inv . date , date = inv . date , prodlot_id = line . prod_lot_id . id )
2010-09-09 12:46:41 +00:00
amount = location_obj . _product_get ( cr , uid , line . location_id . id , [ pid ] , product_context ) [ pid ]
2009-08-28 09:40:49 +00:00
change = line . product_qty - amount
2010-01-19 08:57:59 +00:00
lot_id = line . prod_lot_id . id
2008-07-22 15:11:28 +00:00
if change :
2012-07-27 01:52:02 +00:00
location_id = line . product_id . property_stock_inventory . id
2008-07-22 15:11:28 +00:00
value = {
2012-11-14 12:28:51 +00:00
' name ' : _ ( ' INV: ' ) + ( line . inventory_id . name or ' ' ) ,
2008-07-22 15:11:28 +00:00
' product_id ' : line . product_id . id ,
' product_uom ' : line . product_uom . id ,
2010-02-01 12:50:00 +00:00
' prodlot_id ' : lot_id ,
2008-07-22 15:11:28 +00:00
' date ' : inv . date ,
}
2012-03-30 07:49:01 +00:00
2009-08-28 09:40:49 +00:00
if change > 0 :
2008-07-22 15:11:28 +00:00
value . update ( {
' product_qty ' : change ,
' location_id ' : location_id ,
' location_dest_id ' : line . location_id . id ,
} )
else :
value . update ( {
' product_qty ' : - change ,
' location_id ' : line . location_id . id ,
' location_dest_id ' : location_id ,
} )
2010-02-28 03:18:01 +00:00
move_ids . append ( self . _inventory_line_hook ( cr , uid , line , value ) )
2010-11-24 10:42:05 +00:00
self . write ( cr , uid , [ inv . id ] , { ' state ' : ' confirm ' , ' move_ids ' : [ ( 6 , 0 , move_ids ) ] } )
2011-12-22 09:55:34 +00:00
self . pool . get ( ' stock.move ' ) . action_confirm ( cr , uid , move_ids , context = context )
2008-07-22 15:11:28 +00:00
return True
2011-01-17 10:01:22 +00:00
def action_cancel_draft ( self , cr , uid , ids , context = None ) :
""" Cancels the stock move and change inventory state to draft.
@return : True
"""
for inv in self . browse ( cr , uid , ids , context = context ) :
self . pool . get ( ' stock.move ' ) . action_cancel ( cr , uid , [ x . id for x in inv . move_ids ] , context = context )
self . write ( cr , uid , [ inv . id ] , { ' state ' : ' draft ' } , context = context )
return True
2011-12-21 11:38:22 +00:00
def action_cancel_inventory ( self , cr , uid , ids , context = None ) :
2011-01-06 12:17:55 +00:00
""" Cancels both stock move and inventory
2010-05-26 12:59:30 +00:00
@return : True
"""
2011-01-03 10:49:33 +00:00
move_obj = self . pool . get ( ' stock.move ' )
account_move_obj = self . pool . get ( ' account.move ' )
2010-11-22 10:37:53 +00:00
for inv in self . browse ( cr , uid , ids , context = context ) :
2011-01-06 13:18:49 +00:00
move_obj . action_cancel ( cr , uid , [ x . id for x in inv . move_ids ] , context = context )
2011-01-03 10:49:33 +00:00
for move in inv . move_ids :
2011-01-06 13:18:49 +00:00
account_move_ids = account_move_obj . search ( cr , uid , [ ( ' name ' , ' = ' , move . name ) ] )
2011-01-03 10:49:33 +00:00
if account_move_ids :
2011-01-06 13:18:49 +00:00
account_move_data_l = account_move_obj . read ( cr , uid , account_move_ids , [ ' state ' ] , context = context )
2011-01-03 10:49:33 +00:00
for account_move in account_move_data_l :
if account_move [ ' state ' ] == ' posted ' :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' User Error! ' ) ,
2011-09-18 13:53:10 +00:00
_ ( ' In order to cancel this inventory, you must first unpost related journal entries. ' ) )
2011-01-06 13:18:49 +00:00
account_move_obj . unlink ( cr , uid , [ account_move [ ' id ' ] ] , context = context )
self . write ( cr , uid , [ inv . id ] , { ' state ' : ' cancel ' } , context = context )
2008-07-22 15:11:28 +00:00
return True
2009-08-28 09:40:49 +00:00
2006-12-07 13:41:40 +00:00
class stock_inventory_line ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.inventory.line "
2010-05-19 18:32:32 +00:00
_description = " Inventory Line "
2011-05-05 05:46:31 +00:00
_rec_name = " inventory_id "
2008-07-22 15:11:28 +00:00
_columns = {
2009-08-28 09:40:49 +00:00
' inventory_id ' : fields . many2one ( ' stock.inventory ' , ' Inventory ' , ondelete = ' cascade ' , select = True ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True ) ,
2010-09-06 13:51:23 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True , select = True ) ,
2012-04-25 12:09:08 +00:00
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product Unit of Measure ' , required = True ) ,
' product_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ) ,
2011-01-13 17:34:41 +00:00
' company_id ' : fields . related ( ' inventory_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , select = True , readonly = True ) ,
2012-04-24 13:08:05 +00:00
' prod_lot_id ' : fields . many2one ( ' stock.production.lot ' , ' Serial Number ' , domain = " [( ' product_id ' , ' = ' ,product_id)] " ) ,
2012-05-04 11:57:48 +00:00
' state ' : fields . related ( ' inventory_id ' , ' state ' , type = ' char ' , string = ' Status ' , readonly = True ) ,
2008-07-22 15:11:28 +00:00
}
2010-03-15 13:16:40 +00:00
2012-08-03 12:23:54 +00:00
def _default_stock_location ( self , cr , uid , context = None ) :
2013-07-31 09:27:32 +00:00
try :
location_model , location_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' stock ' , ' stock_location_stock ' )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ location_id ] , ' read ' , context = context )
2013-07-31 09:27:32 +00:00
except ( orm . except_orm , ValueError ) :
location_id = False
return location_id
2012-08-03 12:23:54 +00:00
_defaults = {
' location_id ' : _default_stock_location
}
2010-12-15 11:58:14 +00:00
def on_change_product_id ( self , cr , uid , ids , location_id , product , uom = False , to_date = False ) :
2010-05-26 12:59:30 +00:00
""" Changes UoM and name if product_id changes.
@param location_id : Location id
@param product : Changed product_id
2010-06-16 08:40:57 +00:00
@param uom : UoM product
2010-05-26 12:59:30 +00:00
@return : Dictionary of changed values
"""
2008-07-22 15:11:28 +00:00
if not product :
2012-04-07 09:03:58 +00:00
return { ' value ' : { ' product_qty ' : 0.0 , ' product_uom ' : False , ' prod_lot_id ' : False } }
2011-03-29 13:15:43 +00:00
obj_product = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product )
uom = uom or obj_product . uom_id . id
2011-05-25 10:14:03 +00:00
amount = self . pool . get ( ' stock.location ' ) . _product_get ( cr , uid , location_id , [ product ] , { ' uom ' : uom , ' to_date ' : to_date , ' compute_child ' : False } ) [ product ]
2012-04-07 09:03:58 +00:00
result = { ' product_qty ' : amount , ' product_uom ' : uom , ' prod_lot_id ' : False }
2009-08-28 09:40:49 +00:00
return { ' value ' : result }
2006-12-07 13:41:40 +00:00
#----------------------------------------------------------
# Stock Warehouse
#----------------------------------------------------------
class stock_warehouse ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " stock.warehouse "
_description = " Warehouse "
_columns = {
2010-09-06 13:51:23 +00:00
' name ' : fields . char ( ' Name ' , size = 128 , required = True , select = True ) ,
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , select = True ) ,
2012-03-30 07:49:01 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Owner Address ' ) ,
2010-05-10 13:54:26 +00:00
' lot_input_id ' : fields . many2one ( ' stock.location ' , ' Location Input ' , required = True , domain = [ ( ' usage ' , ' <> ' , ' view ' ) ] ) ,
2011-10-27 10:06:08 +00:00
' lot_stock_id ' : fields . many2one ( ' stock.location ' , ' Location Stock ' , required = True , domain = [ ( ' usage ' , ' = ' , ' internal ' ) ] ) ,
2010-05-10 13:54:26 +00:00
' lot_output_id ' : fields . many2one ( ' stock.location ' , ' Location Output ' , required = True , domain = [ ( ' usage ' , ' <> ' , ' view ' ) ] ) ,
2008-07-22 15:11:28 +00:00
}
2012-08-03 12:23:54 +00:00
def _default_lot_input_stock_id ( self , cr , uid , context = None ) :
2013-05-21 14:06:31 +00:00
try :
2013-08-23 09:59:12 +00:00
lot_input_stock_model , lot_input_stock_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' stock ' , ' stock_location_stock ' )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ lot_input_stock_id ] , ' read ' , context = context )
2013-05-21 14:06:31 +00:00
except ( ValueError , orm . except_orm ) :
2013-08-23 09:59:12 +00:00
# the user does not have read access on the location or it does not exists
2013-05-21 14:06:31 +00:00
lot_input_stock_id = False
return lot_input_stock_id
2012-08-03 12:23:54 +00:00
def _default_lot_output_id ( self , cr , uid , context = None ) :
2013-05-21 14:06:31 +00:00
try :
2013-09-09 09:18:37 +00:00
lot_output_model , lot_output_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' stock ' , ' stock_location_output ' )
2013-11-08 22:47:24 +00:00
with tools . mute_logger ( ' openerp.osv.orm ' ) :
self . pool . get ( ' stock.location ' ) . check_access_rule ( cr , uid , [ lot_output_id ] , ' read ' , context = context )
2013-05-21 14:06:31 +00:00
except ( ValueError , orm . except_orm ) :
2013-08-23 09:59:12 +00:00
# the user does not have read access on the location or it does not exists
2013-05-21 14:06:31 +00:00
lot_output_id = False
return lot_output_id
2012-08-03 12:23:54 +00:00
2009-11-28 09:25:22 +00:00
_defaults = {
2010-07-06 11:21:27 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' stock.inventory ' , context = c ) ,
2012-08-03 12:23:54 +00:00
' lot_input_id ' : _default_lot_input_stock_id ,
' lot_stock_id ' : _default_lot_input_stock_id ,
' lot_output_id ' : _default_lot_output_id ,
2009-11-28 09:25:22 +00:00
}
2010-07-06 11:21:27 +00:00
2006-12-07 13:41:40 +00:00
2012-05-04 15:36:13 +00:00
#----------------------------------------------------------
# "Empty" Classes that are used to vary from the original stock.picking (that are dedicated to the internal pickings)
# in order to offer a different usability with different views, labels, available reports/wizards...
#----------------------------------------------------------
class stock_picking_in ( osv . osv ) :
_name = " stock.picking.in "
_inherit = " stock.picking "
_table = " stock_picking "
2012-07-02 09:41:47 +00:00
_description = " Incoming Shipments "
2012-05-04 15:36:13 +00:00
2013-04-05 11:16:49 +00:00
def search ( self , cr , user , args , offset = 0 , limit = None , order = None , context = None , count = False ) :
return self . pool . get ( ' stock.picking ' ) . search ( cr , user , args , offset , limit , order , context , count )
2013-04-05 15:30:21 +00:00
2013-04-05 11:16:49 +00:00
def read ( self , cr , uid , ids , fields = None , context = None , load = ' _classic_read ' ) :
return self . pool . get ( ' stock.picking ' ) . read ( cr , uid , ids , fields = fields , context = context , load = load )
2012-05-04 15:36:13 +00:00
2014-04-17 16:49:53 +00:00
def read_group ( self , cr , uid , domain , fields , groupby , offset = 0 , limit = None , context = None , orderby = False ) :
2014-04-23 15:06:21 +00:00
return self . pool [ ' stock.picking ' ] . read_group ( cr , uid , domain , fields , groupby , offset = offset , limit = limit , context = context , orderby = orderby )
2014-04-17 16:49:53 +00:00
2012-05-04 15:36:13 +00:00
def check_access_rights ( self , cr , uid , operation , raise_exception = True ) :
#override in order to redirect the check of acces rights on the stock.picking object
return self . pool . get ( ' stock.picking ' ) . check_access_rights ( cr , uid , operation , raise_exception = raise_exception )
def check_access_rule ( self , cr , uid , ids , operation , context = None ) :
#override in order to redirect the check of acces rules on the stock.picking object
return self . pool . get ( ' stock.picking ' ) . check_access_rule ( cr , uid , ids , operation , context = context )
2013-02-13 14:00:52 +00:00
def create_workflow ( self , cr , uid , ids , context = None ) :
# overridden in order to trigger the workflow of stock.picking at the end of create,
# write and unlink operation instead of its own workflow (which is not existing)
return self . pool . get ( ' stock.picking ' ) . create_workflow ( cr , uid , ids , context = context )
def delete_workflow ( self , cr , uid , ids , context = None ) :
# overridden in order to trigger the workflow of stock.picking at the end of create,
# write and unlink operation instead of its own workflow (which is not existing)
return self . pool . get ( ' stock.picking ' ) . delete_workflow ( cr , uid , ids , context = context )
2013-02-13 15:01:36 +00:00
def step_workflow ( self , cr , uid , ids , context = None ) :
2013-02-13 14:00:52 +00:00
# overridden in order to trigger the workflow of stock.picking at the end of create,
# write and unlink operation instead of its own workflow (which is not existing)
2013-02-13 15:01:36 +00:00
return self . pool . get ( ' stock.picking ' ) . step_workflow ( cr , uid , ids , context = context )
2013-02-13 14:00:52 +00:00
def signal_workflow ( self , cr , uid , ids , signal , context = None ) :
# overridden in order to fire the workflow signal on given stock.picking workflow instance
# instead of its own workflow (which is not existing)
return self . pool . get ( ' stock.picking ' ) . signal_workflow ( cr , uid , ids , signal , context = context )
2012-09-19 08:04:09 +00:00
2013-08-28 15:14:34 +00:00
def message_post ( self , * args , * * kwargs ) :
""" Post the message on stock.picking to be able to see it in the form view when using the chatter """
return self . pool . get ( ' stock.picking ' ) . message_post ( * args , * * kwargs )
2013-09-09 08:19:52 +00:00
def message_subscribe ( self , * args , * * kwargs ) :
""" Send the subscribe action on stock.picking model as it uses _name in request """
return self . pool . get ( ' stock.picking ' ) . message_subscribe ( * args , * * kwargs )
def message_unsubscribe ( self , * args , * * kwargs ) :
""" Send the unsubscribe action on stock.picking model to match with subscribe """
return self . pool . get ( ' stock.picking ' ) . message_unsubscribe ( * args , * * kwargs )
2013-11-12 17:01:33 +00:00
def default_get ( self , cr , uid , fields_list , context = None ) :
# merge defaults from stock.picking with possible defaults defined on stock.picking.in
defaults = self . pool [ ' stock.picking ' ] . default_get ( cr , uid , fields_list , context = context )
2013-11-21 15:32:30 +00:00
in_defaults = super ( stock_picking_in , self ) . default_get ( cr , uid , fields_list , context = context )
2013-11-12 17:01:33 +00:00
defaults . update ( in_defaults )
return defaults
2012-05-04 15:36:13 +00:00
_columns = {
2012-09-21 11:00:47 +00:00
' backorder_id ' : fields . many2one ( ' stock.picking.in ' , ' Back Order of ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " If this shipment was split, then this field links to the shipment which contains the already processed part. " , select = True ) ,
2012-05-04 15:36:13 +00:00
' state ' : fields . selection (
[ ( ' draft ' , ' Draft ' ) ,
( ' auto ' , ' Waiting Another Operation ' ) ,
( ' confirmed ' , ' Waiting Availability ' ) ,
( ' assigned ' , ' Ready to Receive ' ) ,
( ' done ' , ' Received ' ) ,
2012-08-07 11:06:16 +00:00
( ' cancel ' , ' Cancelled ' ) , ] ,
2012-10-08 13:20:14 +00:00
' Status ' , readonly = True , select = True ,
2012-05-04 15:36:13 +00:00
help = """ * Draft: not confirmed yet and will not be scheduled until confirmed \n
* Waiting Another Operation : waiting for another move to proceed before it becomes automatically available ( e . g . in Make - To - Order flows ) \n
* Waiting Availability : still waiting for the availability of products \n
* Ready to Receive : products reserved , simply waiting for confirmation . \n
* Received : has been processed , can ' t be modified or cancelled anymore \n
* Cancelled : has been cancelled , can ' t be confirmed anymore " " " ),
}
2012-05-07 15:43:56 +00:00
_defaults = {
' type ' : ' in ' ,
}
2012-05-04 15:36:13 +00:00
class stock_picking_out ( osv . osv ) :
_name = " stock.picking.out "
_inherit = " stock.picking "
_table = " stock_picking "
_description = " Delivery Orders "
2013-04-05 11:16:49 +00:00
def search ( self , cr , user , args , offset = 0 , limit = None , order = None , context = None , count = False ) :
return self . pool . get ( ' stock.picking ' ) . search ( cr , user , args , offset , limit , order , context , count )
def read ( self , cr , uid , ids , fields = None , context = None , load = ' _classic_read ' ) :
return self . pool . get ( ' stock.picking ' ) . read ( cr , uid , ids , fields = fields , context = context , load = load )
2012-05-04 15:36:13 +00:00
2014-04-17 16:49:53 +00:00
def read_group ( self , cr , uid , domain , fields , groupby , offset = 0 , limit = None , context = None , orderby = False ) :
2014-04-23 15:06:21 +00:00
return self . pool [ ' stock.picking ' ] . read_group ( cr , uid , domain , fields , groupby , offset = offset , limit = limit , context = context , orderby = orderby )
2014-04-17 16:49:53 +00:00
2012-05-04 15:36:13 +00:00
def check_access_rights ( self , cr , uid , operation , raise_exception = True ) :
#override in order to redirect the check of acces rights on the stock.picking object
return self . pool . get ( ' stock.picking ' ) . check_access_rights ( cr , uid , operation , raise_exception = raise_exception )
def check_access_rule ( self , cr , uid , ids , operation , context = None ) :
#override in order to redirect the check of acces rules on the stock.picking object
return self . pool . get ( ' stock.picking ' ) . check_access_rule ( cr , uid , ids , operation , context = context )
2013-02-13 14:00:52 +00:00
def create_workflow ( self , cr , uid , ids , context = None ) :
# overridden in order to trigger the workflow of stock.picking at the end of create,
# write and unlink operation instead of its own workflow (which is not existing)
return self . pool . get ( ' stock.picking ' ) . create_workflow ( cr , uid , ids , context = context )
def delete_workflow ( self , cr , uid , ids , context = None ) :
# overridden in order to trigger the workflow of stock.picking at the end of create,
# write and unlink operation instead of its own workflow (which is not existing)
return self . pool . get ( ' stock.picking ' ) . delete_workflow ( cr , uid , ids , context = context )
2013-02-13 15:01:36 +00:00
def step_workflow ( self , cr , uid , ids , context = None ) :
2013-02-13 14:00:52 +00:00
# overridden in order to trigger the workflow of stock.picking at the end of create,
# write and unlink operation instead of its own workflow (which is not existing)
2013-02-13 15:01:36 +00:00
return self . pool . get ( ' stock.picking ' ) . step_workflow ( cr , uid , ids , context = context )
2013-02-13 14:00:52 +00:00
def signal_workflow ( self , cr , uid , ids , signal , context = None ) :
# overridden in order to fire the workflow signal on given stock.picking workflow instance
# instead of its own workflow (which is not existing)
return self . pool . get ( ' stock.picking ' ) . signal_workflow ( cr , uid , ids , signal , context = context )
2012-09-17 18:10:59 +00:00
2013-08-28 15:14:34 +00:00
def message_post ( self , * args , * * kwargs ) :
""" Post the message on stock.picking to be able to see it in the form view when using the chatter """
return self . pool . get ( ' stock.picking ' ) . message_post ( * args , * * kwargs )
2013-09-09 08:19:52 +00:00
def message_subscribe ( self , * args , * * kwargs ) :
""" Send the subscribe action on stock.picking model as it uses _name in request """
return self . pool . get ( ' stock.picking ' ) . message_subscribe ( * args , * * kwargs )
def message_unsubscribe ( self , * args , * * kwargs ) :
""" Send the unsubscribe action on stock.picking model to match with subscribe """
return self . pool . get ( ' stock.picking ' ) . message_unsubscribe ( * args , * * kwargs )
2013-11-12 17:01:33 +00:00
def default_get ( self , cr , uid , fields_list , context = None ) :
# merge defaults from stock.picking with possible defaults defined on stock.picking.out
defaults = self . pool [ ' stock.picking ' ] . default_get ( cr , uid , fields_list , context = context )
out_defaults = super ( stock_picking_out , self ) . default_get ( cr , uid , fields_list , context = context )
defaults . update ( out_defaults )
return defaults
2012-05-04 15:36:13 +00:00
_columns = {
2012-09-21 11:00:47 +00:00
' backorder_id ' : fields . many2one ( ' stock.picking.out ' , ' Back Order of ' , states = { ' done ' : [ ( ' readonly ' , True ) ] , ' cancel ' : [ ( ' readonly ' , True ) ] } , help = " If this shipment was split, then this field links to the shipment which contains the already processed part. " , select = True ) ,
2012-05-04 15:36:13 +00:00
' state ' : fields . selection (
[ ( ' draft ' , ' Draft ' ) ,
( ' auto ' , ' Waiting Another Operation ' ) ,
( ' confirmed ' , ' Waiting Availability ' ) ,
( ' assigned ' , ' Ready to Deliver ' ) ,
( ' done ' , ' Delivered ' ) ,
2012-08-07 11:06:16 +00:00
( ' cancel ' , ' Cancelled ' ) , ] ,
2012-10-12 11:42:58 +00:00
' Status ' , readonly = True , select = True ,
2012-05-04 15:36:13 +00:00
help = """ * Draft: not confirmed yet and will not be scheduled until confirmed \n
* Waiting Another Operation : waiting for another move to proceed before it becomes automatically available ( e . g . in Make - To - Order flows ) \n
* Waiting Availability : still waiting for the availability of products \n
* Ready to Deliver : products reserved , simply waiting for confirmation . \n
* Delivered : has been processed , can ' t be modified or cancelled anymore \n
* Cancelled : has been cancelled , can ' t be confirmed anymore " " " ),
}
2012-05-07 15:43:56 +00:00
_defaults = {
' type ' : ' out ' ,
}
2012-05-04 15:36:13 +00:00
2010-03-31 14:58:35 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: