Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0] backport stock_release_channel_auto_release #566

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions setup/stock_release_channel_auto_release/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
1 change: 1 addition & 0 deletions stock_available_to_promise_release/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"wizards/stock_release_views.xml",
"wizards/stock_unrelease_views.xml",
],
"demo": [],
"installable": True,
"license": "LGPL-3",
"application": False,
Expand Down
9 changes: 9 additions & 0 deletions stock_available_to_promise_release/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ class StockPicking(models.Model):
help="It specifies how to release a transfer partially or all at once",
)

set_printed_at_release = fields.Boolean(compute="_compute_set_printed_at_release")

@api.depends("move_lines")
def _compute_set_printed_at_release(self):
for picking in self:
picking.set_printed_at_release = not (
any(picking.move_lines.mapped("rule_id.no_backorder_at_release"))
)

@api.depends("move_lines.need_release")
def _compute_need_release(self):
data = self.env["stock.move"].read_group(
Expand Down
7 changes: 5 additions & 2 deletions stock_available_to_promise_release/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from odoo import fields
from odoo.tests import common, tagged
from odoo.tests import common


@tagged("post_install", "-at_install")
class PromiseReleaseCommonCase(common.SavepointCase):

at_install = False
post_install = True

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down
7 changes: 7 additions & 0 deletions stock_available_to_promise_release/tests/test_reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@
from dateutil.relativedelta import relativedelta
from freezegun import freeze_time

from odoo.tests import tagged

from .common import PromiseReleaseCommonCase


@tagged("post_install", "-at_install")
class TestAvailableToPromiseRelease(PromiseReleaseCommonCase):

at_install = False
post_install = True

def test_horizon_date(self):
move = self.env["stock.move"].create(
{
Expand Down
6 changes: 6 additions & 0 deletions stock_available_to_promise_release/tests/test_unrelease.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@
from datetime import datetime

from odoo.exceptions import UserError
from odoo.tests import tagged

from .common import PromiseReleaseCommonCase


@tagged("post_install", "-at_install")
class TestAvailableToPromiseRelease(PromiseReleaseCommonCase):

at_install = False
post_install = True

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@

from datetime import datetime

from odoo.tests import tagged

from .common import PromiseReleaseCommonCase


@tagged("post_install", "-at_install")
class TestAvailableToPromiseRelease(PromiseReleaseCommonCase):

at_install = False
post_install = True

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@

from datetime import datetime

from odoo.tests import tagged

from .common import PromiseReleaseCommonCase


@tagged("post_install", "-at_install")
class TestAvailableToPromiseRelease3steps(PromiseReleaseCommonCase):

at_install = False
post_install = True

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down
9 changes: 5 additions & 4 deletions stock_release_channel/i18n/stock_release_channel.pot
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ msgid "Assigned Total"
msgstr ""

#. module: stock_release_channel
#: model:ir.model.fields,field_description:stock_release_channel.field_stock_release_channel__auto_release
#: model:ir.model.fields,field_description:stock_release_channel.field_stock_release_channel__batch_mode
msgid "Auto Release"
msgstr ""

Expand Down Expand Up @@ -243,7 +243,7 @@ msgid "Full Progress"
msgstr ""

#. module: stock_release_channel
#: model:ir.model.fields.selection,name:stock_release_channel.selection__stock_release_channel__auto_release__group_commercial_partner
#: model:ir.model.fields.selection,name:stock_release_channel.selection__stock_release_channel__batch_mode__group_commercial_partner
msgid "Grouped by Commercial Partner"
msgstr ""

Expand Down Expand Up @@ -355,16 +355,17 @@ msgstr ""

#. module: stock_release_channel
#: model:ir.model.fields.selection,name:stock_release_channel.selection__stock_release_channel__auto_release__max
#: model:ir.model.fields.selection,name:stock_release_channel.selection__stock_release_channel__batch_mode__max
msgid "Max"
msgstr ""

#. module: stock_release_channel
#: model:ir.model.fields,field_description:stock_release_channel.field_stock_release_channel__max_auto_release
#: model:ir.model.fields,field_description:stock_release_channel.field_stock_release_channel__max_batch_mode
msgid "Max Transfers to release"
msgstr ""

#. module: stock_release_channel
#: model:ir.model.fields,help:stock_release_channel.field_stock_release_channel__auto_release
#: model:ir.model.fields,help:stock_release_channel.field_stock_release_channel__batch_mode
msgid ""
"Max: release N transfers to have a configured max of X deliveries in progress.\n"
"Grouped by Commercial Partner: release all transfers for acommercial partner at once."
Expand Down
13 changes: 13 additions & 0 deletions stock_release_channel/migrations/14.0.1.3.0/pre-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2022 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging

from odoo.tools import sql

_logger = logging.getLogger(__name__)


def migrate(cr, version):
sql.rename_column(cr, "stock_release_channel", "auto_release", "batch_mode")
sql.rename_column(cr, "stock_release_channel", "max_auto_release", "max_batch_mode")
45 changes: 36 additions & 9 deletions stock_release_channel/models/stock_release_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pytz import timezone

from odoo import _, api, exceptions, fields, models
from odoo.osv.expression import NEGATIVE_TERM_OPERATORS
from odoo.tools.safe_eval import (
datetime as safe_datetime,
dateutil as safe_dateutil,
Expand Down Expand Up @@ -67,8 +68,10 @@ class StockReleaseChannel(models.Model):
help="Write Python code to filter out pickings.",
)
active = fields.Boolean(default=True)

auto_release = fields.Selection(
release_mode = fields.Selection(
[("batch", "Batch (Manual)")], required=True, default="batch"
)
batch_mode = fields.Selection(
jbaudoux marked this conversation as resolved.
Show resolved Hide resolved
selection=[
("max", "Max"),
("group_commercial_partner", "Grouped by Commercial Partner"),
Expand All @@ -79,7 +82,7 @@ class StockReleaseChannel(models.Model):
" in progress.\nGrouped by Commercial Partner: release all transfers for a"
"commercial partner at once.",
)
max_auto_release = fields.Integer(
max_batch_mode = fields.Integer(
jbaudoux marked this conversation as resolved.
Show resolved Hide resolved
string="Max Transfers to release",
default=10,
help="When clicking on the package icon, it releases X transfers minus "
Expand Down Expand Up @@ -202,6 +205,7 @@ class StockReleaseChannel(models.Model):
)
is_release_allowed = fields.Boolean(
compute="_compute_is_release_allowed",
search="_search_is_release_allowed",
help="Technical field to check if the "
"action 'Release Next Batch' is allowed.",
)
Expand Down Expand Up @@ -231,6 +235,25 @@ def _compute_is_release_allowed(self):
for rec in self:
rec.is_release_allowed = rec.state == "open" and not rec.release_forbidden

@api.model
def _get_is_release_allowed_domain(self):
return [("state", "=", "open"), ("release_forbidden", "=", False)]

@api.model
def _get_is_release_not_allowed_domain(self):
return ["|", ("state", "!=", "open"), ("release_forbidden", "=", True)]

@api.model
def _search_is_release_allowed(self, operator, value):
if "in" in operator:
raise ValueError(f"Invalid operator {operator}")
negative_op = operator in NEGATIVE_TERM_OPERATORS
is_release_allowed = (value and not negative_op) or (not value and negative_op)
domain = self._get_is_release_allowed_domain()
if not is_release_allowed:
domain = self._get_is_release_not_allowed_domain()
return domain

def _get_picking_to_unassign_domain(self):
return [
("release_channel_id", "in", self.ids),
Expand Down Expand Up @@ -706,17 +729,23 @@ def _pickings_sort_key(picking):
)

def _get_next_pickings(self):
return getattr(self, "_get_next_pickings_{}".format(self.auto_release))()
return getattr(self, "_get_next_pickings_{}".format(self.batch_mode))()

def _get_pickings_to_release(self):
"""Get the pickings to release."""
domain = self._field_picking_domains()["release_ready"]
domain += [("release_channel_id", "in", self.ids)]
return self.env["stock.picking"].search(domain)

def _get_next_pickings_max(self):
if not self.max_auto_release:
if not self.max_batch_mode:
raise exceptions.UserError(_("No Max transfers to release is configured."))

waiting_domain = self._field_picking_domains()["waiting"]
waiting_domain += [("release_channel_id", "=", self.id)]
released_in_progress = self.env["stock.picking"].search_count(waiting_domain)

release_limit = max(self.max_auto_release - released_in_progress, 0)
release_limit = max(self.max_batch_mode - released_in_progress, 0)
if not release_limit:
raise exceptions.UserError(
_(
Expand All @@ -740,9 +769,7 @@ def _get_next_pickings_group_commercial_partner(self):
# because "date_priority" is computed and not stored. If needed, we
# should evaluate making it a stored field in the module
# "stock_available_to_promise_release".
next_pickings = (
self.env["stock.picking"].search(domain).sorted(self._pickings_sort_key)
)
next_pickings = self._get_pickings_to_release().sorted(self._pickings_sort_key)
if not next_pickings:
return self.env["stock.picking"].browse()
first_picking = next_pickings[0]
Expand Down
17 changes: 17 additions & 0 deletions stock_release_channel/tests/common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright 2020 Camptocamp (https://www.camptocamp.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

import logging

from odoo import fields
from odoo.tests import common

Expand All @@ -20,6 +22,21 @@ def setUpClass(cls):
)
cls._create_base_data()

def setUp(self):
super(ReleaseChannelCase, self).setUp()
loggers = ["odoo.addons.stock_release_channel.models.stock_release_channel"]
for logger in loggers:
logging.getLogger(logger).addFilter(self)

# pylint: disable=unused-variable
@self.addCleanup
def un_mute_logger():
for logger_ in loggers:
logging.getLogger(logger_).removeFilter(self)

def filter(self, record):
return 0

@classmethod
def _create_base_data(cls):
cls.wh = cls.env["stock.warehouse"].create(
Expand Down
8 changes: 4 additions & 4 deletions stock_release_channel/tests/test_channel_release_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def test_release_auto_forbidden(self):
self.channel.release_next_batch()

def test_release_auto_max_next_batch_no_config(self):
self.channel.max_auto_release = 0
self.channel.max_batch_mode = 0
with self.assertRaises(exceptions.UserError):
self.channel.release_next_batch()

def test_release_auto_max_next_batch(self):
self.channel.max_auto_release = 2
self.channel.max_batch_mode = 2
self.channel.release_next_batch()
# 2 have been released
self.assertEqual(
Expand All @@ -60,15 +60,15 @@ def test_release_auto_max_no_next_batch(self):
self._assert_action_nothing_in_the_queue(action)

def test_release_auto_group_commercial_partner(self):
self.channel.auto_release = "group_commercial_partner"
self.channel.batch_mode = "group_commercial_partner"
self.channel.release_next_batch()
self.assertFalse(self.picking.need_release)
self.assertFalse(self.picking2.need_release)
other_pickings = self.pickings - (self.picking | self.picking2)
self.assertTrue(all(p.need_release) for p in other_pickings)

def test_release_auto_group_commercial_partner_no_next_batch(self):
self.channel.auto_release = "group_commercial_partner"
self.channel.batch_mode = "group_commercial_partner"
pickings = self.channel.picking_ids.filtered(lambda p: p.release_ready)
for _i in range(0, len(pickings.partner_id.commercial_partner_id)):
action = self.channel.release_next_batch()
Expand Down
22 changes: 18 additions & 4 deletions stock_release_channel/views/stock_release_channel_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,16 @@
<group name="options" groups="stock.group_stock_manager">
<field name="release_forbidden" />
<field
name="auto_release"
name="release_mode"
attrs="{'invisible': [('release_forbidden', '=', True)]}"
/>
<field
name="max_auto_release"
attrs="{'invisible': ['|', ('release_forbidden', '=', True), ('auto_release', '!=', 'max')]}"
name="batch_mode"
attrs="{'invisible': ['|', ('release_mode','!=', 'batch'), ('release_forbidden', '=', True)]}"
/>
<field
name="max_batch_mode"
attrs="{'invisible': ['|', '|', ('release_mode','!=', 'batch'), ('release_forbidden', '=', True), ('batch_mode', '!=', 'max')]}"
/>
<field name="warehouse_id" />
<field name="picking_type_ids" options="{'no_create': True}">
Expand Down Expand Up @@ -142,7 +146,15 @@
<field name="arch" type="xml">
<tree>
<field name="sequence" widget="handle" />
<field
name="state"
widget="badge"
decoration-muted="state == 'asleep'"
decoration-info="state == 'locked'"
decoration-success="state == 'open'"
/>
<field name="name" />
<field name="release_mode" optional="hide" />
<field name="count_picking_all" sum="All Total" />
<field name="count_picking_release_ready" sum="Release Ready Total" />
<field name="count_picking_released" sum="Released Total" />
Expand All @@ -168,6 +180,8 @@
class="oe_background_grey o_kanban_dashboard o_emphasize_colors o_stock_release_channel"
create="0"
>
<field name="release_mode" invisible="1" />
<field name="is_release_allowed" />
<field name="color" />
<field name="name" readonly="1" />
<field name="count_picking_release_ready" />
Expand Down Expand Up @@ -238,7 +252,7 @@
class="btn btn-primary"
name="release_next_batch"
type="object"
attrs="{'invisible': [('is_release_allowed', '=', False)]}"
attrs="{'invisible': ['|', ('is_release_allowed', '=', False), ('release_mode', '!=', 'batch')]}"
jbaudoux marked this conversation as resolved.
Show resolved Hide resolved
>
<i
class="fa fa-dropbox fa-4x"
Expand Down
Loading
Loading