From f9ee2aefa881d08883b7c4fe072ead99f206c05c Mon Sep 17 00:00:00 2001 From: aykhu-odoo Date: Tue, 19 May 2026 12:56:27 +0530 Subject: [PATCH] [ADD] sale_zero_stock_approval: add zero stock approval control on sale orders In standard behavior, sales users can confirm orders even when there is not enough available stock, which may lead to unintended commitments, stock issues, or reliance on manual validation processes. This change introduces a *Zero Stock Approval* mechanism to enforce better control over such scenarios. A boolean field is added on the sale order to explicitly allow confirmation when stock is insufficient. For regular sales users, this field acts as a required approval flag: they cannot confirm the order unless it is enabled. To maintain flexibility and align with existing access rights, sales managers are not restricted by this rule and can confirm orders regardless of stock availability or field value. The field is read-only for regular users to prevent unauthorized changes, while administrators can manage it as needed. The confirmation logic is extended to validate stock availability per order line and block confirmation when the conditions are not met. This ensures that zero-stock sales are intentional, traceable, and aligned with business approval workflows. --- sale_zero_stock_approval/__init__.py | 1 + sale_zero_stock_approval/__manifest__.py | 10 ++++++++ sale_zero_stock_approval/models/__init__.py | 1 + sale_zero_stock_approval/models/sale_order.py | 23 +++++++++++++++++++ .../views/sale_order_views.xml | 16 +++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 sale_zero_stock_approval/__init__.py create mode 100644 sale_zero_stock_approval/__manifest__.py create mode 100644 sale_zero_stock_approval/models/__init__.py create mode 100644 sale_zero_stock_approval/models/sale_order.py create mode 100644 sale_zero_stock_approval/views/sale_order_views.xml diff --git a/sale_zero_stock_approval/__init__.py b/sale_zero_stock_approval/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/sale_zero_stock_approval/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_zero_stock_approval/__manifest__.py b/sale_zero_stock_approval/__manifest__.py new file mode 100644 index 00000000000..32f25182059 --- /dev/null +++ b/sale_zero_stock_approval/__manifest__.py @@ -0,0 +1,10 @@ +{ + 'name': 'Sale Zero Stock Approval', + 'description': 'Allow sale users to confirm approved zero stock sale orders', + 'depends': ['sale_management', 'sale_stock'], + 'author': 'aykhu', + 'license': 'LGPL-3', + 'data': [ + 'views/sale_order_views.xml', + ] +} diff --git a/sale_zero_stock_approval/models/__init__.py b/sale_zero_stock_approval/models/__init__.py new file mode 100644 index 00000000000..6aacb753131 --- /dev/null +++ b/sale_zero_stock_approval/models/__init__.py @@ -0,0 +1 @@ +from . import sale_order diff --git a/sale_zero_stock_approval/models/sale_order.py b/sale_zero_stock_approval/models/sale_order.py new file mode 100644 index 00000000000..bedd454b672 --- /dev/null +++ b/sale_zero_stock_approval/models/sale_order.py @@ -0,0 +1,23 @@ +from odoo import fields, models +from odoo.exceptions import AccessError + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + zero_stock_approval = fields.Boolean(string="Approval", default=False) + + def action_confirm(self): + for record in self: + for line in record.order_line: + if ( + not record.zero_stock_approval + and not self.env.user.has_group("sales_team.group_sale_manager") + and line.product_id.type == "consu" + and line.product_id.is_storable + and line.product_id.qty_available < line.product_uom_qty + ): + raise AccessError( + "Access denied: You are not authorized to confirm this sales order." + ) + return super().action_confirm() diff --git a/sale_zero_stock_approval/views/sale_order_views.xml b/sale_zero_stock_approval/views/sale_order_views.xml new file mode 100644 index 00000000000..d0d8183a679 --- /dev/null +++ b/sale_zero_stock_approval/views/sale_order_views.xml @@ -0,0 +1,16 @@ + + + + + sale.order.form.inherit.sale.zero.stock + sale.order + + + + + + + + + + \ No newline at end of file