Skip to content
Open
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
105 changes: 105 additions & 0 deletions sale_order_forecast_availability/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
================================
Sale Order Forecast Availability
================================

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

.. |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%2Fsale--workflow-lightgray.png?logo=github
:target: https://github.com/OCA/sale-workflow/tree/18.0/sale_order_forecast_availability
:alt: OCA/sale-workflow
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_order_forecast_availability
: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/sale-workflow&target_branch=18.0
:alt: Try me on Runboat

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

Flag orders as Forecast available according to the lines

Replicates the standard ``qty_at_date_widget`` logic:

**Draft/Sent orders (no moves created):**

- Issue = insufficient virtual stock AND not MTO product

**Confirmed orders (moves created):**

- Issue = insufficient stock OR late delivery (forecast_expected_date >
scheduled_date)

**Confirmed sale state:**

- Uses ``free_qty_today`` instead of ``virtual_available_at_date``

**Always ignored:**

- Lines without ``scheduled_date``
- Display type lines (sections/notes)

The main goal is to filter and to warn users

**Table of contents**

.. contents::
:local:

Usage
=====

- Navigate to sales orders list and use the filter Forecast Issues
- Navigate to a sales order form and the the warnign on top for non
available ones.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-workflow/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/sale-workflow/issues/new?body=module:%20sale_order_forecast_availability%0Aversion:%2018.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
-------

* ForgeFlow

Contributors
------------

- Aaron Henriquez aaron.henriquez@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/sale-workflow <https://github.com/OCA/sale-workflow/tree/18.0/sale_order_forecast_availability>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
3 changes: 3 additions & 0 deletions sale_order_forecast_availability/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import models
from .hooks import pre_init_hook
from .hooks import post_init_hook
22 changes: 22 additions & 0 deletions sale_order_forecast_availability/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2026 ForgeFlow S.L.
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

{
"name": "Sale Order Forecast Availability",
"summary": "Flag orders according to lines forecast availability",
"version": "18.0.1.0.0",
"category": "Sale",
"website": "https://github.com/OCA/sale-workflow",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"sale_stock",
],
"pre_init_hook": "pre_init_hook",
"post_init_hook": "post_init_hook",
"data": [
"views/sale_order_view.xml",
],
}
43 changes: 43 additions & 0 deletions sale_order_forecast_availability/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logging

from odoo.tools.sql import column_exists, create_column

_logger = logging.getLogger(__name__)


def pre_init_hook(env):
"""
Pre-initialize forecast fields with default values (all available).
"""
_logger.info("Pre-initializing forecast availability fields...")
if not column_exists(env.cr, "sale_order_line", "forecasted_issue"):
_logger.info("Adding forecasted_issue column to sale_order_line...")
create_column(env.cr, "sale_order_line", "forecasted_issue", "boolean")
env.cr.execute("""
UPDATE sale_order_line
SET forecasted_issue = FALSE
""")
_logger.info("Set all order lines to no forecast issues (default).")
if not column_exists(env.cr, "sale_order", "sale_forecast_available"):
_logger.info("Adding sale_forecast_available column to sale_order...")
create_column(env.cr, "sale_order", "sale_forecast_available", "boolean")
env.cr.execute("""
UPDATE sale_order
SET sale_forecast_available = TRUE
""")
_logger.info("Set all orders as forecast available (default).")
_logger.info("Pre-initialization complete!")


def post_init_hook(env):
"""
Compute actual forecast values for active orders using ORM.
Completed/cancelled orders keep their default values (available).
"""
_logger.info("Computing forecast values for active orders...")
active_orders = env["sale.order"].search(
[("state", "!=", "cancel"), ("delivery_status", "!=", "full")]
)
active_orders.mapped("order_line")._compute_forecasted_issue()
active_orders._compute_sale_forecast_available()
_logger.info("Post-initialization complete!")
2 changes: 2 additions & 0 deletions sale_order_forecast_availability/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import sale_order
from . import sale_order_line
23 changes: 23 additions & 0 deletions sale_order_forecast_availability/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2026 ForgeFlow S.L.
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import api, fields, models


class SaleOrder(models.Model):
_inherit = "sale.order"

sale_forecast_available = fields.Boolean(
string="Forecast Available",
compute="_compute_sale_forecast_available",
store=True,
help="True if this line has forecast availability issues",
)

@api.depends("order_line.forecasted_issue")
def _compute_sale_forecast_available(self):
for order in self:
order.sale_forecast_available = not any(
line.forecasted_issue
for line in order.order_line
if not line.display_type
)
44 changes: 44 additions & 0 deletions sale_order_forecast_availability/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 ForgeFlow S.L.
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import api, fields, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

forecasted_issue = fields.Boolean(
compute="_compute_forecasted_issue",
store=True,
help="True if this line has forecast availability issues",
)

@api.depends(
"scheduled_date",
"free_qty_today",
"virtual_available_at_date",
"qty_to_deliver",
"forecast_expected_date",
"is_mto",
"state",
)
def _compute_forecasted_issue(self):
for line in self:
forecasted_issue = False
if not line.scheduled_date or line.display_type:
line.forecasted_issue = False
continue
if line.state == "sale":
will_be_fulfilled = line.free_qty_today >= line.qty_to_deliver
else:
will_be_fulfilled = (
line.virtual_available_at_date >= line.qty_to_deliver
)
will_be_late = (
line.forecast_expected_date
and line.forecast_expected_date > line.scheduled_date
)
if line.state in ["draft", "sent"]:
forecasted_issue = not will_be_fulfilled and not line.is_mto
else:
forecasted_issue = not will_be_fulfilled or will_be_late
line.forecasted_issue = forecasted_issue
3 changes: 3 additions & 0 deletions sale_order_forecast_availability/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
1 change: 1 addition & 0 deletions sale_order_forecast_availability/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Aaron Henriquez <aaron.henriquez@forgeflow.com>
19 changes: 19 additions & 0 deletions sale_order_forecast_availability/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Flag orders as Forecast available according to the lines

Replicates the standard `qty_at_date_widget` logic:

**Draft/Sent orders (no moves created):**
- Issue = insufficient virtual stock AND not MTO product

**Confirmed orders (moves created):**
- Issue = insufficient stock OR late delivery (forecast_expected_date > scheduled_date)

**Confirmed sale state:**
- Uses `free_qty_today` instead of `virtual_available_at_date`

**Always ignored:**
- Lines without `scheduled_date`
- Display type lines (sections/notes)


The main goal is to filter and to warn users
3 changes: 3 additions & 0 deletions sale_order_forecast_availability/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Navigate to sales orders list and use the filter Forecast Issues
- Navigate to a sales order form and the the warnign on top for
non available ones.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading