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

fixup! Add option to define how lead days should be consumed when computing stock.warehouse.orderpoint.lead_days_date #3

Merged
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
114 changes: 113 additions & 1 deletion stock_warehouse_calendar_orderpoint/README.rst
Original file line number Diff line number Diff line change
@@ -1 +1,113 @@
waiting bot
===========================================
Stock Warehouse Calendar (reordering rules)
===========================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:71603635fc54c90766f0780453637f940e6bfc8d0169c7cbc5242cb4f7d9a1b0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |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%2Fstock--logistics--warehouse-lightgray.png?logo=github
:target: https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_warehouse_calendar_orderpoint
:alt: OCA/stock-logistics-warehouse
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-stock_warehouse_calendar_orderpoint
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&target_branch=14.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module adds an Reordering Calendar to the Warehouse. This calendar
can then used by reordering rules to compute the forecasted date when goods
will be received.

**Table of contents**

.. contents::
:local:

Configuration
=============

* Go to *Settings* and activate the developer mode.

* Go to *Settings > Technical > Resource > Working Time* and define your
resource calendar.

* Go to *Inventory > Configuration > Settings > Warehouses*
and assign the calendar to the "Reordering Calendar" field.
This will be the default calendar for all newly created warehouses
for the current company.
Optionnaly you can choose to postpone the computed lead date to the next
available weekday.

* In *Inventory > Configuration > Warehouse Management > Warehouses*
you can set a different calendar for your warehouse.

Usage
=====

When dealing with reordering rules, the Reordering Calendar is considered in
the computation of the forecasted date to receive goods.
For example, if we plan to execute the reordering rules on a given weekday
(configuration of the Reordering Calendar), it will take this day as a
starting date instead of the current day (standard behavior).

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_warehouse_calendar_orderpoint%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
~~~~~~~

* Camptocamp

Contributors
~~~~~~~~~~~~

* Sébastien Alix <[email protected]>

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.

.. |maintainer-sebalix| image:: https://github.com/sebalix.png?size=40px
:target: https://github.com/sebalix
:alt: sebalix

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-sebalix|

This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_warehouse_calendar_orderpoint>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
17 changes: 17 additions & 0 deletions stock_warehouse_calendar_orderpoint/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ class ResCompany(models.Model):
string="Schedule the lead date on workday only",
help="Postpone the lead date to the first available workday",
)
orderpoint_on_workday_policy = fields.Selection(
[
("skip_to_first_workday", "Skip to first workday"),
("skip_all_non_workdays", "Skip non-workdays"),
],
string="Reordering on Workday Policy",
help="Policy to postpone the lead date to the first available workday:\n"
"* skip to first workday: compute the date using lead delay days as solar"
" days, then skip to the next workday if the result is not a workday"
" (eg: run action on Friday with 2 days lead delay -> the result is Sunday ->"
" skip to the first following workday, Monday)\n"
"* skip non-workdays: compute the order date consuming lead delay days only on"
" (eg: run action on Friday with 2 days lead delay -> skip Saturday and Sunday"
" -> start consuming lead days on Monday as first lead day -> the result is"
" Tuesday)\n",
default="skip_to_first_workday", # Retro-compatible default value
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ class ResConfigSettings(models.TransientModel):
related="company_id.orderpoint_on_workday",
readonly=False,
)
orderpoint_on_workday_policy = fields.Selection(
related="company_id.orderpoint_on_workday_policy",
readonly=False,
)
20 changes: 20 additions & 0 deletions stock_warehouse_calendar_orderpoint/models/stock_warehouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,29 @@ class StockWarehouse(models.Model):
"This is based on the Working Hours calendar."
),
)
orderpoint_on_workday_policy = fields.Selection(
[
("skip_to_first_workday", "Skip to first workday"),
("skip_all_non_workdays", "Skip non-workdays"),
],
string="Reordering on Workday Policy",
default=lambda o: o._default_orderpoint_on_workday_policy(),
help="Policy to postpone the lead date to the first available workday:\n"
"* skip to first workday: compute the date using lead delay days as solar"
" days, then skip to the next workday if the result is not a workday"
" (eg: run action on Friday with 2 days lead delay -> the result is Sunday ->"
" skip to the first following workday, Monday)\n"
"* skip non-workdays: compute the order date consuming lead delay days only on"
" (eg: run action on Friday with 2 days lead delay -> skip Saturday and Sunday"
" -> start consuming lead days on Monday as first lead day -> the result is"
" Tuesday)\n",
)

def _default_orderpoint_calendar_id(self):
return self.env.company.orderpoint_calendar_id

def _default_orderpoint_on_workday(self):
return self.env.company.orderpoint_on_workday

def _default_orderpoint_on_workday_policy(self):
return self.env.company.orderpoint_on_workday_policy
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl)


from dateutil import relativedelta
from dateutil.relativedelta import relativedelta

from odoo import api, fields, models

Expand All @@ -15,27 +15,46 @@ class StockWarehouseOrderpoint(models.Model):
"product_id.seller_ids",
"product_id.seller_ids.delay",
"warehouse_id.orderpoint_calendar_id",
"warehouse_id.orderpoint_on_workday_policy",
)
def _compute_lead_days(self):
super()._compute_lead_days()
# Override to use the orderpoint calendar to compute the 'lead_days_date'
for orderpoint in self.with_context(bypass_delay_description=True):
op_calendar = orderpoint.warehouse_id.orderpoint_calendar_id
if not op_calendar:
continue
wh = orderpoint.warehouse_id
if not orderpoint.product_id or not orderpoint.location_id:
orderpoint.lead_days_date = False
continue
lead_days = orderpoint._get_lead_days()
# Get the next planned date to execute this orderpoint
start_date = orderpoint._get_next_reordering_date()
lead_days_date = start_date + relativedelta.relativedelta(days=lead_days)
# Postpone to the next available workday if needed
calendar = orderpoint.warehouse_id.calendar_id
if orderpoint.warehouse_id.orderpoint_on_workday and calendar:
# Get the lead days for this orderpoint
lead_days = orderpoint._get_lead_days()
# Get calendar, workday policy from warehouse
calendar = wh.calendar_id
policy = wh.orderpoint_on_workday and wh.orderpoint_on_workday_policy
if calendar and policy == "skip_to_first_workday":
# Consume all the lead days, then move up to the first workday
# according to the calendar
lead_days_date = calendar.plan_hours(
0, lead_days_date, compute_leaves=True
0, start_date + relativedelta(days=lead_days), compute_leaves=True
)
elif calendar and policy == "skip_all_non_workdays":
# Postpone to the next available workday if needed, consuming lead days
# only on workdays
lead_days_date = calendar.plan_hours(0, start_date, compute_leaves=True)
if lead_days_date.date() != start_date.date():
# We've consumed a lead day if the lead date is not the start date
lead_days -= 1
while lead_days > 0:
# Always get the next working day according to the calendar, and
# decrease the lead days at each iteration
lead_days_date = calendar.plan_hours(
0, lead_days_date + relativedelta(days=1), compute_leaves=True
)
lead_days -= 1
else:
# Simply postpone according to delays
lead_days_date = start_date + relativedelta(days=lead_days)
orderpoint.lead_days_date = lead_days_date

def _get_lead_days(self):
Expand All @@ -45,12 +64,11 @@ def _get_lead_days(self):

def _get_next_reordering_date(self):
self.ensure_one()
now = fields.Datetime.now()
calendar = self.warehouse_id.orderpoint_calendar_id
if calendar:
# TODO: should we take into account days off or the reordering
# calendar with 'compute_leaves=True' here?
return calendar.plan_hours(0, fields.Datetime.now())
return False
# TODO: should we take into account days off or the reordering calendar with
# 'compute_leaves=True' here?
return calendar and calendar.plan_hours(0, now) or now

@api.depends("rule_ids", "product_id.seller_ids", "product_id.seller_ids.delay")
def _compute_json_popover(self):
Expand Down
24 changes: 9 additions & 15 deletions stock_warehouse_calendar_orderpoint/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.product = cls.env.ref("product.product_delivery_02").copy()
cls.seller = cls.env["product.supplierinfo"].create(
cls.wh = cls.env.ref("stock.warehouse0")
cls.wh.write(
{
"name": cls.env.ref("base.res_partner_3").id,
"product_tmpl_id": cls.product.product_tmpl_id.id,
"product_id": cls.product.id,
"calendar_id": cls.env.ref("resource.resource_calendar_std").id,
"orderpoint_calendar_id": cls.env.ref(
"stock_warehouse_calendar_orderpoint.resource_calendar_orderpoint_demo"
).id,
"orderpoint_on_workday": True,
"orderpoint_on_workday_policy": "skip_to_first_workday",
}
)
cls.wh = cls.env.ref("stock.warehouse0")
cls.orderpoint = cls.env["stock.warehouse.orderpoint"].create(
{
"warehouse_id": cls.wh.id,
Expand All @@ -28,13 +31,4 @@ def setUpClass(cls):
"product_uom": cls.env.ref("uom.product_uom_unit"),
}
)
# OP calendar to compute lead dates from Wednesday
cls.wh.orderpoint_calendar_id = cls.env.ref(
"stock_warehouse_calendar_orderpoint.resource_calendar_orderpoint_demo"
)
cls.wh.orderpoint_on_workday = True
cls.wh.calendar_id = cls.env.ref("resource.resource_calendar_std")
# 1 day delay for supplier
cls.seller.delay = 1
# Company PO lead time
cls.wh.company_id.po_lead = 1
cls.orderpoint.rule_ids.write({"action": "pull", "delay": 2})
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ def setUp(self):

@freeze_time("2022-05-30 12:00") # Monday
def test_calendar_orderpoint_monday(self):
# Monday -> Wednesday + 2 days (seller delay + PO lead time) => Friday
# Monday -> Wednesday + 2 days rule delay => Friday
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-03")

@freeze_time("2022-06-01 12:00") # Wednesday during working hours
def test_calendar_orderpoint_wednesday_during_working_hours(self):
self.op_model.invalidate_cache(["lead_days_date"]) # Recompute field
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-03")

@freeze_time("2022-06-01 20:00") # Wednesday outside working hours
@freeze_time("2022-06-01 20:00") # Wednesday after working hours
def test_calendar_orderpoint_wednesday_outside_working_hours(self):
self.op_model.invalidate_cache(["lead_days_date"]) # Recompute field
self.assertEqual(
Expand All @@ -49,3 +49,15 @@ def test_calendar_orderpoint_thursday(self):
def test_calendar_orderpoint_next_thursday(self):
self.op_model.invalidate_cache(["lead_days_date"])
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-17")

@freeze_time("2022-06-01 12:00") # Wednesday during working hours
def test_calendar_orderpoint_policy(self):
self.orderpoint.rule_ids.delay = 4
# 4 days are counted from Wednesday to Sunday => end on Monday
self.wh.orderpoint_on_workday_policy = "skip_to_first_workday"
self.op_model.invalidate_cache(["lead_days_date"])
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-06")
# 4 days are counted from Wednesday, skipping the weekend => end on Tuesday
self.wh.orderpoint_on_workday_policy = "skip_all_non_workdays"
self.op_model.invalidate_cache(["lead_days_date"])
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-07")
12 changes: 12 additions & 0 deletions stock_warehouse_calendar_orderpoint/views/res_config_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
>
<label for="orderpoint_on_workday" />
</div>
<div
class="o_setting_left_pane"
attrs="{'invisible': [('orderpoint_on_workday', '=', False)]}"
>
<field name="orderpoint_on_workday_policy" />
</div>
<div
class="o_setting_right_pane"
attrs="{'invisible': [('orderpoint_on_workday', '=', False)]}"
>
<label for="orderpoint_on_workday_policy" />
</div>
</div>
</div>
</field>
Expand Down
4 changes: 4 additions & 0 deletions stock_warehouse_calendar_orderpoint/views/stock_warehouse.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
name="orderpoint_on_workday"
attrs="{'invisible': [('orderpoint_calendar_id', '=', False)]}"
/>
<field
name="orderpoint_on_workday_policy"
attrs="{'invisible': [('orderpoint_calendar_id', '=', False)], 'required': [('orderpoint_on_workday', '=', True)]}"
/>
</group>
<field name="calendar_id" position="attributes">
<attribute
Expand Down
Loading