From 11b680869f8fd97995ac4f438d9a9dd50e4bf248 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 25 Jun 2013 16:40:59 +0200 Subject: [PATCH] [IMP] Change inventory report with quants, add putaway strategies bzr revid: jco@openerp.com-20130625144059-vejvwrbdmttay94s --- addons/stock/report/report_stock_move.py | 91 +++++++++++++------ addons/stock/stock.py | 31 +++++-- addons/stock_location/stock_location.py | 32 +++++-- addons/stock_location/stock_location_view.xml | 27 ++++++ 4 files changed, 134 insertions(+), 47 deletions(-) diff --git a/addons/stock/report/report_stock_move.py b/addons/stock/report/report_stock_move.py index 0f7910ce52b..f54668765d8 100644 --- a/addons/stock/report/report_stock_move.py +++ b/addons/stock/report/report_stock_move.py @@ -216,40 +216,71 @@ class report_stock_inventory(osv.osv): def init(self, cr): tools.drop_view_if_exists(cr, 'report_stock_inventory') cr.execute(""" -CREATE OR REPLACE view report_stock_inventory AS ( - - SELECT - m.id as id, m.date as date, - to_char(m.date, 'YYYY') as year, - to_char(m.date, 'MM') as month, - m.partner_id as partner_id, m.location_dest_id as location_id, - m.product_id as product_id, pt.categ_id as product_categ_id, l.usage as location_type, l.scrap_location as scrap_location, - m.company_id, - m.state as state, m.prodlot_id as prodlot_id, - coalesce(sum(m.price_unit * m.qty_remaining)::decimal, 0.0) as value, - coalesce(sum(m.qty_remaining * pu.factor / pu2.factor)::decimal, 0.0) as product_qty, - p.name as ref, - l.usage as location_dest_type, - l_other.usage as location_src_type - FROM - stock_move m - LEFT JOIN stock_picking p ON (m.picking_id=p.id) - LEFT JOIN product_product pp ON (m.product_id=pp.id) - LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id) - LEFT JOIN product_uom pu ON (pt.uom_id=pu.id) - LEFT JOIN product_uom pu2 ON (m.product_uom=pu2.id) - LEFT JOIN product_uom u ON (m.product_uom=u.id) - LEFT JOIN stock_location l ON (m.location_dest_id=l.id) - LEFT JOIN stock_location l_other ON (m.location_id=l_other.id) - WHERE m.state != 'cancel' - GROUP BY - m.id, m.product_id, m.product_uom, pt.categ_id, m.partner_id, m.location_id, m.location_dest_id, - m.prodlot_id, m.date, m.state, l.usage, l_other.usage, l.scrap_location, m.company_id, pt.uom_id, to_char(m.date, 'YYYY'), to_char(m.date, 'MM'), p.name -); + CREATE OR REPLACE view report_stock_inventory AS ( + SELECT + sq.id as id, sq.in_date as date, + to_char(sq.in_date, 'YYYY') as year, + to_char(sq.in_date, 'MM') as month, + m.partner_id as partner_id, sq.location_id as location_id, + sq.product_id as product_id, pt.categ_id as product_categ_id, location.usage as location_type, location.scrap_location as scrap_location, + sq.company_id, + m.state as state, sq.prodlot_id as prodlot_id, + sq.price_unit * sq.qty as value, + sq.qty as product_qty, + p.name as ref, + location_dest.usage as location_dest_type, + location.usage as location_src_type + FROM stock_quant sq + LEFT JOIN quant_move_rel qm ON (qm.quant_id = sq.id) + LEFT JOIN stock_move m ON (qm.move_id = m.id) + LEFT JOIN stock_picking p ON (m.picking_id=p.id) + LEFT JOIN stock_location location ON (m.location_id = location.id) + LEFT JOIN stock_location location_dest ON (m.location_dest_id = location_dest.id) + LEFT JOIN product_template pt ON (sq.product_id=pt.id) + WHERE location_dest.usage = 'internal' and location.usage <> 'internal' + GROUP BY + sq.id, sq.product_id, pt.categ_id, m.partner_id, m.location_id, m.location_dest_id, + sq.prodlot_id, sq.in_date, m.state, location.usage, location_dest.usage, location.scrap_location, sq.company_id, to_char(sq.in_date, 'YYYY'), to_char(sq.in_date, 'MM'), p.name + ); """) +# +# CREATE OR REPLACE view report_stock_inventory AS ( +# +# SELECT +# m.id as id, m.date as date, +# to_char(m.date, 'YYYY') as year, +# to_char(m.date, 'MM') as month, +# m.partner_id as partner_id, m.location_dest_id as location_id, +# m.product_id as product_id, pt.categ_id as product_categ_id, l.usage as location_type, l.scrap_location as scrap_location, +# m.company_id, +# m.state as state, m.prodlot_id as prodlot_id, +# coalesce(sum(m.price_unit * m.qty_remaining)::decimal, 0.0) as value, +# coalesce(sum(m.qty_remaining * pu.factor / pu2.factor)::decimal, 0.0) as product_qty, +# p.name as ref, +# l.usage as location_dest_type, +# l_other.usage as location_src_type +# FROM +# stock_move m +# LEFT JOIN stock_picking p ON (m.picking_id=p.id) +# LEFT JOIN product_product pp ON (m.product_id=pp.id) +# LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id) +# LEFT JOIN product_uom pu ON (pt.uom_id=pu.id) +# LEFT JOIN product_uom pu2 ON (m.product_uom=pu2.id) +# LEFT JOIN product_uom u ON (m.product_uom=u.id) +# LEFT JOIN stock_location l ON (m.location_dest_id=l.id) +# LEFT JOIN stock_location l_other ON (m.location_id=l_other.id) +# WHERE m.state != 'cancel' +# GROUP BY +# m.id, m.product_id, m.product_uom, pt.categ_id, m.partner_id, m.location_id, m.location_dest_id, +# m.prodlot_id, m.date, m.state, l.usage, l_other.usage, l.scrap_location, m.company_id, pt.uom_id, to_char(m.date, 'YYYY'), to_char(m.date, 'MM'), p.name +# ); + + + + class report_stock_valuation(osv.osv): _name = "report.stock.valuation" diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 732d32655bb..704ece83bc0 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -212,6 +212,8 @@ class stock_location(osv.osv): "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."), + 'quant_ids': fields.one2many('stock.quant', 'location_id', 'Quants associated with this location'), + 'destination_move_ids': fields.one2many('stock.move', 'location_dest_id', 'Destination moves'), } _defaults = { 'active': True, @@ -628,7 +630,7 @@ class stock_quant(osv.osv): product_uom_price = uom_obj._compute_price(cr, uid, move.product_uom.id, move.price_unit, move.product_id.uom_id.id) qty_to_go = product_uom_qty if neg_quants: - recres = self.reconcile_negative_quants(cr, uid, neg_quants, move, qty_to_go, product_uom_price, context) + recres = self.reconcile_negative_quants(cr, uid, neg_quants, move, qty_to_go, product_uom_price, context=context) product_uom_qty = recres['amount'] quants_rec += recres['refreshed_quants'] if product_uom_qty > 0.0: @@ -638,9 +640,10 @@ class stock_quant(osv.osv): 'price_unit': product_uom_price, 'history_ids': [(4, move.id)], 'in_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'prodlot_id': move.prodlot_id.id, + 'company_id': move.company_id.id, } quant_id = self.pool.get("stock.quant").create(cr, uid, vals, context=context) - print "Negative quants, ", quants_rec,self.filter_quants_with_out_history(cr, uid, quants_rec, context=context) return self.filter_quants_with_out_history(cr, uid, quants_rec, context=context) @@ -693,10 +696,12 @@ class stock_quant(osv.osv): :param location_id: child_of this location_id :param product_id: id of product :param qty in UoM of product + :TODOparam prodlot_id :returns: tuples of (quant_id, qty) """ #TODO Normally, you should check the removal strategy now #But we will assume it is FIFO for the moment + #Will need to create search string beforehand if self.pool.get('stock.location').get_removal_strategy(cr, uid, location_id, product_id, context=context) == 'lifo': possible_quants = self.search(cr, uid, [('location_id', 'child_of', location_id), ('product_id','=',product_id), ('qty', '>', 0.0), ('reservation_id', '=', False)], order = 'in_date desc, id desc', context=context) @@ -1063,7 +1068,7 @@ class stock_picking(osv.osv): @return: True """ for picking in self.browse(cr, uid, ids, context=context): - self.pool.get('stock.move').action_assign(cr, uid, [move.id for move in picking.move_lines], context=context) + self.pool.get('stock.move').action_assign(cr, uid, [move.id for move in picking.move_lines]) self.write(cr, uid, ids, {'state': 'assigned'}) return True @@ -2323,6 +2328,10 @@ class stock_move(osv.osv): new_moves += self.create_chained_picking(cr, uid, new_moves, context) return new_moves + + def splitforputaway (self, cr, uid, ids, context=None): + return True + def action_confirm(self, cr, uid, ids, context=None): """ Confirms stock move. @return: List of ids. @@ -2381,6 +2390,7 @@ class stock_move(osv.osv): pickings = {} quant_obj = self.pool.get("stock.quant") uom_obj = self.pool.get("product.uom") + print "check assign ", ids if context is None: context = {} for move in self.browse(cr, uid, ids, context=context): @@ -2389,13 +2399,17 @@ class stock_move(osv.osv): done.append(move.id) pickings[move.picking_id.id] = 1 continue + print "move state:", move.state if move.state in ('confirmed', 'waiting'): # 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) #Convert UoM qty -> check rounding now in product_reserver + + #Split for source locations qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id) res2 = quant_obj.choose_quants(cr, uid, move.location_id.id, move.product_id.id, qty, context=context) + print res2 #Should group quants by location: quants = {} qtys = {} @@ -2428,7 +2442,10 @@ class stock_move(osv.osv): r = res.pop(0) move_id = self.copy(cr, uid, move.id, {'product_uos_qty': product_uos_qty, 'product_qty': r[0], 'location_id': r[1]}) done.append(move_id) - + + self.splitforputaway(cr, uid, [move.id], context=context) + + if done: count += len(done) self.write(cr, uid, done, {'state': 'assigned'}) @@ -2646,7 +2663,6 @@ class stock_move(osv.osv): for move in self.browse(cr, uid, ids, context=context): #Split according to pack wizard if necessary res[move.id] = [x.id for x in move.reserved_quant_ids] - print "Get quants", move.reserved_quant_ids return res @@ -2661,10 +2677,8 @@ class stock_move(osv.osv): uom_obj = self.pool.get("product.uom") for move in self.browse(cr, uid, ids, context=context): product_qty = 0.0 - print "Reserved quants", move.reserved_quant_ids for quant in move.reserved_quant_ids: product_qty += quant.qty - print "quantity to SUM, ", quant.qty, product_qty qty_from_move = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id) #Check if the entire quantity has been transformed in to quants if qty_from_move > product_qty: @@ -2675,10 +2689,8 @@ class stock_move(osv.osv): product_qty = 0.0 for quant in move.reserved_quant_ids: product_qty += quant.qty - print "QTYFROMMOVE", qty_from_move, product_qty if qty_from_move <= product_qty: create_neg_quant = False - print create_neg_quant if create_neg_quant: #To solve this, we should create a negative quant at destination and a positive quant at the source vals_neg = { @@ -3058,7 +3070,6 @@ class stock_move(osv.osv): quants_dict = quant_obj.get_out_moves_from_quants(cr, uid, quants, context=context) for out_mov in self.browse(cr, uid, quants_dict.keys(), context=context): quants_from_move = quant_obj.search(cr, uid, [('history_ids', 'in', out_mov.id), ('propagated_from_id', '=', False)], context=context) - print "Quants from move", quants_from_move out_qty_converted = uom_obj._compute_qty(cr, uid, out_mov.product_uom.id, out_mov.product_qty, move.product_uom.id, round=False) amount = 0.0 total_price = 0.0 diff --git a/addons/stock_location/stock_location.py b/addons/stock_location/stock_location.py index 61add344643..0bc5637ded9 100644 --- a/addons/stock_location/stock_location.py +++ b/addons/stock_location/stock_location.py @@ -136,14 +136,12 @@ class product_product(osv.osv): } - - class product_category(osv.osv): _inherit = 'product.category' _columns = { #'route_ids': fields.many2many('stock.route', 'product_catg_id', 'route_id', 'Routes'), - 'removal_strategy_ids': fields.many2many('product.removal', 'product_catg_id', 'removal_strat_id' 'Removal Strategies'), - 'putaway_strategy_ids': fields.many2many('product.putaway', 'product_catg_id', 'putaway_strat_id', 'Put Away Strategies'), + 'removal_strategy_ids': fields.one2many('product.removal', 'product_categ_id', 'Removal Strategies'), + 'putaway_strategy_ids': fields.one2many('product.putaway', 'product_categ_id', 'Put Away Strategies'), } @@ -161,6 +159,26 @@ class stock_move(osv.osv): res = super(stock_move,self).action_cancel(cr,uid,ids,context) return res + def splitforputaway (self, cr, uid, ids, context=None): + ''' + Splits this move in order to do the put away + ''' + putaway_obj = self.pool.get("product.putaway") + location_obj = self.pool.get("stock.location") + print "SPLIT FOR PUTAWAY" + for move in self.browse(cr, uid, ids, context=context): + putaways = putaway_obj.search(cr, uid, [('product_categ_id','=', move.product_id.categ_id.id), ('location_id', '=', move.location_dest_id.id)], context=context) + print putaways + if putaways: + #Search for locations for PutAway + locs = location_obj.search(cr, uid, [('id', 'child_of', move.location_dest_id.id), ('id', '!=', move.location_dest_id.id), ('quant_ids', '=', False), + ('destination_move_ids', '=', False)], context=context) + if locs: + self.write(cr, uid, [move.id], {'location_dest_id': locs[0]}, context=context) + return True + + + def _prepare_chained_picking(self, cr, uid, picking_name, picking, picking_type, moves_todo, context=None): res = super(stock_move, self)._prepare_chained_picking(cr, uid, picking_name, picking, picking_type, moves_todo, context=context) res.update({'invoice_state': moves_todo[0][1][6] or 'none'}) @@ -169,8 +187,8 @@ class stock_move(osv.osv): class stock_location(osv.osv): _inherit = 'stock.location' _columns = { - 'removal_strategy_ids': fields.many2many('product.removal', 'location_id', 'removal_strat_id' 'Removal Strategies'), - 'putaway_strategy_ids': fields.many2many('product.putaway', 'location_id', 'putaway_strat_id', 'Put Away Strategies'), + 'removal_strategy_ids': fields.one2many('product.removal', 'location_id', 'Removal Strategies'), + 'putaway_strategy_ids': fields.one2many('product.putaway', 'location_id', 'Put Away Strategies'), } @@ -183,7 +201,7 @@ class stock_location(osv.osv): product = self.pool.get("product.product").browse(cr, uid, product_id, context=context) strats = self.pool.get('product.removal').search(cr, uid, [('location_id','=',id), ('product_categ_id','child_of', product.categ_id.id)], context=context) #Also child_of for location??? if not strats: - strat = product.categ_id.removal_strategy + strat = product.categ_id.removal_strategy else: strat = strats[0] return strat or product.categ_id.removal_strategy or 'fifo' diff --git a/addons/stock_location/stock_location_view.xml b/addons/stock_location/stock_location_view.xml index 09a934a4b9e..0ace8fecc42 100644 --- a/addons/stock_location/stock_location_view.xml +++ b/addons/stock_location/stock_location_view.xml @@ -97,5 +97,32 @@ + + + + product.category.form + product.category + + + + + + + + + + + + + + + + + + + + + +