From 7635265b09c2aa7e495414a9931ad828fe61a76e Mon Sep 17 00:00:00 2001
From: Joan Sisquella
Date: Wed, 18 Oct 2023 11:57:39 +0200
Subject: [PATCH 01/13] [ADD] sale_order_line_remove: allows the removal of
sale order lines
---
sale_order_line_remove/__init__.py | 2 +
sale_order_line_remove/__manifest__.py | 16 +++
sale_order_line_remove/models/__init__.py | 1 +
sale_order_line_remove/models/sale_order.py | 42 ++++++
.../readme/CONTRIBUTORS.rst | 1 +
sale_order_line_remove/readme/DESCRIPTION.rst | 4 +
sale_order_line_remove/readme/USAGE.rst | 4 +
sale_order_line_remove/tests/__init__.py | 1 +
.../tests/test_sale_order_line.py | 123 ++++++++++++++++++
9 files changed, 194 insertions(+)
create mode 100644 sale_order_line_remove/__init__.py
create mode 100644 sale_order_line_remove/__manifest__.py
create mode 100644 sale_order_line_remove/models/__init__.py
create mode 100644 sale_order_line_remove/models/sale_order.py
create mode 100644 sale_order_line_remove/readme/CONTRIBUTORS.rst
create mode 100644 sale_order_line_remove/readme/DESCRIPTION.rst
create mode 100644 sale_order_line_remove/readme/USAGE.rst
create mode 100644 sale_order_line_remove/tests/__init__.py
create mode 100644 sale_order_line_remove/tests/test_sale_order_line.py
diff --git a/sale_order_line_remove/__init__.py b/sale_order_line_remove/__init__.py
new file mode 100644
index 00000000000..0ee8b5073e2
--- /dev/null
+++ b/sale_order_line_remove/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from . import tests
diff --git a/sale_order_line_remove/__manifest__.py b/sale_order_line_remove/__manifest__.py
new file mode 100644
index 00000000000..60d43dcb2f1
--- /dev/null
+++ b/sale_order_line_remove/__manifest__.py
@@ -0,0 +1,16 @@
+# Copyright 2023 ForgeFlow, S.L. (https://www.forgeflow.com)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+{
+ "name": "Sale Order Line Remove",
+ "version": "13.0.1.0.0",
+ "category": "Sales",
+ "summary": "Allows removal of sale order lines from confirmed "
+ "orders if not invoiced or received",
+ "author": "ForgeFlow, Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/sale-workflow",
+ "license": "AGPL-3",
+ "depends": ["sale_stock"],
+ "data": [],
+ "installable": True,
+ "application": False,
+}
diff --git a/sale_order_line_remove/models/__init__.py b/sale_order_line_remove/models/__init__.py
new file mode 100644
index 00000000000..6aacb753131
--- /dev/null
+++ b/sale_order_line_remove/models/__init__.py
@@ -0,0 +1 @@
+from . import sale_order
diff --git a/sale_order_line_remove/models/sale_order.py b/sale_order_line_remove/models/sale_order.py
new file mode 100644
index 00000000000..0b9150c7953
--- /dev/null
+++ b/sale_order_line_remove/models/sale_order.py
@@ -0,0 +1,42 @@
+# Copyright 2023 ForgeFlow, S.L. (https://www.forgeflow.com)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+from odoo import _, models
+from odoo.exceptions import UserError
+
+
+class SaleOrderLine(models.Model):
+ _inherit = "sale.order.line"
+
+ def _check_line_unlink(self):
+ non_removable_lines = super(SaleOrderLine, self)._check_line_unlink()
+ removable_lines = self.filtered(
+ lambda line: line.state in ("sale", "done")
+ and not line.invoice_lines
+ and not line.move_ids.filtered(lambda move: move.state == "done")
+ )
+ invoiced_lines = self.filtered(
+ lambda line: line.state in ("sale", "done") and line.invoice_lines
+ )
+ if invoiced_lines:
+ raise UserError(
+ _("You can not remove an order line that has been invoiced")
+ )
+ delivered_lines = self.filtered(
+ lambda line: line.state in ("sale", "done")
+ and line.move_ids.filtered(lambda move: move.state == "done")
+ )
+ if delivered_lines:
+ raise UserError(
+ _("You can not remove an order line that has been delivered")
+ )
+ return non_removable_lines - removable_lines
+
+ def unlink(self):
+ non_removable_lines = self._check_line_unlink()
+ for line in self - non_removable_lines:
+ line.move_ids.filtered(
+ lambda move: move.state not in ("done", "cancel")
+ )._action_cancel()
+ line.move_ids.filtered(lambda move: move.state != "done").unlink()
+ return super(SaleOrderLine, self).unlink()
diff --git a/sale_order_line_remove/readme/CONTRIBUTORS.rst b/sale_order_line_remove/readme/CONTRIBUTORS.rst
new file mode 100644
index 00000000000..1023a5dd6b1
--- /dev/null
+++ b/sale_order_line_remove/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Joan Sisquella
diff --git a/sale_order_line_remove/readme/DESCRIPTION.rst b/sale_order_line_remove/readme/DESCRIPTION.rst
new file mode 100644
index 00000000000..a780df2764b
--- /dev/null
+++ b/sale_order_line_remove/readme/DESCRIPTION.rst
@@ -0,0 +1,4 @@
+This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
+
+- It has not been invoiced.
+- It has not been delivered.
diff --git a/sale_order_line_remove/readme/USAGE.rst b/sale_order_line_remove/readme/USAGE.rst
new file mode 100644
index 00000000000..9690625f535
--- /dev/null
+++ b/sale_order_line_remove/readme/USAGE.rst
@@ -0,0 +1,4 @@
+Once the module is installed:
+
+1. Go to a sale order.
+2. You can now delete a sale order line if it hasn't been invoiced or delivered.
diff --git a/sale_order_line_remove/tests/__init__.py b/sale_order_line_remove/tests/__init__.py
new file mode 100644
index 00000000000..2283b3b0b2f
--- /dev/null
+++ b/sale_order_line_remove/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_sale_order_line
diff --git a/sale_order_line_remove/tests/test_sale_order_line.py b/sale_order_line_remove/tests/test_sale_order_line.py
new file mode 100644
index 00000000000..f41fa33325e
--- /dev/null
+++ b/sale_order_line_remove/tests/test_sale_order_line.py
@@ -0,0 +1,123 @@
+# Copyright 2023 ForgeFlow, S.L. (https://www.forgeflow.com)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+from odoo.tests import TransactionCase
+
+
+class TestSaleOrderLine(TransactionCase):
+ def setUp(self):
+ super(TestSaleOrderLine, self).setUp()
+ self.SaleOrder = self.env["sale.order"]
+ self.SaleOrderLine = self.env["sale.order.line"]
+ self.partner = self.env.ref("base.res_partner_2")
+ self.product = self.env.ref("product.product_product_4")
+ self.uom = self.env.ref("uom.product_uom_unit")
+
+ def test_check_line_unlink(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ non_removable_lines = sale_order_line._check_line_unlink()
+ self.assertFalse(non_removable_lines, "Line should not be non-removable")
+
+ def test_unlink(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ sale_order_line.unlink()
+ self.assertFalse(sale_order_line.exists(), "Sale order line was not deleted")
+
+ def test_check_line_not_unlinkable(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ picking = sale_order.picking_ids[0]
+ picking.action_confirm()
+ picking.action_assign()
+ for move in picking.move_ids_without_package:
+ move.quantity_done = move.product_uom_qty
+ picking.button_validate()
+ with self.assertRaises(Exception):
+ sale_order_line._check_line_unlink()
+
+ def test_not_unlinkable_after_picking(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ picking = sale_order.picking_ids[0]
+ picking.action_confirm()
+ picking.action_assign()
+ for move in picking.move_ids_without_package:
+ move.quantity_done = move.product_uom_qty
+ picking.button_validate()
+ with self.assertRaises(Exception):
+ sale_order_line.unlink()
+
+ def test_check_line_unlink_delivered(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ picking = sale_order.picking_ids[0]
+ picking.action_confirm()
+ picking.action_assign()
+ for move in picking.move_ids_without_package:
+ move.quantity_done = move.product_uom_qty
+ picking.button_validate()
+ with self.assertRaises(Exception):
+ sale_order_line._check_line_unlink()
+
+ def test_check_line_unlink_invoiced(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ picking = sale_order.picking_ids[0]
+ picking.action_confirm()
+ picking.action_assign()
+ for move in picking.move_ids_without_package:
+ move.quantity_done = move.product_uom_qty
+ picking.button_validate()
+ sale_order._create_invoices()
+ with self.assertRaises(Exception):
+ sale_order_line._check_line_unlink()
From c84c52d76d61add703e53ff3a3dc6c5958d7bd59 Mon Sep 17 00:00:00 2001
From: Joan Sisquella
Date: Mon, 23 Oct 2023 10:17:12 +0200
Subject: [PATCH 02/13] [ADD] sale_order_line_remove: cancel related picking if
all the lines are cancelled
---
sale_order_line_remove/models/sale_order.py | 8 +++--
.../tests/test_sale_order_line.py | 30 +++++++++++++++++++
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/sale_order_line_remove/models/sale_order.py b/sale_order_line_remove/models/sale_order.py
index 0b9150c7953..fb64c13f2ef 100644
--- a/sale_order_line_remove/models/sale_order.py
+++ b/sale_order_line_remove/models/sale_order.py
@@ -15,14 +15,14 @@ def _check_line_unlink(self):
and not line.invoice_lines
and not line.move_ids.filtered(lambda move: move.state == "done")
)
- invoiced_lines = self.filtered(
+ invoiced_lines = self.sudo().filtered(
lambda line: line.state in ("sale", "done") and line.invoice_lines
)
if invoiced_lines:
raise UserError(
_("You can not remove an order line that has been invoiced")
)
- delivered_lines = self.filtered(
+ delivered_lines = self.sudo().filtered(
lambda line: line.state in ("sale", "done")
and line.move_ids.filtered(lambda move: move.state == "done")
)
@@ -35,8 +35,12 @@ def _check_line_unlink(self):
def unlink(self):
non_removable_lines = self._check_line_unlink()
for line in self - non_removable_lines:
+ related_pickings = line.move_ids.mapped("picking_id")
line.move_ids.filtered(
lambda move: move.state not in ("done", "cancel")
)._action_cancel()
line.move_ids.filtered(lambda move: move.state != "done").unlink()
+ for picking in related_pickings:
+ if not picking.move_ids_without_package:
+ picking.unlink()
return super(SaleOrderLine, self).unlink()
diff --git a/sale_order_line_remove/tests/test_sale_order_line.py b/sale_order_line_remove/tests/test_sale_order_line.py
index f41fa33325e..beaf0d1fb97 100644
--- a/sale_order_line_remove/tests/test_sale_order_line.py
+++ b/sale_order_line_remove/tests/test_sale_order_line.py
@@ -121,3 +121,33 @@ def test_check_line_unlink_invoiced(self):
sale_order._create_invoices()
with self.assertRaises(Exception):
sale_order_line._check_line_unlink()
+
+ def test_unlink_empty_picking(self):
+ sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
+ sale_order.action_confirm()
+ sale_order_line1 = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ sale_order_line2 = self.SaleOrderLine.create(
+ {
+ "order_id": sale_order.id,
+ "product_id": self.product.id,
+ "product_uom_qty": 1,
+ "product_uom": self.uom.id,
+ }
+ )
+ picking = sale_order.picking_ids[0]
+ picking.action_confirm()
+ picking.action_assign()
+ sale_order_line1.unlink()
+ self.assertTrue(picking.exists(), "Picking was deleted")
+ self.assertNotEqual(
+ picking.state, "cancel", "Picking should not be cancelled yet"
+ )
+ sale_order_line2.unlink()
+ self.assertFalse(picking.exists(), "Picking was not deleted")
From fe5bdf236f1d1e2b50425566fabad64d7a98d5b5 Mon Sep 17 00:00:00 2001
From: oca-ci
Date: Fri, 27 Oct 2023 09:49:33 +0000
Subject: [PATCH 03/13] [UPD] Update sale_order_line_remove.pot
---
.../i18n/sale_order_line_remove.pot | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 sale_order_line_remove/i18n/sale_order_line_remove.pot
diff --git a/sale_order_line_remove/i18n/sale_order_line_remove.pot b/sale_order_line_remove/i18n/sale_order_line_remove.pot
new file mode 100644
index 00000000000..cb55d497000
--- /dev/null
+++ b/sale_order_line_remove/i18n/sale_order_line_remove.pot
@@ -0,0 +1,31 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * sale_order_line_remove
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 13.0\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: sale_order_line_remove
+#: model:ir.model,name:sale_order_line_remove.model_sale_order_line
+msgid "Sales Order Line"
+msgstr ""
+
+#. module: sale_order_line_remove
+#: code:addons/sale_order_line_remove/models/sale_order.py:0
+#, python-format
+msgid "You can not remove an order line that has been delivered"
+msgstr ""
+
+#. module: sale_order_line_remove
+#: code:addons/sale_order_line_remove/models/sale_order.py:0
+#, python-format
+msgid "You can not remove an order line that has been invoiced"
+msgstr ""
From 1b378be604b8e559e193ed0f3b73bd7d911a299c Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Fri, 27 Oct 2023 10:03:17 +0000
Subject: [PATCH 04/13] [BOT] post-merge updates
---
sale_order_line_remove/README.rst | 87 ++++
.../static/description/icon.png | Bin 0 -> 9455 bytes
.../static/description/index.html | 434 ++++++++++++++++++
3 files changed, 521 insertions(+)
create mode 100644 sale_order_line_remove/README.rst
create mode 100644 sale_order_line_remove/static/description/icon.png
create mode 100644 sale_order_line_remove/static/description/index.html
diff --git a/sale_order_line_remove/README.rst b/sale_order_line_remove/README.rst
new file mode 100644
index 00000000000..6e91c375642
--- /dev/null
+++ b/sale_order_line_remove/README.rst
@@ -0,0 +1,87 @@
+======================
+Sale Order Line Remove
+======================
+
+..
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! source digest: sha256:2017e8c089ea3193365d2780d28f22d8fe129ffe9f0796aef565f75343968bdd
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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/13.0/sale_order_line_remove
+ :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-13-0/sale-workflow-13-0-sale_order_line_remove
+ :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=13.0
+ :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
+
+- It has not been invoiced.
+- It has not been delivered.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+Once the module is installed:
+
+1. Go to a sale order.
+2. You can now delete a sale order line if it hasn't been invoiced or delivered.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub 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 `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* ForgeFlow
+
+Contributors
+~~~~~~~~~~~~
+
+* Joan Sisquella
+
+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 `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/static/description/icon.png b/sale_order_line_remove/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d
GIT binary patch
literal 9455
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
z
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
Xviia!U7SGha1wx#SCgwmn*{w2TRX*I
literal 0
HcmV?d00001
diff --git a/sale_order_line_remove/static/description/index.html b/sale_order_line_remove/static/description/index.html
new file mode 100644
index 00000000000..72a81b07a7f
--- /dev/null
+++ b/sale_order_line_remove/static/description/index.html
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+Sale Order Line Remove
+
+
+
+
+
Sale Order Line Remove
+
+
+

+
This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
+
+- It has not been invoiced.
+- It has not been delivered.
+
+
Table of contents
+
+
+
+
Once the module is installed:
+
+- Go to a sale order.
+- You can now delete a sale order line if it hasn’t been invoiced or delivered.
+
+
+
+
+
Bugs are tracked on GitHub 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.
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
This module is maintained by the OCA.
+

+
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 project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+
+
+
+
+
From 2fd46ed55f2d27f9c4af0d8fc32b415927bfef75 Mon Sep 17 00:00:00 2001
From: Joan Sisquella
Date: Wed, 8 Nov 2023 12:13:28 +0100
Subject: [PATCH 05/13] [MIG] sale_order_line_remove: Migration to 15.0
---
sale_order_line_remove/README.rst | 12 ++++++------
sale_order_line_remove/__init__.py | 1 -
sale_order_line_remove/__manifest__.py | 2 +-
sale_order_line_remove/static/description/index.html | 8 ++++----
sale_order_line_remove/tests/test_sale_order_line.py | 9 +++++----
5 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/sale_order_line_remove/README.rst b/sale_order_line_remove/README.rst
index 6e91c375642..36dd7656ec6 100644
--- a/sale_order_line_remove/README.rst
+++ b/sale_order_line_remove/README.rst
@@ -7,7 +7,7 @@ Sale Order Line Remove
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:2017e8c089ea3193365d2780d28f22d8fe129ffe9f0796aef565f75343968bdd
+ !! source digest: sha256:19987b79fe99d4ff465bda1d9b1f07c5570e70b5347127ddb2dcc5849efb6bc4
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@@ -17,13 +17,13 @@ Sale Order Line Remove
: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/13.0/sale_order_line_remove
+ :target: https://github.com/OCA/sale-workflow/tree/15.0/sale_order_line_remove
: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-13-0/sale-workflow-13-0-sale_order_line_remove
+ :target: https://translation.odoo-community.org/projects/sale-workflow-15-0/sale-workflow-15-0-sale_order_line_remove
: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=13.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=15.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -52,7 +52,7 @@ Bug Tracker
Bugs are tracked on `GitHub 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -82,6 +82,6 @@ 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 `_ project on GitHub.
+This module is part of the `OCA/sale-workflow `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/__init__.py b/sale_order_line_remove/__init__.py
index 0ee8b5073e2..0650744f6bc 100644
--- a/sale_order_line_remove/__init__.py
+++ b/sale_order_line_remove/__init__.py
@@ -1,2 +1 @@
from . import models
-from . import tests
diff --git a/sale_order_line_remove/__manifest__.py b/sale_order_line_remove/__manifest__.py
index 60d43dcb2f1..3d348e941aa 100644
--- a/sale_order_line_remove/__manifest__.py
+++ b/sale_order_line_remove/__manifest__.py
@@ -2,7 +2,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Sale Order Line Remove",
- "version": "13.0.1.0.0",
+ "version": "15.0.1.0.0",
"category": "Sales",
"summary": "Allows removal of sale order lines from confirmed "
"orders if not invoiced or received",
diff --git a/sale_order_line_remove/static/description/index.html b/sale_order_line_remove/static/description/index.html
index 72a81b07a7f..7467a885c78 100644
--- a/sale_order_line_remove/static/description/index.html
+++ b/sale_order_line_remove/static/description/index.html
@@ -367,9 +367,9 @@ Sale Order Line Remove
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:2017e8c089ea3193365d2780d28f22d8fe129ffe9f0796aef565f75343968bdd
+!! source digest: sha256:19987b79fe99d4ff465bda1d9b1f07c5570e70b5347127ddb2dcc5849efb6bc4
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
- It has not been invoiced.
@@ -401,7 +401,7 @@
Bugs are tracked on GitHub 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.
+feedback.
Do not contact contributors directly about support or help with technical issues.
@@ -425,7 +425,7 @@
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 project on GitHub.
+
This module is part of the OCA/sale-workflow project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/tests/test_sale_order_line.py b/sale_order_line_remove/tests/test_sale_order_line.py
index beaf0d1fb97..03ecad1439e 100644
--- a/sale_order_line_remove/tests/test_sale_order_line.py
+++ b/sale_order_line_remove/tests/test_sale_order_line.py
@@ -1,6 +1,7 @@
# Copyright 2023 ForgeFlow, S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+from odoo.exceptions import UserError
from odoo.tests import TransactionCase
@@ -58,7 +59,7 @@ def test_check_line_not_unlinkable(self):
for move in picking.move_ids_without_package:
move.quantity_done = move.product_uom_qty
picking.button_validate()
- with self.assertRaises(Exception):
+ with self.assertRaises(UserError):
sale_order_line._check_line_unlink()
def test_not_unlinkable_after_picking(self):
@@ -78,7 +79,7 @@ def test_not_unlinkable_after_picking(self):
for move in picking.move_ids_without_package:
move.quantity_done = move.product_uom_qty
picking.button_validate()
- with self.assertRaises(Exception):
+ with self.assertRaises(UserError):
sale_order_line.unlink()
def test_check_line_unlink_delivered(self):
@@ -98,7 +99,7 @@ def test_check_line_unlink_delivered(self):
for move in picking.move_ids_without_package:
move.quantity_done = move.product_uom_qty
picking.button_validate()
- with self.assertRaises(Exception):
+ with self.assertRaises(UserError):
sale_order_line._check_line_unlink()
def test_check_line_unlink_invoiced(self):
@@ -119,7 +120,7 @@ def test_check_line_unlink_invoiced(self):
move.quantity_done = move.product_uom_qty
picking.button_validate()
sale_order._create_invoices()
- with self.assertRaises(Exception):
+ with self.assertRaises(UserError):
sale_order_line._check_line_unlink()
def test_unlink_empty_picking(self):
From b9df52da4bacdf02d1138b0c91f6badc4b64e75f Mon Sep 17 00:00:00 2001
From: oca-ci
Date: Tue, 14 Nov 2023 11:50:30 +0000
Subject: [PATCH 06/13] [UPD] Update sale_order_line_remove.pot
---
sale_order_line_remove/i18n/sale_order_line_remove.pot | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sale_order_line_remove/i18n/sale_order_line_remove.pot b/sale_order_line_remove/i18n/sale_order_line_remove.pot
index cb55d497000..12b0de40c2a 100644
--- a/sale_order_line_remove/i18n/sale_order_line_remove.pot
+++ b/sale_order_line_remove/i18n/sale_order_line_remove.pot
@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 13.0\n"
+"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
From 984221c16f3a375584d7ccd978a3dc92fc305057 Mon Sep 17 00:00:00 2001
From: Arnau
Date: Tue, 9 Sep 2025 13:45:29 +0200
Subject: [PATCH 07/13] [IMP] sale_order_line_remove: pre-commit auto fixes
---
sale_order_line_remove/README.rst | 25 +++++++++++--------
sale_order_line_remove/models/sale_order.py | 4 +--
sale_order_line_remove/pyproject.toml | 3 +++
sale_order_line_remove/readme/CONTRIBUTORS.md | 1 +
.../readme/CONTRIBUTORS.rst | 1 -
.../{DESCRIPTION.rst => DESCRIPTION.md} | 4 ++-
sale_order_line_remove/readme/USAGE.md | 5 ++++
sale_order_line_remove/readme/USAGE.rst | 4 ---
.../static/description/index.html | 25 +++++++++++--------
.../tests/test_sale_order_line.py | 2 +-
10 files changed, 44 insertions(+), 30 deletions(-)
create mode 100644 sale_order_line_remove/pyproject.toml
create mode 100644 sale_order_line_remove/readme/CONTRIBUTORS.md
delete mode 100644 sale_order_line_remove/readme/CONTRIBUTORS.rst
rename sale_order_line_remove/readme/{DESCRIPTION.rst => DESCRIPTION.md} (53%)
create mode 100644 sale_order_line_remove/readme/USAGE.md
delete mode 100644 sale_order_line_remove/readme/USAGE.rst
diff --git a/sale_order_line_remove/README.rst b/sale_order_line_remove/README.rst
index 36dd7656ec6..8a52e1024ca 100644
--- a/sale_order_line_remove/README.rst
+++ b/sale_order_line_remove/README.rst
@@ -17,18 +17,20 @@ Sale Order Line Remove
: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/15.0/sale_order_line_remove
+ :target: https://github.com/OCA/sale-workflow/tree/18.0/sale_order_line_remove
: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-15-0/sale-workflow-15-0-sale_order_line_remove
+ :target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_order_line_remove
: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=15.0
+ :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|
-This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
+This module allows the removal of sale order lines even after the order
+has been confirmed, under certain conditions. Specifically, a sale order
+line can be removed if:
- It has not been invoiced.
- It has not been delivered.
@@ -44,7 +46,8 @@ Usage
Once the module is installed:
1. Go to a sale order.
-2. You can now delete a sale order line if it hasn't been invoiced or delivered.
+2. You can now delete a sale order line if it hasn't been invoiced or
+ delivered.
Bug Tracker
===========
@@ -52,7 +55,7 @@ Bug Tracker
Bugs are tracked on `GitHub 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -60,17 +63,17 @@ Credits
=======
Authors
-~~~~~~~
+-------
* ForgeFlow
Contributors
-~~~~~~~~~~~~
+------------
-* Joan Sisquella
+- Joan Sisquella
Maintainers
-~~~~~~~~~~~
+-----------
This module is maintained by the OCA.
@@ -82,6 +85,6 @@ 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 `_ project on GitHub.
+This module is part of the `OCA/sale-workflow `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/models/sale_order.py b/sale_order_line_remove/models/sale_order.py
index fb64c13f2ef..7f3cc907bb9 100644
--- a/sale_order_line_remove/models/sale_order.py
+++ b/sale_order_line_remove/models/sale_order.py
@@ -9,7 +9,7 @@ class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
def _check_line_unlink(self):
- non_removable_lines = super(SaleOrderLine, self)._check_line_unlink()
+ non_removable_lines = super()._check_line_unlink()
removable_lines = self.filtered(
lambda line: line.state in ("sale", "done")
and not line.invoice_lines
@@ -43,4 +43,4 @@ def unlink(self):
for picking in related_pickings:
if not picking.move_ids_without_package:
picking.unlink()
- return super(SaleOrderLine, self).unlink()
+ return super().unlink()
diff --git a/sale_order_line_remove/pyproject.toml b/sale_order_line_remove/pyproject.toml
new file mode 100644
index 00000000000..4231d0cccb3
--- /dev/null
+++ b/sale_order_line_remove/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/sale_order_line_remove/readme/CONTRIBUTORS.md b/sale_order_line_remove/readme/CONTRIBUTORS.md
new file mode 100644
index 00000000000..2fa75245648
--- /dev/null
+++ b/sale_order_line_remove/readme/CONTRIBUTORS.md
@@ -0,0 +1 @@
+- Joan Sisquella \
diff --git a/sale_order_line_remove/readme/CONTRIBUTORS.rst b/sale_order_line_remove/readme/CONTRIBUTORS.rst
deleted file mode 100644
index 1023a5dd6b1..00000000000
--- a/sale_order_line_remove/readme/CONTRIBUTORS.rst
+++ /dev/null
@@ -1 +0,0 @@
-* Joan Sisquella
diff --git a/sale_order_line_remove/readme/DESCRIPTION.rst b/sale_order_line_remove/readme/DESCRIPTION.md
similarity index 53%
rename from sale_order_line_remove/readme/DESCRIPTION.rst
rename to sale_order_line_remove/readme/DESCRIPTION.md
index a780df2764b..593ed1efdc3 100644
--- a/sale_order_line_remove/readme/DESCRIPTION.rst
+++ b/sale_order_line_remove/readme/DESCRIPTION.md
@@ -1,4 +1,6 @@
-This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
+This module allows the removal of sale order lines even after the order
+has been confirmed, under certain conditions. Specifically, a sale order
+line can be removed if:
- It has not been invoiced.
- It has not been delivered.
diff --git a/sale_order_line_remove/readme/USAGE.md b/sale_order_line_remove/readme/USAGE.md
new file mode 100644
index 00000000000..65a6e577552
--- /dev/null
+++ b/sale_order_line_remove/readme/USAGE.md
@@ -0,0 +1,5 @@
+Once the module is installed:
+
+1. Go to a sale order.
+2. You can now delete a sale order line if it hasn't been invoiced or
+ delivered.
diff --git a/sale_order_line_remove/readme/USAGE.rst b/sale_order_line_remove/readme/USAGE.rst
deleted file mode 100644
index 9690625f535..00000000000
--- a/sale_order_line_remove/readme/USAGE.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Once the module is installed:
-
-1. Go to a sale order.
-2. You can now delete a sale order line if it hasn't been invoiced or delivered.
diff --git a/sale_order_line_remove/static/description/index.html b/sale_order_line_remove/static/description/index.html
index 7467a885c78..938a8a60108 100644
--- a/sale_order_line_remove/static/description/index.html
+++ b/sale_order_line_remove/static/description/index.html
@@ -1,4 +1,3 @@
-
@@ -9,10 +8,11 @@
/*
:Author: David Goodger (goodger@python.org)
-:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
+:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
+Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -275,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }
-pre.code .ln { color: grey; } /* line numbers */
+pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -301,7 +301,7 @@
span.pre {
white-space: pre }
-span.problematic {
+span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@@ -369,8 +369,10 @@ Sale Order Line Remove
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:19987b79fe99d4ff465bda1d9b1f07c5570e70b5347127ddb2dcc5849efb6bc4
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

-This module allows the removal of sale order lines even after the order has been confirmed, under certain conditions. Specifically, a sale order line can be removed if:
+

+This module allows the removal of sale order lines even after the order
+has been confirmed, under certain conditions. Specifically, a sale order
+line can be removed if:
- It has not been invoiced.
- It has not been delivered.
@@ -393,7 +395,8 @@
Once the module is installed:
- Go to a sale order.
-- You can now delete a sale order line if it hasn’t been invoiced or delivered.
+- You can now delete a sale order line if it hasn’t been invoiced or
+delivered.
@@ -401,7 +404,7 @@
Bugs are tracked on GitHub 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.
+
feedback.
Do not contact contributors directly about support or help with technical issues.
@@ -421,11 +424,13 @@
This module is maintained by the OCA.
-

+
+
+
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 project on GitHub.
+
This module is part of the OCA/sale-workflow project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/tests/test_sale_order_line.py b/sale_order_line_remove/tests/test_sale_order_line.py
index 03ecad1439e..dcf621c0fc6 100644
--- a/sale_order_line_remove/tests/test_sale_order_line.py
+++ b/sale_order_line_remove/tests/test_sale_order_line.py
@@ -7,7 +7,7 @@
class TestSaleOrderLine(TransactionCase):
def setUp(self):
- super(TestSaleOrderLine, self).setUp()
+ super().setUp()
self.SaleOrder = self.env["sale.order"]
self.SaleOrderLine = self.env["sale.order.line"]
self.partner = self.env.ref("base.res_partner_2")
From 634a347e483d8c1b12e8844280656801f2b5592e Mon Sep 17 00:00:00 2001
From: Arnau
Date: Tue, 9 Sep 2025 13:58:16 +0200
Subject: [PATCH 08/13] [MIG] sale_order_line_remove: Migration to 18.0
---
sale_order_line_remove/__manifest__.py | 6 +-
sale_order_line_remove/models/__init__.py | 1 +
.../models/res_config_settings.py | 14 ++++
sale_order_line_remove/models/sale_order.py | 72 +++++++++++--------
.../tests/test_sale_order_line.py | 22 +++---
.../views/res_config_settings_views.xml | 20 ++++++
6 files changed, 93 insertions(+), 42 deletions(-)
create mode 100644 sale_order_line_remove/models/res_config_settings.py
create mode 100644 sale_order_line_remove/views/res_config_settings_views.xml
diff --git a/sale_order_line_remove/__manifest__.py b/sale_order_line_remove/__manifest__.py
index 3d348e941aa..30466a3cb7b 100644
--- a/sale_order_line_remove/__manifest__.py
+++ b/sale_order_line_remove/__manifest__.py
@@ -2,15 +2,15 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Sale Order Line Remove",
- "version": "15.0.1.0.0",
+ "version": "18.0.1.0.0",
"category": "Sales",
"summary": "Allows removal of sale order lines from confirmed "
"orders if not invoiced or received",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/sale-workflow",
"license": "AGPL-3",
- "depends": ["sale_stock"],
- "data": [],
+ "depends": ["sale_stock", "sale_management"],
+ "data": ["views/res_config_settings_views.xml"],
"installable": True,
"application": False,
}
diff --git a/sale_order_line_remove/models/__init__.py b/sale_order_line_remove/models/__init__.py
index 6aacb753131..a556602094c 100644
--- a/sale_order_line_remove/models/__init__.py
+++ b/sale_order_line_remove/models/__init__.py
@@ -1 +1,2 @@
from . import sale_order
+from . import res_config_settings
diff --git a/sale_order_line_remove/models/res_config_settings.py b/sale_order_line_remove/models/res_config_settings.py
new file mode 100644
index 00000000000..a0e5b101fc3
--- /dev/null
+++ b/sale_order_line_remove/models/res_config_settings.py
@@ -0,0 +1,14 @@
+# Copyright 2025 ForgeFlow, S.L. (https://www.forgeflow.com)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+from odoo import fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = "res.config.settings"
+ restrict_sale_order_line_remove = fields.Boolean(
+ "Allow Sale Order Line Remove",
+ config_parameter="sale.order.line.remove",
+ help="Allow removing confirmed sale order lines only "
+ "if they are not invoiced or delivered.",
+ )
diff --git a/sale_order_line_remove/models/sale_order.py b/sale_order_line_remove/models/sale_order.py
index 7f3cc907bb9..d166e7a6a5d 100644
--- a/sale_order_line_remove/models/sale_order.py
+++ b/sale_order_line_remove/models/sale_order.py
@@ -10,37 +10,51 @@ class SaleOrderLine(models.Model):
def _check_line_unlink(self):
non_removable_lines = super()._check_line_unlink()
- removable_lines = self.filtered(
- lambda line: line.state in ("sale", "done")
- and not line.invoice_lines
- and not line.move_ids.filtered(lambda move: move.state == "done")
- )
- invoiced_lines = self.sudo().filtered(
- lambda line: line.state in ("sale", "done") and line.invoice_lines
- )
- if invoiced_lines:
- raise UserError(
- _("You can not remove an order line that has been invoiced")
+ if (
+ not self.env["ir.config_parameter"]
+ .sudo()
+ .get_param("sale.order.line.remove")
+ ):
+ return non_removable_lines
+ else:
+ removable_lines = self.filtered(
+ lambda line: line.state in ("sale", "done")
+ and not line.invoice_lines
+ and not line.move_ids.filtered(lambda move: move.state == "done")
)
- delivered_lines = self.sudo().filtered(
- lambda line: line.state in ("sale", "done")
- and line.move_ids.filtered(lambda move: move.state == "done")
- )
- if delivered_lines:
- raise UserError(
- _("You can not remove an order line that has been delivered")
+ invoiced_lines = self.sudo().filtered(
+ lambda line: line.state in ("sale", "done") and line.invoice_lines
)
- return non_removable_lines - removable_lines
+ if invoiced_lines:
+ raise UserError(
+ _("You can not remove an order line that has been invoiced")
+ )
+ delivered_lines = self.sudo().filtered(
+ lambda line: line.state in ("sale", "done")
+ and line.move_ids.filtered(lambda move: move.state == "done")
+ )
+ if delivered_lines:
+ raise UserError(
+ _("You can not remove an order line that has been delivered")
+ )
+ return non_removable_lines - removable_lines
def unlink(self):
non_removable_lines = self._check_line_unlink()
- for line in self - non_removable_lines:
- related_pickings = line.move_ids.mapped("picking_id")
- line.move_ids.filtered(
- lambda move: move.state not in ("done", "cancel")
- )._action_cancel()
- line.move_ids.filtered(lambda move: move.state != "done").unlink()
- for picking in related_pickings:
- if not picking.move_ids_without_package:
- picking.unlink()
- return super().unlink()
+ if (
+ not self.env["ir.config_parameter"]
+ .sudo()
+ .get_param("sale.order.line.remove")
+ ):
+ return super().unlink()
+ else:
+ for line in self - non_removable_lines:
+ related_pickings = line.move_ids.mapped("picking_id")
+ line.move_ids.filtered(
+ lambda move: move.state not in ("done", "cancel")
+ )._action_cancel()
+ line.move_ids.filtered(lambda move: move.state != "done").unlink()
+ for picking in related_pickings:
+ if not picking.move_ids_without_package:
+ picking.unlink()
+ return super().unlink()
diff --git a/sale_order_line_remove/tests/test_sale_order_line.py b/sale_order_line_remove/tests/test_sale_order_line.py
index dcf621c0fc6..1936822fcff 100644
--- a/sale_order_line_remove/tests/test_sale_order_line.py
+++ b/sale_order_line_remove/tests/test_sale_order_line.py
@@ -6,13 +6,15 @@
class TestSaleOrderLine(TransactionCase):
- def setUp(self):
+ def setUp(cls):
super().setUp()
- self.SaleOrder = self.env["sale.order"]
- self.SaleOrderLine = self.env["sale.order.line"]
- self.partner = self.env.ref("base.res_partner_2")
- self.product = self.env.ref("product.product_product_4")
- self.uom = self.env.ref("uom.product_uom_unit")
+ cls.SaleOrder = cls.env["sale.order"]
+ cls.SaleOrderLine = cls.env["sale.order.line"]
+ cls.partner = cls.env.ref("base.res_partner_2")
+ cls.product = cls.env.ref("product.product_product_4")
+ cls.uom = cls.env.ref("uom.product_uom_unit")
+ cls.config_param = cls.env["ir.config_parameter"].sudo()
+ cls.config_param.set_param("sale.order.line.remove", True)
def test_check_line_unlink(self):
sale_order = self.SaleOrder.create({"partner_id": self.partner.id})
@@ -57,7 +59,7 @@ def test_check_line_not_unlinkable(self):
picking.action_confirm()
picking.action_assign()
for move in picking.move_ids_without_package:
- move.quantity_done = move.product_uom_qty
+ move.quantity = move.product_uom_qty
picking.button_validate()
with self.assertRaises(UserError):
sale_order_line._check_line_unlink()
@@ -77,7 +79,7 @@ def test_not_unlinkable_after_picking(self):
picking.action_confirm()
picking.action_assign()
for move in picking.move_ids_without_package:
- move.quantity_done = move.product_uom_qty
+ move.quantity = move.product_uom_qty
picking.button_validate()
with self.assertRaises(UserError):
sale_order_line.unlink()
@@ -97,7 +99,7 @@ def test_check_line_unlink_delivered(self):
picking.action_confirm()
picking.action_assign()
for move in picking.move_ids_without_package:
- move.quantity_done = move.product_uom_qty
+ move.quantity = move.product_uom_qty
picking.button_validate()
with self.assertRaises(UserError):
sale_order_line._check_line_unlink()
@@ -117,7 +119,7 @@ def test_check_line_unlink_invoiced(self):
picking.action_confirm()
picking.action_assign()
for move in picking.move_ids_without_package:
- move.quantity_done = move.product_uom_qty
+ move.quantity = move.product_uom_qty
picking.button_validate()
sale_order._create_invoices()
with self.assertRaises(UserError):
diff --git a/sale_order_line_remove/views/res_config_settings_views.xml b/sale_order_line_remove/views/res_config_settings_views.xml
new file mode 100644
index 00000000000..938b6213a17
--- /dev/null
+++ b/sale_order_line_remove/views/res_config_settings_views.xml
@@ -0,0 +1,20 @@
+
+
+
+ res.config.settings.view.form.inherit.sale.order.line.remove
+ res.config.settings
+
+
+
+
+
+
+
+
+
+
From 0cf1c1ad35fd8a1d48d4e59cb89094650ce4e402 Mon Sep 17 00:00:00 2001
From: oca-ci
Date: Sat, 27 Sep 2025 14:05:41 +0000
Subject: [PATCH 09/13] [UPD] Update sale_order_line_remove.pot
---
.../i18n/sale_order_line_remove.pot | 24 ++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/sale_order_line_remove/i18n/sale_order_line_remove.pot b/sale_order_line_remove/i18n/sale_order_line_remove.pot
index 12b0de40c2a..de81f3f4c88 100644
--- a/sale_order_line_remove/i18n/sale_order_line_remove.pot
+++ b/sale_order_line_remove/i18n/sale_order_line_remove.pot
@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 15.0\n"
+"Project-Id-Version: Odoo Server 18.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -13,19 +13,37 @@ msgstr ""
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
+#. module: sale_order_line_remove
+#: model:ir.model.fields,field_description:sale_order_line_remove.field_res_config_settings__restrict_sale_order_line_remove
+#: model_terms:ir.ui.view,arch_db:sale_order_line_remove.res_config_settings_view_form_sale
+msgid "Allow Sale Order Line Remove"
+msgstr ""
+
+#. module: sale_order_line_remove
+#: model:ir.model.fields,help:sale_order_line_remove.field_res_config_settings__restrict_sale_order_line_remove
+msgid ""
+"Allow removing confirmed sale order lines only if they are not invoiced or "
+"delivered."
+msgstr ""
+
+#. module: sale_order_line_remove
+#: model:ir.model,name:sale_order_line_remove.model_res_config_settings
+msgid "Config Settings"
+msgstr ""
+
#. module: sale_order_line_remove
#: model:ir.model,name:sale_order_line_remove.model_sale_order_line
msgid "Sales Order Line"
msgstr ""
#. module: sale_order_line_remove
+#. odoo-python
#: code:addons/sale_order_line_remove/models/sale_order.py:0
-#, python-format
msgid "You can not remove an order line that has been delivered"
msgstr ""
#. module: sale_order_line_remove
+#. odoo-python
#: code:addons/sale_order_line_remove/models/sale_order.py:0
-#, python-format
msgid "You can not remove an order line that has been invoiced"
msgstr ""
From b69171350ac19705dab358f39869f1540ef5db6a Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Sat, 27 Sep 2025 14:14:12 +0000
Subject: [PATCH 10/13] [BOT] post-merge updates
---
sale_order_line_remove/README.rst | 8 ++++--
.../static/description/index.html | 28 +++++++++++--------
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/sale_order_line_remove/README.rst b/sale_order_line_remove/README.rst
index 8a52e1024ca..68de293dfd0 100644
--- a/sale_order_line_remove/README.rst
+++ b/sale_order_line_remove/README.rst
@@ -1,3 +1,7 @@
+.. image:: https://odoo-community.org/readme-banner-image
+ :target: https://odoo-community.org/get-involved?utm_source=readme
+ :alt: Odoo Community Association
+
======================
Sale Order Line Remove
======================
@@ -7,13 +11,13 @@ Sale Order Line Remove
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:19987b79fe99d4ff465bda1d9b1f07c5570e70b5347127ddb2dcc5849efb6bc4
+ !! source digest: sha256:92c4d50bdbf5dda19487295fa744ed8f013ec29079a78d1ef8eb9bf5d22692d9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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
+.. |badge2| image:: https://img.shields.io/badge/license-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
diff --git a/sale_order_line_remove/static/description/index.html b/sale_order_line_remove/static/description/index.html
index 938a8a60108..007ba5b96b0 100644
--- a/sale_order_line_remove/static/description/index.html
+++ b/sale_order_line_remove/static/description/index.html
@@ -3,7 +3,7 @@
-Sale Order Line Remove
+README.rst
-
-
Sale Order Line Remove
+
+
+
+
+
+
+
Sale Order Line Remove
-

+

This module allows the removal of sale order lines even after the order
has been confirmed, under certain conditions. Specifically, a sale order
line can be removed if:
@@ -391,7 +396,7 @@
Sale Order Line Remove
-
+
Once the module is installed:
- Go to a sale order.
@@ -400,7 +405,7 @@
-
+
Bugs are tracked on GitHub 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
@@ -408,21 +413,21 @@
Do not contact contributors directly about support or help with technical issues.
+
From c347ab26fc9112b42916ff043b4527db960bc5e4 Mon Sep 17 00:00:00 2001
From: mymage
Date: Sun, 28 Sep 2025 14:01:44 +0000
Subject: [PATCH 11/13] Added translation using Weblate (Italian)
---
sale_order_line_remove/i18n/it.po | 50 +++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 sale_order_line_remove/i18n/it.po
diff --git a/sale_order_line_remove/i18n/it.po b/sale_order_line_remove/i18n/it.po
new file mode 100644
index 00000000000..b077ade8c76
--- /dev/null
+++ b/sale_order_line_remove/i18n/it.po
@@ -0,0 +1,50 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * sale_order_line_remove
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 18.0\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#. module: sale_order_line_remove
+#: model:ir.model.fields,field_description:sale_order_line_remove.field_res_config_settings__restrict_sale_order_line_remove
+#: model_terms:ir.ui.view,arch_db:sale_order_line_remove.res_config_settings_view_form_sale
+msgid "Allow Sale Order Line Remove"
+msgstr ""
+
+#. module: sale_order_line_remove
+#: model:ir.model.fields,help:sale_order_line_remove.field_res_config_settings__restrict_sale_order_line_remove
+msgid ""
+"Allow removing confirmed sale order lines only if they are not invoiced or "
+"delivered."
+msgstr ""
+
+#. module: sale_order_line_remove
+#: model:ir.model,name:sale_order_line_remove.model_res_config_settings
+msgid "Config Settings"
+msgstr ""
+
+#. module: sale_order_line_remove
+#: model:ir.model,name:sale_order_line_remove.model_sale_order_line
+msgid "Sales Order Line"
+msgstr ""
+
+#. module: sale_order_line_remove
+#. odoo-python
+#: code:addons/sale_order_line_remove/models/sale_order.py:0
+msgid "You can not remove an order line that has been delivered"
+msgstr ""
+
+#. module: sale_order_line_remove
+#. odoo-python
+#: code:addons/sale_order_line_remove/models/sale_order.py:0
+msgid "You can not remove an order line that has been invoiced"
+msgstr ""
From 5abe4db8f7a35256d07ef7ef58040222abd65ec2 Mon Sep 17 00:00:00 2001
From: mymage
Date: Mon, 29 Sep 2025 15:08:59 +0000
Subject: [PATCH 12/13] Translated using Weblate (Italian)
Currently translated at 100.0% (6 of 6 strings)
Translation: sale-workflow-18.0/sale-workflow-18.0-sale_order_line_remove
Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_order_line_remove/it/
---
sale_order_line_remove/i18n/it.po | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/sale_order_line_remove/i18n/it.po b/sale_order_line_remove/i18n/it.po
index b077ade8c76..fc1850d1d7d 100644
--- a/sale_order_line_remove/i18n/it.po
+++ b/sale_order_line_remove/i18n/it.po
@@ -6,19 +6,21 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0\n"
"Report-Msgid-Bugs-To: \n"
-"Last-Translator: Automatically generated\n"
+"PO-Revision-Date: 2025-09-29 17:43+0000\n"
+"Last-Translator: mymage \n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 5.10.4\n"
#. module: sale_order_line_remove
#: model:ir.model.fields,field_description:sale_order_line_remove.field_res_config_settings__restrict_sale_order_line_remove
#: model_terms:ir.ui.view,arch_db:sale_order_line_remove.res_config_settings_view_form_sale
msgid "Allow Sale Order Line Remove"
-msgstr ""
+msgstr "Consente rimozione riga ordine vendita"
#. module: sale_order_line_remove
#: model:ir.model.fields,help:sale_order_line_remove.field_res_config_settings__restrict_sale_order_line_remove
@@ -26,25 +28,27 @@ msgid ""
"Allow removing confirmed sale order lines only if they are not invoiced or "
"delivered."
msgstr ""
+"Consente la rimozione di righe ordine di vendita confermate solo se non sono "
+"fatturate o consegnate."
#. module: sale_order_line_remove
#: model:ir.model,name:sale_order_line_remove.model_res_config_settings
msgid "Config Settings"
-msgstr ""
+msgstr "Impostazioni configurazione"
#. module: sale_order_line_remove
#: model:ir.model,name:sale_order_line_remove.model_sale_order_line
msgid "Sales Order Line"
-msgstr ""
+msgstr "Riga ordine di vendita"
#. module: sale_order_line_remove
#. odoo-python
#: code:addons/sale_order_line_remove/models/sale_order.py:0
msgid "You can not remove an order line that has been delivered"
-msgstr ""
+msgstr "Non si può rimuovere una riga ordine che è stata consegnata"
#. module: sale_order_line_remove
#. odoo-python
#: code:addons/sale_order_line_remove/models/sale_order.py:0
msgid "You can not remove an order line that has been invoiced"
-msgstr ""
+msgstr "NOn si può rimuovere una riga ordine che è stata fatturata"
From f487af5d6f2650badaf06f75f4ff96ebf8080238 Mon Sep 17 00:00:00 2001
From: Bhavesh Heliconia
Date: Fri, 17 Apr 2026 13:50:34 +0530
Subject: [PATCH 13/13] [MIG] sale_order_line_remove: Migration to 19.0
---
sale_order_line_remove/README.rst | 13 ++++---
sale_order_line_remove/__manifest__.py | 2 +-
.../models/res_config_settings.py | 1 +
sale_order_line_remove/models/sale_order.py | 12 ++++--
sale_order_line_remove/readme/CONTRIBUTORS.md | 2 +
.../static/description/index.html | 10 +++--
.../tests/test_sale_order_line.py | 39 ++++++++++---------
7 files changed, 48 insertions(+), 31 deletions(-)
diff --git a/sale_order_line_remove/README.rst b/sale_order_line_remove/README.rst
index 68de293dfd0..3125f4ac67e 100644
--- a/sale_order_line_remove/README.rst
+++ b/sale_order_line_remove/README.rst
@@ -21,13 +21,13 @@ Sale Order Line Remove
: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_line_remove
+ :target: https://github.com/OCA/sale-workflow/tree/19.0/sale_order_line_remove
: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_line_remove
+ :target: https://translation.odoo-community.org/projects/sale-workflow-19-0/sale-workflow-19-0-sale_order_line_remove
: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
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=19.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -59,7 +59,7 @@ Bug Tracker
Bugs are tracked on `GitHub 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -75,6 +75,9 @@ Contributors
------------
- Joan Sisquella
+- `Heliconia Solutions Pvt. Ltd. `__
+
+ - Bhavesh Heliconia
Maintainers
-----------
@@ -89,6 +92,6 @@ 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 `_ project on GitHub.
+This module is part of the `OCA/sale-workflow `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/__manifest__.py b/sale_order_line_remove/__manifest__.py
index 30466a3cb7b..1565663f145 100644
--- a/sale_order_line_remove/__manifest__.py
+++ b/sale_order_line_remove/__manifest__.py
@@ -2,7 +2,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Sale Order Line Remove",
- "version": "18.0.1.0.0",
+ "version": "19.0.1.0.0",
"category": "Sales",
"summary": "Allows removal of sale order lines from confirmed "
"orders if not invoiced or received",
diff --git a/sale_order_line_remove/models/res_config_settings.py b/sale_order_line_remove/models/res_config_settings.py
index a0e5b101fc3..935dd5c583a 100644
--- a/sale_order_line_remove/models/res_config_settings.py
+++ b/sale_order_line_remove/models/res_config_settings.py
@@ -6,6 +6,7 @@
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
+
restrict_sale_order_line_remove = fields.Boolean(
"Allow Sale Order Line Remove",
config_parameter="sale.order.line.remove",
diff --git a/sale_order_line_remove/models/sale_order.py b/sale_order_line_remove/models/sale_order.py
index d166e7a6a5d..6c1808e0465 100644
--- a/sale_order_line_remove/models/sale_order.py
+++ b/sale_order_line_remove/models/sale_order.py
@@ -1,7 +1,7 @@
# Copyright 2023 ForgeFlow, S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
-from odoo import _, models
+from odoo import models
from odoo.exceptions import UserError
@@ -27,7 +27,9 @@ def _check_line_unlink(self):
)
if invoiced_lines:
raise UserError(
- _("You can not remove an order line that has been invoiced")
+ self.env._(
+ "You can not remove an order line that has been invoiced"
+ )
)
delivered_lines = self.sudo().filtered(
lambda line: line.state in ("sale", "done")
@@ -35,7 +37,9 @@ def _check_line_unlink(self):
)
if delivered_lines:
raise UserError(
- _("You can not remove an order line that has been delivered")
+ self.env._(
+ "You can not remove an order line that has been delivered"
+ )
)
return non_removable_lines - removable_lines
@@ -55,6 +59,6 @@ def unlink(self):
)._action_cancel()
line.move_ids.filtered(lambda move: move.state != "done").unlink()
for picking in related_pickings:
- if not picking.move_ids_without_package:
+ if not picking.move_ids:
picking.unlink()
return super().unlink()
diff --git a/sale_order_line_remove/readme/CONTRIBUTORS.md b/sale_order_line_remove/readme/CONTRIBUTORS.md
index 2fa75245648..28afb87e378 100644
--- a/sale_order_line_remove/readme/CONTRIBUTORS.md
+++ b/sale_order_line_remove/readme/CONTRIBUTORS.md
@@ -1 +1,3 @@
- Joan Sisquella \
+- [Heliconia Solutions Pvt. Ltd.](https://www.heliconia.io)
+ - Bhavesh Heliconia
diff --git a/sale_order_line_remove/static/description/index.html b/sale_order_line_remove/static/description/index.html
index 007ba5b96b0..029ed4a9f96 100644
--- a/sale_order_line_remove/static/description/index.html
+++ b/sale_order_line_remove/static/description/index.html
@@ -374,7 +374,7 @@ Sale Order Line Remove
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:92c4d50bdbf5dda19487295fa744ed8f013ec29079a78d1ef8eb9bf5d22692d9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

This module allows the removal of sale order lines even after the order
has been confirmed, under certain conditions. Specifically, a sale order
line can be removed if:
@@ -409,7 +409,7 @@
Bugs are tracked on GitHub 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.
+feedback.
Do not contact contributors directly about support or help with technical issues.
@@ -435,7 +439,7 @@
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 project on GitHub.
+
This module is part of the OCA/sale-workflow project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/sale_order_line_remove/tests/test_sale_order_line.py b/sale_order_line_remove/tests/test_sale_order_line.py
index 1936822fcff..d5ff36cef09 100644
--- a/sale_order_line_remove/tests/test_sale_order_line.py
+++ b/sale_order_line_remove/tests/test_sale_order_line.py
@@ -2,16 +2,19 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import UserError
-from odoo.tests import TransactionCase
+from odoo.addons.base.tests.common import BaseCommon
-class TestSaleOrderLine(TransactionCase):
- def setUp(cls):
- super().setUp()
+
+class TestSaleOrderLine(BaseCommon):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
cls.SaleOrder = cls.env["sale.order"]
cls.SaleOrderLine = cls.env["sale.order.line"]
- cls.partner = cls.env.ref("base.res_partner_2")
- cls.product = cls.env.ref("product.product_product_4")
+ cls.product = cls.env["product.product"].create(
+ {"name": "Test Product", "type": "consu"}
+ )
cls.uom = cls.env.ref("uom.product_uom_unit")
cls.config_param = cls.env["ir.config_parameter"].sudo()
cls.config_param.set_param("sale.order.line.remove", True)
@@ -24,7 +27,7 @@ def test_check_line_unlink(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
non_removable_lines = sale_order_line._check_line_unlink()
@@ -38,7 +41,7 @@ def test_unlink(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
sale_order_line.unlink()
@@ -52,13 +55,13 @@ def test_check_line_not_unlinkable(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
picking = sale_order.picking_ids[0]
picking.action_confirm()
picking.action_assign()
- for move in picking.move_ids_without_package:
+ for move in picking.move_ids:
move.quantity = move.product_uom_qty
picking.button_validate()
with self.assertRaises(UserError):
@@ -72,13 +75,13 @@ def test_not_unlinkable_after_picking(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
picking = sale_order.picking_ids[0]
picking.action_confirm()
picking.action_assign()
- for move in picking.move_ids_without_package:
+ for move in picking.move_ids:
move.quantity = move.product_uom_qty
picking.button_validate()
with self.assertRaises(UserError):
@@ -92,13 +95,13 @@ def test_check_line_unlink_delivered(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
picking = sale_order.picking_ids[0]
picking.action_confirm()
picking.action_assign()
- for move in picking.move_ids_without_package:
+ for move in picking.move_ids:
move.quantity = move.product_uom_qty
picking.button_validate()
with self.assertRaises(UserError):
@@ -112,13 +115,13 @@ def test_check_line_unlink_invoiced(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
picking = sale_order.picking_ids[0]
picking.action_confirm()
picking.action_assign()
- for move in picking.move_ids_without_package:
+ for move in picking.move_ids:
move.quantity = move.product_uom_qty
picking.button_validate()
sale_order._create_invoices()
@@ -133,7 +136,7 @@ def test_unlink_empty_picking(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
sale_order_line2 = self.SaleOrderLine.create(
@@ -141,7 +144,7 @@ def test_unlink_empty_picking(self):
"order_id": sale_order.id,
"product_id": self.product.id,
"product_uom_qty": 1,
- "product_uom": self.uom.id,
+ "product_uom_id": self.uom.id,
}
)
picking = sale_order.picking_ids[0]