[IMP] Change inventory report with quants, add putaway strategies

bzr revid: jco@openerp.com-20130625144059-vejvwrbdmttay94s
This commit is contained in:
Josse Colpaert 2013-06-25 16:40:59 +02:00
parent 58b2b57fa3
commit 11b680869f
4 changed files with 134 additions and 47 deletions

View File

@ -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"

View File

@ -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

View File

@ -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'

View File

@ -97,5 +97,32 @@
</group>
</field>
</record>
<record id="product_category_form_view_inherit" model="ir.ui.view">
<field name="name">product.category.form</field>
<field name="model">product.category</field>
<field name="inherit_id" ref="product.product_category_form_view"/>
<field name="arch" type="xml">
<group name="parent" position="after">
<separator name="Strategy"/>
<group name="Strategies">
<field name = "putaway_strategy_ids">
<tree string="Put Away strategies" editable="top">
<field name="location_id"/>
<field name="method"/>
</tree>
</field>
<field name = "removal_strategy_ids" >
<tree string="Removal strategies" editable="top">
<field name="location_id"/>
<field name="method"/>
</tree>
</field>
</group>
</group>
</field>
</record>
</data>
</openerp>