Merge PR #918 into 13.0

Signed-off-by LoisRForgeFlow
This commit is contained in:
OCA-git-bot
2023-01-11 08:50:10 +00:00
10 changed files with 220 additions and 44 deletions

View File

@@ -413,13 +413,15 @@ class MultiLevelMrp(models.TransientModel):
move_data = self._prepare_mrp_move_data_from_stock_move(
product_mrp_area, move, direction="in"
)
mrp_move_obj.create(move_data)
if move_data:
mrp_move_obj.create(move_data)
if out_moves:
for move in out_moves:
move_data = self._prepare_mrp_move_data_from_stock_move(
product_mrp_area, move, direction="out"
)
mrp_move_obj.create(move_data)
if move_data:
mrp_move_obj.create(move_data)
return True
@api.model

View File

@@ -10,9 +10,9 @@ MRP Multi Level Estimate
.. |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
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
:target: https://github.com/OCA/manufacture/tree/13.0/mrp_multi_level_estimate
:alt: OCA/manufacture
@@ -23,7 +23,7 @@ MRP Multi Level Estimate
:target: https://runbot.odoo-community.org/runbot/129/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
|badge1| |badge2| |badge3| |badge4| |badge5|
Integration for MRP Multi Level and `Stock Demand Estimates <https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate>`_ system.
@@ -84,7 +84,7 @@ promote its widespread use.
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-LoisRForgeFlow|
|maintainer-LoisRForgeFlow|
This module is part of the `OCA/manufacture <https://github.com/OCA/manufacture/tree/13.0/mrp_multi_level_estimate>`_ project on GitHub.

View File

@@ -1,18 +1,18 @@
# Copyright 2019-20 ForgeFlow S.L. (http://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/Agpl.html).
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
{
"name": "MRP Multi Level Estimate",
"version": "13.0.1.0.2",
"development_status": "Beta",
"license": "AGPL-3",
"license": "LGPL-3",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"maintainers": ["LoisRForgeFlow"],
"summary": "Allows to consider demand estimates using MRP multi level.",
"website": "https://github.com/OCA/manufacture",
"category": "Manufacturing",
"depends": ["mrp_multi_level", "stock_demand_estimate_matrix"],
"data": ["views/product_mrp_area_views.xml"],
"depends": ["mrp_multi_level", "stock_demand_estimate"],
"data": ["views/product_mrp_area_views.xml", "views/mrp_area_views.xml"],
"installable": True,
"application": False,
"auto_install": True,

View File

@@ -1 +1,2 @@
from . import product_mrp_area
from . import mrp_area

View File

@@ -0,0 +1,36 @@
# Copyright 2022 ForgeFlow S.L. (http://www.forgeflow.com)
# - Lois Rilo Antelo <lois.rilo@forgeflow.com>
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models
class MRPArea(models.Model):
_inherit = "mrp.area"
estimate_demand_and_other_sources_strat = fields.Selection(
string="Demand Estimates and Other Demand Sources Strategy",
selection=[
("all", "Always consider all sources"),
(
"ignore_others_if_estimates",
"Ignore other sources for products with estimates",
),
(
"ignore_overlapping",
"Ignore other sources during periods with estimates",
),
],
default="all",
help="Define the strategy to follow in MRP multi level when there is a"
"coexistence of demand from demand estimates and other sources.\n"
"* Always consider all sources: nothing is excluded or ignored.\n"
"* Ignore other sources for products with estimates: When there "
"are estimates entered for product and they are in a present or "
"future period, all other sources of demand are ignored for those "
"products.\n"
"* Ignore other sources during periods with estimates: When "
"you create demand estimates for a period and product, "
"other sources of demand will be ignored during that period "
"for those products.",
)

View File

@@ -1,6 +1,6 @@
# Copyright 2019-20 ForgeFlow S.L. (http://www.forgeflow.com)
# - Lois Rilo Antelo <lois.rilo@forgeflow.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models
@@ -14,7 +14,7 @@ class ProductMRPArea(models.Model):
help="The days to group your estimates as demand for the MRP."
"It can be different from the length of the date ranges you "
"use in the estimates but it should not be greater, in that case"
"only grouping until the total lenght of the date range will be done.",
"only grouping until the total length of the date range will be done.",
)
_sql_constraints = [

View File

@@ -367,7 +367,7 @@ ul.auto-toc {
!! 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/13.0/mrp_multi_level_estimate"><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-13-0/manufacture-13-0-mrp_multi_level_estimate"><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/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<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/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/manufacture/tree/13.0/mrp_multi_level_estimate"><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-13-0/manufacture-13-0-mrp_multi_level_estimate"><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/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>Integration for MRP Multi Level and <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/12.0/stock_demand_estimate">Stock Demand Estimates</a> system.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">

View File

@@ -1,10 +1,8 @@
# Copyright 2018-20 ForgeFlow S.L. (http://www.forgeflow.com)
# Copyright 2018-22 ForgeFlow S.L. (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from datetime import datetime, timedelta
from dateutil.rrule import WEEKLY
from odoo.addons.mrp_multi_level.tests.common import TestMrpMultiLevelCommon
@@ -39,42 +37,38 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon):
}
)
# Create Date Ranges:
cls.dr_type = cls.env["date.range.type"].create(
{"name": "Weeks", "company_id": False, "allow_overlap": False}
)
# Create 3 consecutive estimates of 1 week length each.
today = datetime.today().replace(hour=0)
generator = cls.env["date.range.generator"].create(
{
"date_start": today - timedelta(days=3),
"name_prefix": "W-",
"type_id": cls.dr_type.id,
"duration_count": 1,
"unit_of_time": str(WEEKLY),
"count": 3,
}
)
generator.action_apply()
date_start_1 = today - timedelta(days=3)
date_end_1 = date_start_1 + timedelta(days=6)
date_start_2 = date_end_1 + timedelta(days=1)
date_end_2 = date_start_2 + timedelta(days=6)
date_start_3 = date_end_2 + timedelta(days=1)
date_end_3 = date_start_3 + timedelta(days=6)
start_dates = [date_start_1, date_start_2, date_start_3]
end_dates = [date_end_1, date_end_2, date_end_3]
cls.date_within_ranges = today - timedelta(days=2)
cls.date_without_ranges = today + timedelta(days=150)
# Create Demand Estimates:
ranges = cls.env["date.range"].search([("type_id", "=", cls.dr_type.id)])
qty = 140.0
for dr in ranges:
for sd, ed in zip(start_dates, end_dates):
qty += 70.0
cls._create_demand_estimate(cls.prod_test, cls.stock_location, dr, qty)
cls._create_demand_estimate(cls.prod_test, cls.estimate_loc, dr, qty)
cls._create_demand_estimate(cls.prod_test, cls.stock_location, sd, ed, qty)
cls._create_demand_estimate(cls.prod_test, cls.estimate_loc, sd, ed, qty)
cls.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
@classmethod
def _create_demand_estimate(cls, product, location, date_range, qty):
def _create_demand_estimate(cls, product, location, date_from, date_to, qty):
cls.estimate_obj.create(
{
"product_id": product.id,
"location_id": location.id,
"product_uom": product.uom_id.id,
"product_uom_qty": qty,
"date_range_id": date_range.id,
"manual_date_from": date_from,
"manual_date_to": date_to,
}
)
@@ -152,7 +146,9 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon):
def test_03_group_demand_estimates(self):
"""Test demand grouping functionality, `group_estimate_days`."""
self.test_mrp_parameter.group_estimate_days = 7
self.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
estimates = self.estimate_obj.search(
[
("product_id", "=", self.prod_test.id),
@@ -177,7 +173,9 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon):
# Test group_estimate_days greater than date range, it should not
# generate greater demand.
self.test_mrp_parameter.group_estimate_days = 10
self.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
moves = self.mrp_move_obj.search(
[
("product_id", "=", self.prod_test.id),
@@ -192,7 +190,9 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon):
# Test group_estimate_days smaller than date range, it should not
# generate greater demand.
self.test_mrp_parameter.group_estimate_days = 5
self.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
moves = self.mrp_move_obj.search(
[
("product_id", "=", self.prod_test.id),
@@ -229,7 +229,9 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon):
estimate.product_uom_qty = qty
qty += 100
self.mrp_multi_level_wiz.create({}).run_mrp_multi_level()
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
moves = self.mrp_move_obj.search(
[
("product_id", "=", self.prod_test.id),
@@ -248,3 +250,86 @@ class TestMrpMultiLevelEstimate(TestMrpMultiLevelCommon):
self.assertEqual(moves_from_estimates[1].mrp_qty, -500)
# 600 weekly -> 85.71 daily -> 85.71 * 7 = 600
self.assertEqual(moves_from_estimates[2].mrp_qty, -600)
def test_05_estimate_and_other_sources_strat(self):
"""Tests demand estimates and other sources strategies."""
estimates = self.estimate_obj.search(
[
("product_id", "=", self.prod_test.id),
("location_id", "=", self.estimate_loc.id),
]
)
self.assertEqual(len(estimates), 3)
self._create_picking_out(
self.prod_test, 25, self.date_within_ranges, location=self.estimate_loc
)
self._create_picking_out(
self.prod_test, 25, self.date_without_ranges, location=self.estimate_loc
)
# 1. "all"
self.estimate_area.estimate_demand_and_other_sources_strat = "all"
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
moves = self.mrp_move_obj.search(
[
("product_id", "=", self.prod_test.id),
("mrp_area_id", "=", self.estimate_area.id),
]
)
# 3 weeks - 3 days in the past = 18 days of valid estimates:
demand_from_estimates = moves.filtered(
lambda m: m.mrp_type == "d" and m.mrp_origin == "fc"
)
demand_from_other_sources = moves.filtered(
lambda m: m.mrp_type == "d" and m.mrp_origin != "fc"
)
self.assertEqual(len(demand_from_estimates), 18)
self.assertEqual(len(demand_from_other_sources), 2)
# 2. "ignore_others_if_estimates"
self.estimate_area.estimate_demand_and_other_sources_strat = (
"ignore_others_if_estimates"
)
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
moves = self.mrp_move_obj.search(
[
("product_id", "=", self.prod_test.id),
("mrp_area_id", "=", self.estimate_area.id),
]
)
demand_from_estimates = moves.filtered(
lambda m: m.mrp_type == "d" and m.mrp_origin == "fc"
)
demand_from_other_sources = moves.filtered(
lambda m: m.mrp_type == "d" and m.mrp_origin != "fc"
)
self.assertEqual(len(demand_from_estimates), 18)
self.assertEqual(len(demand_from_other_sources), 0)
# 3. "ignore_overlapping"
self.estimate_area.estimate_demand_and_other_sources_strat = (
"ignore_overlapping"
)
self.mrp_multi_level_wiz.create(
{"mrp_area_ids": [(6, 0, self.estimate_area.ids)]}
).run_mrp_multi_level()
moves = self.mrp_move_obj.search(
[
("product_id", "=", self.prod_test.id),
("mrp_area_id", "=", self.estimate_area.id),
]
)
demand_from_estimates = moves.filtered(
lambda m: m.mrp_type == "d" and m.mrp_origin == "fc"
)
demand_from_other_sources = moves.filtered(
lambda m: m.mrp_type == "d" and m.mrp_origin != "fc"
)
self.assertEqual(len(demand_from_estimates), 18)
self.assertEqual(len(demand_from_other_sources), 1)
self.assertEqual(
demand_from_other_sources.mrp_date, self.date_without_ranges.date()
)

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" ?>
<odoo>
<record id="mrp_area_form" model="ir.ui.view">
<field name="name">mrp.area.form - mrp_multi_level_estimate</field>
<field name="model">mrp.area</field>
<field name="inherit_id" ref="mrp_multi_level.mrp_area_form" />
<field name="arch" type="xml">
<group name="settings" position="inside">
<field name="estimate_demand_and_other_sources_strat" />
</group>
</field>
</record>
</odoo>

View File

@@ -1,6 +1,6 @@
# Copyright 2019-20 ForgeFlow S.L. (http://www.forgeflow.com)
# - Lois Rilo <lois.rilo@forgeflow.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import logging
from datetime import timedelta
@@ -88,3 +88,42 @@ class MultiLevelMrp(models.TransientModel):
self.env["mrp.move"].create(mrp_move_data)
mrp_date += delta
return res
def _exclude_considering_estimate_demand_and_other_sources_strat(
self, product_mrp_area, mrp_date
):
estimate_strat = (
product_mrp_area.mrp_area_id.estimate_demand_and_other_sources_strat
)
if estimate_strat == "all":
return False
domain = self._estimates_domain(product_mrp_area)
estimates = self.env["stock.demand.estimate"].search(domain)
if not estimates:
return False
if estimate_strat == "ignore_others_if_estimates":
# Ignore
return True
if estimate_strat == "ignore_overlapping":
for estimate in estimates:
if mrp_date >= estimate.date_from and mrp_date <= estimate.date_to:
# Ignore
return True
return False
@api.model
def _prepare_mrp_move_data_from_stock_move(
self, product_mrp_area, move, direction="in"
):
res = super()._prepare_mrp_move_data_from_stock_move(
product_mrp_area, move, direction=direction
)
if direction == "out":
mrp_date = res.get("mrp_date")
if self._exclude_considering_estimate_demand_and_other_sources_strat(
product_mrp_area, mrp_date
):
return False
return res