[ADD]:Added Upstream Patch for mrp

This commit is contained in:
Fatemi Lokhandwala 2018-07-09 18:07:58 +05:30
parent a861b436a1
commit 0da768c114
13 changed files with 120 additions and 64 deletions

View File

@ -80,6 +80,8 @@ class MrpBom(models.Model):
def onchange_product_tmpl_id(self): def onchange_product_tmpl_id(self):
if self.product_tmpl_id: if self.product_tmpl_id:
self.product_uom_id = self.product_tmpl_id.uom_id.id self.product_uom_id = self.product_tmpl_id.uom_id.id
if self.product_id.product_tmpl_id != self.product_tmpl_id:
self.product_id = False
@api.onchange('routing_id') @api.onchange('routing_id')
def onchange_routing_id(self): def onchange_routing_id(self):

View File

@ -389,7 +389,7 @@ class MrpProduction(models.Model):
source_location = routing.location_id source_location = routing.location_id
else: else:
source_location = self.location_src_id source_location = self.location_src_id
original_quantity = self.product_qty - self.qty_produced original_quantity = (self.product_qty - self.qty_produced) or 1.0
data = { data = {
'sequence': bom_line.sequence, 'sequence': bom_line.sequence,
'name': self.name, 'name': self.name,
@ -474,10 +474,16 @@ class MrpProduction(models.Model):
@api.multi @api.multi
def _generate_workorders(self, exploded_boms): def _generate_workorders(self, exploded_boms):
workorders = self.env['mrp.workorder'] workorders = self.env['mrp.workorder']
original_one = False
for bom, bom_data in exploded_boms: for bom, bom_data in exploded_boms:
# If the routing of the parent BoM and phantom BoM are the same, don't recreate work orders, but use one master routing # If the routing of the parent BoM and phantom BoM are the same, don't recreate work orders, but use one master routing
if bom.routing_id.id and (not bom_data['parent_line'] or bom_data['parent_line'].bom_id.routing_id.id != bom.routing_id.id): if bom.routing_id.id and (not bom_data['parent_line'] or bom_data['parent_line'].bom_id.routing_id.id != bom.routing_id.id):
workorders += self._workorders_create(bom, bom_data) temp_workorders = self._workorders_create(bom, bom_data)
workorders += temp_workorders
if temp_workorders: # In order to avoid two "ending work orders"
if original_one:
temp_workorders[-1].next_work_order_id = original_one
original_one = temp_workorders[0]
return workorders return workorders
def _workorders_create(self, bom, bom_data): def _workorders_create(self, bom, bom_data):
@ -558,7 +564,7 @@ class MrpProduction(models.Model):
order._cal_price(moves_to_do) order._cal_price(moves_to_do)
moves_to_finish = order.move_finished_ids.filtered(lambda x: x.state not in ('done','cancel')) moves_to_finish = order.move_finished_ids.filtered(lambda x: x.state not in ('done','cancel'))
moves_to_finish._action_done() moves_to_finish._action_done()
#order.action_assign() order.action_assign()
consume_move_lines = moves_to_do.mapped('active_move_line_ids') consume_move_lines = moves_to_do.mapped('active_move_line_ids')
for moveline in moves_to_finish.mapped('active_move_line_ids'): for moveline in moves_to_finish.mapped('active_move_line_ids'):
if moveline.product_id == order.product_id and moveline.move_id.has_tracking != 'none': if moveline.product_id == order.product_id and moveline.move_id.has_tracking != 'none':

View File

@ -219,6 +219,7 @@ class MrpWorkorder(models.Model):
'done_wo': False, 'done_wo': False,
'location_id': move.location_id.id, 'location_id': move.location_id.id,
'location_dest_id': move.location_dest_id.id, 'location_dest_id': move.location_dest_id.id,
'date': move.date,
}) })
qty_todo -= 1 qty_todo -= 1
elif float_compare(qty_todo, 0.0, precision_rounding=rounding) < 0: elif float_compare(qty_todo, 0.0, precision_rounding=rounding) < 0:
@ -284,6 +285,18 @@ class MrpWorkorder(models.Model):
self.final_lot_id = self.env['stock.production.lot'].search([('use_next_on_work_order_id', '=', self.id)], self.final_lot_id = self.env['stock.production.lot'].search([('use_next_on_work_order_id', '=', self.id)],
order='create_date, id', limit=1) order='create_date, id', limit=1)
def _get_byproduct_move_line(self, by_product_move, quantity):
return {
'move_id': by_product_move.id,
'product_id': by_product_move.product_id.id,
'product_uom_qty': quantity,
'product_uom_id': by_product_move.product_uom.id,
'qty_done': quantity,
'workorder_id': self.id,
'location_id': by_product_move.location_id.id,
'location_dest_id': by_product_move.location_dest_id.id,
}
@api.multi @api.multi
def record_production(self): def record_production(self):
self.ensure_one() self.ensure_one()
@ -339,33 +352,37 @@ class MrpWorkorder(models.Model):
# If last work order, then post lots used # If last work order, then post lots used
# TODO: should be same as checking if for every workorder something has been done? # TODO: should be same as checking if for every workorder something has been done?
if not self.next_work_order_id: if not self.next_work_order_id:
production_moves = self.production_id.move_finished_ids.filtered(lambda x: (x.state not in ('done', 'cancel'))) production_move = self.production_id.move_finished_ids.filtered(
for production_move in production_moves: lambda x: (x.product_id.id == self.production_id.product_id.id) and (x.state not in ('done', 'cancel')))
if production_move.product_id.id == self.production_id.product_id.id and production_move.has_tracking != 'none': if production_move.product_id.tracking != 'none':
move_line = production_move.move_line_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id) move_line = production_move.move_line_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
if move_line: if move_line:
move_line.product_uom_qty += self.qty_producing move_line.product_uom_qty += self.qty_producing
else: move_line.qty_done += self.qty_producing
move_line.create({'move_id': production_move.id,
'product_id': production_move.product_id.id,
'lot_id': self.final_lot_id.id,
'product_uom_qty': self.qty_producing,
'product_uom_id': production_move.product_uom.id,
'qty_done': self.qty_producing,
'workorder_id': self.id,
'location_id': production_move.location_id.id,
'location_dest_id': production_move.location_dest_id.id,
})
elif production_move.unit_factor:
rounding = production_move.product_uom.rounding
production_move.quantity_done += float_round(self.qty_producing * production_move.unit_factor, precision_rounding=rounding)
else: else:
production_move.quantity_done += self.qty_producing move_line.create({'move_id': production_move.id,
'product_id': production_move.product_id.id,
'lot_id': self.final_lot_id.id,
'product_uom_qty': self.qty_producing,
'product_uom_id': production_move.product_uom.id,
'qty_done': self.qty_producing,
'workorder_id': self.id,
'location_id': production_move.location_id.id,
'location_dest_id': production_move.location_dest_id.id,
})
else:
production_move.quantity_done += self.qty_producing
if not self.next_work_order_id: if not self.next_work_order_id:
for by_product_move in self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))): for by_product_move in self.production_id.move_finished_ids.filtered(lambda x: (x.product_id.id != self.production_id.product_id.id) and (x.state not in ('done', 'cancel'))):
if by_product_move.has_tracking == 'none': if by_product_move.has_tracking != 'serial':
by_product_move.quantity_done += self.qty_producing * by_product_move.unit_factor values = self._get_byproduct_move_line(by_product_move, self.qty_producing * by_product_move.unit_factor)
self.env['stock.move.line'].create(values)
elif by_product_move.has_tracking == 'serial':
qty_todo = by_product_move.product_uom._compute_quantity(self.qty_producing * by_product_move.unit_factor, by_product_move.product_id.uom_id)
for i in range(0, int(float_round(qty_todo, precision_digits=0))):
values = self._get_byproduct_move_line(by_product_move, 1)
self.env['stock.move.line'].create(values)
# Update workorder quantity produced # Update workorder quantity produced
self.qty_produced += self.qty_producing self.qty_produced += self.qty_producing
@ -395,7 +412,12 @@ class MrpWorkorder(models.Model):
@api.multi @api.multi
def button_start(self): def button_start(self):
# TDE CLEANME self.ensure_one()
# As button_start is automatically called in the new view
if self.state in ('done', 'cancel'):
return True
# Need a loss in case of the real time exceeding the expected
timeline = self.env['mrp.workcenter.productivity'] timeline = self.env['mrp.workcenter.productivity']
if self.duration < self.duration_expected: if self.duration < self.duration_expected:
loss_id = self.env['mrp.workcenter.productivity.loss'].search([('loss_type','=','productive')], limit=1) loss_id = self.env['mrp.workcenter.productivity.loss'].search([('loss_type','=','productive')], limit=1)

View File

@ -73,6 +73,13 @@ class StockMove(models.Model):
order_finished_lot_ids = fields.Many2many('stock.production.lot', compute='_compute_order_finished_lot_ids') order_finished_lot_ids = fields.Many2many('stock.production.lot', compute='_compute_order_finished_lot_ids')
finished_lots_exist = fields.Boolean('Finished Lots Exist', compute='_compute_order_finished_lot_ids') finished_lots_exist = fields.Boolean('Finished Lots Exist', compute='_compute_order_finished_lot_ids')
def _unreserve_initial_demand(self, new_move):
# If you were already putting stock.move.lots on the next one in the work order, transfer those to the new move
self.filtered(lambda m: m.production_id or m.raw_material_production_id)\
.mapped('move_line_ids')\
.filtered(lambda ml: ml.qty_done == 0.0)\
.write({'move_id': new_move, 'product_uom_qty': 0})
@api.depends('active_move_line_ids.qty_done', 'active_move_line_ids.product_uom_id') @api.depends('active_move_line_ids.qty_done', 'active_move_line_ids.product_uom_id')
def _compute_done_quantity(self): def _compute_done_quantity(self):
super(StockMove, self)._compute_done_quantity() super(StockMove, self)._compute_done_quantity()

View File

@ -29,12 +29,12 @@ class StockWarehouse(models.Model):
return result return result
def _get_manufacture_route_id(self): def _get_manufacture_route_id(self):
manufacture_route_id = self.env.ref('mrp.route_warehouse0_manufacture').id manufacture_route = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False)
if not manufacture_route_id: if not manufacture_route:
manufacture_route_id = self.env['stock.location.route'].search([('name', 'like', _('Manufacture'))], limit=1).id manufacture_route = self.env['stock.location.route'].search([('name', 'like', _('Manufacture'))], limit=1)
if not manufacture_route_id: if not manufacture_route:
raise exceptions.UserError(_('Can\'t find any generic Manufacture route.')) raise exceptions.UserError(_('Can\'t find any generic Manufacture route.'))
return manufacture_route_id return manufacture_route.id
def _get_manufacture_pull_rules_values(self, route_values): def _get_manufacture_pull_rules_values(self, route_values):
if not self.manu_type_id: if not self.manu_type_id:

View File

@ -36,7 +36,7 @@
<span t-att-res-id="bom_line['product_id'].id" res-model="product.product" view-type="form" t-esc="bom_line['product_id'].name"/> <span t-att-res-id="bom_line['product_id'].id" res-model="product.product" view-type="form" t-esc="bom_line['product_id'].name"/>
</td> </td>
<td class="text-right"> <td class="text-right">
<span t-esc="bom_line['product_uom_qty']"/> <span t-esc="bom_line['product_uom'].name" groups="product.group_uom"/> <span t-esc="bom_line['product_uom_qty']" t-esc-options='{"widget": "float", "decimal_precision": "Product Unit of Measure"}'/> <span t-esc="bom_line['product_uom'].name" groups="product.group_uom"/>
</td> </td>
<td class="text-right"> <td class="text-right">
<span t-esc="bom_line['price_unit']" t-options='{"widget": "monetary", "display_currency": currency}'/> <span t-esc="bom_line['price_unit']" t-options='{"widget": "monetary", "display_currency": currency}'/>

View File

@ -7,36 +7,49 @@ from flectra import api, models
class BomStructureReport(models.AbstractModel): class BomStructureReport(models.AbstractModel):
_name = 'report.mrp.mrp_bom_structure_report' _name = 'report.mrp.mrp_bom_structure_report'
def get_children(self, object, level=0): @api.model
def _get_child_vals(self, record, level, qty, uom):
"""Get bom.line values.
:param record: mrp.bom.line record
:param level: level of recursion
:param qty: quantity of the product
:param uom: unit of measurement of a product
"""
child = {
'pname': record.product_id.name_get()[0][1],
'pcode': record.product_id.default_code,
'puom': record.product_uom_id,
'uname': record.product_uom_id.name,
'level': level,
'code': record.bom_id.code,
}
qty_per_bom = record.bom_id.product_qty
if uom:
if uom != record.bom_id.product_uom_id:
qty = uom._compute_quantity(qty, record.bom_id.product_uom_id)
child['pqty'] = (record.product_qty * qty) / qty_per_bom
else:
# for the first case, the ponderation is right
child['pqty'] = (record.product_qty * qty)
return child
def get_children(self, records, level=0):
result = [] result = []
def _get_rec(object, level, qty=1.0, uom=False): def _get_rec(records, level, qty=1.0, uom=False):
for l in object: for l in records:
res = {} child = self._get_child_vals(l, level, qty, uom)
res['pname'] = l.product_id.name_get()[0][1] result.append(child)
res['pcode'] = l.product_id.default_code
qty_per_bom = l.bom_id.product_qty
if uom:
if uom != l.bom_id.product_uom_id:
qty = uom._compute_quantity(qty, l.bom_id.product_uom_id)
res['pqty'] = (l.product_qty *qty)/ qty_per_bom
else:
#for the first case, the ponderation is right
res['pqty'] = (l.product_qty *qty)
res['puom'] = l.product_uom_id
res['uname'] = l.product_uom_id.name
res['level'] = level
res['code'] = l.bom_id.code
result.append(res)
if l.child_line_ids: if l.child_line_ids:
if level < 6: if level < 6:
level += 1 level += 1
_get_rec(l.child_line_ids, level, qty=res['pqty'], uom=res['puom']) _get_rec(l.child_line_ids, level, qty=child['pqty'], uom=child['puom'])
if level > 0 and level < 6: if level > 0 and level < 6:
level -= 1 level -= 1
return result return result
children = _get_rec(object, level) children = _get_rec(records, level)
return children return children

View File

@ -126,7 +126,7 @@
<field name="state" invisible="1" force_save="1"/> <field name="state" invisible="1" force_save="1"/>
<field name="product_uom_qty" string="To Consume"/> <field name="product_uom_qty" string="To Consume"/>
<field name="reserved_availability" attrs="{'invisible': [('is_done', '=', True)]}" string="Reserved"/> <field name="reserved_availability" attrs="{'invisible': [('is_done', '=', True)]}" string="Reserved"/>
<field name="quantity_done" string="Consumed"/> <field name="quantity_done" string="Consumed" readonly="1"/>
</tree> </tree>
</field> </field>
</page> </page>

View File

@ -177,4 +177,4 @@
groups="group_mrp_routings" groups="group_mrp_routings"
sequence="50"/> sequence="50"/>
</data> </data>
</flectra> </flectra>

View File

@ -190,18 +190,19 @@
<label for="duration"/> <label for="duration"/>
<div> <div>
<button style="pointer-events: none;" class="oe_inline label label-default"> <button style="pointer-events: none;" class="oe_inline label label-default">
<field name="duration" widget="mrp_time_counter"/> <field name="duration" widget="mrp_time_counter" help="Time the currently logged user spent on this workorder."/>
</button> </button>
</div> </div>
</group> </group>
</group> </group>
<group> <group>
<field name="time_ids" nolabel="1"> <field name="time_ids" nolabel="1" context="{'default_workcenter_id': workcenter_id}">
<tree> <tree>
<field name="date_start"/> <field name="date_start"/>
<field name="date_end"/> <field name="date_end"/>
<field name="duration" widget="float_time" sum="Total duration"/> <field name="duration" widget="float_time" sum="Total duration"/>
<field name="user_id"/> <field name="user_id"/>
<field name="workcenter_id" invisible="1"/>
<field name="loss_id" string="Efficiency"/> <field name="loss_id" string="Efficiency"/>
</tree> </tree>
<form> <form>
@ -213,6 +214,7 @@
</group> </group>
<group> <group>
<field name="user_id"/> <field name="user_id"/>
<field name="workcenter_id"/>
<field name="loss_id"/> <field name="loss_id"/>
</group> </group>
</group> </group>
@ -404,6 +406,8 @@
<field name="context">{'search_default_ready': True, 'search_default_progress': True}</field> <field name="context">{'search_default_ready': True, 'search_default_progress': True}</field>
<field name="help" type="html"> <field name="help" type="html">
<p class="oe_view_nocontent_create"> <p class="oe_view_nocontent_create">
Click to start a new work order.
</p><p>
Work Orders are operations to be processed at a Work Center to realize a Work Orders are operations to be processed at a Work Center to realize a
Manufacturing Order. Work Orders are trigerred by Manufacturing Orders, Manufacturing Order. Work Orders are trigerred by Manufacturing Orders,
they are based on the Routing defined on these ones. they are based on the Routing defined on these ones.

View File

@ -22,7 +22,7 @@
<label for="product_uom_qty"/> <label for="product_uom_qty"/>
<div class="o_row"> <div class="o_row">
<span><field name="product_uom_qty" readonly="1" nolabel="1"/></span> <span><field name="product_uom_qty" readonly="1" nolabel="1"/></span>
<span><field name="product_uom" attrs="{'readonly': [('id', '!=', False)]}" nolabel="1"/></span> <span><field name="product_uom" readonly="1" force_save="1" nolabel="1"/></span>
</div> </div>
<label for="quantity_done"/> <label for="quantity_done"/>
<div class="o_row"> <div class="o_row">
@ -44,7 +44,7 @@
<field name="finished_lots_exist" invisible="1"/> <field name="finished_lots_exist" invisible="1"/>
</group> </group>
</group> </group>
<field name="active_move_line_ids" attrs="{'readonly': [('is_locked', '=', True)], 'invisible': [('has_tracking', '=', 'none'), ('finished_lots_exist', '=', False)]}" context="{'default_workorder_id': workorder_id, 'default_product_uom_id': product_uom, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_production_id': production_id or raw_material_production_id}"> <field name="active_move_line_ids" attrs="{'readonly': [('is_locked', '=', True)]}" context="{'default_workorder_id': workorder_id, 'default_product_uom_id': product_uom, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_production_id': production_id or raw_material_production_id}">
<tree editable="bottom" decoration-success="product_qty==qty_done" decoration-danger="(product_qty &gt; 0) and (qty_done&gt;product_qty)"> <tree editable="bottom" decoration-success="product_qty==qty_done" decoration-danger="(product_qty &gt; 0) and (qty_done&gt;product_qty)">
<field name="lot_id" attrs="{'column_invisible': [('parent.has_tracking', '=', 'none')]}" domain="[('product_id', '=', parent.product_id)]" context="{'default_product_id': parent.product_id}"/> <field name="lot_id" attrs="{'column_invisible': [('parent.has_tracking', '=', 'none')]}" domain="[('product_id', '=', parent.product_id)]" context="{'default_product_id': parent.product_id}"/>
<field name="lot_produced_id" options="{'no_open': True, 'no_create': True}" domain="[('id', 'in', parent.order_finished_lot_ids)]" invisible="not context.get('final_lots')"/> <field name="lot_produced_id" options="{'no_open': True, 'no_create': True}" domain="[('id', 'in', parent.order_finished_lot_ids)]" invisible="not context.get('final_lots')"/>

View File

@ -43,7 +43,8 @@ class ChangeProductionQty(models.TransientModel):
production = wizard.mo_id production = wizard.mo_id
produced = sum(production.move_finished_ids.filtered(lambda m: m.product_id == production.product_id).mapped('quantity_done')) produced = sum(production.move_finished_ids.filtered(lambda m: m.product_id == production.product_id).mapped('quantity_done'))
if wizard.product_qty < produced: if wizard.product_qty < produced:
raise UserError(_("You have already processed %d. Please input a quantity higher than %d ")%(produced, produced)) format_qty = '%.{precision}f'.format(precision=precision)
raise UserError(_("You have already processed %s. Please input a quantity higher than %s ") % (format_qty % produced, format_qty % produced))
production.write({'product_qty': wizard.product_qty}) production.write({'product_qty': wizard.product_qty})
done_moves = production.move_finished_ids.filtered(lambda x: x.state == 'done' and x.product_id == production.product_id) done_moves = production.move_finished_ids.filtered(lambda x: x.state == 'done' and x.product_id == production.product_id)
qty_produced = production.product_id.uom_id._compute_quantity(sum(done_moves.mapped('product_qty')), production.product_uom_id) qty_produced = production.product_id.uom_id._compute_quantity(sum(done_moves.mapped('product_qty')), production.product_uom_id)

View File

@ -38,7 +38,8 @@ class MrpProductProduce(models.TransientModel):
if 'produce_line_ids' in fields: if 'produce_line_ids' in fields:
lines = [] lines = []
for move in production.move_raw_ids.filtered(lambda x: (x.product_id.tracking != 'none') and x.state not in ('done', 'cancel') and x.bom_line_id): for move in production.move_raw_ids.filtered(lambda x: (x.product_id.tracking != 'none') and x.state not in ('done', 'cancel') and x.bom_line_id):
qty_to_consume = todo_quantity / move.bom_line_id.bom_id.product_qty * move.bom_line_id.product_qty qty_to_consume = float_round(todo_quantity / move.bom_line_id.bom_id.product_qty * move.bom_line_id.product_qty,
precision_rounding=move.product_uom.rounding, rounding_method="UP")
for move_line in move.move_line_ids: for move_line in move.move_line_ids:
if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0: if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
break break
@ -176,9 +177,9 @@ class MrpProductProduceLine(models.TransientModel):
product_produce_id = fields.Many2one('mrp.product.produce') product_produce_id = fields.Many2one('mrp.product.produce')
product_id = fields.Many2one('product.product', 'Product') product_id = fields.Many2one('product.product', 'Product')
lot_id = fields.Many2one('stock.production.lot', 'Lot') lot_id = fields.Many2one('stock.production.lot', 'Lot')
qty_to_consume = fields.Float('To Consume') qty_to_consume = fields.Float('To Consume', digits=dp.get_precision('Product Unit of Measure'))
product_uom_id = fields.Many2one('product.uom', 'Unit of Measure') product_uom_id = fields.Many2one('product.uom', 'Unit of Measure')
qty_done = fields.Float('Done') qty_done = fields.Float('Done', digits=dp.get_precision('Product Unit of Measure'))
move_id = fields.Many2one('stock.move') move_id = fields.Many2one('stock.move')
@api.onchange('lot_id') @api.onchange('lot_id')