mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
[IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests
This commit is contained in:
committed by
Lois Rilo
parent
97c1425c8e
commit
3a3a2fac6b
@@ -6,11 +6,14 @@
|
|||||||
Production Grouped By Product
|
Production Grouped By Product
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
Groups pending productions by product.
|
When you have several sales orders with make to (MTO) order products that
|
||||||
|
require to be manufactured, you end up with one manufacturing order for each of
|
||||||
Configuration
|
these sales orders, which is very bad for the management.
|
||||||
=============
|
|
||||||
|
|
||||||
|
With this module, each time an MTO manufacturing order is required to be
|
||||||
|
created, it first checks that there's no other existing order not yet started
|
||||||
|
for the same product and bill of materials, and if there's one, then the
|
||||||
|
quantity of that order is increased instead of creating a new one.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
@@ -19,6 +22,11 @@ Usage
|
|||||||
:alt: Try me on Runbot
|
:alt: Try me on Runbot
|
||||||
:target: https://runbot.odoo-community.org/runbot/129/11.0
|
:target: https://runbot.odoo-community.org/runbot/129/11.0
|
||||||
|
|
||||||
|
Known issues / Roadmap
|
||||||
|
======================
|
||||||
|
|
||||||
|
* Add a check in the product form for excluding it from being grouped.
|
||||||
|
|
||||||
Bug Tracker
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
|
|
||||||
@@ -38,7 +46,12 @@ Images
|
|||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* David Vidal <david.vidal@tecnativa.com>
|
* Tecnativa <https://www.tecnativa.com>_
|
||||||
|
|
||||||
|
* David Vidal
|
||||||
|
* Pedro M. Baeza
|
||||||
|
|
||||||
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
Maintainer
|
Maintainer
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# Copyright 2018 Tecnativa - David Vidal
|
# Copyright 2018 Tecnativa - David Vidal
|
||||||
|
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,15 +1,51 @@
|
|||||||
# Copyright 2018 Tecnativa - David Vidal
|
# Copyright 2018 Tecnativa - David Vidal
|
||||||
|
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, models
|
||||||
|
from odoo.tools import config
|
||||||
|
|
||||||
|
|
||||||
class MrpProduction(models.Model):
|
class MrpProduction(models.Model):
|
||||||
_inherit = 'mrp.production'
|
_inherit = 'mrp.production'
|
||||||
|
|
||||||
|
def _post_mo_merging_adjustments(self, vals):
|
||||||
|
"""Called when a new MO is merged onto existing one for adjusting the
|
||||||
|
needed values according this merging.
|
||||||
|
|
||||||
|
:param self: Single record of the target record where merging.
|
||||||
|
:param vals: Dictionary with the new record values.
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
new_vals = {
|
||||||
|
'origin': (self.origin or '') + ",%s" % vals['origin'],
|
||||||
|
}
|
||||||
|
if vals.get('move_dest_ids'):
|
||||||
|
new_vals['move_dest_ids'] = vals['move_dest_ids']
|
||||||
|
self.move_finished_ids.move_dest_ids = vals['move_dest_ids']
|
||||||
|
self.write(new_vals)
|
||||||
|
|
||||||
|
def _find_grouping_target(self, vals):
|
||||||
|
mo = self.env['mrp.production'].search([
|
||||||
|
('product_id', '=', vals['product_id']),
|
||||||
|
('bom_id', '=', vals.get('bom_id', False)),
|
||||||
|
('routing_id', '=', vals.get('routing_id', False)),
|
||||||
|
('company_id', '=', vals.get('company_id', False)),
|
||||||
|
('state', '=', 'confirmed'),
|
||||||
|
], limit=1)
|
||||||
|
return mo
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
if not self._context.get('merge_products_into_mo'):
|
context = self.env.context
|
||||||
return super(MrpProduction, self).create(vals)
|
if (context.get('group_mo_by_product') and
|
||||||
# We return the MO to merge into
|
(not config['test_enable'] or context.get('test_group_mo'))):
|
||||||
return self._context.get('merge_products_into_mo')
|
mo = self._find_grouping_target(vals)
|
||||||
|
if mo:
|
||||||
|
self.env['change.production.qty'].create({
|
||||||
|
'mo_id': mo.id,
|
||||||
|
'product_qty': mo.product_qty + vals['product_qty'],
|
||||||
|
}).change_prod_qty()
|
||||||
|
mo._post_mo_merging_adjustments(vals)
|
||||||
|
return mo
|
||||||
|
return super(MrpProduction, self).create(vals)
|
||||||
|
|||||||
@@ -1,44 +1,18 @@
|
|||||||
# Copyright 2018 Tecnativa - David Vidal
|
# Copyright 2018 Tecnativa - David Vidal
|
||||||
|
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
class ProcuermentRule(models.Model):
|
class ProcurementRule(models.Model):
|
||||||
_inherit = 'procurement.rule'
|
_inherit = 'procurement.rule'
|
||||||
|
|
||||||
def _run_manufacture(self, product_id, product_qty, product_uom,
|
def _run_manufacture(self, product_id, product_qty, product_uom,
|
||||||
location_id, name, origin, values):
|
location_id, name, origin, values):
|
||||||
bom = self._get_matching_bom(product_id, values)
|
return super(
|
||||||
# Send to super for exception
|
ProcurementRule, self.with_context(group_mo_by_product=True),
|
||||||
if not bom:
|
)._run_manufacture(
|
||||||
return super(ProcuermentRule, self)._run_manufacture(
|
product_id, product_qty, product_uom, location_id, name, origin,
|
||||||
product_id, product_qty, product_uom, location_id,
|
values,
|
||||||
name, origin, values)
|
)
|
||||||
open_mo = self._find_equal_open_mo(
|
|
||||||
product_id, bom, location_id, values)
|
|
||||||
# Create mo as usual
|
|
||||||
if not open_mo:
|
|
||||||
return super(ProcuermentRule, self)._run_manufacture(
|
|
||||||
product_id, product_qty, product_uom, location_id,
|
|
||||||
name, origin, values)
|
|
||||||
# Add product qty to mo
|
|
||||||
self.env['change.production.qty'].create({
|
|
||||||
'mo_id': open_mo.id,
|
|
||||||
'product_qty': open_mo.product_qty + product_qty,
|
|
||||||
}).change_prod_qty()
|
|
||||||
# We pass the record in the context so the chatter is correctly written
|
|
||||||
additional_context={'merge_products_into_mo': open_mo}
|
|
||||||
return super(ProcuermentRule, self.with_context(**additional_context)
|
|
||||||
)._run_manufacture(product_id, product_qty, product_uom,
|
|
||||||
location_id,name, origin, values)
|
|
||||||
|
|
||||||
def _find_equal_open_mo(self, product_id, bom, location_id, values):
|
|
||||||
"""Returns the first occurrence according to conditions"""
|
|
||||||
return self.env['mrp.production'].search([
|
|
||||||
('state', 'not in', ['progress', 'done', 'cancel']),
|
|
||||||
('product_id', '=', product_id.id),
|
|
||||||
('bom_id', '=', bom.id),
|
|
||||||
('location_dest_id', '=', location_id.id),
|
|
||||||
('company_id', '=', values.get('company_id').id),
|
|
||||||
], limit=1)
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# Copyright 2018 Tecnativa - David Vidal
|
||||||
# Copyright 2017 Tecnativa - David Vidal
|
# Copyright 2018 Tecnativa - Pedro M. Baeza
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo.tests import common
|
from odoo.tests import common
|
||||||
|
|
||||||
|
|
||||||
class TestProductionGroupedByProduct(common.SavepointCase):
|
class TestProductionGroupedByProduct(common.SavepointCase):
|
||||||
|
at_install = False
|
||||||
|
post_install = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@@ -36,14 +38,6 @@ class TestProductionGroupedByProduct(common.SavepointCase):
|
|||||||
'product_qty': 0.2,
|
'product_qty': 0.2,
|
||||||
})]
|
})]
|
||||||
})
|
})
|
||||||
cls.env['stock.change.product.qty'].create({
|
|
||||||
'product_id': cls.product2.id,
|
|
||||||
'new_quantity': 100.0,
|
|
||||||
}).change_product_qty()
|
|
||||||
cls.env['stock.change.product.qty'].create({
|
|
||||||
'product_id': cls.product3.id,
|
|
||||||
'new_quantity': 100.0,
|
|
||||||
}).change_product_qty()
|
|
||||||
cls.stock_picking_type = cls.env.ref('stock.picking_type_out')
|
cls.stock_picking_type = cls.env.ref('stock.picking_type_out')
|
||||||
cls.procurement_rule = cls.env['stock.warehouse.orderpoint'].create({
|
cls.procurement_rule = cls.env['stock.warehouse.orderpoint'].create({
|
||||||
'name': 'XXX/00000',
|
'name': 'XXX/00000',
|
||||||
@@ -51,35 +45,43 @@ class TestProductionGroupedByProduct(common.SavepointCase):
|
|||||||
'product_min_qty': 10,
|
'product_min_qty': 10,
|
||||||
'product_max_qty': 100,
|
'product_max_qty': 100,
|
||||||
})
|
})
|
||||||
|
cls.mo = cls.env['mrp.production'].create({
|
||||||
|
'bom_id': cls.bom.id,
|
||||||
|
'product_id': cls.product1.id,
|
||||||
|
'product_qty': 2,
|
||||||
|
'product_uom_id': cls.product1.uom_id.id,
|
||||||
|
})
|
||||||
|
cls.warehouse = cls.env['stock.warehouse'].search([
|
||||||
|
('company_id', '=', cls.env.user.company_id.id),
|
||||||
|
], limit=1)
|
||||||
|
cls.ProcurementGroup = cls.env['procurement.group']
|
||||||
|
cls.MrpProduction = cls.env['mrp.production']
|
||||||
|
|
||||||
def test_mo_by_product(self):
|
def test_mo_by_product(self):
|
||||||
self.env['procurement.group'].run_scheduler()
|
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
|
||||||
mo = self.env['mrp.production'].search([
|
mo = self.MrpProduction.search([('product_id', '=', self.product1.id)])
|
||||||
('product_id', '=', self.product1.id),
|
self.assertEqual(len(mo), 1)
|
||||||
])
|
self.assertEqual(mo.product_qty, 100)
|
||||||
self.assertTrue(mo)
|
# Add an MTO move
|
||||||
#
|
|
||||||
picking = self.env['stock.picking'].create({
|
|
||||||
'picking_type_id': self.stock_picking_type.id,
|
|
||||||
'location_id': self.env.ref('stock.stock_location_stock').id,
|
|
||||||
'location_dest_id': self.env.ref(
|
|
||||||
'stock.stock_location_customers').id,
|
|
||||||
})
|
|
||||||
move = self.env['stock.move'].create({
|
move = self.env['stock.move'].create({
|
||||||
'name': self.product1.name,
|
'name': self.product1.name,
|
||||||
'product_id': self.product1.id,
|
'product_id': self.product1.id,
|
||||||
'product_uom_qty': 1000,
|
'product_uom_qty': 10,
|
||||||
'product_uom': self.product1.uom_id.id,
|
'product_uom': self.product1.uom_id.id,
|
||||||
'picking_id': picking.id,
|
'location_id': self.warehouse.lot_stock_id.id,
|
||||||
'picking_type_id': self.stock_picking_type.id,
|
'location_dest_id': (
|
||||||
'location_id': picking.location_id.id,
|
self.env.ref('stock.stock_location_customers').id
|
||||||
'location_dest_id': picking.location_id.id,
|
),
|
||||||
|
'procure_method': 'make_to_order',
|
||||||
|
'warehouse_id': self.warehouse.id,
|
||||||
})
|
})
|
||||||
move.quantity_done = 1000
|
move.with_context(test_group_mo=True)._action_confirm(merge=False)
|
||||||
picking.action_assign()
|
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
|
||||||
self.product1.virtual_available = -500
|
mo = self.MrpProduction.search([('product_id', '=', self.product1.id)])
|
||||||
self.env['procurement.group'].run_scheduler()
|
|
||||||
mo = self.env['mrp.production'].search([
|
|
||||||
('product_id', '=', self.product1.id),
|
|
||||||
])
|
|
||||||
self.assertEqual(len(mo), 1)
|
self.assertEqual(len(mo), 1)
|
||||||
|
self.assertEqual(mo.product_qty, 110)
|
||||||
|
# Run again the scheduler to see if quantities are altered
|
||||||
|
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
|
||||||
|
mo = self.MrpProduction.search([('product_id', '=', self.product1.id)])
|
||||||
|
self.assertEqual(len(mo), 1)
|
||||||
|
self.assertEqual(mo.product_qty, 110)
|
||||||
|
|||||||
Reference in New Issue
Block a user