Skip to content
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
10 changes: 4 additions & 6 deletions sale_order_line_multi_warehouse/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes license to licence in the badge URL. The shields.io badge text parameter is license (American English) -- changing it to licence will alter the badge display text. This looks like an unintended change; please revert.

===============================
Sale Order Line Multi Warehouse
===============================
Expand All @@ -17,7 +13,7 @@ Sale Order Line Multi Warehouse
.. |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/license-AGPL--3-blue.png
.. |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%2Fsale--workflow-lightgray.png?logo=github
Expand All @@ -32,7 +28,7 @@ Sale Order Line Multi Warehouse

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

This module allows to select multiple warehouses in sale order lines so the sale order is split into multiple pickings depending on the warehouses selected in the lines.
This module allows to select multiple warehouses in sale order lines so the sale order is split into multiple pickings depending on the warehouses selected in the lines. It also adds a new widget to sale order lines when the multiwarehouse options in sale orders are enabled in order to check the stock in each warehouse.

**Table of contents**

Expand Down Expand Up @@ -70,6 +66,8 @@ Usage

* Once the sale order is validated, the order will be split into multiple pickings, one for each warehouse selected in the warehouse distribution lines.

* To open the multi warehouse widget in a sale order line, you need to click on the bar chart in a sale order line. Please, note that this widget replaces the widget added by the sale_stock module when the multi warehouse options in sale orders are enabled.

* **IMPORTANT: In case this module is uninstalled, the warehouse distribution lines will be lost.**

Known issues / Roadmap
Expand Down
5 changes: 5 additions & 0 deletions sale_order_line_multi_warehouse/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@
"views/res_config_settings_views.xml",
"wizard/so_multi_warehouse_change_wizard_views.xml",
],
"assets": {
"web.assets_backend": [
"sale_order_line_multi_warehouse/static/src/**/*",
],
},
}
174 changes: 174 additions & 0 deletions sale_order_line_multi_warehouse/models/sale_order_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
comodel_name="stock.warehouse",
related="order_id.suitable_warehouse_ids",
)
qty_by_warehouse = fields.Binary(
compute="_compute_qty_by_warehouse", exportable=False
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using fields.Json instead of fields.Binary for storing structured data. fields.Json is available in Odoo 16 and is the idiomatic way to store JSON-serializable Python dicts. With fields.Binary, the JS side needs to do JSON.parse(JSON.stringify(...)) workarounds, whereas fields.Json handles serialization natively.

display_qty_by_warehouse_widget = fields.Boolean(
compute="_compute_display_qty_by_warehouse_widget"
)

@api.constrains("sale_order_line_warehouse_ids")
def _check_warehouses(self):
Expand All @@ -41,6 +47,174 @@
):
raise ValidationError(_("Only one warehouse per line allowed"))

def _compute_qty_to_deliver(self):
ret_vals = super()._compute_qty_to_deliver()
for line in self.filtered("allow_sale_multi_warehouse"):
line.display_qty_widget = False
return ret_vals

@api.depends(
"product_type",
"qty_delivered",
"state",
"move_ids",
"product_uom",
"allow_sale_multi_warehouse",
)
def _compute_display_qty_by_warehouse_widget(self):
for line in self:
display_qty_by_warehouse_widget = False
if (
line.allow_sale_multi_warehouse
and line.product_type == "product"
and line.product_uom
and line.qty_to_deliver > 0
and (
line.state in ["draft", "sent"]
or (line.state == "sale" and line.move_ids)
)
):
display_qty_by_warehouse_widget = True
line.display_qty_by_warehouse_widget = display_qty_by_warehouse_widget

@api.depends(
"product_id",
"product_uom_qty",
"product_uom",
"order_id.commitment_date",
"move_ids",
"move_ids.forecast_expected_date",
"move_ids.forecast_availability",
"sale_order_line_warehouse_ids",
)
def _compute_qty_by_warehouse(self):
for line in self:
qty_by_warehouse = {}
warehouses = []
for warehouse in line.suitable_warehouse_ids:
warehouses.append(line._get_qty_by_warehouse_vals(warehouse))
qty_by_warehouse["warehouses"] = warehouses
forecasted_issue = any(
warehouse.get("forecasted_issue") for warehouse in warehouses
)
qty_by_warehouse["forecasted_issue"] = forecasted_issue
line.qty_by_warehouse = qty_by_warehouse

# Based on _compute_qty_at_date method in sale.order.line
def _get_qty_by_warehouse_vals(self, warehouse):
self.ensure_one()
scheduled_date = self.order_id.commitment_date or self._expected_date()
moves = self.move_ids | self.env["stock.move"].browse(
self.move_ids._rollup_move_origs()
)
moves = moves.filtered(
lambda m: m.product_id == self.product_id
and m.state not in ("cancel", "done")
and m.warehouse_id == warehouse
)

# qty_available_today
qty_available_today = 0
for move in moves:
qty_available_today += move.product_uom._compute_quantity(
move.reserved_availability, self.product_uom
)

# forecast_expected_date
forecast_expected_date = False
if moves:
forecast_expected_date = max(moves.mapped("forecast_expected_date"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential ValueError here: moves.mapped("forecast_expected_date") can contain False values (the field defaults to False when not set). max() will fail when comparing datetime and bool types.

Suggested fix:

forecast_dates = [d for d in moves.mapped("forecast_expected_date") if d]
forecast_expected_date = max(forecast_dates) if forecast_dates else False


# free_qty_today
free_qty_today = 0.0
if self.state == "sale":
for move in moves:
free_qty_today += move.product_id.uom_id._compute_quantity(
move.forecast_availability, self.product_uom
)
elif self.state in ["draft", "sent"]:
free_qty_today = self.product_id.with_context(
to_date=scheduled_date, warehouse=warehouse.id
).free_qty

# virtual_available_at_date
virtual_available_at_date = self.product_id.with_context(
to_date=scheduled_date, warehouse=warehouse.id
).virtual_available

# qty_to_deliver
to_deliver_from_warehouse = (
sum(
self.sale_order_line_warehouse_ids.filtered(
lambda a: a.warehouse_id == warehouse
).mapped("product_uom_qty")
)
or 0.0
)
# taken from _compute_qty_delivered in sale.order.line in module
# sale_stock
qty_delivered = 0.0
qty_to_deliver = 0.0
if self.qty_delivered_method == "stock_move":
outgoing_moves, incoming_moves = self._get_outgoing_incoming_moves()
for move in outgoing_moves.filtered(lambda a: a.warehouse_id == warehouse):
if move.state != "done":
continue
qty_delivered += move.product_uom._compute_quantity(

Check warning on line 163 in sale_order_line_multi_warehouse/models/sale_order_line.py

View check run for this annotation

Codecov / codecov/patch

sale_order_line_multi_warehouse/models/sale_order_line.py#L163

Added line #L163 was not covered by tests
move.quantity_done,
self.product_uom,
rounding_method="HALF-UP",
)
for move in incoming_moves.filtered(lambda a: a.warehouse_id == warehouse):
if move.state != "done":
continue
qty_delivered -= move.product_uom._compute_quantity(

Check warning on line 171 in sale_order_line_multi_warehouse/models/sale_order_line.py

View check run for this annotation

Codecov / codecov/patch

sale_order_line_multi_warehouse/models/sale_order_line.py#L170-L171

Added lines #L170 - L171 were not covered by tests
move.quantity_done,
self.product_uom,
rounding_method="HALF-UP",
)
qty_to_deliver = to_deliver_from_warehouse - qty_delivered

# will_be_fulfilled
if self.state in ["sale", "done"]:
will_be_fulfilled = free_qty_today >= qty_to_deliver
else:
will_be_fulfilled = virtual_available_at_date >= qty_to_deliver

# forecasted_issue
forecasted_issue = False
if (
self.state in ["draft", "sent"]
and not will_be_fulfilled
and not self.is_mto
):
forecasted_issue = True
elif not will_be_fulfilled or (
forecast_expected_date and forecast_expected_date > scheduled_date
):
forecasted_issue = True

# format forecast_expected_date formatted
forecast_expected_date_str = ""
lang = self.env.context.get("lang") or "en_US"
date_format = self.env["res.lang"]._lang_get(lang).date_format
if forecast_expected_date:
forecast_expected_date_str = forecast_expected_date.strftime(date_format)

return {
"warehouse": warehouse.id,
"warehouse_name": warehouse.name,
"qty_available_today": qty_available_today,
"virtual_available_at_date": virtual_available_at_date,
"free_qty_today": free_qty_today,
"qty_to_deliver": qty_to_deliver,
"will_be_fulfilled": will_be_fulfilled,
"forecast_expected_date": forecast_expected_date,
"scheduled_date": scheduled_date,
"forecast_expected_date_str": forecast_expected_date_str,
"forecasted_issue": forecasted_issue,
}

def write(self, values):
# Do not assign quantity to warehouse distribution lines
# if this write is triggered by write method in
Expand Down
2 changes: 1 addition & 1 deletion sale_order_line_multi_warehouse/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This module allows to select multiple warehouses in sale order lines so the sale order is split into multiple pickings depending on the warehouses selected in the lines.
This module allows to select multiple warehouses in sale order lines so the sale order is split into multiple pickings depending on the warehouses selected in the lines. It also adds a new widget to sale order lines when the multiwarehouse options in sale orders are enabled in order to check the stock in each warehouse.
2 changes: 2 additions & 0 deletions sale_order_line_multi_warehouse/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@

* Once the sale order is validated, the order will be split into multiple pickings, one for each warehouse selected in the warehouse distribution lines.

* To open the multi warehouse widget in a sale order line, you need to click on the bar chart in a sale order line. Please, note that this widget replaces the widget added by the sale_stock module when the multi warehouse options in sale orders are enabled.

* **IMPORTANT: In case this module is uninstalled, the warehouse distribution lines will be lost.**
33 changes: 14 additions & 19 deletions sale_order_line_multi_warehouse/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Sale Order Line Multi Warehouse</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,22 +360,17 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="sale-order-line-multi-warehouse">
<h1 class="title">Sale Order Line Multi Warehouse</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="sale-order-line-multi-warehouse">
<h1>Sale Order Line Multi Warehouse</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ca5f990c0b287e23ac49c7bd5ac747d6ce666fce30bc7688b12efbf095a11dab
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" 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 image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/sale-workflow/tree/16.0/sale_order_line_multi_warehouse"><img alt="OCA/sale-workflow" src="https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/sale-workflow-16-0/sale-workflow-16-0-sale_order_line_multi_warehouse"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows to select multiple warehouses in sale order lines so the sale order is split into multiple pickings depending on the warehouses selected in the lines.</p>
<p><a class="reference external image-reference" 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 image-reference" 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 image-reference" href="https://github.com/OCA/sale-workflow/tree/16.0/sale_order_line_multi_warehouse"><img alt="OCA/sale-workflow" src="https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/sale-workflow-16-0/sale-workflow-16-0-sale_order_line_multi_warehouse"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows to select multiple warehouses in sale order lines so the sale order is split into multiple pickings depending on the warehouses selected in the lines. It also adds a new widget to sale order lines when the multiwarehouse options in sale orders are enabled in order to check the stock in each warehouse.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
Expand All @@ -392,7 +387,7 @@ <h1>Sale Order Line Multi Warehouse</h1>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>To activate the sale orders multi-warehouse options you need to:</p>
<ol class="arabic simple">
<li>Go to Inventory &gt; Configuration &gt; Settings.</li>
Expand All @@ -405,7 +400,7 @@ <h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
</ol>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<ul class="simple">
<li><dl class="first docutils">
<dt>To split a sale order line into multiple warehouses you need to click on the tree graph icon in a sale order line. A popup will open, where warehouses and quantities can be selected. Only the warehouses set in the “Alternative Warehouses” field in the warehouse set in the sale order can be selected. Sale order line quantity and its warehouse distributions line quantities are synchronized as follows:</dt>
Expand Down Expand Up @@ -436,41 +431,42 @@ <h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<li>When the multi warehouse options in sale order lines are enabled, changing the general sale order warehouse needs to be done through a wizard, located in the “Other Info” tab, in the “Delivery” section. The button is only visible for users in group “Technical / Manage Multiple Warehouses”.</li>
<li>A sale order line cannot have multiple warehouse distribution lines related to the same warehouse.</li>
<li>Once the sale order is validated, the order will be split into multiple pickings, one for each warehouse selected in the warehouse distribution lines.</li>
<li>To open the multi warehouse widget in a sale order line, you need to click on the bar chart in a sale order line. Please, note that this widget replaces the widget added by the sale_stock module when the multi warehouse options in sale orders are enabled.</li>
<li><strong>IMPORTANT: In case this module is uninstalled, the warehouse distribution lines will be lost.</strong></li>
</ul>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>The warehouse distribution lines display could be improved in the future so it looks similar to the pop up used for analytic distribution.</li>
<li>Module sale_procurement_group_by_line should not be used along with this module as it provides the base to split sale order lines depending on different criteria, which might make sale_order_line_multi_warehouse module malfunction.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/sale-workflow/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 to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/sale-workflow/issues/new?body=module:%20sale_order_line_multi_warehouse%0Aversion:%2016.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">
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-5">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-6">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Authors</a></h2>
<ul class="simple">
<li>Sygel</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-7">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-7">Contributors</a></h2>
<ul class="simple">
<li>Manuel Regidor &lt;<a class="reference external" href="mailto:manuel.regidor&#64;sygel.es">manuel.regidor&#64;sygel.es</a>&gt;</li>
<li>Valentín Vinagre &lt;<a class="reference external" href="mailto:valentin.vinagre&#64;sygel.es">valentin.vinagre&#64;sygel.es</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-8">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" />
Expand All @@ -483,6 +479,5 @@ <h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>
Loading