Merge PR #849 into 15.0

Signed-off-by LoisRForgeFlow
This commit is contained in:
OCA-git-bot
2022-09-16 08:34:50 +00:00
23 changed files with 1466 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
=============================
Production Grouped By Product
=============================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
:target: https://github.com/OCA/manufacture/tree/14.0/mrp_production_grouped_by_product
:alt: OCA/manufacture
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_production_grouped_by_product
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/129/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
When you have several sales orders with make to order (MTO) products that
require to be manufactured, you end up with one manufacturing order for each of
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 inside the specied time frame , and
if there's one, then the quantity of that order is increased instead of
creating a new one.
**Table of contents**
.. contents::
:local:
Configuration
=============
To configure the time frame for grouping manufacturing order:
#. Go to *Inventory > Configuration > Warehouse Management > Operation Types*
#. Locate the manufacturing type you are using (default one is called
"Manufacturing").
#. Open it and change these 2 values:
* MO grouping max. hour (UTC): The maximum hour (between 0 and 23) for
considering new manufacturing orders inside the same interval period, and
thus being grouped on the same MO. IMPORTANT: The hour should be expressed
in UTC.
* MO grouping interval (days): The number of days for grouping together on
the same manufacturing order.
Example: If you leave the default values 19 and 1, all the planned orders
between 19:00:01 of the previous day and 20:00:00 of the target date will
be grouped together.
Known issues / Roadmap
======================
* Add a check in the product form for excluding it from being grouped.
Changelog
=========
14.0.1.0.0 (2021-11-16)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v14.
13.0.1.0.0 (2020-01-09)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v13.
12.0.1.0.0 (2019-04-17)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v12:
11.0.2.0.1 (2018-07-02)
~~~~~~~~~~~~~~~~~~~~~~~
* [FIX] fix test in mrp_production_grouped_by_product
11.0.2.0.0 (2018-06-04)
~~~~~~~~~~~~~~~~~~~~~~~
* [IMP] mrp_production_grouped_by_product: Time frames
11.0.1.0.1 (2018-05-11)
~~~~~~~~~~~~~~~~~~~~~~~
* [IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests
11.0.1.0.0 (2018-05-11)
~~~~~~~~~~~~~~~~~~~~~~~
* Start of the history.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/manufacture/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_production_grouped_by_product%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Tecnativa
Contributors
~~~~~~~~~~~~
* `Tecnativa <https://www.tecnativa.com>`__
* David Vidal
* Pedro M. Baeza
* `Ecosoft <https://ecosoft.co.th/>`__:
* Pimolnat Suntian <pimolnats@ecosoft.co.th>
* `ForgeFlow <https://www.forgeflow.com/>`__:
* Lois Rilo <lois.rilo@forgeflow.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/14.0/mrp_production_grouped_by_product>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,16 @@
# Copyright 2018 Tecnativa - David Vidal
# Copyright 2018 Tecnativa - Pedro M. Baeza
# Copyright 2019 Rubén Bravo <rubenred18@gmail.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Production Grouped By Product",
"version": "15.0.1.0.0",
"category": "MRP",
"author": "Tecnativa, " "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/manufacture",
"license": "AGPL-3",
"depends": ["mrp"],
"data": ["views/stock_picking_type_views.xml"],
"installable": True,
}

View File

@@ -0,0 +1,84 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_production_grouped_by_product
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2020-05-29 14:19+0000\n"
"Last-Translator: ortlam <ruben.ortlam@initos.com>\n"
"Language-Team: none\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.10\n"
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval
msgid "MO grouping interval (days)"
msgstr "Gruppierungsintervall für Fertigungsaufträge (Tage)"
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour
msgid "MO grouping max. hour (UTC)"
msgstr "Späteste Uhrzeit (UTC) für die Gruppierung von Fertigungsaufträgen"
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_picking_type
msgid "Picking Type"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_mrp_production
msgid "Production Order"
msgstr "Fertigungsauftrag"
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_rule
msgid "Stock Rule"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour
msgid ""
"The maximum hour (between 0 and 23) for considering new manufacturing orders "
"inside the same interval period, and thus being grouped on the same MO. "
"IMPORTANT: The hour should be expressed in UTC."
msgstr ""
"Dies ist die späteste Uhrzeit (zwischen 0 und 23) zur Berücksichtigung von "
"neuen Fertigungsaufträgen innerhalb desselben Intervals, die dabei zum "
"selben Fertigungsauftrag gruppiert werden. WICHTIG: Die Uhrzeit muss in UTC "
"angegeben werden."
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval
msgid ""
"The number of days for grouping together on the same manufacturing order."
msgstr ""
"Dies ist die Anzahl von Tagen zur Gruppierung in demselben Fertigungsauftrag."
#. module: mrp_production_grouped_by_product
#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:0
#, python-format
msgid "You have to enter a positive value for interval."
msgstr "Sie müssen für das Intervall einen Wert größer 0 eingeben."
#. module: mrp_production_grouped_by_product
#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:0
#, python-format
msgid "You have to enter a valid hour between 0 and 23."
msgstr "Sie müssen eine gültige Uhrzeit zwischen 0 und 23 eingeben."
#~ msgid "Procurement Rule"
#~ msgstr "Beschaffungsregel"
#~ msgid "The operation type determines the picking view"
#~ msgstr "Der Vorgangstyp legt die Pick-Ansicht fest"

View File

@@ -0,0 +1,83 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_production_grouped_by_product
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-06-03 23:16+0000\n"
"PO-Revision-Date: 2018-06-03 23:16+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval
msgid "MO grouping interval (days)"
msgstr "Intervalo de agrupación de OFs (días)"
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour
msgid "MO grouping max. hour (UTC)"
msgstr "Hora máx. agrupación OFs (UTC)"
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_picking_type
msgid "Picking Type"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_mrp_production
#, fuzzy
msgid "Production Order"
msgstr "Orden de fabricación"
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_rule
msgid "Stock Rule"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour
msgid ""
"The maximum hour (between 0 and 23) for considering new manufacturing orders "
"inside the same interval period, and thus being grouped on the same MO. "
"IMPORTANT: The hour should be expressed in UTC."
msgstr ""
"La hora máxima (entre 0 y 23) para considerar nuevas órdenes de fabricación "
"dentro del mismo periodo de tiempo, y por tanto siendo agrupadas dentro de "
"la misma OF. IMPORTANTE: La hora debe expresarse en UTC."
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval
msgid ""
"The number of days for grouping together on the same manufacturing order."
msgstr "El número de días para agrupar juntas las órdenes de fabricación."
#. module: mrp_production_grouped_by_product
#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:0
#, python-format
msgid "You have to enter a positive value for interval."
msgstr "Debe introducir un valor positivo para el intervalo."
#. module: mrp_production_grouped_by_product
#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:0
#, python-format
msgid "You have to enter a valid hour between 0 and 23."
msgstr "Debe introducir una hora válida entre 0 y 23."
#~ msgid "Procurement Rule"
#~ msgstr "Regla de abastecimiento"
#~ msgid "The operation type determines the picking view"
#~ msgstr "El tipo de operación determina la vista de la operación"

View File

@@ -0,0 +1,94 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mrp_production_grouped_by_product
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_mrp_production__display_name
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_move__display_name
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__display_name
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_rule__display_name
msgid "Display Name"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_mrp_production__id
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_move__id
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__id
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_rule__id
msgid "ID"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_mrp_production____last_update
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_move____last_update
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type____last_update
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_rule____last_update
msgid "Last Modified on"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval
msgid "MO grouping interval (days)"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,field_description:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour
msgid "MO grouping max. hour (UTC)"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_picking_type
msgid "Picking Type"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_mrp_production
msgid "Production Order"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model,name:mrp_production_grouped_by_product.model_stock_rule
msgid "Stock Rule"
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_max_hour
msgid ""
"The maximum hour (between 0 and 23) for considering new manufacturing orders"
" inside the same interval period, and thus being grouped on the same MO. "
"IMPORTANT: The hour should be expressed in UTC."
msgstr ""
#. module: mrp_production_grouped_by_product
#: model:ir.model.fields,help:mrp_production_grouped_by_product.field_stock_picking_type__mo_grouping_interval
msgid ""
"The number of days for grouping together on the same manufacturing order."
msgstr ""
#. module: mrp_production_grouped_by_product
#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:0
#, python-format
msgid "You have to enter a positive value for interval."
msgstr ""
#. module: mrp_production_grouped_by_product
#: code:addons/mrp_production_grouped_by_product/models/stock_picking_type.py:0
#, python-format
msgid "You have to enter a valid hour between 0 and 23."
msgstr ""

View File

@@ -0,0 +1,4 @@
from . import mrp_production
from . import stock_rule
from . import stock_picking_type
from . import stock_move

View File

@@ -0,0 +1,135 @@
# Copyright 2018 Tecnativa - David Vidal
# Copyright 2018 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.tools import config
class MrpProduction(models.Model):
_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 _get_grouping_target_vals(self):
self.ensure_one()
return {
"product_id": self.product_id.id,
"picking_type_id": self.picking_type_id.id,
"bom_id": self.bom_id.id,
"company_id": self.company_id.id,
"state": self.state,
"date_deadline": self.date_deadline,
}
# NOTE: when extending this logic, remember to also adapt
# `_get_grouping_target_vals` accordingly.
def _get_grouping_target_domain(self, vals):
"""Get the domain for searching manufacturing orders that can match
with the criteria we want to use.
:param vals: Values dictionary of the MO to be created.
:return: Odoo domain.
"""
bom_has_routing = bool(self.env["mrp.bom"].browse(vals["bom_id"]).operation_ids)
domain = [
("product_id", "=", vals["product_id"]),
("picking_type_id", "=", vals["picking_type_id"]),
("bom_id", "=", vals.get("bom_id", False)),
("company_id", "=", vals.get("company_id", False)),
("state", "in", ["draft", "confirmed"]),
]
if bom_has_routing:
domain.append(("workorder_ids", "!=", False))
else:
domain.append(("workorder_ids", "=", False))
if not vals.get("date_deadline"):
return domain
date = fields.Datetime.from_string(vals["date_deadline"])
pt = self.env["stock.picking.type"].browse(vals["picking_type_id"])
if date.hour < pt.mo_grouping_max_hour:
date_end = date.replace(hour=pt.mo_grouping_max_hour, minute=0, second=0)
else:
date_end = date.replace(
day=date.day + 1, hour=pt.mo_grouping_max_hour, minute=0, second=0
)
date_start = date_end - relativedelta(days=pt.mo_grouping_interval)
domain += [
("date_deadline", ">", fields.Datetime.to_string(date_start)),
("date_deadline", "<=", fields.Datetime.to_string(date_end)),
]
return domain
def _find_grouping_target(self, vals):
"""Return the matching order for grouping.
:param vals: Values dictionary of the MO to be created.
:return: Target manufacturing order record (or empty record).
"""
return self.env["mrp.production"].search(
self._get_grouping_target_domain(vals), limit=1
)
@api.model
def create(self, vals):
context = self.env.context
if context.get("group_mo_by_product") and (
not config["test_enable"] or context.get("test_group_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)
def _create_workorder(self):
# We need to skip the creation of workorders during `_run_manufacture`.
# It is not possible to pass a context from the `_post_mo_merging_adjustments`
# because the create is called with sudo in `_run_manufacture` and that
# creates a new context that does not reach `_create_workorder` call.
context = self.env.context
to_create_wos = self
if context.get("group_mo_by_product") and (
not config["test_enable"] or context.get("test_group_mo")
):
for rec in self:
vals = rec._get_grouping_target_vals()
mo = self._find_grouping_target(vals)
if mo:
to_create_wos -= rec
return super(MrpProduction, to_create_wos)._create_workorder()
def _get_moves_finished_values(self):
# We need to skip the creation of more finished moves during `_run_manufacture`.
new_self = self
if self.env.context.get("group_mo_by_product"):
for rec in self:
if not rec.move_finished_ids:
continue
vals = rec._get_grouping_target_vals()
mo = self._find_grouping_target(vals)
if mo:
new_self -= rec
return super(MrpProduction, new_self)._get_moves_finished_values()

View File

@@ -0,0 +1,29 @@
# Copyright 2018 Tecnativa - David Vidal
# Copyright 2018 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, models
class StockMove(models.Model):
_inherit = "stock.move"
@api.model_create_multi
def create(self, vals_list):
new_vals_list = []
for val in vals_list:
if (
self.env.context.get("group_mo_by_product", False)
and "raw_material_production_id" in val
):
mo = self.env["mrp.production"].browse(
val["raw_material_production_id"]
)
# MO already has raw materials
if mo.move_raw_ids:
continue
else:
new_vals_list.append(val)
else:
new_vals_list.append(val)
return super(StockMove, self).create(new_vals_list)

View File

@@ -0,0 +1,37 @@
# Copyright 2018 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, exceptions, fields, models
class StockPickingType(models.Model):
_inherit = "stock.picking.type"
mo_grouping_max_hour = fields.Integer(
string="MO grouping max. hour (UTC)",
help="The maximum hour (between 0 and 23) for considering new "
"manufacturing orders inside the same interval period, and thus "
"being grouped on the same MO. IMPORTANT: The hour should be "
"expressed in UTC.",
default=19,
)
mo_grouping_interval = fields.Integer(
string="MO grouping interval (days)",
help="The number of days for grouping together on the same "
"manufacturing order.",
default=1,
)
@api.constrains("mo_grouping_max_hour")
def _check_mo_grouping_max_hour(self):
if self.mo_grouping_max_hour < 0 or self.mo_grouping_max_hour > 23:
raise exceptions.ValidationError(
_("You have to enter a valid hour between 0 and 23.")
)
@api.constrains("mo_grouping_interval")
def _check_mo_grouping_interval(self):
if self.mo_grouping_interval < 0:
raise exceptions.ValidationError(
_("You have to enter a positive value for interval.")
)

View File

@@ -0,0 +1,14 @@
# Copyright 2018 Tecnativa - David Vidal
# Copyright 2018 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
class StockRule(models.Model):
_inherit = "stock.rule"
def _run_manufacture(self, procurements):
return super(
StockRule, self.with_context(group_mo_by_product=True)
)._run_manufacture(procurements)

View File

@@ -0,0 +1,17 @@
To configure the time frame for grouping manufacturing order:
#. Go to *Inventory > Configuration > Warehouse Management > Operation Types*
#. Locate the manufacturing type you are using (default one is called
"Manufacturing").
#. Open it and change these 2 values:
* MO grouping max. hour (UTC): The maximum hour (between 0 and 23) for
considering new manufacturing orders inside the same interval period, and
thus being grouped on the same MO. IMPORTANT: The hour should be expressed
in UTC.
* MO grouping interval (days): The number of days for grouping together on
the same manufacturing order.
Example: If you leave the default values 19 and 1, all the planned orders
between 19:00:01 of the previous day and 20:00:00 of the target date will
be grouped together.

View File

@@ -0,0 +1,16 @@
* `Tecnativa <https://www.tecnativa.com>`__
* David Vidal
* Pedro M. Baeza
* `Ecosoft <https://ecosoft.co.th/>`__:
* Pimolnat Suntian <pimolnats@ecosoft.co.th>
* `ForgeFlow <https://www.forgeflow.com/>`__:
* Lois Rilo <lois.rilo@forgeflow.com>
* `Punt Sistemes <https://www.puntsistemes.com/>`__:
* Salva Benlloch <sbenlloch@puntsistemes.es>

View File

@@ -0,0 +1,9 @@
When you have several sales orders with make to order (MTO) products that
require to be manufactured, you end up with one manufacturing order for each of
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 inside the specied time frame , and
if there's one, then the quantity of that order is increased instead of
creating a new one.

View File

@@ -0,0 +1,39 @@
15.0.1.0.0 (2022-09-12)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v15.
14.0.1.0.0 (2021-11-16)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v14.
13.0.1.0.0 (2020-01-09)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v13.
12.0.1.0.0 (2019-04-17)
~~~~~~~~~~~~~~~~~~~~~~~
* [MIG] Migration to v12:
11.0.2.0.1 (2018-07-02)
~~~~~~~~~~~~~~~~~~~~~~~
* [FIX] fix test in mrp_production_grouped_by_product
11.0.2.0.0 (2018-06-04)
~~~~~~~~~~~~~~~~~~~~~~~
* [IMP] mrp_production_grouped_by_product: Time frames
11.0.1.0.1 (2018-05-11)
~~~~~~~~~~~~~~~~~~~~~~~
* [IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests
11.0.1.0.0 (2018-05-11)
~~~~~~~~~~~~~~~~~~~~~~~
* Start of the history.

View File

@@ -0,0 +1 @@
* Add a check in the product form for excluding it from being grouped.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,525 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Production Grouped By Product</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="production-grouped-by-product">
<h1 class="title">Production Grouped By Product</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/manufacture/tree/14.0/mrp_production_grouped_by_product"><img alt="OCA/manufacture" src="https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-mrp_production_grouped_by_product"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/129/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>When you have several sales orders with make to order (MTO) products that
require to be manufactured, you end up with one manufacturing order for each of
these sales orders, which is very bad for the management.</p>
<p>With this module, each time an MTO manufacturing order is required to be
created, it first checks that theres no other existing order not yet started
for the same product and bill of materials inside the specied time frame , and
if theres one, then the quantity of that order is increased instead of
creating a new one.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id8">Configuration</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id9">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#changelog" id="id10">Changelog</a><ul>
<li><a class="reference internal" href="#id1" id="id11">14.0.1.0.0 (2021-11-16)</a></li>
<li><a class="reference internal" href="#id2" id="id12">13.0.1.0.0 (2020-01-09)</a></li>
<li><a class="reference internal" href="#id3" id="id13">12.0.1.0.0 (2019-04-17)</a></li>
<li><a class="reference internal" href="#id4" id="id14">11.0.2.0.1 (2018-07-02)</a></li>
<li><a class="reference internal" href="#id5" id="id15">11.0.2.0.0 (2018-06-04)</a></li>
<li><a class="reference internal" href="#id6" id="id16">11.0.1.0.1 (2018-05-11)</a></li>
<li><a class="reference internal" href="#id7" id="id17">11.0.1.0.0 (2018-05-11)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="id18">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id19">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id20">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id21">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id22">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id8">Configuration</a></h1>
<p>To configure the time frame for grouping manufacturing order:</p>
<ol class="arabic">
<li><p class="first">Go to <em>Inventory &gt; Configuration &gt; Warehouse Management &gt; Operation Types</em></p>
</li>
<li><p class="first">Locate the manufacturing type you are using (default one is called
“Manufacturing”).</p>
</li>
<li><p class="first">Open it and change these 2 values:</p>
<ul class="simple">
<li>MO grouping max. hour (UTC): The maximum hour (between 0 and 23) for
considering new manufacturing orders inside the same interval period, and
thus being grouped on the same MO. IMPORTANT: The hour should be expressed
in UTC.</li>
<li>MO grouping interval (days): The number of days for grouping together on
the same manufacturing order.</li>
</ul>
<p>Example: If you leave the default values 19 and 1, all the planned orders
between 19:00:01 of the previous day and 20:00:00 of the target date will
be grouped together.</p>
</li>
</ol>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id9">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Add a check in the product form for excluding it from being grouped.</li>
</ul>
</div>
<div class="section" id="changelog">
<h1><a class="toc-backref" href="#id10">Changelog</a></h1>
<div class="section" id="id1">
<h2><a class="toc-backref" href="#id11">14.0.1.0.0 (2021-11-16)</a></h2>
<ul class="simple">
<li>[MIG] Migration to v14.</li>
</ul>
</div>
<div class="section" id="id2">
<h2><a class="toc-backref" href="#id12">13.0.1.0.0 (2020-01-09)</a></h2>
<ul class="simple">
<li>[MIG] Migration to v13.</li>
</ul>
</div>
<div class="section" id="id3">
<h2><a class="toc-backref" href="#id13">12.0.1.0.0 (2019-04-17)</a></h2>
<ul class="simple">
<li>[MIG] Migration to v12:</li>
</ul>
</div>
<div class="section" id="id4">
<h2><a class="toc-backref" href="#id14">11.0.2.0.1 (2018-07-02)</a></h2>
<ul class="simple">
<li>[FIX] fix test in mrp_production_grouped_by_product</li>
</ul>
</div>
<div class="section" id="id5">
<h2><a class="toc-backref" href="#id15">11.0.2.0.0 (2018-06-04)</a></h2>
<ul class="simple">
<li>[IMP] mrp_production_grouped_by_product: Time frames</li>
</ul>
</div>
<div class="section" id="id6">
<h2><a class="toc-backref" href="#id16">11.0.1.0.1 (2018-05-11)</a></h2>
<ul class="simple">
<li>[IMP] mrp_production_grouped_by_company: Context evaluation on mrp.production + tests</li>
</ul>
</div>
<div class="section" id="id7">
<h2><a class="toc-backref" href="#id17">11.0.1.0.0 (2018-05-11)</a></h2>
<ul class="simple">
<li>Start of the history.</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id18">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/manufacture/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/manufacture/issues/new?body=module:%20mrp_production_grouped_by_product%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id19">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id20">Authors</a></h2>
<ul class="simple">
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id21">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a><ul>
<li>David Vidal</li>
<li>Pedro M. Baeza</li>
</ul>
</li>
<li><a class="reference external" href="https://ecosoft.co.th/">Ecosoft</a>:<ul>
<li>Pimolnat Suntian &lt;<a class="reference external" href="mailto:pimolnats&#64;ecosoft.co.th">pimolnats&#64;ecosoft.co.th</a>&gt;</li>
</ul>
</li>
<li><a class="reference external" href="https://www.forgeflow.com/">ForgeFlow</a>:<ul>
<li>Lois Rilo &lt;<a class="reference external" href="mailto:lois.rilo&#64;forgeflow.com">lois.rilo&#64;forgeflow.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id22">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/manufacture/tree/14.0/mrp_production_grouped_by_product">OCA/manufacture</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
from . import test_mrp_production_grouped_by_product

View File

@@ -0,0 +1,183 @@
# Copyright 2018 Tecnativa - David Vidal
# Copyright 2018 Tecnativa - Pedro M. Baeza
# Copyright 2019 Rubén Bravo <rubenred18@gmail.com>
# Copyright 2021 ForgeFlow S.L. (http://www.forgeflow.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import exceptions
from odoo.tests import common
class TestProductionGroupedByProduct(common.TransactionCase):
at_install = False
post_install = True
@classmethod
def setUpClass(cls):
super(TestProductionGroupedByProduct, cls).setUpClass()
cls.ProcurementGroup = cls.env["procurement.group"]
cls.MrpProduction = cls.env["mrp.production"]
cls.env.user.company_id.manufacturing_lead = 0
cls.env.user.tz = False # Make sure there's no timezone in user
cls.picking_type = cls.env["stock.picking.type"].search(
[
("code", "=", "mrp_operation"),
("sequence_id.company_id", "=", cls.env.user.company_id.id),
],
limit=1,
)
cls.product1 = cls.env["product.product"].create(
{
"name": "TEST Muffin",
"route_ids": [
(6, 0, [cls.env.ref("mrp.route_warehouse0_manufacture").id])
],
"type": "product",
"produce_delay": 0,
}
)
cls.product2 = cls.env["product.product"].create(
{"name": "TEST Paper muffin cup", "type": "product"}
)
cls.product3 = cls.env["product.product"].create(
{"name": "TEST Muffin paset", "type": "product"}
)
cls.bom = cls.env["mrp.bom"].create(
{
"product_id": cls.product1.id,
"product_tmpl_id": cls.product1.product_tmpl_id.id,
"type": "normal",
"bom_line_ids": [
(0, 0, {"product_id": cls.product2.id, "product_qty": 1}),
(0, 0, {"product_id": cls.product3.id, "product_qty": 0.2}),
],
}
)
cls.stock_picking_type = cls.env.ref("stock.picking_type_out")
cls.mo = cls.MrpProduction.create(
{
"bom_id": cls.bom.id,
"product_id": cls.product1.id,
"product_qty": 2,
"product_uom_id": cls.product1.uom_id.id,
"date_deadline": "2018-06-01 15:00:00",
"date_planned_start": "2018-06-01 15:00:00",
}
)
cls.mo._onchange_move_raw()
cls.mo._onchange_move_finished()
cls.warehouse = cls.env["stock.warehouse"].search(
[("company_id", "=", cls.env.user.company_id.id)], limit=1
)
# Add an MTO move
cls.move = cls.env["stock.move"].create(
{
"name": cls.product1.name,
"product_id": cls.product1.id,
"product_uom_qty": 10,
"product_uom": cls.product1.uom_id.id,
"location_id": cls.warehouse.lot_stock_id.id,
"location_dest_id": (cls.env.ref("stock.stock_location_customers").id),
"procure_method": "make_to_order",
"warehouse_id": cls.warehouse.id,
"date": "2018-06-01 18:00:00",
}
)
cls.move_2 = cls.env["stock.move"].create(
{
"name": cls.product1.name,
"product_id": cls.product1.id,
"product_uom_qty": 5,
"product_uom": cls.product1.uom_id.id,
"location_id": cls.warehouse.lot_stock_id.id,
"location_dest_id": (cls.env.ref("stock.stock_location_customers").id),
"procure_method": "make_to_order",
"warehouse_id": cls.warehouse.id,
"date": "2018-06-01 18:00:00",
}
)
def test_01_group_mo_by_product(self):
"""Test functionality using grouping in a previous manually-created MO."""
self.assertEqual(self.mo.state, "draft")
self.assertEqual(len(self.mo.move_finished_ids), 1)
self.move.with_context(test_group_mo=True)._action_confirm(merge=False)
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
mo = self.MrpProduction.search([("product_id", "=", self.product1.id)])
self.assertEqual(len(mo), 1)
move_finished = mo.move_finished_ids
self.assertEqual(len(mo.move_finished_ids), 1)
self.assertEqual(mo.product_qty, 12)
self.assertEqual(move_finished.product_qty, 12)
mto_prod = mo.move_raw_ids.search([("product_id", "=", self.product2.id)])
self.assertEqual(len(mto_prod), 1)
self.assertEqual(mto_prod[0].product_qty, 12)
# 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, 12)
def test_02_group_mo_by_product_double_procurement(self):
"""Test functionality using groping in a previous procurement-created MO."""
# Cancelling the manual MO.
self.mo.action_cancel()
self.assertEqual(self.mo.state, "cancel")
mo = self.MrpProduction.search(
[("product_id", "=", self.product1.id), ("state", "!=", "cancel")]
)
self.assertFalse(mo)
# First procurement
self.move.with_context(test_group_mo=True)._action_confirm(merge=False)
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
mo = self.MrpProduction.search(
[("product_id", "=", self.product1.id), ("state", "!=", "cancel")]
)
self.assertEqual(len(mo), 1)
self.assertEqual(mo.state, "confirmed")
move_finished = mo.move_finished_ids
self.assertEqual(len(move_finished), 1)
self.assertEqual(mo.product_qty, 10)
self.assertEqual(move_finished.product_qty, 10)
mto_prod = mo.move_raw_ids.search(
[("product_id", "=", self.product2.id), ("state", "!=", "cancel")]
)
self.assertEqual(len(mto_prod), 1)
self.assertEqual(mto_prod[0].product_qty, 10)
# Do a second procurement
self.move_2.with_context(test_group_mo=True)._action_confirm(merge=False)
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
mo = self.MrpProduction.search(
[("product_id", "=", self.product1.id), ("state", "!=", "cancel")]
)
self.assertEqual(len(mo), 1)
self.assertEqual(mo.state, "confirmed")
move_finished = mo.move_finished_ids
self.assertEqual(len(move_finished), 1)
self.assertEqual(mo.product_qty, 15)
self.assertEqual(move_finished.product_qty, 15)
mto_prod = mo.move_raw_ids.search(
[("product_id", "=", self.product2.id), ("state", "!=", "cancel")]
)
self.assertEqual(len(mto_prod), 1)
self.assertEqual(mto_prod[0].product_qty, 15)
def test_mo_other_date(self):
self.move.write({"date": "2018-06-01 20:01:00"})
self.move.with_context(test_group_mo=True)._action_confirm(merge=False)
self.ProcurementGroup.with_context(test_group_mo=True).run_scheduler()
mo = self.MrpProduction.search([("product_id", "=", self.product1.id)])
self.assertEqual(len(mo), 2)
def test_check_mo_grouping_max_hour(self):
if self.picking_type:
with self.assertRaises(exceptions.ValidationError):
self.picking_type.mo_grouping_max_hour = 25
with self.assertRaises(exceptions.ValidationError):
self.picking_type.mo_grouping_max_hour = -1
def test_check_mo_grouping_interval(self):
if self.picking_type:
with self.assertRaises(exceptions.ValidationError):
self.picking_type.mo_grouping_interval = -1

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2018 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_picking_type_form" model="ir.ui.view">
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.view_picking_type_form" />
<field name="arch" type="xml">
<field name="code" position="after">
<field name="mo_grouping_max_hour" />
<field name="mo_grouping_interval" />
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
../../../../mrp_production_grouped_by_product

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)