diff --git a/addons/delivery/models/stock_move.py b/addons/delivery/models/stock_move.py index 635fbf27494..79db247b879 100644 --- a/addons/delivery/models/stock_move.py +++ b/addons/delivery/models/stock_move.py @@ -23,5 +23,5 @@ class StockMove(models.Model): def _get_new_picking_values(self): vals = super(StockMove, self)._get_new_picking_values() - vals['carrier_id'] = self.group_id.sale_order_id.carrier_id.id + vals['carrier_id'] = self.sale_line_id.order_id.carrier_id.id return vals diff --git a/addons/mrp/data/mrp_demo.xml b/addons/mrp/data/mrp_demo.xml index 18ee117f94f..f8a333a67f4 100644 --- a/addons/mrp/data/mrp_demo.xml +++ b/addons/mrp/data/mrp_demo.xml @@ -538,7 +538,7 @@ - + diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index da28b39b610..796a3a69387 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -9,7 +9,6 @@ from odoo.addons import decimal_precision as dp from odoo.exceptions import UserError from odoo.tools import float_compare - class MrpProduction(models.Model): """ Manufacturing Orders """ _name = 'mrp.production' @@ -112,6 +111,7 @@ class MrpProduction(models.Model): copy=False, oldname='workcenter_lines', readonly=True) workorder_count = fields.Integer('# Work Orders', compute='_compute_workorder_count') workorder_done_count = fields.Integer('# Done Work Orders', compute='_compute_workorder_done_count') + move_dest_ids = fields.One2many('stock.move', 'created_production_id') state = fields.Selection([ ('confirmed', 'Confirmed'), @@ -149,7 +149,6 @@ class MrpProduction(models.Model): procurement_group_id = fields.Many2one( 'procurement.group', 'Procurement Group', copy=False) - procurement_ids = fields.One2many('procurement.order', 'production_id', 'Related Procurements') propagate = fields.Boolean( 'Propagate cancel and split', help='If checked, when the previous move of the move (which was generated by a next procurement) is cancelled or split, the move generated by this move will too') @@ -316,13 +315,12 @@ class MrpProduction(models.Model): 'product_uom_qty': self.product_qty, 'location_id': self.product_id.property_stock_production.id, 'location_dest_id': self.location_dest_id.id, - 'move_dest_ids': self.procurement_ids and [(4, p) for p in self.procurement_ids.mapped('move_dest_id').ids] or False, - 'procurement_id': self.procurement_ids and self.procurement_ids[0].id or False, 'company_id': self.company_id.id, 'production_id': self.id, 'origin': self.name, 'group_id': self.procurement_group_id.id, 'propagate': self.propagate, + 'move_dest_ids': [(4, x.id) for x in self.move_dest_ids], }) move.action_confirm() return move @@ -493,7 +491,6 @@ class MrpProduction(models.Model): orders in exception """ if any(workorder.state == 'progress' for workorder in self.mapped('workorder_ids')): raise UserError(_('You can not cancel production order, a work order is still in progress.')) - ProcurementOrder = self.env['procurement.order'] for production in self: production.workorder_ids.filtered(lambda x: x.state != 'cancel').action_cancel() @@ -501,13 +498,6 @@ class MrpProduction(models.Model): raw_moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) (finish_moves | raw_moves).action_cancel() - procurements = ProcurementOrder.search([('move_dest_id', 'in', (finish_moves | raw_moves).ids)]) - if procurements: - procurements.cancel() - - # Put relatfinish_to_canceled procurements in exception -> I agree - ProcurementOrder.search([('production_id', 'in', self.ids)]).write({'state': 'exception'}) - self.write({'state': 'cancel'}) return True @@ -547,7 +537,6 @@ class MrpProduction(models.Model): moves_to_cancel = (self.move_raw_ids | self.move_finished_ids).filtered(lambda x: x.state not in ('done', 'cancel')) moves_to_cancel.action_cancel() self.write({'state': 'done', 'date_finished': fields.Datetime.now()}) - self.env["procurement.order"].search([('production_id', 'in', self.ids)]).check() return self.write({'state': 'done'}) @api.multi diff --git a/addons/mrp/models/procurement.py b/addons/mrp/models/procurement.py index ba11ffc9e81..ccf430be369 100644 --- a/addons/mrp/models/procurement.py +++ b/addons/mrp/models/procurement.py @@ -4,98 +4,74 @@ from dateutil.relativedelta import relativedelta from odoo import api, fields, models, _ +from odoo.exceptions import UserError class ProcurementRule(models.Model): _inherit = 'procurement.rule' + action = fields.Selection(selection_add=[('manufacture', 'Manufacture')]) - @api.model - def _get_action(self): - return [('manufacture', _('Manufacture'))] + super(ProcurementRule, self)._get_action() - - -class ProcurementOrder(models.Model): - _inherit = 'procurement.order' - - bom_id = fields.Many2one('mrp.bom', 'BoM', index=True, ondelete='cascade') - production_id = fields.Many2one('mrp.production', 'Manufacturing Order') +class ProcurementGroup(models.Model): + _inherit = 'procurement.group' @api.multi - def propagate_cancels(self): - cancel_man_orders = self.filtered(lambda procurement: procurement.rule_id.action == 'manufacture' and procurement.production_id).mapped('production_id') - if cancel_man_orders: - cancel_man_orders.action_cancel() - return super(ProcurementOrder, self).propagate_cancels() + def _run(self, values, rule, doraise=True): + if rule.action == 'manufacture': + Production = self.env['mrp.production'] + ProductionSudo = Production.sudo().with_context(force_company=values['company_id'].id) + bom = self._get_matching_bom(values, rule) + if not bom: + msg = _('No Bill of Material found for product %s.') % (values['product_id'].display_name,) + if doraise: + raise UserError(msg) + else: + self.log_next_activity(values['product_id'], msg) + return False + + # create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example) + production = ProductionSudo.create(self._prepare_mo_vals(values, rule, bom)) + origin_production = values.get('move_dest_ids') and values ['move_dest_ids'][0].raw_material_production_id or False + orderpoint = values.get('orderpoint_id') + if orderpoint: + production.message_post_with_view('mail.message_origin_link', + values={'self': production, 'origin': orderpoint.id}, + subtype_id=self.env.ref('mail.mt_note').id) + if origin_production: + production.message_post_with_view('mail.message_origin_link', + values={'self': production, 'origin': origin_production}, + subtype_id=self.env.ref('mail.mt_note').id) + return True + return super(ProcurementGroup, self)._run(values, rule, doraise) @api.multi - def _run(self): - self.ensure_one() - if self.rule_id.action == 'manufacture': - # make a manufacturing order for the procurement - return self.make_mo()[self.id] - return super(ProcurementOrder, self)._run() - - @api.multi - def _check(self): - return self.production_id.state == 'done' or super(ProcurementOrder, self)._check() - - @api.multi - def _get_matching_bom(self): - """ Finds the bill of material for the product from procurement order. """ - if self.bom_id: - return self.bom_id + def _get_matching_bom(self, values, rule): + if values.get('bom_id', False): + return values['bom_id'] return self.env['mrp.bom'].with_context( - company_id=self.company_id.id, force_company=self.company_id.id - )._bom_find(product=self.product_id, picking_type=self.rule_id.picking_type_id) # TDE FIXME: context bullshit + company_id=values['company_id'].id, force_company=values['company_id'].id + )._bom_find(product=values['product_id'], picking_type=rule.picking_type_id) # TDE FIXME: context bullshit - def _get_date_planned(self): - format_date_planned = fields.Datetime.from_string(self.date_planned) - date_planned = format_date_planned - relativedelta(days=self.product_id.produce_delay or 0.0) - date_planned = date_planned - relativedelta(days=self.company_id.manufacturing_lead) + def _get_date_planned(self, values, rule): + format_date_planned = fields.Datetime.from_string(values['date_planned']) + date_planned = format_date_planned - relativedelta(days=values['product_id'].produce_delay or 0.0) + date_planned = date_planned - relativedelta(days=values['company_id'].manufacturing_lead) return date_planned - def _prepare_mo_vals(self, bom): + def _prepare_mo_vals(self, values, rule, bom): return { - 'origin': self.origin, - 'product_id': self.product_id.id, - 'product_qty': self.product_qty, - 'product_uom_id': self.product_uom.id, - 'location_src_id': self.rule_id.location_src_id.id or self.location_id.id, - 'location_dest_id': self.location_id.id, + 'origin': values['origin'], + 'product_id': values['product_id'].id, + 'product_qty': values['product_qty'], + 'product_uom_id': values['product_uom'].id, + 'location_src_id': rule.location_src_id.id or values['location_id'].id, + 'location_dest_id': values['location_id'].id, 'bom_id': bom.id, - 'date_planned_start': fields.Datetime.to_string(self._get_date_planned()), - 'date_planned_finished': self.date_planned, - 'procurement_group_id': self.group_id.id, - 'propagate': self.rule_id.propagate, - 'picking_type_id': self.rule_id.picking_type_id.id or self.warehouse_id.manu_type_id.id, - 'company_id': self.company_id.id, - 'procurement_ids': [(6, 0, [self.id])], + 'date_planned_start': fields.Datetime.to_string(self._get_date_planned(values, rule)), + 'date_planned_finished': values['date_planned'], + 'procurement_group_id': values.get('group_id').id if values.get('group_id', False) else False, + 'propagate': rule.propagate, + 'picking_type_id': rule.picking_type_id.id or values['warehouse_id'].manu_type_id.id, + 'company_id': values['company_id'].id, + 'move_dest_ids': values.get('move_dest_ids') and [(4, x.id) for x in values['move_dest_ids']] or False, } - @api.multi - def make_mo(self): - """ Create production orders from procurements """ - res = {} - Production = self.env['mrp.production'] - for procurement in self: - ProductionSudo = Production.sudo().with_context(force_company=procurement.company_id.id) - bom = procurement._get_matching_bom() - if bom: - # create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example) - production = ProductionSudo.create(procurement._prepare_mo_vals(bom)) - origin_production = procurement.move_dest_id.raw_material_production_id - orderpoint = procurement.orderpoint_id - if orderpoint: - production.message_post_with_view('mail.message_origin_link', - values={'self': production, 'origin': orderpoint}, - subtype_id=self.env.ref('mail.mt_note').id) - if origin_production: - production.message_post_with_view('mail.message_origin_link', - values={'self': production, 'origin': origin_production}, - subtype_id=self.env.ref('mail.mt_note').id) - - res[procurement.id] = production.id - else: - res[procurement.id] = False - procurement.message_post(body=_("No BoM exists for this product!")) - return res diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 40098f5f16e..a391f986a99 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -42,6 +42,7 @@ class StockMoveLine(models.Model): class StockMove(models.Model): _inherit = 'stock.move' + created_production_id = fields.Many2one('mrp.production', 'Created Production Order') production_id = fields.Many2one( 'mrp.production', 'Production Order for finished products') raw_material_production_id = fields.Many2one( @@ -178,7 +179,6 @@ class StockMove(models.Model): 'product_uom_qty': quantity, 'state': 'draft', # will be confirmed below 'name': self.name, - 'procurement_id': self.procurement_id.id, }) return self.env['stock.move'] diff --git a/addons/mrp/report/mrp_production_templates.xml b/addons/mrp/report/mrp_production_templates.xml index d6869e71b33..1dfead642db 100644 --- a/addons/mrp/report/mrp_production_templates.xml +++ b/addons/mrp/report/mrp_production_templates.xml @@ -38,14 +38,6 @@ -
- Partner Ref:
- -
-
- SO Number:
- -
diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv index ebdc3c6c170..9d5a89fca90 100644 --- a/addons/mrp/security/ir.model.access.csv +++ b/addons/mrp/security/ir.model.access.csv @@ -8,7 +8,6 @@ access_mrp_routing_workcenter,mrp.routing.workcenter,model_mrp_routing_workcente access_mrp_bom,mrp.bom,model_mrp_bom,group_mrp_user,1,0,0,0 access_mrp_bom_line,mrp.bom.line,model_mrp_bom_line,group_mrp_user,1,0,0,0 access_mrp_production,mrp.production user,model_mrp_production,mrp.group_mrp_user,1,1,1,1 -access_procurement,procurement.order,model_procurement_order,mrp.group_mrp_user,1,1,1,1 access_mrp_workcenter_manager,mrp.workcenter.manager,model_mrp_workcenter,mrp.group_mrp_manager,1,1,1,1 access_mrp_resource_manager,access_mrp_resource manager,resource.model_resource_resource,mrp.group_mrp_manager,1,1,1,1 access_mrp_routing_manager,mrp.routing.manager,model_mrp_routing,mrp.group_mrp_manager,1,1,1,1 @@ -19,7 +18,6 @@ access_stock_location_mrp_worker,stock.location mrp_worker,stock.model_stock_loc access_stock_move_mrp_worker,stock.move mrp_worker,stock.model_stock_move,mrp.group_mrp_user,1,1,1,0 access_stock_picking_mrp_worker,stock.picking mrp_worker,stock.model_stock_picking,mrp.group_mrp_user,1,1,1,1 access_stock_warehouse,stock.warehouse mrp_worker,stock.model_stock_warehouse,mrp.group_mrp_user,1,0,0,0 -access_procurement_user,procurement.order.user,model_procurement_order,base.group_user,1,1,1,1 access_mrp_production_stock_worker,mrp.production stock_worker,model_mrp_production,stock.group_stock_user,1,0,0,0 access_product_product_user,product.product user,product.model_product_product,mrp.group_mrp_user,1,0,0,0 access_product_template_user,product.template user,product.model_product_template,mrp.group_mrp_user,1,0,0,0 diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py index 98074e5e004..16c6dbe4a32 100644 --- a/addons/mrp/tests/test_procurement.py +++ b/addons/mrp/tests/test_procurement.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.addons.mrp.tests.common import TestMrpCommon - +from odoo.exceptions import UserError class TestProcurement(TestMrpCommon): @@ -38,11 +38,11 @@ class TestProcurement(TestMrpCommon): self.assertEqual(production_product_6.state, 'confirmed', 'Production order should be for Confirmed state') # Check procurement for product 4 created or not. - procurement = self.env['procurement.order'].search([('group_id', '=', production_product_6.procurement_group_id.id), ('product_id', '=', self.product_4.id)]) - self.assertTrue(procurement, 'No procurement are created !') - self.assertEqual(procurement.state, 'running', 'Procurement order should be in state running') + # Check it created a purchase order - produce_product_4 = procurement.production_id + move_raw_product4 = production_product_6.move_raw_ids.filtered(lambda x: x.product_id == self.product_4) + produce_product_4 = self.env['mrp.production'].search([('product_id', '=', self.product_4.id), + ('move_dest_ids', '=', move_raw_product4[0].id)]) # produce product self.assertEqual(produce_product_4.availability, 'waiting', "Consume material not available") @@ -76,7 +76,6 @@ class TestProcurement(TestMrpCommon): # Check procurement and Production state for product 4. produce_product_4.button_mark_done() self.assertEqual(produce_product_4.state, 'done', 'Production order should be in state done') - self.assertEqual(procurement.state, 'done', 'Procurement order should be in state done') # Produce product 6 # ------------------ @@ -126,17 +125,12 @@ class TestProcurement(TestMrpCommon): mto_route.product_categ_selectable = True all_categ_id.write({'route_ids': [(6, 0, [mto_route.id])]}) - # create MO - production_product_4 = self.env['mrp.production'].create({ - 'name': 'MO/Test-00002', - 'product_id': self.product_4.id, - 'product_qty': 1, - 'bom_id': self.bom_1.id, - 'product_uom_id': self.product_4.uom_id.id, - }) - - # check that procurement are correctly created - procurement = self.env['procurement.order'].search( - [('group_id', '=', production_product_4.procurement_group_id.id), - ('product_id', 'in', self.bom_1.bom_line_ids.mapped('product_id.id'))]) - self.assertEqual(len(procurement), 2) + # create MO, but check it raises error as components are in make to order and not everyone has + with self.assertRaises(UserError): + production_product_4 = self.env['mrp.production'].create({ + 'name': 'MO/Test-00002', + 'product_id': self.product_4.id, + 'product_qty': 1, + 'bom_id': self.bom_1.id, + 'product_uom_id': self.product_4.uom_id.id, + }) \ No newline at end of file diff --git a/addons/mrp/views/procurement_views.xml b/addons/mrp/views/procurement_views.xml index fac569428db..9101fa31b83 100644 --- a/addons/mrp/views/procurement_views.xml +++ b/addons/mrp/views/procurement_views.xml @@ -5,7 +5,7 @@ procurement.rule.form.stock.inherit.mrp procurement.rule - + {'required': [('action', '=', 'move')], 'invisible': [('action', 'not in', ['move', 'manufacture'])]} @@ -13,18 +13,5 @@ - - procurement.order.form.inherit - procurement.order - - - - - - - - - - - \ No newline at end of file + diff --git a/addons/mrp/views/res_config_settings_views.xml b/addons/mrp/views/res_config_settings_views.xml index ab83a854335..4cef988f45d 100644 --- a/addons/mrp/views/res_config_settings_views.xml +++ b/addons/mrp/views/res_config_settings_views.xml @@ -89,7 +89,7 @@