[IMP] Basic version working with lots

bzr revid: jco@openerp.com-20140117164232-8gu0bb8qbbk67yrx
This commit is contained in:
Josse Colpaert
2014-01-17 17:42:32 +01:00
parent 338013803d
commit b50247109c
6 changed files with 60 additions and 128 deletions
+9 -101
View File
@@ -774,13 +774,6 @@ class mrp_production(osv.osv):
def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz, context=None):
""" To produce final product based on production mode (consume/consume&produce).
If Production mode is consume, all stock move lines of raw materials will be done/consumed.
@@ -812,17 +805,18 @@ class mrp_production(osv.osv):
if not consumed_data.get(consumed.product_id.id, False):
consumed_data[consumed.product_id.id] = 0
consumed_data[consumed.product_id.id] += consumed.product_qty
consumed_moves = []
for consume in wiz.consume_lines:
# Search move for the product
corresponding_move = [x for x in production.move_lines if x.product_id.id == consume.product_id.id]
if corresponding_move:
corresponding_move = corresponding_move[0]
if corresponding_move.product_qty > consume.product_qty:
stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], consume.product_qty, corresponding_move.location_id.id,
restrict_lot_id = consume.lot_id.id, context=context)
consumed_moves.append(stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], consume.product_qty, corresponding_move.location_id.id,
restrict_lot_id = consume.lot_id.id, context=context))
else:
stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], corresponding_move.product_qty, corresponding_move.location_id.id,
restrict_lot_id = consume.lot_id.id, context=context)
consumed_moves.append(stock_mov_obj.action_consume(cr, uid, [corresponding_move.id], corresponding_move.product_qty, corresponding_move.location_id.id,
restrict_lot_id = consume.lot_id.id, context=context))
if production_mode == 'consume_produce':
# To produce remaining qty of final product
@@ -842,106 +836,20 @@ class mrp_production(osv.osv):
produced_qty = produced_products.get(produce_product.product_id.id, 0)
subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context)
rest_qty = (subproduct_factor * production.product_qty) - produced_qty
if rest_qty < (subproduct_factor * production_qty):
prod_name = produce_product.product_id.name_get()[0][1]
raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % ((subproduct_factor * production_qty), prod_name, rest_qty))
if rest_qty > 0 :
stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), context=context)
new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id = produce_product.location_id.id, restrict_lot_id = wiz.lot_id.id, context=context)
if produce_product.product_id.id == production.bom_id.product_id.id:
stock_mov_obj.write(cr, uid, new_moves, {'consumed_for': produce_product.id}, context=context)
self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context)
self.signal_button_produce_done(cr, uid, [production_id])
return True
def action_produce2(self, cr, uid, production_id, production_qty, production_mode, context=None):
""" To produce final product based on production mode (consume/consume&produce).
If Production mode is consume, all stock move lines of raw materials will be done/consumed.
If Production mode is consume & produce, all stock move lines of raw materials will be done/consumed
and stock move lines of final product will be also done/produced.
@param production_id: the ID of mrp.production object
@param production_qty: specify qty to produce
@param production_mode: specify production mode (consume/consume&produce).
@return: True
"""
stock_mov_obj = self.pool.get('stock.move')
production = self.browse(cr, uid, production_id, context=context)
if not production.move_lines and production.state == 'ready':
# trigger workflow if not products to consume (eg: services)
self.signal_button_produce(cr, uid, [production_id])
produced_qty = 0
for produced_product in production.move_created_ids2:
if (produced_product.scrapped) or (produced_product.product_id.id != production.product_id.id):
continue
produced_qty += produced_product.product_qty
if production_mode in ['consume','consume_produce']:
consumed_data = {}
# Calculate already consumed qtys
for consumed in production.move_lines2:
if consumed.scrapped:
continue
if not consumed_data.get(consumed.product_id.id, False):
consumed_data[consumed.product_id.id] = 0
consumed_data[consumed.product_id.id] += consumed.product_qty
# Find product qty to be consumed and consume it
for scheduled in production.product_lines:
# total qty of consumed product we need after this consumption
total_consume = ((production_qty + produced_qty) * scheduled.product_qty / production.product_qty)
# qty available for consume and produce
qty_avail = scheduled.product_qty - consumed_data.get(scheduled.product_id.id, 0.0)
if qty_avail <= 0.0:
# there will be nothing to consume for this raw material
continue
raw_product = [move for move in production.move_lines if move.product_id.id==scheduled.product_id.id]
if raw_product:
# qtys we have to consume
qty = total_consume - consumed_data.get(scheduled.product_id.id, 0.0)
if float_compare(qty, qty_avail, precision_rounding=scheduled.product_id.uom_id.rounding) == 1:
# if qtys we have to consume is more than qtys available to consume
prod_name = scheduled.product_id.name_get()[0][1]
raise osv.except_osv(_('Warning!'), _('You are going to consume total %s quantities of "%s".\nBut you can only consume up to total %s quantities.') % (qty, prod_name, qty_avail))
if qty <= 0.0:
# we already have more qtys consumed than we need
continue
raw_product[0].action_consume(qty, raw_product[0].location_id.id, context=context)
if production_mode == 'consume_produce':
# To produce remaining qty of final product
#vals = {'state':'confirmed'}
#final_product_todo = [x.id for x in production.move_created_ids]
#stock_mov_obj.write(cr, uid, final_product_todo, vals)
#stock_mov_obj.action_confirm(cr, uid, final_product_todo, context)
produced_products = {}
for produced_product in production.move_created_ids2:
if produced_product.scrapped:
continue
if not produced_products.get(produced_product.product_id.id, False):
produced_products[produced_product.product_id.id] = 0
produced_products[produced_product.product_id.id] += produced_product.product_qty
for produce_product in production.move_created_ids:
produced_qty = produced_products.get(produce_product.product_id.id, 0)
subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context)
rest_qty = (subproduct_factor * production.product_qty) - produced_qty
if rest_qty < (subproduct_factor * production_qty):
prod_name = produce_product.product_id.name_get()[0][1]
raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % ((subproduct_factor * production_qty), prod_name, rest_qty))
if rest_qty > 0 :
stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), context=context)
self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context)
self.signal_button_produce_done(cr, uid, [production_id])
return True
def _costs_generate(self, cr, uid, production):
""" Calculates total costs at the end of the production.
+4 -13
View File
@@ -725,19 +725,12 @@
<field name="product_id"/>
<field name="product_qty" string="Quantity"/>
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
<field name="restrict_lot_id"/>
<field name="restrict_partner_id"/> <!-- TODO group on lot and ownerhsip feature -->
<field name="state" invisible="1"/>
<button name="%(stock.move_consume)d"
string="Consume Products" type="action"
icon="gtk-go-forward" context="{'consume': True}"
states="draft,waiting,confirmed,assigned"/>
<button
name="%(stock.track_line)d"
string="Split in Serial Numbers"
type="action" icon="gtk-justify-fill"
states="draft,waiting,confirmed,assigned"
groups="stock.group_tracking_lot"/>
states="draft,waiting,confirmed,assigned"
/>
<button name="%(stock.move_scrap)d"
string="Scrap Products" type="action"
icon="terp-gtk-jump-to-ltr" context="{'scrap': True}"
@@ -749,9 +742,9 @@
<field name="move_lines2" nolabel="1" options="{'reload_on_button': true}">
<tree colors="red:scrapped==True;blue:state == 'draft';black:state in('picking_except','confirmed','ready','in_production');gray:state == 'cancel' " string="Consumed Products" editable="bottom">
<field name="product_id" readonly="1"/>
<field name="restrict_lot_id" context="{'product_id': product_id}" groups="stock.group_tracking_lot"/>
<field name="product_qty" readonly="1"/>
<field name="product_uom" readonly="1" string="Unit of Measure" groups="product.group_uom"/>
<!--<field name="prodlot_id" context="{'product_id': product_id}" groups="stock.group_tracking_lot"/>-->
<field name="state" invisible="1"/>
<field name="scrapped" invisible="1"/>
</tree>
@@ -777,13 +770,11 @@
<tree colors="red:scrapped==True;blue:state == 'draft';black:state in('picking_except','confirmed','ready','in_production');gray:state in('cancel','done') " string="Finished Products">
<field name="product_id" readonly="1"/>
<field name="product_qty" readonly="1"/>
<field name="restrict_lot_id" groups="stock.group_tracking_lot"/>
<field name="product_uom" readonly="1" string="Unit of Measure" groups="product.group_uom"/>
<field name="location_dest_id" readonly="1" string="Destination Loc." widget="selection" groups="stock.group_locations"/>
<!--<field name="prodlot_id" context="{'product_id': product_id}" groups="stock.group_tracking_lot"/>-->
<field name="scrapped" invisible="1"/>
<field name="state" invisible="1"/>
<button name="%(stock.track_line)d"
string="Split in Serial Numbers" type="action" icon="gtk-justify-fill" states="done,cancel"/>
<button name="%(stock.move_scrap)d"
string="Scrap Products" type="action" icon="terp-gtk-jump-to-ltr"
states="done,cancel"/>
+4 -6
View File
@@ -31,7 +31,6 @@ class StockMove(osv.osv):
'production_id': fields.many2one('mrp.production', 'Production Order for Produced Products', select=True),
'raw_material_production_id': fields.many2one('mrp.production', 'Production Order for Raw Materials', select=True),
'consumed_for': fields.many2one('stock.move', 'Consumed for'),
'track_production': fields.related('product_id', 'track_production'),
}
def check_tracking(self, cr, uid, move, lot_id, context=None):
@@ -103,7 +102,7 @@ class StockMove(osv.osv):
return processed_ids
def action_consume(self, cr, uid, ids, product_qty, location_id=False, restrict_lot_id = False, restrict_partner_id = False, context=None):
""" Consumed product with specific quatity from specific source location.
""" Consumed product with specific quantity from specific source location.
@param product_qty: Consumed product quantity
@param location_id: Source location
@return: Consumed lines
@@ -120,10 +119,9 @@ class StockMove(osv.osv):
production_obj.force_production(cr, uid, [prod.id])
production_obj.signal_button_produce(cr, uid, production_ids)
for new_move in new_moves:
if new_move == move.id:
#This move is already there in move lines of production order
continue
production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]})
if new_move != move.id:
#This move is not already there in move lines of production order
production_obj.write(cr, uid, production_ids, {'move_lines': [(4, new_move)]})
res.append(new_move)
return res
+40 -5
View File
@@ -31,7 +31,8 @@ class mrp_product_produce_line(osv.osv_memory):
'product_id': fields.many2one('product.product', 'Product'),
'product_qty': fields.float('Quantity'),
'lot_id': fields.many2one('stock.production.lot', 'Lot'),
'produce_id': fields.many2one('mrp.product.produce')
'produce_id': fields.many2one('mrp.product.produce'),
'track_production': fields.related('product_id', 'track_production', type='boolean')
}
class mrp_product_produce(osv.osv_memory):
@@ -39,6 +40,7 @@ class mrp_product_produce(osv.osv_memory):
_description = "Product Produce"
_columns = {
'product_id': fields.many2one('product.product', type='many2one'),
'product_qty': fields.float('Select Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
'mode': fields.selection([('consume_produce', 'Consume & Produce'),
('consume', 'Consume Only')], 'Mode', required=True,
@@ -47,12 +49,14 @@ class mrp_product_produce(osv.osv_memory):
"and it will finish the production order when total ordered quantities are produced."),
'lot_id': fields.many2one('stock.production.lot', 'Lot'), #Should only be visible when it is consume and produce mode
'consume_lines': fields.one2many('mrp.product.produce.line', 'produce_id', 'Products Consumed'),
'track_production': fields.boolean('Track production'),
}
def on_change_qty(self, cr, uid, ids, product_qty, consume_lines, context=None):
""" Will calculate number of products based on
"""
quant_obj = self.pool.get("stock.quant")
prod_obj = self.pool.get("mrp.production")
production = prod_obj.browse(cr, uid, context['active_id'], context=context)
@@ -88,10 +92,24 @@ class mrp_product_produce(osv.osv_memory):
if qty_avail <= 0.0:
# there will be nothing to consume for this raw material
continue
qty = total_consume - consumed_data.get(scheduled.product_id.id, 0.0)
dict_new = {'product_id': scheduled.product_id.id, 'product_qty': qty}
new_consume_lines.append([0, False, dict_new])
# Search for quants related to this related move
move = [x for x in production.move_lines if x.product_id.id == scheduled.product_id.id]
#TODO: check if not already in dict_new
if move:
move = move[0]
quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, scheduled.product_id, qty, domain=None,
prefered_domain=[('reservation_id', '=', move.id)], fallback_domain=[('reservation_id', '=', False)], context=context)
for quant in quants:
if quant[0]:
dict_new = {'product_id': scheduled.product_id.id, 'product_qty': quant[1], 'lot_id': quant[0].lot_id.id}
new_consume_lines.append([0, False, dict_new])
qty -= quant[1]
if qty > 0:
dict_new = {'product_id': scheduled.product_id.id, 'product_qty': qty}
new_consume_lines.append([0, False, dict_new])
return {'value': {'consume_lines': new_consume_lines}}
@@ -115,9 +133,26 @@ class mrp_product_produce(osv.osv_memory):
done += move.product_qty
return (prod.product_qty - done) or prod.product_qty
def _get_product_id(self, cr, uid, context=None):
""" To obtain product id
@return: id
"""
prod=False
if context and context.get("active_id"):
prod = self.pool.get('mrp.production').browse(cr, uid,
context['active_id'], context=context)
return prod and prod.product_id.id or False
def _get_track(self, cr, uid, context=None):
prod = self._get_product_id(cr, uid, context=context)
prod_obj = self.pool.get("product.product")
return prod and prod_obj.browse(cr, uid, prod, context=context).track_production or False
_defaults = {
'product_qty': _get_product_qty,
'mode': lambda *x: 'consume_produce'
'mode': lambda *x: 'consume_produce',
'product_id': _get_product_id,
'track_production': _get_track,
}
def do_produce(self, cr, uid, ids, context=None):
@@ -13,9 +13,11 @@
<group string="Produce">
<field name="mode"/>
<field name="product_qty" colspan="2" on_change="on_change_qty(product_qty, consume_lines, context)"/>
<field name="product_id" invisible="1"/>
<field name="track_production" invisible="1"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]"
attrs="{'required': [('track_production','=',True)]}"
context="{'default_product_id':product_id}"
attrs="{'required': [('track_production','=',True)]}"
groups="stock.group_tracking_lot"/>
</group>
<separator/>
@@ -25,7 +27,6 @@
<field name="product_id"/>
<field name="product_qty"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]"
attrs="{'required': [('track_production','=',True)]}"
context="{'default_product_id':product_id}"
groups="stock.group_tracking_lot"/>
</tree>
-1
View File
@@ -1920,7 +1920,6 @@ class stock_move(osv.osv):
if location_id:
self.write(cr, uid, [move.id], {'location_id': location_id, 'restrict_lot_id': restrict_lot_id,
'restrict_partner_id': restrict_partner_id}, context=context)
self.action_done(cr, uid, res, context=context)
return res