2006-12-07 13:41:40 +00:00
##############################################################################
#
# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# $Id: stock.py 1005 2005-07-25 08:41:42Z nicoe $
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import datetime
import time
import netsvc
from osv import fields , osv
import ir
2007-08-07 14:30:19 +00:00
from tools import config
2006-12-07 13:41:40 +00:00
#----------------------------------------------------------
# Incoterms
#----------------------------------------------------------
class stock_incoterms ( osv . osv ) :
_name = " stock.incoterms "
_description = " Incoterms "
_columns = {
' name ' : fields . char ( ' Name ' , size = 64 , required = True ) ,
' code ' : fields . char ( ' Code ' , size = 3 , required = True ) ,
' active ' : fields . boolean ( ' Active ' ) ,
}
_defaults = {
' active ' : lambda * a : True ,
}
stock_incoterms ( )
class stock_lot ( osv . osv ) :
_name = " stock.lot "
_description = " Lot "
_columns = {
' name ' : fields . char ( ' Lot Name ' , size = 64 , required = True ) ,
' active ' : fields . boolean ( ' Active ' ) ,
' tracking ' : fields . char ( ' Tracking ' , size = 64 ) ,
' move_ids ' : fields . one2many ( ' stock.move ' , ' lot_id ' , ' Move lines ' ) ,
}
_defaults = {
' active ' : lambda * a : True ,
}
stock_lot ( )
#----------------------------------------------------------
# Stock Location
#----------------------------------------------------------
class stock_location ( osv . osv ) :
_name = " stock.location "
_description = " Location "
2007-06-07 08:43:46 +00:00
_parent_name = " location_id "
2006-12-07 13:41:40 +00:00
_columns = {
' name ' : fields . char ( ' Location Name ' , size = 64 , required = True ) ,
' active ' : fields . boolean ( ' Active ' ) ,
' usage ' : fields . selection ( [ ( ' supplier ' , ' Supplier Location ' ) , ( ' internal ' , ' Internal Location ' ) , ( ' customer ' , ' Customer Location ' ) , ( ' inventory ' , ' Inventory ' ) , ( ' procurement ' , ' Procurement ' ) , ( ' production ' , ' Production ' ) ] , ' Location type ' ) ,
' allocation_method ' : fields . selection ( [ ( ' fifo ' , ' FIFO ' ) , ( ' lifo ' , ' LIFO ' ) , ( ' nearest ' , ' Nearest ' ) ] , ' Allocation Method ' , required = True ) ,
2007-04-25 14:08:50 +00:00
' account_id ' : fields . many2one ( ' account.account ' , string = ' Inventory Account ' , domain = [ ( ' type ' , ' != ' , ' view ' ) ] ) ,
2006-12-07 13:41:40 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Parent Location ' , select = True ) ,
' child_ids ' : fields . one2many ( ' stock.location ' , ' location_id ' , ' Contains ' ) ,
' comment ' : fields . text ( ' Additional Information ' ) ,
2007-08-08 20:30:40 +00:00
' posx ' : fields . integer ( ' Corridor (X) ' , required = True ) ,
' posy ' : fields . integer ( ' Shelves (Y) ' , required = True ) ,
' posz ' : fields . integer ( ' Height (Z) ' , required = True )
2006-12-07 13:41:40 +00:00
}
_defaults = {
' active ' : lambda * a : 1 ,
' usage ' : lambda * a : ' internal ' ,
' allocation_method ' : lambda * a : ' fifo ' ,
' posx ' : lambda * a : 0 ,
' posy ' : lambda * a : 0 ,
' posz ' : lambda * a : 0 ,
}
2007-10-05 05:24:29 +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 )
def _product_get_report ( self , cr , uid , ids , product_ids = False ,
context = None , recursive = False ) :
if context is None :
context = { }
product_obj = self . pool . get ( ' product.product ' )
2006-12-07 13:41:40 +00:00
if not product_ids :
2007-10-05 05:24:29 +00:00
product_ids = product_obj . search ( cr , uid , [ ] )
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
2006-12-07 13:41:40 +00:00
result = [ ]
for id in ids :
2007-10-05 05:24:29 +00:00
for uom_id in products_by_uom . keys ( ) :
2006-12-07 13:41:40 +00:00
fnc = self . _product_get
if recursive :
fnc = self . _product_all_get
2007-10-05 05:24:29 +00:00
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 ]
2006-12-07 13:41:40 +00:00
result . append ( {
' price ' : product . list_price ,
' name ' : product . name ,
2007-10-05 05:24:29 +00:00
' code ' : product . default_code , # used by lot_overview_all report!
2006-12-07 13:41:40 +00:00
' variants ' : product . variants or ' ' ,
' uom ' : product . uom_id . name ,
2007-10-05 05:24:29 +00:00
' amount ' : qty [ product_id ] ,
2006-12-07 13:41:40 +00:00
} )
return result
def _product_get_multi_location ( self , cr , uid , ids , product_ids = False , context = { } , states = [ ' done ' ] , what = ( ' in ' , ' out ' ) ) :
2008-01-04 08:04:42 +00:00
product_obj = self . pool . get ( ' product.product ' )
2006-12-07 13:41:40 +00:00
states_str = ' , ' . join ( map ( lambda s : " ' %s ' " % s , states ) )
if not product_ids :
2008-01-04 08:04:42 +00:00
product_ids = product_obj . search ( cr , uid , [ ] )
2006-12-07 13:41:40 +00:00
res = { }
for id in product_ids :
res [ id ] = 0.0
if not ids :
return res
2008-01-04 08:04:42 +00:00
product2uom = { }
for product in product_obj . browse ( cr , uid , product_ids , context = context ) :
product2uom [ product . id ] = product . uom_id . id
2006-12-07 13:41:40 +00:00
prod_ids_str = ' , ' . join ( map ( str , product_ids ) )
location_ids_str = ' , ' . join ( map ( str , ids ) )
results = [ ]
2007-10-05 13:31:05 +00:00
results2 = [ ]
2006-12-07 13:41:40 +00:00
if ' in ' in what :
# all moves from a location out of the set to a location in the set
cr . execute (
' select sum(product_qty), product_id, product_uom ' \
' from stock_move ' \
' where location_id not in ( ' + location_ids_str + ' ) ' \
' and location_dest_id in ( ' + location_ids_str + ' ) ' \
' and product_id in ( ' + prod_ids_str + ' ) ' \
' and state in ( ' + states_str + ' ) ' \
' group by product_id,product_uom '
)
2007-10-05 13:31:05 +00:00
results = cr . fetchall ( )
2006-12-07 13:41:40 +00:00
if ' out ' in what :
# all moves from a location in the set to a location out of the set
cr . execute (
2007-10-10 05:44:33 +00:00
' select sum(product_qty), product_id, product_uom ' \
2006-12-07 13:41:40 +00:00
' from stock_move ' \
' where location_id in ( ' + location_ids_str + ' ) ' \
' and location_dest_id not in ( ' + location_ids_str + ' ) ' \
' and product_id in ( ' + prod_ids_str + ' ) ' \
' and state in ( ' + states_str + ' ) ' \
' group by product_id,product_uom '
)
2007-10-05 13:31:05 +00:00
results2 = cr . fetchall ( )
2006-12-07 13:41:40 +00:00
uom_obj = self . pool . get ( ' product.uom ' )
for amount , prod_id , prod_uom in results :
2007-10-05 13:31:05 +00:00
amount = uom_obj . _compute_qty ( cr , uid , prod_uom , amount ,
2008-01-04 08:04:42 +00:00
context . get ( ' uom ' , product2uom [ prod_id ] ) )
2006-12-07 13:41:40 +00:00
res [ prod_id ] + = amount
2007-10-05 13:31:05 +00:00
for amount , prod_id , prod_uom in results2 :
amount = uom_obj . _compute_qty ( cr , uid , prod_uom , amount ,
2008-01-04 08:04:42 +00:00
context . get ( ' uom ' , product2uom [ prod_id ] ) )
2007-10-05 13:31:05 +00:00
res [ prod_id ] - = amount
2006-12-07 13:41:40 +00:00
return res
2007-10-05 13:31:05 +00:00
2006-12-07 13:41:40 +00:00
def _product_get ( self , cr , uid , id , product_ids = False , context = { } , states = [ ' done ' ] ) :
ids = id and [ id ] or [ ]
return self . _product_get_multi_location ( cr , uid , ids , product_ids , context , states )
def _product_all_get ( self , cr , uid , id , product_ids = False , context = { } , states = [ ' done ' ] ) :
# 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 )
def _product_virtual_get ( self , cr , uid , id , product_ids = False , context = { } , states = [ ' done ' ] ) :
return self . _product_all_get ( cr , uid , id , product_ids , context , [ ' confirmed ' , ' waiting ' , ' assigned ' , ' done ' ] )
#
# TODO:
# Improve this function
#
# Returns:
# [ (tracking_id, product_qty, location_id) ]
#
def _product_reserve ( self , cr , uid , ids , product_id , product_qty , context = { } ) :
result = [ ]
amount = 0.0
for id in self . search ( cr , uid , [ ( ' location_id ' , ' child_of ' , ids ) ] ) :
cr . execute ( " select product_uom,sum(product_qty) as product_qty from stock_move where location_dest_id= %d and product_id= %d and state= ' done ' group by product_uom " , ( id , product_id ) )
results = cr . dictfetchall ( )
cr . execute ( " select product_uom,-sum(product_qty) as product_qty from stock_move where location_id= %d and product_id= %d and state in ( ' done ' , ' assigned ' ) group by product_uom " , ( id , product_id ) )
results + = cr . dictfetchall ( )
total = 0.0
results2 = 0.0
for r in results :
amount = self . pool . get ( ' product.uom ' ) . _compute_qty ( cr , uid , r [ ' product_uom ' ] , r [ ' product_qty ' ] , context . get ( ' uom ' , False ) )
results2 + = amount
total + = amount
if total < = 0.0 :
continue
amount = results2
if amount > 0 :
if amount > min ( total , product_qty ) :
amount = min ( product_qty , total )
result . append ( ( amount , id ) )
product_qty - = amount
total - = amount
if product_qty < = 0.0 :
return result
if total < = 0.0 :
continue
return False
stock_location ( )
#----------------------------------------------------------
# Stock Move
#----------------------------------------------------------
class stock_move_lot ( osv . osv ) :
_name = " stock.move.lot "
_description = " Move Lot "
_columns = {
' name ' : fields . char ( ' Move Description ' , size = 64 , required = True ) ,
' active ' : fields . boolean ( ' Active ' ) ,
' state ' : fields . selection ( ( ( ' draft ' , ' Draft ' ) , ( ' done ' , ' Moved ' ) ) , ' State ' , readonly = True ) ,
' serial ' : fields . char ( ' Tracking Number ' , size = 32 ) ,
2007-08-08 19:55:39 +00:00
' date_planned ' : fields . date ( ' Scheduled date ' ) ,
' date_moved ' : fields . date ( ' Actual date ' ) ,
2006-12-07 13:41:40 +00:00
' lot_id ' : fields . many2one ( ' stock.lot ' , ' Lot ' , required = True ) ,
' loc_dest_id ' : fields . many2one ( ' stock.location ' , ' Destination Location ' , required = True ) ,
' address_id ' : fields . many2one ( ' res.partner.address ' , ' Destination Address ' ) ,
' origin ' : fields . char ( ' Origin ' , size = 64 ) ,
}
_defaults = {
' active ' : lambda * a : 1 ,
' state ' : lambda * a : ' draft ' ,
' date_planned ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
}
#
# TODO: test if valid
# ERROR: does this function should call action_done instead of doing him self on
# stock.move
#
def action_move ( self , cr , uid , ids , context = { } ) :
for move in self . browse ( cr , uid , ids , context ) :
lot_remove = [ ]
for m in move . lot_id . move_ids :
new_id = self . pool . get ( ' stock.move ' ) . copy ( cr , uid , m . id , { ' location_id ' : m . location_dest_id . id , ' location_dest_id ' : move . loc_dest_id . id , ' date_moved ' : time . strftime ( ' % Y- % m- %d ' ) , ' picking_id ' : False , ' state ' : ' draft ' , ' prodlot_id ' : False , ' tracking_id ' : False , ' lot_id ' : False , ' move_history_ids ' : [ ] , ' move_history_ids2 ' : [ ] } )
self . pool . get ( ' stock.move ' ) . action_done ( cr , uid , [ new_id ] , context )
cr . execute ( ' insert into stock_move_history_ids (parent_id,child_id) values ( %d , %d ) ' , ( m . id , new_id ) )
lot_remove . append ( m . id )
self . pool . get ( ' stock.move ' ) . write ( cr , uid , lot_remove , { ' lot_id ' : False } )
self . write ( cr , uid , ids , { ' state ' : ' done ' , ' date_moved ' : time . strftime ( ' % Y- % m- %d ' ) } )
return True
stock_move_lot ( )
class stock_tracking ( osv . osv ) :
_name = " stock.tracking "
_description = " Stock Tracking Lots "
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 )
def make_sscc ( self , cr , uid , context = { } ) :
sequence = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.lot.tracking ' )
return sequence + str ( self . checksum ( sequence ) )
_columns = {
' name ' : fields . char ( ' Tracking ' , size = 64 , required = True ) ,
' active ' : fields . boolean ( ' Active ' ) ,
' serial ' : fields . char ( ' Reference ' , size = 64 ) ,
' move_ids ' : fields . one2many ( ' stock.move ' , ' tracking_id ' , ' Moves tracked ' ) ,
' date ' : fields . datetime ( ' Date create ' , required = True ) ,
}
_defaults = {
' active ' : lambda * a : 1 ,
' name ' : make_sscc ,
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
}
2007-10-10 13:32:33 +00:00
2007-07-26 08:31:51 +00:00
def name_search ( self , cr , user , name , args = None , operator = ' ilike ' , context = None , limit = 80 ) :
if not args :
args = [ ]
if not context :
context = { }
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 )
return self . name_get ( cr , user , ids , context )
2006-12-07 13:41:40 +00:00
def name_get ( self , cr , uid , ids , context = { } ) :
if not len ( ids ) :
return [ ]
res = [ ( r [ ' id ' ] , r [ ' name ' ] + ' [ ' + ( r [ ' serial ' ] or ' ' ) + ' ] ' ) for r in self . read ( cr , uid , ids , [ ' name ' , ' serial ' ] , context ) ]
return res
def unlink ( self , cr , uid , ids ) :
2007-08-09 08:18:06 +00:00
raise Exception , ' You can not remove a lot line ! '
2006-12-07 13:41:40 +00:00
stock_tracking ( )
#----------------------------------------------------------
# Stock Picking
#----------------------------------------------------------
class stock_picking ( osv . osv ) :
_name = " stock.picking "
2007-06-27 13:59:46 +00:00
_description = " Packing list "
2006-12-07 13:41:40 +00:00
_columns = {
2007-06-27 13:59:46 +00:00
' name ' : fields . char ( ' Packing name ' , size = 64 , required = True , select = True ) ,
2006-12-07 13:41:40 +00:00
' origin ' : fields . char ( ' Origin ' , size = 64 ) ,
' type ' : fields . selection ( [ ( ' out ' , ' Sending Goods ' ) , ( ' in ' , ' Getting Goods ' ) , ( ' internal ' , ' Internal ' ) ] , ' Shipping Type ' , required = True , select = True ) ,
' active ' : fields . boolean ( ' Active ' ) ,
' note ' : fields . text ( ' Notes ' ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' ) ,
' location_dest_id ' : fields . many2one ( ' stock.location ' , ' Dest. Location ' ) ,
' move_type ' : fields . selection ( [ ( ' direct ' , ' Direct Delivery ' ) , ( ' one ' , ' All at once ' ) ] , ' Delivery Method ' , required = True ) ,
' state ' : fields . selection ( [
2007-07-20 12:04:33 +00:00
( ' draft ' , ' Draft ' ) ,
( ' auto ' , ' Waiting ' ) ,
( ' confirmed ' , ' Confirmed ' ) ,
( ' assigned ' , ' Assigned ' ) ,
( ' done ' , ' Done ' ) ,
( ' cancel ' , ' Cancel ' ) ,
2006-12-07 13:41:40 +00:00
] , ' State ' , readonly = True ) ,
' date ' : fields . datetime ( ' Date create ' ) ,
2007-08-08 20:17:24 +00:00
' move_lines ' : fields . one2many ( ' stock.move ' , ' picking_id ' , ' Move lines ' ) ,
2006-12-07 13:41:40 +00:00
2007-06-27 13:59:46 +00:00
' auto_picking ' : fields . boolean ( ' Auto-Packing ' ) ,
2006-12-07 13:41:40 +00:00
' work ' : fields . boolean ( ' Work todo ' ) ,
2007-08-08 20:17:24 +00:00
' loc_move_id ' : fields . many2one ( ' stock.location ' , ' Final location ' ) ,
2007-01-14 22:01:04 +00:00
' address_id ' : fields . many2one ( ' res.partner.address ' , ' Partner ' ) ,
2007-08-08 20:17:24 +00:00
' lot_id ' : fields . many2one ( ' stock.lot ' , ' Consumer lot created ' ) ,
' move_lot_id ' : fields . many2one ( ' stock.move.lot ' , ' Moves created ' ) ,
2006-12-07 13:41:40 +00:00
' invoice_state ' : fields . selection ( [
( " invoiced " , " Invoiced " ) ,
( " 2binvoiced " , " To be invoiced " ) ,
2007-10-18 06:47:57 +00:00
( " none " , " Not from Packing " ) ] , " Invoice state " ) ,
2006-12-07 13:41:40 +00:00
}
_defaults = {
2007-01-23 07:49:58 +00:00
' name ' : lambda * a : ' / ' ,
2006-12-07 13:41:40 +00:00
' work ' : lambda * a : 0 ,
' active ' : lambda * a : 1 ,
' state ' : lambda * a : ' draft ' ,
' move_type ' : lambda * a : ' direct ' ,
' type ' : lambda * a : ' in ' ,
' invoice_state ' : lambda * a : ' none ' ,
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
}
def action_confirm ( self , cr , uid , ids , * args ) :
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
todo = [ ]
for picking in self . browse ( cr , uid , ids ) :
2007-12-11 08:14:47 +00:00
if picking . name == self . _defaults [ ' name ' ] ( ) :
number = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.picking. %s ' % picking . type )
self . write ( cr , uid , [ picking . id ] , { ' name ' : number } )
2006-12-07 13:41:40 +00:00
for r in picking . move_lines :
if r . state == ' draft ' :
todo . append ( r . id )
if len ( todo ) :
self . pool . get ( ' stock.move ' ) . action_confirm ( cr , uid , todo )
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 ) :
for pick in self . browse ( cr , uid , ids ) :
move_ids = [ x . id for x in pick . move_lines if x . state == ' confirmed ' ]
self . pool . get ( ' stock.move ' ) . action_assign ( cr , uid , move_ids )
return True
def force_assign ( self , cr , uid , ids , * args ) :
wf_service = netsvc . LocalService ( " workflow " )
for pick in self . browse ( cr , uid , ids ) :
# move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed']
move_ids = [ x . id for x in pick . move_lines ]
self . pool . get ( ' stock.move ' ) . force_assign ( cr , uid , move_ids )
wf_service . trg_write ( uid , ' stock.picking ' , pick . id , cr )
return True
2006-12-18 14:10:01 +00:00
def cancel_assign ( self , cr , uid , ids , * args ) :
wf_service = netsvc . LocalService ( " workflow " )
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 )
wf_service . trg_write ( uid , ' stock.picking ' , pick . id , cr )
return True
2006-12-07 13:41:40 +00:00
def action_assign_wkf ( self , cr , uid , ids ) :
self . write ( cr , uid , ids , { ' state ' : ' assigned ' } )
return True
def test_finnished ( self , cr , uid , ids ) :
2007-04-13 13:33:23 +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 ' ) :
if move . product_qty != 0.0 :
2006-12-07 13:41:40 +00:00
return False
2007-04-13 13:33:23 +00:00
else :
move . write ( cr , uid , [ move . id ] , { ' state ' : ' done ' } )
2006-12-07 13:41:40 +00:00
return True
def test_assigned ( self , cr , uid , ids ) :
ok = True
for pick in self . browse ( cr , uid , ids ) :
mt = pick . move_type
for move in pick . move_lines :
if ( move . state in ( ' confirmed ' , ' draft ' ) ) and ( mt == ' one ' ) :
return False
2006-12-18 06:33:52 +00:00
if ( mt == ' direct ' ) and ( move . state == ' assigned ' ) and ( move . product_qty ) :
2006-12-07 13:41:40 +00:00
return True
ok = ok and ( move . state in ( ' cancel ' , ' done ' , ' assigned ' ) )
return ok
def action_cancel ( self , cr , uid , ids , context = { } ) :
for pick in self . browse ( cr , uid , ids ) :
ids2 = [ move . id for move in pick . move_lines ]
self . pool . get ( ' stock.move ' ) . action_cancel ( cr , uid , ids2 , context )
self . write ( cr , uid , ids , { ' state ' : ' cancel ' , ' invoice_state ' : ' none ' } )
return True
#
# TODO: change and create a move if not parents
#
2007-12-12 15:39:54 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2006-12-07 13:41:40 +00:00
for pick in self . browse ( cr , uid , ids ) :
if pick . move_type == ' one ' and pick . loc_move_id :
if pick . lot_id :
id = self . pool . get ( ' stock.move.lot ' ) . create ( cr , uid , {
' name ' : ' MOVE: ' + pick . name ,
' origin ' : ' PICK: ' + str ( pick . id ) ,
' lot_id ' : pick . lot_id . id ,
' loc_dest_id ' : pick . loc_move_id . id ,
' address_id ' : pick . address_id . id
} )
self . write ( cr , uid , [ pick . id ] , { ' move_lot_id ' : id } )
self . write ( cr , uid , ids , { ' state ' : ' done ' } )
return True
def action_move ( self , cr , uid , ids , context = { } ) :
for pick in self . browse ( cr , uid , ids ) :
todo = [ ]
for move in pick . move_lines :
if move . state == ' assigned ' :
todo . append ( move . id )
if len ( todo ) :
2007-12-12 15:39:54 +00:00
self . pool . get ( ' stock.move ' ) . action_done ( cr , uid , todo ,
context = context )
2006-12-07 13:41:40 +00:00
2007-04-12 08:19:28 +00:00
lot_id = self . pool . get ( ' stock.lot ' ) . create ( cr , uid , { ' name ' : pick . name } )
2006-12-07 13:41:40 +00:00
self . pool . get ( ' stock.move ' ) . write ( cr , uid , todo , { ' lot_id ' : lot_id } )
self . write ( cr , uid , [ pick . id ] , { ' lot_id ' : lot_id } )
if pick . move_type == ' direct ' and pick . loc_move_id :
id = self . pool . get ( ' stock.move.lot ' ) . create ( cr , uid , {
' name ' : ' MOVE: ' + pick . name ,
' origin ' : ' PICK: ' + str ( pick . id ) ,
' lot_id ' : lot_id ,
' loc_dest_id ' : pick . loc_move_id . id ,
' address_id ' : pick . address_id . id
} )
self . write ( cr , uid , [ pick . id ] , { ' move_lot_id ' : id } )
return True
2007-10-19 12:15:57 +00:00
def _get_address_invoice ( self , cursor , user , picking ) :
''' Return { ' contact ' : address, ' invoice ' : address} for invoice '''
partner_obj = self . pool . get ( ' res.partner ' )
partner = picking . address_id . partner_id
return partner_obj . address_get ( cursor , user , [ partner . id ] ,
[ ' contact ' , ' invoice ' ] )
def _get_comment_invoice ( self , cursor , user , picking ) :
''' Return comment string for invoice '''
return picking . note or ' '
def _get_price_unit_invoice ( self , cursor , user , move_line , type ) :
''' Return the price unit for the move line '''
if type in ( ' in_invoice ' , ' in_refund ' ) :
return move_line . product_id . standard_price
else :
return move_line . product_id . list_price
def _get_discount_invoice ( self , cursor , user , move_line ) :
''' Return the discount for the move line '''
return 0.0
def _get_taxes_invoice ( self , cursor , user , move_line , type ) :
''' Return taxes ids for the move line '''
if type in ( ' in_invoice ' , ' in_refund ' ) :
return [ x . id for x in move_line . product_id . supplier_taxes_id ]
else :
return [ x . id for x in move_line . product_id . taxes_id ]
def _get_account_analytic_invoice ( self , cursor , user , picking , move_line ) :
return False
def _invoice_line_hook ( self , cursor , user , move_line , invoice_line_id ) :
''' Call after the creation of the invoice line '''
return
def _invoice_hook ( self , cursor , user , picking , invoice_id ) :
''' Call after the creation of the invoice '''
return
def action_invoice_create ( self , cursor , user , ids , journal_id = False ,
group = False , type = ' out_invoice ' , context = None ) :
''' Return ids of created invoices for the pickings '''
invoice_obj = self . pool . get ( ' account.invoice ' )
invoice_line_obj = self . pool . get ( ' account.invoice.line ' )
invoices_group = { }
res = { }
for picking in self . browse ( cursor , user , ids , context = context ) :
if picking . invoice_state != ' 2binvoiced ' :
2006-12-07 13:41:40 +00:00
continue
2007-10-19 12:15:57 +00:00
partner = picking . address_id . partner_id
if type in ( ' out_invoice ' , ' out_refund ' ) :
account_id = partner . property_account_receivable . id
2006-12-07 13:41:40 +00:00
else :
2007-10-19 12:15:57 +00:00
account_id = partner . property_account_payable . id
payment_term_id = False
if partner . property_payment_term :
payment_term_id = partner . property_payment_term . id
2006-12-07 13:41:40 +00:00
2007-10-19 12:15:57 +00:00
address_contact_id , address_invoice_id = \
self . _get_address_invoice ( cursor , user , picking ) . values ( )
2006-12-07 13:41:40 +00:00
2007-10-19 12:15:57 +00:00
comment = self . _get_comment_invoice ( cursor , user , picking )
2006-12-07 13:41:40 +00:00
2007-10-19 12:15:57 +00:00
if group and partner . id in invoices_group :
invoice_id = invoices_group [ partner . id ]
else :
invoice_vals = {
' name ' : picking . name ,
' origin ' : picking . name + ' : ' + picking . origin ,
' type ' : type ,
' account_id ' : account_id ,
' partner_id ' : partner . id ,
' address_invoice_id ' : address_invoice_id ,
' address_contact_id ' : address_contact_id ,
' comment ' : comment ,
' payment_term ' : payment_term_id ,
}
if journal_id :
invoice_vals [ ' journal_id ' ] = journal_id
invoice_id = invoice_obj . create ( cursor , user , invoice_vals ,
context = context )
invoices_group [ partner . id ] = invoice_id
res [ picking . id ] = invoice_id
for move_line in picking . move_lines :
if group :
name = picking . name + ' - ' + move_line . name
2007-02-15 13:57:11 +00:00
else :
2007-10-19 12:15:57 +00:00
name = move_line . name
if type in ( ' out_invoice ' , ' out_refund ' ) :
account_id = move_line . product_id . product_tmpl_id . \
property_account_income . id
if not account_id :
account_id = move_line . product_id . categ_id . \
property_account_income_categ . id
else :
account_id = move_line . product_id . product_tmpl_id . \
property_account_expense . id
if not account_id :
account_id = move_line . product_id . categ_id . \
property_account_expense_categ . id
price_unit = self . _get_price_unit_invoice ( cursor , user ,
move_line , type )
discount = self . _get_discount_invoice ( cursor , user , move_line )
tax_ids = self . _get_taxes_invoice ( cursor , user , move_line , type )
account_analytic_id = self . _get_account_analytic_invoice ( cursor ,
user , picking , move_line )
invoice_line_id = invoice_line_obj . create ( cursor , user , {
' name ' : name ,
2006-12-07 13:41:40 +00:00
' invoice_id ' : invoice_id ,
2007-10-19 12:15:57 +00:00
' uos_id ' : move_line . product_uos . id ,
' product_id ' : move_line . product_id . id ,
2006-12-07 13:41:40 +00:00
' account_id ' : account_id ,
2007-10-19 12:15:57 +00:00
' price_unit ' : price_unit ,
' discount ' : discount ,
' quantity ' : move_line . product_uos_qty ,
' invoice_line_tax_id ' : [ ( 6 , 0 , tax_ids ) ] ,
' account_analytic_id ' : account_analytic_id ,
} , context = context )
self . _invoice_line_hook ( cursor , user , move_line , invoice_line_id )
invoice_obj . button_compute ( cursor , user , [ invoice_id ] , context = context ,
set_total = ( type in ( ' in_invoice ' , ' in_refund ' ) ) )
self . write ( cursor , user , [ picking . id ] , {
' invoice_state ' : ' invoiced ' ,
} , context = context )
self . _invoice_hook ( cursor , user , picking , invoice_id )
self . write ( cursor , user , res . keys ( ) , {
' invoice_state ' : ' invoiced ' ,
} , context = context )
2006-12-07 13:41:40 +00:00
return res
2007-09-26 05:20:30 +00:00
2006-12-07 13:41:40 +00:00
stock_picking ( )
2007-10-19 12:15:57 +00:00
2006-12-07 13:41:40 +00:00
class stock_production_lot ( osv . osv ) :
def name_get ( self , cr , uid , ids , context = { } ) :
if not ids :
return [ ]
reads = self . read ( cr , uid , ids , [ ' name ' , ' ref ' ] , context )
res = [ ]
for record in reads :
name = record [ ' name ' ]
if record [ ' ref ' ] :
name = name + ' / ' + record [ ' ref ' ]
res . append ( ( record [ ' id ' ] , name ) )
return res
_name = ' stock.production.lot '
_description = ' Production lot '
_columns = {
2007-04-12 08:19:28 +00:00
' name ' : fields . char ( ' Serial ' , size = 64 , required = True ) ,
2006-12-07 13:41:40 +00:00
' ref ' : fields . char ( ' Reference ' , size = 64 ) ,
' date ' : fields . datetime ( ' Date create ' , required = True ) ,
' revisions ' : fields . one2many ( ' stock.production.lot.revision ' , ' lot_id ' , ' Revisions ' ) ,
}
_defaults = {
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
' name ' : lambda x , y , z , c : x . pool . get ( ' ir.sequence ' ) . get ( y , z , ' stock.lot.serial ' ) ,
}
2007-04-12 08:19:28 +00:00
_sql_constraints = [
2007-09-12 13:21:07 +00:00
( ' name_ref_uniq ' , ' unique (name, ref) ' , ' The serial/ref must be unique ! ' ) ,
2007-04-12 08:19:28 +00:00
]
2007-09-12 13:21:07 +00:00
2006-12-07 13:41:40 +00:00
stock_production_lot ( )
class stock_production_lot_revision ( osv . osv ) :
_name = ' stock.production.lot.revision '
_description = ' Production lot revisions '
_columns = {
' name ' : fields . char ( ' Revision name ' , size = 64 , required = True ) ,
' description ' : fields . text ( ' Description ' ) ,
' date ' : fields . date ( ' Revision date ' ) ,
' indice ' : fields . char ( ' Revision ' , size = 16 ) ,
' author_id ' : fields . many2one ( ' res.users ' , ' Author ' ) ,
' lot_id ' : fields . many2one ( ' stock.production.lot ' , ' Production lot ' , select = True ) ,
}
_defaults = {
' author_id ' : lambda x , y , z , c : z ,
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
}
stock_production_lot_revision ( )
# ----------------------------------------------------
# Move
# ----------------------------------------------------
#
# Fields:
# location_dest_id is only used for predicting futur stocks
#
class stock_move ( osv . osv ) :
def _getSSCC ( self , cr , uid , context = { } ) :
cr . execute ( ' select id from stock_tracking where create_uid= %d order by id desc limit 1 ' , ( uid , ) )
res = cr . fetchone ( )
return ( res and res [ 0 ] ) or False
_name = " stock.move "
_description = " Stock Move "
_columns = {
' name ' : fields . char ( ' Name ' , size = 64 , required = True , select = True ) ,
' priority ' : fields . selection ( [ ( ' 0 ' , ' Not urgent ' ) , ( ' 1 ' , ' Urgent ' ) ] , ' Priority ' ) ,
' date ' : fields . datetime ( ' Date Created ' ) ,
2007-08-08 19:55:39 +00:00
' date_planned ' : fields . date ( ' Scheduled date ' , required = True ) ,
2006-12-07 13:41:40 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True ) ,
2007-08-08 19:55:39 +00:00
' product_qty ' : fields . float ( ' Quantity ' , required = True ) ,
2006-12-07 13:41:40 +00:00
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product UOM ' , required = True ) ,
' product_uos_qty ' : fields . float ( ' Quantity (UOS) ' ) ,
' product_uos ' : fields . many2one ( ' product.uom ' , ' Product UOS ' ) ,
2007-08-08 19:55:39 +00:00
' product_packaging ' : fields . many2one ( ' product.packaging ' , ' Packaging ' ) ,
2006-12-07 13:41:40 +00:00
2007-04-12 08:19:28 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Source Location ' , required = True ) ,
2006-12-07 13:41:40 +00:00
' location_dest_id ' : fields . many2one ( ' stock.location ' , ' Dest. Location ' , required = True ) ,
' address_id ' : fields . many2one ( ' res.partner.address ' , ' Dest. Address ' ) ,
2007-04-12 08:19:28 +00:00
' prodlot_id ' : fields . many2one ( ' stock.production.lot ' , ' Production lot ' , help = " Production lot is used to put a serial number on the production " ) ,
' tracking_id ' : fields . many2one ( ' stock.tracking ' , ' Tracking lot ' , select = True , help = " Tracking lot is the code that will be put on the logistic unit/pallet " ) ,
' lot_id ' : fields . many2one ( ' stock.lot ' , ' Consumer lot ' , select = True , readonly = True ) ,
2006-12-07 13:41:40 +00:00
' move_dest_id ' : fields . many2one ( ' stock.move ' , ' Dest. Move ' ) ,
' move_history_ids ' : fields . many2many ( ' stock.move ' , ' stock_move_history_ids ' , ' parent_id ' , ' child_id ' , ' Move History ' ) ,
' move_history_ids2 ' : fields . many2many ( ' stock.move ' , ' stock_move_history_ids ' , ' child_id ' , ' parent_id ' , ' Move History ' ) ,
2007-06-27 13:59:46 +00:00
' picking_id ' : fields . many2one ( ' stock.picking ' , ' Packing list ' , select = True ) ,
2006-12-07 13:41:40 +00:00
' note ' : fields . text ( ' Notes ' ) ,
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' waiting ' , ' Waiting ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' assigned ' , ' Assigned ' ) , ( ' done ' , ' Done ' ) , ( ' cancel ' , ' cancel ' ) ] , ' State ' , readonly = True , select = True ) ,
2007-08-07 14:30:19 +00:00
' price_unit ' : fields . float ( ' Unit Price ' ,
digits = ( 16 , int ( config [ ' price_accuracy ' ] ) ) ) ,
2006-12-07 13:41:40 +00:00
}
_defaults = {
' state ' : lambda * a : ' draft ' ,
' priority ' : lambda * a : ' 1 ' ,
' product_qty ' : lambda * a : 1.0 ,
' date_planned ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
}
2007-09-13 14:22:08 +00:00
def _auto_init ( self , cursor ) :
super ( stock_move , self ) . _auto_init ( cursor )
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 \
ON stock_move ( location_id , location_dest_id , product_id , state ) ' )
cursor . commit ( )
2006-12-07 13:41:40 +00:00
def onchange_product_id ( self , cr , uid , context , prod_id = False , loc_id = False , loc_dest_id = False ) :
if not prod_id :
return { }
product = self . pool . get ( ' product.product ' ) . browse ( cr , uid , [ prod_id ] ) [ 0 ]
result = {
' name ' : product . name ,
' product_uom ' : product . uom_id . id ,
}
if loc_id :
result [ ' location_id ' ] = loc_id
if loc_dest_id :
result [ ' location_dest_id ' ] = loc_dest_id
return { ' value ' : result }
def action_confirm ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
return True
def action_assign ( self , cr , uid , ids , * args ) :
todo = [ ]
for move in self . browse ( cr , uid , ids ) :
if move . state in ( ' confirmed ' , ' waiting ' ) :
todo . append ( move . id )
res = self . check_assign ( cr , uid , todo )
return res
def force_assign ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' assigned ' } )
return True
2006-12-18 14:10:01 +00:00
def cancel_assign ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
return True
2006-12-07 13:41:40 +00:00
#
# Duplicate stock.move
#
def check_assign ( self , cr , uid , ids , context = { } ) :
done = [ ]
2006-12-13 08:53:39 +00:00
count = 0
2006-12-07 13:41:40 +00:00
pickings = { }
for move in self . browse ( cr , uid , ids ) :
if move . product_id . type == ' consu ' :
2007-08-09 06:06:19 +00:00
if mode . state in ( ' confirmed ' , ' waiting ' ) :
done . append ( move . id )
2007-03-09 10:02:23 +00:00
pickings [ move . picking_id . id ] = 1
2006-12-07 13:41:40 +00:00
continue
if move . state in ( ' confirmed ' , ' waiting ' ) :
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 } )
if res :
done . append ( move . id )
pickings [ move . picking_id . id ] = 1
r = res . pop ( 0 )
cr . execute ( ' update stock_move set location_id= %d , product_qty= %f where id= %d ' , ( r [ 1 ] , r [ 0 ] , move . id ) )
while res :
r = res . pop ( 0 )
move_id = self . copy ( cr , uid , move . id , { ' product_qty ' : r [ 0 ] , ' location_id ' : r [ 1 ] } )
done . append ( move_id )
#cr.execute('insert into stock_move_history_ids values (%d,%d)', (move.id,move_id))
2007-03-09 10:02:23 +00:00
if done :
count + = len ( done )
self . write ( cr , uid , done , { ' state ' : ' assigned ' } )
2006-12-07 13:41:40 +00:00
2006-12-13 08:53:39 +00:00
if count :
2006-12-07 13:41:40 +00:00
for pick_id in pickings :
wf_service = netsvc . LocalService ( " workflow " )
wf_service . trg_write ( uid , ' stock.picking ' , pick_id , cr )
2006-12-13 08:53:39 +00:00
return count
2006-12-07 13:41:40 +00:00
#
# Cancel move => cancel others move and pickings
#
def action_cancel ( self , cr , uid , ids , context = { } ) :
if not len ( ids ) :
return True
pickings = { }
for move in self . browse ( cr , uid , ids ) :
if move . state in ( ' confirmed ' , ' waiting ' , ' assigned ' , ' draft ' ) :
if move . picking_id :
pickings [ move . picking_id . id ] = True
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } )
2007-12-11 08:14:32 +00:00
2006-12-07 13:41:40 +00:00
for pick_id in pickings :
wf_service = netsvc . LocalService ( " workflow " )
wf_service . trg_validate ( uid , ' stock.picking ' , pick_id , ' button_cancel ' , cr )
ids2 = [ ]
for res in self . read ( cr , uid , ids , [ ' move_dest_id ' ] ) :
if res [ ' move_dest_id ' ] :
ids2 . append ( res [ ' move_dest_id ' ] [ 0 ] )
wf_service = netsvc . LocalService ( " workflow " )
for id in ids :
wf_service . trg_trigger ( uid , ' stock.move ' , id , cr )
self . action_cancel ( cr , uid , ids2 , context )
return True
2007-12-12 15:39:54 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2006-12-07 13:41:40 +00:00
for move in self . browse ( cr , uid , ids ) :
if move . move_dest_id . id and ( move . state != ' done ' ) :
mid = move . move_dest_id . id
if move . move_dest_id . id :
cr . execute ( ' insert into stock_move_history_ids (parent_id,child_id) values ( %d , %d ) ' , ( move . id , move . move_dest_id . id ) )
if move . move_dest_id . state in ( ' waiting ' , ' confirmed ' ) :
self . write ( cr , uid , [ move . move_dest_id . id ] , { ' state ' : ' assigned ' } )
if move . move_dest_id . picking_id :
wf_service = netsvc . LocalService ( " workflow " )
wf_service . trg_write ( uid , ' stock.picking ' , move . move_dest_id . picking_id . id , cr )
else :
pass
# self.action_done(cr, uid, [move.move_dest_id.id])
#
# Accounting Entries
#
2007-04-25 14:08:50 +00:00
acc_src = None
acc_dest = None
if move . location_id . account_id :
acc_src = move . location_id . account_id . id
if move . location_dest_id . account_id :
acc_dest = move . location_dest_id . account_id . id
if acc_src or acc_dest :
test = [ ( ' product.product ' , move . product_id . id ) ]
if move . product_id . categ_id :
test . append ( ( ' product.category ' , move . product_id . categ_id . id ) )
if not acc_src :
2007-10-19 12:15:38 +00:00
acc_src = move . product_id . product_tmpl_id . \
2007-10-23 18:04:32 +00:00
property_stock_account_input . id
2007-08-09 06:06:56 +00:00
if not acc_src :
2007-10-19 12:15:38 +00:00
acc_src = move . product_id . categ_id . \
2007-10-23 18:04:32 +00:00
property_stock_account_input_categ . id
2007-08-09 06:06:56 +00:00
if not acc_src :
2007-10-19 12:15:38 +00:00
raise osv . except_osv ( ' Error! ' ,
2007-10-23 18:04:32 +00:00
' There is no stock input account defined ' \
2007-10-19 12:15:38 +00:00
' for this product: " %s " (id: %d ) ' % \
( move . product_id . name ,
move . product_id . id , ) )
2007-04-25 14:08:50 +00:00
if not acc_dest :
2007-10-19 12:15:38 +00:00
acc_dest = move . product_id . product_tmpl_id . \
2007-10-23 18:04:32 +00:00
property_stock_account_output . id
2007-08-09 06:06:56 +00:00
if not acc_dest :
2007-10-19 12:15:38 +00:00
acc_dest = move . product_id . categ_id . \
2007-10-23 18:04:32 +00:00
property_stock_account_output_categ . id
2007-08-09 06:06:56 +00:00
if not acc_dest :
2007-10-19 12:15:38 +00:00
raise osv . except_osv ( ' Error! ' ,
2007-10-23 18:04:32 +00:00
' There is no stock output account defined ' \
2007-10-19 12:15:38 +00:00
' for this product: " %s " (id: %d ) ' % \
( move . product_id . name ,
move . product_id . id , ) )
2007-08-09 06:06:56 +00:00
if not move . product_id . categ_id . property_stock_journal . id :
2007-10-19 12:15:38 +00:00
raise osv . except_osv ( ' Error! ' ,
' There is no journal defined ' \
' on the product category: " %s " (id: %d ) ' % \
( move . product_id . categ_id . name ,
move . product_id . categ_id . id , ) )
2007-08-09 06:06:56 +00:00
journal_id = move . product_id . categ_id . property_stock_journal . id
2007-04-25 14:08:50 +00:00
if acc_src != acc_dest :
ref = move . picking_id and move . picking_id . name or False
2007-08-07 14:30:19 +00:00
if move . product_id . cost_method == ' average ' and move . price_unit :
amount = move . product_qty * move . price_unit
else :
amount = move . product_qty * move . product_id . standard_price
date = time . strftime ( ' % Y- % m- %d ' )
2007-04-25 14:08:50 +00:00
lines = [
2007-08-07 14:30:19 +00:00
( 0 , 0 , {
' name ' : move . name ,
' quantity ' : move . product_qty ,
' credit ' : amount ,
' account_id ' : acc_src ,
' ref ' : ref ,
' date ' : date } ) ,
( 0 , 0 , {
' name ' : move . name ,
' quantity ' : move . product_qty ,
' debit ' : amount ,
' account_id ' : acc_dest ,
' ref ' : ref ,
' date ' : date } )
2007-04-25 14:08:50 +00:00
]
2007-08-07 14:30:19 +00:00
self . pool . get ( ' account.move ' ) . create ( cr , uid ,
{
' name ' : move . name ,
' journal_id ' : journal_id ,
' line_id ' : lines ,
' ref ' : ref ,
} )
2006-12-07 13:41:40 +00:00
self . write ( cr , uid , ids , { ' state ' : ' done ' } )
wf_service = netsvc . LocalService ( " workflow " )
for id in ids :
wf_service . trg_trigger ( uid , ' stock.move ' , id , cr )
return True
2007-08-07 14:30:19 +00:00
2007-12-31 13:31:32 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
for move in self . browse ( cr , uid , ids , context = context ) :
if move . state != ' draft ' :
raise osv . except_osv ( ' UserError ' ,
' You can only delete draft moves. ' )
return super ( stock_move , self ) . unlink (
cr , uid , ids , context = context )
2006-12-07 13:41:40 +00:00
stock_move ( )
class stock_inventory ( osv . osv ) :
_name = " stock.inventory "
_description = " Inventory "
_columns = {
' name ' : fields . char ( ' Inventory ' , size = 64 , required = True ) ,
' date ' : fields . datetime ( ' Date create ' , required = True ) ,
' date_done ' : fields . datetime ( ' Date done ' ) ,
' inventory_line_id ' : fields . one2many ( ' stock.inventory.line ' , ' inventory_id ' , ' Inventories ' ) ,
' move_ids ' : fields . many2many ( ' stock.move ' , ' stock_inventory_move_rel ' , ' inventory_id ' , ' move_id ' , ' Created Moves ' ) ,
' state ' : fields . selection ( ( ( ' draft ' , ' Draft ' ) , ( ' done ' , ' Done ' ) ) , ' State ' , readonly = True ) ,
}
_defaults = {
' date ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
' state ' : lambda * a : ' draft ' ,
}
#
# Update to support tracking
#
2007-12-12 15:39:54 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2006-12-07 13:41:40 +00:00
for inv in self . browse ( cr , uid , ids ) :
move_ids = [ ]
move_line = [ ]
for line in inv . inventory_line_id :
pid = line . product_id . id
price = line . product_id . standard_price or 0.0
amount = self . pool . get ( ' stock.location ' ) . _product_get ( cr , uid , line . location_id . id , [ pid ] , { ' uom ' : line . product_uom . id } ) [ pid ]
change = line . product_qty - amount
if change :
2007-08-09 06:06:56 +00:00
location_id = line . product_id . product_tmpl_id . property_stock_inventory . id
2006-12-07 13:41:40 +00:00
value = {
' name ' : ' INV: ' + str ( line . inventory_id . id ) + ' : ' + line . inventory_id . name ,
' product_id ' : line . product_id . id ,
' product_uom ' : line . product_uom . id ,
' date ' : inv . date ,
' date_planned ' : inv . date ,
' state ' : ' assigned '
}
if change > 0 :
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 ,
} )
move_ids . append ( self . pool . get ( ' stock.move ' ) . create ( cr , uid , value ) )
if len ( move_ids ) :
2007-12-12 15:39:54 +00:00
self . pool . get ( ' stock.move ' ) . action_done ( cr , uid , move_ids ,
context = context )
2006-12-07 13:41:40 +00:00
self . write ( cr , uid , [ inv . id ] , { ' state ' : ' done ' , ' date_done ' : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) , ' move_ids ' : [ ( 6 , 0 , move_ids ) ] } )
return True
def action_cancel ( self , cr , uid , ids , context = { } ) :
for inv in self . browse ( cr , uid , ids ) :
self . pool . get ( ' stock.move ' ) . action_cancel ( cr , uid , [ x . id for x in inv . move_ids ] , context )
self . write ( cr , uid , [ inv . id ] , { ' state ' : ' draft ' } )
return True
stock_inventory ( )
class stock_inventory_line ( osv . osv ) :
_name = " stock.inventory.line "
_description = " Inventory line "
_columns = {
' inventory_id ' : fields . many2one ( ' stock.inventory ' , ' Inventory ' , ondelete = ' cascade ' , select = True ) ,
' location_id ' : fields . many2one ( ' stock.location ' , ' Location ' , required = True ) ,
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , required = True ) ,
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product UOM ' , required = True ) ,
' product_qty ' : fields . float ( ' Quantity ' )
}
def on_change_product_id ( self , cr , uid , ids , location_id , product , uom = False ) :
if not product :
return { }
if not uom :
prod = self . pool . get ( ' product.product ' ) . browse ( cr , uid , [ product ] , { ' uom ' : uom } ) [ 0 ]
uom = prod . uom_id . id
amount = self . pool . get ( ' stock.location ' ) . _product_get ( cr , uid , location_id , [ product ] , { ' uom ' : uom } ) [ product ]
result = { ' product_qty ' : amount , ' product_uom ' : uom }
return { ' value ' : result }
stock_inventory_line ( )
#----------------------------------------------------------
# Stock Warehouse
#----------------------------------------------------------
class stock_warehouse ( osv . osv ) :
_name = " stock.warehouse "
_description = " Warehouse "
_columns = {
' name ' : fields . char ( ' Name ' , size = 60 , required = True ) ,
# 'partner_id': fields.many2one('res.partner', 'Owner'),
' partner_address_id ' : fields . many2one ( ' res.partner.address ' , ' Owner Address ' ) ,
' lot_input_id ' : fields . many2one ( ' stock.location ' , ' Location Input ' , required = True ) ,
' lot_stock_id ' : fields . many2one ( ' stock.location ' , ' Location Stock ' , required = True ) ,
' lot_output_id ' : fields . many2one ( ' stock.location ' , ' Location Output ' , required = True ) ,
}
stock_warehouse ( )
# Product
class product_product ( osv . osv ) :
_inherit = " product.product "
#
# Utiliser browse pour limiter les queries !
#
2007-06-21 15:31:07 +00:00
def view_header_get ( self , cr , user , view_id , view_type , context ) :
if ( not context . get ( ' location ' , False ) ) :
return False
cr . execute ( ' select name from stock_location where id= %d ' , ( context [ ' location ' ] , ) )
j = cr . fetchone ( ) [ 0 ]
if j :
return ' Products: ' + j
return False
2006-12-07 13:41:40 +00:00
def _get_product_available_func ( states , what ) :
def _product_available ( self , cr , uid , ids , name , arg , context = { } ) :
if context . get ( ' shop ' , False ) :
cr . execute ( ' select warehouse_id from sale_shop where id= %d ' , ( int ( context [ ' shop ' ] ) , ) )
res2 = cr . fetchone ( )
if res2 :
context [ ' warehouse ' ] = res2 [ 0 ]
if context . get ( ' warehouse ' , False ) :
cr . execute ( ' select lot_stock_id from stock_warehouse where id= %d ' , ( int ( context [ ' warehouse ' ] ) , ) )
res2 = cr . fetchone ( )
if res2 :
context [ ' location ' ] = res2 [ 0 ]
if context . get ( ' location ' , False ) :
location_ids = [ context [ ' location ' ] ]
else :
# get the list of ids of the stock location of all warehouses
cr . execute ( " select lot_stock_id from stock_warehouse " )
location_ids = [ id for ( id , ) in cr . fetchall ( ) ]
# build the list of ids of children of the location given by id
location_ids = self . pool . get ( ' stock.location ' ) . search ( cr , uid , [ ( ' location_id ' , ' child_of ' , location_ids ) ] )
res = self . pool . get ( ' stock.location ' ) . _product_get_multi_location ( cr , uid , location_ids , ids , context , states , what )
for id in ids :
res . setdefault ( id , 0.0 )
return res
return _product_available
_product_qty_available = _get_product_available_func ( ( ' done ' , ) , ( ' in ' , ' out ' ) )
_product_virtual_available = _get_product_available_func ( ( ' confirmed ' , ' waiting ' , ' assigned ' , ' done ' ) , ( ' in ' , ' out ' ) )
_product_outgoing_qty = _get_product_available_func ( ( ' confirmed ' , ' waiting ' , ' assigned ' ) , ( ' out ' , ) )
_product_incoming_qty = _get_product_available_func ( ( ' confirmed ' , ' waiting ' , ' assigned ' ) , ( ' in ' , ) )
_columns = {
' qty_available ' : fields . function ( _product_qty_available , method = True , type = ' float ' , string = ' Real Stock ' ) ,
' virtual_available ' : fields . function ( _product_virtual_available , method = True , type = ' float ' , string = ' Virtual Stock ' ) ,
' incoming_qty ' : fields . function ( _product_incoming_qty , method = True , type = ' float ' , string = ' Incoming ' ) ,
' outgoing_qty ' : fields . function ( _product_outgoing_qty , method = True , type = ' float ' , string = ' Outgoing ' ) ,
}
product_product ( )