Skip to content

Commit 27b4b49

Browse files
[ADD] sale_order_forecast_availability
1 parent 1bb9225 commit 27b4b49

File tree

16 files changed

+918
-0
lines changed

16 files changed

+918
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
================================
2+
Sale Order Forecast Availability
3+
================================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:52908ae8af09491754a811422661c43e7a6dfaae462e4ec1bb64244753e18db4
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github
20+
:target: https://github.com/OCA/sale-workflow/tree/18.0/sale_order_forecast_availability
21+
:alt: OCA/sale-workflow
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_order_forecast_availability
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
Flag orders as Forecast available according to the lines
32+
33+
Replicates the standard ``qty_at_date_widget`` logic:
34+
35+
**Draft/Sent orders (no moves created):**
36+
37+
- Issue = insufficient virtual stock AND not MTO product
38+
39+
**Confirmed orders (moves created):**
40+
41+
- Issue = insufficient stock OR late delivery (forecast_expected_date >
42+
scheduled_date)
43+
44+
**Confirmed sale state:**
45+
46+
- Uses ``free_qty_today`` instead of ``virtual_available_at_date``
47+
48+
**Always ignored:**
49+
50+
- Lines without ``scheduled_date``
51+
- Display type lines (sections/notes)
52+
53+
The main goal is to filter and to warn users
54+
55+
**Table of contents**
56+
57+
.. contents::
58+
:local:
59+
60+
Usage
61+
=====
62+
63+
- Navigate to sales orders list and use the filter Forecast Issues
64+
- Navigate to a sales order form and the the warnign on top for non
65+
available ones.
66+
67+
Bug Tracker
68+
===========
69+
70+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-workflow/issues>`_.
71+
In case of trouble, please check there if your issue has already been reported.
72+
If you spotted it first, help us to smash it by providing a detailed and welcomed
73+
`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**>`_.
74+
75+
Do not contact contributors directly about support or help with technical issues.
76+
77+
Credits
78+
=======
79+
80+
Authors
81+
-------
82+
83+
* ForgeFlow
84+
85+
Contributors
86+
------------
87+
88+
- Aaron Henriquez aaron.henriquez@forgeflow.com
89+
90+
Maintainers
91+
-----------
92+
93+
This module is maintained by the OCA.
94+
95+
.. image:: https://odoo-community.org/logo.png
96+
:alt: Odoo Community Association
97+
:target: https://odoo-community.org
98+
99+
OCA, or the Odoo Community Association, is a nonprofit organization whose
100+
mission is to support the collaborative development of Odoo features and
101+
promote its widespread use.
102+
103+
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.
104+
105+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import models
2+
from .hooks import pre_init_hook
3+
from .hooks import post_init_hook
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2026 ForgeFlow S.L.
2+
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3+
4+
{
5+
"name": "Sale Order Forecast Availability",
6+
"summary": "Flag orders according to lines forecast availability",
7+
"version": "18.0.1.0.0",
8+
"category": "Sale",
9+
"website": "https://github.com/OCA/sale-workflow",
10+
"author": "ForgeFlow, Odoo Community Association (OCA)",
11+
"license": "AGPL-3",
12+
"application": False,
13+
"installable": True,
14+
"depends": [
15+
"sale_stock",
16+
],
17+
"pre_init_hook": "pre_init_hook",
18+
"post_init_hook": "post_init_hook",
19+
"data": [
20+
"views/sale_order_view.xml",
21+
],
22+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import logging
2+
3+
from odoo.tools.sql import column_exists, create_column
4+
5+
_logger = logging.getLogger(__name__)
6+
7+
8+
def pre_init_hook(env):
9+
"""
10+
Pre-initialize forecast fields with default values (all available).
11+
"""
12+
_logger.info("Pre-initializing forecast availability fields...")
13+
if not column_exists(env.cr, "sale_order_line", "forecasted_issue"):
14+
_logger.info("Adding forecasted_issue column to sale_order_line...")
15+
create_column(env.cr, "sale_order_line", "forecasted_issue", "boolean")
16+
env.cr.execute("""
17+
UPDATE sale_order_line
18+
SET forecasted_issue = FALSE
19+
""")
20+
_logger.info("Set all order lines to no forecast issues (default).")
21+
if not column_exists(env.cr, "sale_order", "sale_forecast_available"):
22+
_logger.info("Adding sale_forecast_available column to sale_order...")
23+
create_column(env.cr, "sale_order", "sale_forecast_available", "boolean")
24+
env.cr.execute("""
25+
UPDATE sale_order
26+
SET sale_forecast_available = TRUE
27+
""")
28+
_logger.info("Set all orders as forecast available (default).")
29+
_logger.info("Pre-initialization complete!")
30+
31+
32+
def post_init_hook(env):
33+
"""
34+
Compute actual forecast values for active orders using ORM.
35+
Completed/cancelled orders keep their default values (available).
36+
"""
37+
_logger.info("Computing forecast values for active orders...")
38+
active_orders = env["sale.order"].search(
39+
[("state", "!=", "cancel"), ("delivery_status", "!=", "full")]
40+
)
41+
active_orders.mapped("order_line")._compute_forecasted_issue()
42+
active_orders._compute_sale_forecast_available()
43+
_logger.info("Post-initialization complete!")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import sale_order
2+
from . import sale_order_line
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2026 ForgeFlow S.L.
2+
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3+
from odoo import api, fields, models
4+
5+
6+
class SaleOrder(models.Model):
7+
_inherit = "sale.order"
8+
9+
sale_forecast_available = fields.Boolean(
10+
string="Forecast Available",
11+
compute="_compute_sale_forecast_available",
12+
store=True,
13+
help="True if this line has forecast availability issues",
14+
)
15+
16+
@api.depends("order_line.forecasted_issue")
17+
def _compute_sale_forecast_available(self):
18+
for order in self:
19+
order.sale_forecast_available = not any(
20+
line.forecasted_issue
21+
for line in order.order_line
22+
if not line.display_type
23+
)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2026 ForgeFlow S.L.
2+
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3+
from odoo import api, fields, models
4+
5+
6+
class SaleOrderLine(models.Model):
7+
_inherit = "sale.order.line"
8+
9+
forecasted_issue = fields.Boolean(
10+
compute="_compute_forecasted_issue",
11+
store=True,
12+
help="True if this line has forecast availability issues",
13+
)
14+
15+
@api.depends(
16+
"scheduled_date",
17+
"free_qty_today",
18+
"virtual_available_at_date",
19+
"qty_to_deliver",
20+
"forecast_expected_date",
21+
"is_mto",
22+
"state",
23+
)
24+
def _compute_forecasted_issue(self):
25+
for line in self:
26+
forecasted_issue = False
27+
if not line.scheduled_date or line.display_type:
28+
line.forecasted_issue = False
29+
continue
30+
if line.state == "sale":
31+
will_be_fulfilled = line.free_qty_today >= line.qty_to_deliver
32+
else:
33+
will_be_fulfilled = (
34+
line.virtual_available_at_date >= line.qty_to_deliver
35+
)
36+
will_be_late = (
37+
line.forecast_expected_date
38+
and line.forecast_expected_date > line.scheduled_date
39+
)
40+
if line.state in ["draft", "sent"]:
41+
forecasted_issue = not will_be_fulfilled and not line.is_mto
42+
else:
43+
forecasted_issue = not will_be_fulfilled or will_be_late
44+
line.forecasted_issue = forecasted_issue
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Aaron Henriquez <aaron.henriquez@forgeflow.com>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Flag orders as Forecast available according to the lines
2+
3+
Replicates the standard `qty_at_date_widget` logic:
4+
5+
**Draft/Sent orders (no moves created):**
6+
- Issue = insufficient virtual stock AND not MTO product
7+
8+
**Confirmed orders (moves created):**
9+
- Issue = insufficient stock OR late delivery (forecast_expected_date > scheduled_date)
10+
11+
**Confirmed sale state:**
12+
- Uses `free_qty_today` instead of `virtual_available_at_date`
13+
14+
**Always ignored:**
15+
- Lines without `scheduled_date`
16+
- Display type lines (sections/notes)
17+
18+
19+
The main goal is to filter and to warn users

0 commit comments

Comments
 (0)