diff options
| author | Josse Colpaert <jco@odoo.com> | 2016-09-09 15:40:36 +0200 |
|---|---|---|
| committer | Josse Colpaert <jco@odoo.com> | 2016-09-28 12:38:13 +0200 |
| commit | 24a16f5db58c4dfe955f97da0a432e128ff548bd (patch) | |
| tree | a740bf64c63e2a897f90479177f1c50c13bfb520 | |
| parent | 3e95e4e58e95e573d74792fd236c3380f69a439d (diff) | |
[FIX] mrp, mrp_byproduct: updating the quantity of production orders fixes
When updating quantity, it should take use a similar
method as for generating the moves, but with
the quantity already produced (and posted) deduced.
The parameters for these method needed to be changed.
When all the moves are posted,
we should create the necessary extra moves.
If the quantity is put back to the quantity posted, it
might also need to delete moves.
In byproduct, it should take into account the subproduct factor.
When the quantity produced on the work orders has reached the quantity to do,
it should not allow anymore to use the "Done" button on work orders, but
rather use the Finish button instead. With the Update button on the manufacturing order
it is possible to do more.
| -rw-r--r-- | addons/mrp/models/mrp_bom.py | 4 | ||||
| -rw-r--r-- | addons/mrp/models/mrp_production.py | 19 | ||||
| -rw-r--r-- | addons/mrp/views/mrp_workorder_views.xml | 2 | ||||
| -rw-r--r-- | addons/mrp/wizard/change_production_qty.py | 45 | ||||
| -rw-r--r-- | addons/mrp_byproduct/models/mrp_production.py | 50 | ||||
| -rw-r--r-- | addons/mrp_byproduct/models/stock_move.py | 2 | ||||
| -rw-r--r-- | addons/mrp_byproduct/wizard/change_production_qty.py | 21 | ||||
| -rw-r--r-- | addons/stock/models/stock_move.py | 1 |
8 files changed, 83 insertions, 61 deletions
diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 91ee51bd9f1..f38cb346007 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -111,6 +111,10 @@ class MrpBom(models.Model): return self.search(domain, order='sequence, product_id', limit=1) def explode(self, product, quantity, picking_type=False): + """ + Explodes the BoM and creates two lists with all the information you need: bom_done and line_done + Quantity should be passed in the UoM of the BoM (don't divide by its quantity) + """ boms_done = [(self, {'qty': quantity, 'product': product, 'original_qty': quantity, 'parent_line': False})] lines_done = [] templates_done = product.product_tmpl_id diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index df3b72a6e91..7ff5bbba781 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -311,8 +311,7 @@ class MrpProduction(models.Model): source_location = self.bom_id.routing_id.location_id else: source_location = self.location_src_id - original_quantity = self.env['product.uom']._compute_qty(self.product_uom_id.id, self.product_qty, self.bom_id.product_uom_id.id) - + original_quantity = self.product_qty - self.qty_produced data = { 'name': self.name, 'date': self.date_planned_start, @@ -354,14 +353,21 @@ class MrpProduction(models.Model): move.procure_method = 'make_to_order' @api.multi - def _update_raw_move(self, bom_line, quantity, **kw): + def _update_raw_move(self, bom_line, line_data): + quantity = line_data['qty'] self.ensure_one() move = self.move_raw_ids.filtered(lambda x: x.bom_line_id.id == bom_line.id and x.state not in ('done', 'cancel')) if move: - move.write({'product_uom_qty': quantity}) + if quantity > 0: + move[0].write({'product_uom_qty': quantity}) + else: + if move[0].quantity_done > 0: + raise UserError(_('Lines need to be deleted, but can not as you still have some quantities to consume in them. ')) + move[0].action_cancel() + move[0].unlink() return move else: - self._generate_raw_move(bom_line, quantity) + self._generate_raw_move(bom_line, line_data) @api.multi def action_assign(self): @@ -398,8 +404,7 @@ class MrpProduction(models.Model): workorders = self.env['mrp.workorder'] qty = bom_data['qty'] - use_serial = any(product.tracking == 'serial' for product in self.mapped('move_finished_ids.product_id')) - if use_serial: + if self.product_id.tracking == 'serial': quantity = 1.0 else: quantity = self.product_qty - sum(self.move_finished_ids.mapped('quantity_done')) diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml index b52fa0b2ff1..f2df2dd66f0 100644 --- a/addons/mrp/views/mrp_workorder_views.xml +++ b/addons/mrp/views/mrp_workorder_views.xml @@ -100,7 +100,7 @@ <button name="button_finish" type="object" string="Finish Order" attrs="{'invisible': ['|', ('state', '!=', 'progress'), ('is_produced', '=', False)]}" class="btn-info"/> <button name="button_start" type="object" string="Start Working" attrs="{'invisible': ['|', ('working_state', '=', 'blocked'), ('state', '!=', 'pending')]}"/> <button name="button_start" type="object" string="Start Working" attrs="{'invisible': ['|', ('working_state', '=', 'blocked'), ('state', '!=', 'ready')]}" class="btn-success"/> - <button name="record_production" type="object" string="Done" class="btn-success" attrs="{'invisible': ['|', '|', ('working_state', '=', 'blocked'), ('state', '!=', 'progress'), ('is_user_working', '=', False)]}"/> + <button name="record_production" type="object" string="Done" class="btn-success" attrs="{'invisible': ['|', '|', '|', ('is_produced', '=', True), ('working_state', '=', 'blocked'), ('state', '!=', 'progress'), ('is_user_working', '=', False)]}"/> <button name="button_pending" type="object" string="Pause" class="btn-warning" attrs="{'invisible': ['|', '|', ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'pending', 'ready', 'cancel')), ('is_user_working', '=', False)]}"/> <button name="%(act_mrp_block_workcenter_wo)d" type="action" context="{'default_workcenter_id': workcenter_id}" string="Block" class="btn-danger" attrs="{'invisible': ['|', '|', ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'pending', 'ready', 'cancel')), ('is_user_working', '=', False)]}"/> <button name="button_unblock" type="object" string="Unblock" class="btn-danger" attrs="{'invisible': [('working_state', '!=', 'blocked')]}"/> diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index b093b0c9eb7..0e6382fa398 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -37,32 +37,41 @@ class ChangeProductionQty(models.TransientModel): @api.multi def change_prod_qty(self): - MrpBom = self.env['mrp.bom'] for wizard in self: production = wizard.mo_id produced = sum(production.move_finished_ids.mapped('quantity_done')) if wizard.product_qty < produced: raise UserError(_("You have already processed %d. Please input a quantity higher than %d ")%(produced, produced)) production.write({'product_qty': wizard.product_qty}) - #production.action_compute() - #TODO: Do we still need to change the quantity of a production order? - production_move = production.move_finished_ids.filtered(lambda x : x.state not in ('done', 'cancel') and production.product_id.id == x.product_id.id) - for move in production.move_raw_ids: - bom_point = production.bom_id - # TDE FIXME: this is not the place to do that kind of computation, please - if not bom_point: - bom_point = MrpBom._bom_find(product=production.product_id, picking_type=production.picking_type_id) - if not bom_point: - raise UserError(_("Cannot find bill of material for this production.")) - production.write({'bom_id': bom_point.id}) - if not bom_point: - raise UserError(_("Cannot find bill of material for this production.")) - factor = (production.product_qty - production.qty_produced) * production.product_uom_id.factor / bom_point.product_uom_id.factor - boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id) - for line, line_data in lines: - production._update_raw_move(line, line_data['qty']) + bom_point = production.bom_id + factor = self.env['product.uom']._compute_qty(production.product_uom_id.id, production.product_qty - production.qty_produced, production.bom_id.product_uom_id.id) + boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id) + for line, line_data in lines: + production._update_raw_move(line, line_data) self._update_product_to_produce(production, production.product_qty - production.qty_produced) moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) moves.do_unreserve() moves.action_assign() + for wo in production.workorder_ids: + if production.product_id.tracking == 'serial': + quantity = 1.0 + else: + quantity = wo.qty_production - wo.qty_produced + quantity = quantity if (quantity > 0) else 0 + wo.qty_producing = quantity + if wo.qty_produced < wo.qty_production and wo.state == 'done': + wo.state = 'progress' + # assign moves; last operation receive all unassigned moves (which case ?) + operation = wo.operation_id + + # TODO: following could be put in a function as it is similar as code in _workorders_create + # TODO: only needed when creating new moves + moves_raw = production.move_raw_ids.filtered(lambda move: move.operation_id == operation and move.state not in ('done', 'cancel')) + if wo == production.workorder_ids[-1]: + moves_raw |= production.move_raw_ids.filtered(lambda move: not move.operation_id) + moves_finished = production.move_finished_ids.filtered(lambda move: move.operation_id == operation) #TODO: code does nothing, unless maybe by_products? + moves_raw.mapped('move_lot_ids').write({'workorder_id': wo.id}) + (moves_finished + moves_raw).write({'workorder_id': wo.id}) + if wo.move_raw_ids.filtered(lambda x: x.product_id.tracking != 'none') and not wo.active_move_lot_ids: + wo._generate_lot_ids() return {} diff --git a/addons/mrp_byproduct/models/mrp_production.py b/addons/mrp_byproduct/models/mrp_production.py index 2355b67a60f..a8c34ea4068 100644 --- a/addons/mrp_byproduct/models/mrp_production.py +++ b/addons/mrp_byproduct/models/mrp_production.py @@ -4,39 +4,41 @@ from odoo import api, models -class Production(models.Model): +class MrpProduction(models.Model): _description = 'Production' _inherit = 'mrp.production' + def _create_byproduct_move(self, sub_product): + UoM = self.env['product.uom'] + Move = self.env['stock.move'] + for production in self: + source = production.product_id.property_stock_production.id + product_uom_factor = UoM._compute_qty_obj(production.product_uom_id, production.product_qty - production.qty_produced, production.bom_id.product_uom_id) + qty1 = sub_product.product_qty + qty1 *= product_uom_factor / production.bom_id.product_qty + data = { + 'name': 'PROD:%s' % production.name, + 'date': production.date_planned_start, + 'product_id': sub_product.product_id.id, + 'product_uom_qty': qty1, + 'product_uom': sub_product.product_uom_id.id, + 'location_id': source, + 'location_dest_id': production.location_dest_id.id, + 'production_id': production.id, + 'origin': production.name, + 'unit_factor': qty1 / (production.product_qty - production.qty_produced), + 'subproduct_id': sub_product.id + } + move = Move.create(data) + move.action_confirm() @api.multi def _generate_moves(self): """ Generates moves and work orders @return: Newly generated picking Id. """ - # TDE FIXME: was action_confirm ?? -> refactored - Move = self.env['stock.move'] - res = super(Production, self)._generate_moves() - UoM = self.env['product.uom'] + res = super(MrpProduction, self)._generate_moves() for production in self.filtered(lambda production: production.bom_id): - source = production.product_id.property_stock_production.id for sub_product in production.bom_id.sub_products: - product_uom_factor = UoM._compute_qty_obj(production.product_uom_id, production.product_qty, production.bom_id.product_uom_id) - qty1 = sub_product.product_qty - qty1 *= product_uom_factor / production.bom_id.product_qty - data = { - 'name': 'PROD:%s' % production.name, - 'date': production.date_planned_start, - 'product_id': sub_product.product_id.id, - 'product_uom_qty': qty1, - 'product_uom': sub_product.product_uom_id.id, - 'location_id': source, - 'location_dest_id': production.location_dest_id.id, - 'production_id': production.id, - 'origin': production.name, - 'unit_factor': qty1 / production.product_qty, - 'subproduct_id': sub_product.id - } - move = Move.create(data) - move.action_confirm() + production._create_byproduct_move(sub_product) return res diff --git a/addons/mrp_byproduct/models/stock_move.py b/addons/mrp_byproduct/models/stock_move.py index e75c2f8351c..119cc8fda94 100644 --- a/addons/mrp_byproduct/models/stock_move.py +++ b/addons/mrp_byproduct/models/stock_move.py @@ -4,7 +4,7 @@ from odoo import fields, models -class Stok_Move(models.Model): +class StockMove(models.Model): _inherit = 'stock.move' subproduct_id = fields.Many2one( diff --git a/addons/mrp_byproduct/wizard/change_production_qty.py b/addons/mrp_byproduct/wizard/change_production_qty.py index ac553ced65c..1ac4b74f867 100644 --- a/addons/mrp_byproduct/wizard/change_production_qty.py +++ b/addons/mrp_byproduct/wizard/change_production_qty.py @@ -4,19 +4,20 @@ from odoo import api, models -class change_production_qty(models.TransientModel): +class ChangeProductionQty(models.TransientModel): _inherit = 'change.production.qty' @api.model def _update_product_to_produce(self, prod, qty): - # U + super(ChangeProductionQty, self)._update_product_to_produce(prod, qty) Production = self.env['mrp.production'] - for move in prod.move_finished_ids: - if move.product_id == prod.product_id: - move.write({'product_uom_qty': qty}) + UoM = self.env['product.uom'] + for sub_product_line in prod.bom_id.sub_products: + move = prod.move_finished_ids.filtered(lambda x: x.sub_product_id == sub_product_line and x.state not in ('done', 'cancel')) + if move: + product_uom_factor = UoM._compute_qty_obj(prod.product_uom_id, prod.product_qty - prod.qty_produced, prod.bom_id.product_uom_id) + qty1 = sub_product_line.product_qty + qty1 *= product_uom_factor / prod.bom_id.product_qty + move[0].write({'product_uom_qty': qty1}) else: - for sub_product_line in prod.bom_id.sub_products: - if sub_product_line.product_id == move.product_id: - factor = Production._get_subproduct_factor(move) - subproduct_qty = qty * factor - move.write({'product_uom_qty': subproduct_qty}) + prod._create_byproduct_move(sub_product_line)
\ No newline at end of file diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index c9e497c4477..f824bf1b549 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -629,6 +629,7 @@ class StockMove(models.Model): if move.state != 'assigned' and not self.env.context.get('reserve_only_ops'): qty_already_assigned = move.reserved_availability qty = move.product_qty - qty_already_assigned + quants = Quant.quants_get_preferred_domain(qty, move, domain=main_domain[move.id], preferred_domain_list=[]) Quant.quants_reserve(quants, move) |
