From dcddd6d5b987473f60e7ea7b8dc89d9132052a84 Mon Sep 17 00:00:00 2001
From: Mourad
Date: Thu, 18 Jun 2020 15:55:44 +0200
Subject: [PATCH 01/19] [IMP] rename module sale_order_min_qty to
sale_restricted_qty
---
sale_restricted_qty/README.rst | 89 ++++
sale_restricted_qty/__init__.py | 2 +
sale_restricted_qty/__manifest__.py | 20 +
sale_restricted_qty/hooks.py | 19 +
.../i18n/sale_order_min_qty.pot | 82 ++++
sale_restricted_qty/models/__init__.py | 5 +
.../models/product_category.py | 48 ++
sale_restricted_qty/models/product_product.py | 51 ++
.../models/product_restricted_qty_mixin.py | 105 +++++
.../models/product_template.py | 49 ++
sale_restricted_qty/models/sale.py | 147 ++++++
sale_restricted_qty/readme/CONTRIBUTORS.rst | 1 +
sale_restricted_qty/readme/DESCRIPTION.rst | 11 +
sale_restricted_qty/readme/USAGE.rst | 7 +
.../static/description/icon.png | Bin 0 -> 9455 bytes
.../static/description/index.html | 443 ++++++++++++++++++
sale_restricted_qty/tests/__init__.py | 1 +
sale_restricted_qty/tests/test_sale.py | 172 +++++++
.../views/product_category_views.xml | 23 +
.../views/product_product_views.xml | 25 +
.../views/product_template_views.xml | 24 +
sale_restricted_qty/views/sale_views.xml | 41 ++
22 files changed, 1365 insertions(+)
create mode 100644 sale_restricted_qty/README.rst
create mode 100644 sale_restricted_qty/__init__.py
create mode 100644 sale_restricted_qty/__manifest__.py
create mode 100644 sale_restricted_qty/hooks.py
create mode 100644 sale_restricted_qty/i18n/sale_order_min_qty.pot
create mode 100644 sale_restricted_qty/models/__init__.py
create mode 100644 sale_restricted_qty/models/product_category.py
create mode 100644 sale_restricted_qty/models/product_product.py
create mode 100644 sale_restricted_qty/models/product_restricted_qty_mixin.py
create mode 100644 sale_restricted_qty/models/product_template.py
create mode 100644 sale_restricted_qty/models/sale.py
create mode 100644 sale_restricted_qty/readme/CONTRIBUTORS.rst
create mode 100644 sale_restricted_qty/readme/DESCRIPTION.rst
create mode 100644 sale_restricted_qty/readme/USAGE.rst
create mode 100644 sale_restricted_qty/static/description/icon.png
create mode 100644 sale_restricted_qty/static/description/index.html
create mode 100644 sale_restricted_qty/tests/__init__.py
create mode 100644 sale_restricted_qty/tests/test_sale.py
create mode 100644 sale_restricted_qty/views/product_category_views.xml
create mode 100644 sale_restricted_qty/views/product_product_views.xml
create mode 100644 sale_restricted_qty/views/product_template_views.xml
create mode 100644 sale_restricted_qty/views/sale_views.xml
diff --git a/sale_restricted_qty/README.rst b/sale_restricted_qty/README.rst
new file mode 100644
index 00000000000..7949177ad2d
--- /dev/null
+++ b/sale_restricted_qty/README.rst
@@ -0,0 +1,89 @@
+=======================
+Sale order min quantity
+=======================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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/12.0/sale_order_min_qty
+ :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-12-0/sale-workflow-12-0-sale_order_min_qty
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/167/12.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module allows to define minimum order quantity on product.
+Minimum order quantity is either required or recommanded.
+If you check "Force min Qty" on product the min qty do not block sale
+order.
+This can be used if the available stock is less then the min qty
+or if you want to sell sample products.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+#. Go to **Sales > Products > Product Variants** and Set "Min Sale Qty".
+ If you dont want block product sale if qty is less the "Min Sale Qty",
+ chek "Force Min Qty"
+#. Create a sale order and add a sale order line with this product and check
+ if Qty is less then "Min Qty" A message in displayed "Min Qty Required"
+ Or "Min Qty Recommanded". Check if Min qty is required you can't
+ validate order.
+
+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 smashing it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* Akretion
+
+Contributors
+~~~~~~~~~~~~
+
+* Mourad EL HADJ MIMOUN
+
+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_restricted_qty/__init__.py b/sale_restricted_qty/__init__.py
new file mode 100644
index 00000000000..8e0862219aa
--- /dev/null
+++ b/sale_restricted_qty/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from .hooks import rename_module
diff --git a/sale_restricted_qty/__manifest__.py b/sale_restricted_qty/__manifest__.py
new file mode 100644
index 00000000000..a686c872f4d
--- /dev/null
+++ b/sale_restricted_qty/__manifest__.py
@@ -0,0 +1,20 @@
+# Copyright 2019 Akretion ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ "name": "Sale order min quantity",
+ "version": "12.0.1.0.0",
+ "category": "Sales Management",
+ "author": "Akretion, " "Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/sale-workflow/",
+ "license": "AGPL-3",
+ "depends": ["sale_management"],
+ "data": [
+ "views/product_category_views.xml",
+ "views/product_template_views.xml",
+ "views/product_product_views.xml",
+ "views/sale_views.xml",
+ ],
+ "pre_init_hook": "rename_module",
+ "installable": True,
+}
diff --git a/sale_restricted_qty/hooks.py b/sale_restricted_qty/hooks.py
new file mode 100644
index 00000000000..2a0f812af7d
--- /dev/null
+++ b/sale_restricted_qty/hooks.py
@@ -0,0 +1,19 @@
+# Copyright 2019 Akretion
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import logging
+
+_logger = logging.getLogger(__name__)
+
+try:
+ from openupgradelib import openupgrade
+except (ImportError, IOError) as err:
+ _logger.debug(err)
+
+
+def rename_module(cr):
+ openupgrade.update_module_names(
+ cr,
+ [("sale_order_min_qty", "sale_restricted_qty")],
+ merge_modules=True,
+ )
diff --git a/sale_restricted_qty/i18n/sale_order_min_qty.pot b/sale_restricted_qty/i18n/sale_order_min_qty.pot
new file mode 100644
index 00000000000..1400edffa5d
--- /dev/null
+++ b/sale_restricted_qty/i18n/sale_order_min_qty.pot
@@ -0,0 +1,82 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * sale_order_min_qty
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.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_min_qty
+#: code:addons/sale_order_min_qty/models/sale.py:51
+#, python-format
+msgid "\n"
+"* If you want sell quantity less than Min Quantity,Check \"force min quatity\" on product"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: code:addons/sale_order_min_qty/models/sale.py:48
+#, python-format
+msgid "Check minimum order quantity for this products: * \n"
+""
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model.fields,field_description:sale_order_min_qty.field_product_product__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_order_min_qty.field_sale_order_line__force_sale_min_qty
+msgid "Force Min Qty"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model.fields,help:sale_order_min_qty.field_product_product__force_sale_min_qty
+#: model:ir.model.fields,help:sale_order_min_qty.field_sale_order_line__force_sale_min_qty
+msgid "If force min qty is checked, the min quantity is only indicative value."
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model.fields,field_description:sale_order_min_qty.field_sale_order_line__sale_min_qty
+msgid "Min Qty"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model_terms:ir.ui.view,arch_db:sale_order_min_qty.view_order_form
+msgid "Min Quantity Recommended"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model_terms:ir.ui.view,arch_db:sale_order_min_qty.view_order_form
+msgid "Min Quantity Required"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model.fields,field_description:sale_order_min_qty.field_product_product__sale_min_qty
+msgid "Min Sale Qty"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model,name:sale_order_min_qty.model_product_product
+msgid "Product"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: code:addons/sale_order_min_qty/models/sale.py:43
+#, python-format
+msgid "Product \"%s\": Min Quantity %s."
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model.fields,field_description:sale_order_min_qty.field_sale_order_line__is_qty_less_min_qty
+msgid "Qty < Min Qty"
+msgstr ""
+
+#. module: sale_order_min_qty
+#: model:ir.model,name:sale_order_min_qty.model_sale_order_line
+msgid "Sales Order Line"
+msgstr ""
+
diff --git a/sale_restricted_qty/models/__init__.py b/sale_restricted_qty/models/__init__.py
new file mode 100644
index 00000000000..a1cc9138a71
--- /dev/null
+++ b/sale_restricted_qty/models/__init__.py
@@ -0,0 +1,5 @@
+from . import product_restricted_qty_mixin
+from . import product_category
+from . import product_template
+from . import product_product
+from . import sale
diff --git a/sale_restricted_qty/models/product_category.py b/sale_restricted_qty/models/product_category.py
new file mode 100644
index 00000000000..bb98cc5d44a
--- /dev/null
+++ b/sale_restricted_qty/models/product_category.py
@@ -0,0 +1,48 @@
+# Copyright 2019 Akretion
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, models
+
+
+class ProductCategory(models.Model):
+ _name = "product.category"
+ _inherit = ["product.category", "product.restricted.qty.mixin"]
+
+ def _get_sale_restricted_qty(self):
+ res = super()._get_sale_restricted_qty()
+ force_sale_min_qty = False
+ if self.manual_force_sale_min_qty == 'force':
+ force_sale_min_qty = True
+ if self.manual_force_sale_min_qty == 'not_force':
+ force_sale_min_qty = False
+ if not self.manual_force_sale_min_qty:
+ force_sale_min_qty = self.parent_id.force_sale_min_qty
+ force_sale_max_qty = False
+ if self.manual_force_sale_max_qty == 'force':
+ force_sale_max_qty = True
+ if self.manual_force_sale_max_qty == 'not_force':
+ force_sale_max_qty = False
+ if not self.manual_force_sale_max_qty:
+ force_sale_max_qty = self.parent_id.force_sale_max_qty
+ res.update(
+ {
+ "sale_min_qty": self.manual_sale_min_qty or self.parent_id.sale_min_qty,
+ "force_sale_min_qty": force_sale_min_qty,
+ "sale_max_qty": self.manual_sale_max_qty or self.parent_id.sale_max_qty,
+ "force_sale_max_qty": force_sale_max_qty,
+ "sale_multiple_qty": self.manual_sale_multiple_qty
+ or self.parent_id.sale_multiple_qty,
+ }
+ )
+ return res
+
+ @api.depends(
+ "parent_id.force_sale_min_qty",
+ "parent_id.sale_min_qty",
+ "parent_id.force_sale_max_qty",
+ "parent_id.sale_max_qty",
+ "parent_id.sale_multiple_qty",
+ )
+ def _compute_sale_restricted_qty(self):
+ for rec in self:
+ rec.update(rec._get_sale_restricted_qty())
diff --git a/sale_restricted_qty/models/product_product.py b/sale_restricted_qty/models/product_product.py
new file mode 100644
index 00000000000..dc43340bddc
--- /dev/null
+++ b/sale_restricted_qty/models/product_product.py
@@ -0,0 +1,51 @@
+# Copyright 2019 Akretion
+# @author Mourad EL HADJ MIMOUNE
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, models
+
+
+class ProductProduct(models.Model):
+ _name = "product.product"
+ _inherit = ["product.product", "product.restricted.qty.mixin"]
+
+ def _get_sale_restricted_qty(self):
+ res = super()._get_sale_restricted_qty()
+ force_sale_min_qty = False
+ if self.manual_force_sale_min_qty == 'force':
+ force_sale_min_qty = True
+ if self.manual_force_sale_min_qty == 'not_force':
+ force_sale_min_qty = False
+ if not self.manual_force_sale_min_qty:
+ force_sale_min_qty = self.product_tmpl_id.force_sale_min_qty
+ force_sale_max_qty = False
+ if self.manual_force_sale_max_qty == 'force':
+ force_sale_max_qty = True
+ if self.manual_force_sale_max_qty == 'not_force':
+ force_sale_max_qty = False
+ if not self.manual_force_sale_max_qty:
+ force_sale_max_qty = self.product_tmpl_id.force_sale_max_qty
+ res.update(
+ {
+ "sale_min_qty": self.manual_sale_min_qty
+ or self.product_tmpl_id.sale_min_qty,
+ "force_sale_min_qty": force_sale_min_qty,
+ "sale_max_qty": self.manual_sale_max_qty
+ or self.product_tmpl_id.sale_max_qty,
+ "force_sale_max_qty": force_sale_max_qty,
+ "sale_multiple_qty": self.manual_sale_multiple_qty
+ or self.product_tmpl_id.sale_multiple_qty,
+ }
+ )
+ return res
+
+ @api.depends(
+ "product_tmpl_id.force_sale_min_qty",
+ "product_tmpl_id.sale_min_qty",
+ "product_tmpl_id.force_sale_max_qty",
+ "product_tmpl_id.sale_max_qty",
+ "product_tmpl_id.sale_multiple_qty",
+ )
+ def _compute_sale_restricted_qty(self):
+ for rec in self:
+ rec.update(rec._get_sale_restricted_qty())
diff --git a/sale_restricted_qty/models/product_restricted_qty_mixin.py b/sale_restricted_qty/models/product_restricted_qty_mixin.py
new file mode 100644
index 00000000000..105fc4c41eb
--- /dev/null
+++ b/sale_restricted_qty/models/product_restricted_qty_mixin.py
@@ -0,0 +1,105 @@
+# Copyright 2019 Akretion
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+from odoo.addons import decimal_precision as dp
+
+
+class ProductMinMultipleMixin(models.AbstractModel):
+ _name = "product.restricted.qty.mixin"
+
+ sale_multiple_qty = fields.Float(
+ compute="_compute_sale_restricted_qty",
+ store=True,
+ help="Define sale multiple qty"
+ " 'If not set', Odoo will"
+ " use the value defined in the parent object."
+ "Hierarchy is in this order :"
+ "Product/product Template/product category/parent categoroies ",
+ digits=dp.get_precision("Product Unit of Measure"),
+ )
+ manual_sale_multiple_qty = fields.Float(
+ string="Multiple Sale Qty", digits=dp.get_precision("Product Unit of Measure")
+ )
+ sale_min_qty = fields.Float(
+ compute="_compute_sale_restricted_qty",
+ store=True,
+ help="Define sale min qty"
+ " 'If not set, Odoo will"
+ " use the value defined in the parent object."
+ "Hierarchy is in this order :"
+ "Product/product Template/product category/parent categoroies ",
+ digits=dp.get_precision("Product Unit of Measure"),
+ )
+ manual_sale_min_qty = fields.Float(
+ string="Min Sale Qty", digits=dp.get_precision("Product Unit of Measure")
+ )
+ force_sale_min_qty = fields.Boolean(
+ compute="_compute_sale_restricted_qty",
+ string="Force Min Qty",
+ store=True,
+ help="Define if user can force sale min qty"
+ " 'If not set', Odoo will"
+ " use the value defined in the parent object."
+ "Hierarchy is in this order :"
+ "Product/product Template/product category/parent categoroies ",
+ )
+ manual_force_sale_min_qty = fields.Selection([
+ ('force', 'Yes'),
+ ('not_force', 'No'),
+ ],
+ string="Manual Force Min Qty",
+ help="If force min qty is checked, the min quantity "
+ "is only indicative value."
+ "If is not test we check parent value",
+ )
+ sale_max_qty = fields.Float(
+ compute="_compute_sale_restricted_qty",
+ store=True,
+ help="Define sale max qty"
+ " 'If not set, Odoo will"
+ " use the value defined in the parent object."
+ "Hierarchy is in this order :"
+ "Product/product Template/product category/parent categoroies ",
+ digits=dp.get_precision("Product Unit of Measure"),
+ )
+ manual_sale_max_qty = fields.Float(
+ string="Max Sale Qty", digits=dp.get_precision("Product Unit of Measure")
+ )
+ force_sale_max_qty = fields.Boolean(
+ compute="_compute_sale_restricted_qty",
+ string="Force Max Qty",
+ store=True,
+ help="Define if user can force sale max qty"
+ " 'If not set', Odoo will"
+ " use the value defined in the parent object."
+ "Hierarchy is in this order :"
+ "Product/product Template/product category/parent categoroies ",
+ )
+ manual_force_sale_max_qty = fields.Selection([
+ ('force', 'Yes'),
+ ('not_force', 'No'),
+ ],
+ string="Manual Force Max Qty",
+ help="If force max qty is checked, the max quantity "
+ "is only indicative value."
+ "If is not test we check parent value",
+ )
+
+ def _get_sale_restricted_qty(self):
+ self.ensure_one()
+ res = {
+ "sale_min_qty": self.manual_sale_min_qty,
+ "force_sale_min_qty": self.manual_force_sale_min_qty == 'force',
+ "sale_max_qty": self.manual_sale_max_qty,
+ "force_sale_max_qty": self.manual_force_sale_max_qty == 'force',
+ "sale_multiple_qty": self.manual_sale_multiple_qty,
+ }
+ return res
+
+ @api.depends("manual_force_sale_min_qty", "manual_sale_min_qty",
+ "manual_force_sale_max_qty", "manual_sale_max_qty",
+ "manual_sale_multiple_qty")
+ def _compute_sale_restricted_qty(self):
+ for rec in self:
+ rec.update(rec._get_sale_restricted_qty())
diff --git a/sale_restricted_qty/models/product_template.py b/sale_restricted_qty/models/product_template.py
new file mode 100644
index 00000000000..6de94b4342b
--- /dev/null
+++ b/sale_restricted_qty/models/product_template.py
@@ -0,0 +1,49 @@
+# Copyright 2019 Akretion
+# @author Mourad EL HADJ MIMOUNE
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, models
+
+
+class ProductTemplate(models.Model):
+ _name = "product.template"
+ _inherit = ["product.template", "product.restricted.qty.mixin"]
+
+ def _get_sale_restricted_qty(self):
+ res = super()._get_sale_restricted_qty()
+ force_sale_min_qty = False
+ if self.manual_force_sale_min_qty == 'force':
+ force_sale_min_qty = True
+ if self.manual_force_sale_min_qty == 'not_force':
+ force_sale_min_qty = False
+ if not self.manual_force_sale_min_qty:
+ force_sale_min_qty = self.categ_id.force_sale_min_qty
+ force_sale_max_qty = False
+ if self.manual_force_sale_max_qty == 'force':
+ force_sale_max_qty = True
+ if self.manual_force_sale_max_qty == 'not_force':
+ force_sale_max_qty = False
+ if not self.manual_force_sale_max_qty:
+ force_sale_max_qty = self.categ_id.force_sale_max_qty
+ res.update(
+ {
+ "sale_min_qty": self.manual_sale_min_qty or self.categ_id.sale_min_qty,
+ "force_sale_min_qty": force_sale_min_qty,
+ "sale_max_qty": self.manual_sale_max_qty or self.categ_id.sale_max_qty,
+ "force_sale_max_qty": force_sale_max_qty,
+ "sale_multiple_qty": self.manual_sale_multiple_qty
+ or self.categ_id.sale_multiple_qty,
+ }
+ )
+ return res
+
+ @api.depends(
+ "categ_id.force_sale_min_qty",
+ "categ_id.sale_min_qty",
+ "categ_id.force_sale_max_qty",
+ "categ_id.sale_max_qty",
+ "categ_id.sale_multiple_qty",
+ )
+ def _compute_sale_restricted_qty(self):
+ for rec in self:
+ rec.update(rec._get_sale_restricted_qty())
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
new file mode 100644
index 00000000000..86bc1f73595
--- /dev/null
+++ b/sale_restricted_qty/models/sale.py
@@ -0,0 +1,147 @@
+# Copyright 2019 Akretion
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import _, api, fields, models
+from odoo.addons import decimal_precision as dp
+from odoo.exceptions import ValidationError
+from odoo.tools import float_compare
+
+
+class SaleOrderLine(models.Model):
+ _inherit = "sale.order.line"
+
+ sale_min_qty = fields.Float(
+ string="Min Qty",
+ related="product_id.sale_min_qty",
+ readonly=True,
+ store=True,
+ digits=dp.get_precision("Product Unit of Measure"),
+ )
+ force_sale_min_qty = fields.Boolean(
+ related="product_id.force_sale_min_qty", readonly=True, store=True
+ )
+ is_qty_less_min_qty = fields.Boolean(
+ string="Qty < Min Qty", compute="_compute_is_qty_less_min_qty"
+ )
+
+ sale_max_qty = fields.Float(
+ string="Max Qty",
+ related="product_id.sale_max_qty",
+ readonly=True,
+ store=True,
+ digits=dp.get_precision("Product Unit of Measure"),
+ )
+ force_sale_max_qty = fields.Boolean(
+ related="product_id.force_sale_max_qty", readonly=True, store=True
+ )
+ is_qty_bigger_max_qty = fields.Boolean(
+ string="Qty > max Qty", compute="_compute_is_qty_bigger_max_qty"
+ )
+ sale_multiple_qty = fields.Float(
+ string="Multiple Qty",
+ related="product_id.sale_multiple_qty",
+ readonly=True,
+ store=True,
+ digits=dp.get_precision("Product Unit of Measure"),
+ )
+ is_qty_not_multiple_qty = fields.Boolean(
+ string="Not Multiple Qty", compute="_compute_is_qty_not_multiple_qty"
+ )
+
+ @api.constrains("product_uom_qty")
+ def check_constraint_restricted_qty(self):
+ msg = ""
+ invaild_min_lines = []
+ line_to_test = self.filtered(
+ lambda l: not l.product_id.force_sale_min_qty and l.is_qty_less_min_qty
+ )
+ for line in line_to_test:
+ invaild_min_lines.append(
+ _('Product "%s": Min Quantity %s.')
+ % (line.product_id.name, line.product_id.sale_min_qty)
+ )
+
+ if invaild_min_lines:
+ msg += _(
+ "Check minimum order quantity for this products: * \n"
+ ) + "\n ".join(invaild_min_lines)
+ msg += _(
+ "\n* If you want sell quantity less than Min Quantity"
+ ',Check "force min quatity" on product'
+ )
+ invaild_max_lines = []
+ line_to_test = self.filtered(
+ lambda l: not l.product_id.force_sale_max_qty and l.is_qty_bigger_max_qty
+ )
+ for line in line_to_test:
+ invaild_max_lines.append(
+ _('Product "%s": max Quantity %s.')
+ % (line.product_id.name, line.product_id.sale_max_qty)
+ )
+
+ if invaild_max_lines:
+ msg += _(
+ "Check maximum order quantity for this products: * \n"
+ ) + "\n ".join(invaild_max_lines)
+ msg += _(
+ "\n* If you want sell quantity bigger than max Quantity"
+ ',Check "force max quatity" on product'
+ )
+ invaild_multiple_lines = []
+ line_to_test = self.filtered(lambda l: l.is_qty_not_multiple_qty)
+ for line in line_to_test:
+ invaild_multiple_lines.append(
+ _('Product "%s": multiple Quantity %s.')
+ % (line.product_id.name, line.product_id.sale_multiple_qty)
+ )
+
+ if invaild_multiple_lines:
+ msg += _(
+ "Check multiple order quantity for this products: * \n"
+ ) + "\n ".join(invaild_multiple_lines)
+ msg += _(
+ "\n* If you want sell quantity not multiple Quantity"
+ ",Set multiple quantity to 0 on product or product template"
+ " or product category"
+ )
+
+ if msg:
+ raise ValidationError(msg)
+
+ @api.depends("product_uom_qty", "sale_min_qty")
+ def _compute_is_qty_less_min_qty(self):
+ for line in self:
+ rounding = line.product_uom.rounding
+ product_qty = line.product_uom._compute_quantity(
+ line.product_uom_qty, line.product_id.uom_id
+ )
+ line.is_qty_less_min_qty = (
+ float_compare(
+ product_qty, line.sale_min_qty, precision_rounding=rounding
+ )
+ < 0
+ )
+
+ @api.depends("product_uom_qty", "sale_max_qty")
+ def _compute_is_qty_bigger_max_qty(self):
+ for line in self:
+ rounding = line.product_uom.rounding
+ product_qty = line.product_uom._compute_quantity(
+ line.product_uom_qty, line.product_id.uom_id
+ )
+ line.is_qty_bigger_max_qty = (
+ float_compare(
+ product_qty, line.sale_max_qty, precision_rounding=rounding
+ )
+ < 0
+ )
+
+ @api.depends("product_uom_qty", "sale_multiple_qty")
+ def _compute_is_qty_not_multiple_qty(self):
+ for line in self:
+ product_qty = line.product_uom._compute_quantity(
+ line.product_uom_qty, line.product_id.uom_id
+ )
+ line.is_qty_not_multiple_qty = (
+ line.sale_multiple_qty > 0 and product_qty % line.sale_multiple_qty != 0
+ )
diff --git a/sale_restricted_qty/readme/CONTRIBUTORS.rst b/sale_restricted_qty/readme/CONTRIBUTORS.rst
new file mode 100644
index 00000000000..e803c0a3fba
--- /dev/null
+++ b/sale_restricted_qty/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Mourad EL HADJ MIMOUN
diff --git a/sale_restricted_qty/readme/DESCRIPTION.rst b/sale_restricted_qty/readme/DESCRIPTION.rst
new file mode 100644
index 00000000000..f1fd4fa94fa
--- /dev/null
+++ b/sale_restricted_qty/readme/DESCRIPTION.rst
@@ -0,0 +1,11 @@
+This module allows to define minimum and multiple order quantity on product.
+Minimum order quantity is either required or recommanded.
+If you check "Force min Qty" on product the min qty do not block sale
+order.
+This can be used if the available stock is less then the min qty
+or if you want to sell sample products.
+
+Setings are defined on product, if values are not set we use settings from
+product template.
+If settings are not set on product template we use settings from
+product category otherwise parent category.
diff --git a/sale_restricted_qty/readme/USAGE.rst b/sale_restricted_qty/readme/USAGE.rst
new file mode 100644
index 00000000000..5623152596f
--- /dev/null
+++ b/sale_restricted_qty/readme/USAGE.rst
@@ -0,0 +1,7 @@
+#. Go to **Sales > Products > Product Variants** and Set "Min Sale Qty".
+ If you dont want block product sale if qty is less the "Min Sale Qty",
+ chek "Force Min Qty"
+#. Create a sale order and add a sale order line with this product and check
+ if Qty is less then "Min Qty" A message in displayed "Min Qty Required"
+ Or "Min Qty Recommanded". Check if Min qty is required you can't
+ validate order.
diff --git a/sale_restricted_qty/static/description/icon.png b/sale_restricted_qty/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_restricted_qty/static/description/index.html b/sale_restricted_qty/static/description/index.html
new file mode 100644
index 00000000000..3bada92d5b5
--- /dev/null
+++ b/sale_restricted_qty/static/description/index.html
@@ -0,0 +1,443 @@
+
+
+
+
+
+
+Sale order min quantity
+
+
+
+
+
Sale order min quantity
+
+
+
+
This module allows to define minimum order quantity on product.
+Minimum order quantity is either required or recommanded.
+If you check “Force min Qty” on product the min qty do not block sale
+order.
+This can be used if the available stock is less then the min qty
+or if you want to sell sample products.
Go to Sales > Products > Product Variants and Set “Min Sale Qty”.
+
If you dont want block product sale if qty is less the “Min Sale Qty”,
+chek “Force Min Qty”
+
+
+
+
Create a sale order and add a sale order line with this product and check
+
if Qty is less then “Min Qty” A message in displayed “Min Qty Required”
+Or “Min Qty Recommanded”. Check if Min qty is required you can’t
+validate order.
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 smashing it by providing a detailed and welcomed
+feedback.
+
Do not contact contributors directly about support or help with technical issues.
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.
+
+
diff --git a/sale_restricted_qty/tests/__init__.py b/sale_restricted_qty/tests/__init__.py
new file mode 100644
index 00000000000..67709762e39
--- /dev/null
+++ b/sale_restricted_qty/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_sale
diff --git a/sale_restricted_qty/tests/test_sale.py b/sale_restricted_qty/tests/test_sale.py
new file mode 100644
index 00000000000..167a49c5258
--- /dev/null
+++ b/sale_restricted_qty/tests/test_sale.py
@@ -0,0 +1,172 @@
+# Copyright 2019 Akretion
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import odoo.tests.common as common
+from odoo.exceptions import ValidationError
+
+
+@common.at_install(False)
+@common.post_install(True)
+class TestSaleOrderLineMinQty(common.TransactionCase):
+ def setUp(self):
+ super(TestSaleOrderLineMinQty, self).setUp()
+
+ # Create models
+ self.sale_order_model = self.env["sale.order"]
+ self.sale_order_line_model = self.env["sale.order.line"]
+ self.partner_model = self.env["res.partner"]
+ self.product_categ_model = self.env["product.category"]
+ self.product_model = self.env["product.product"]
+ self.sale_order = self.sale_order_model
+ # Create partner and product
+ self.partner = self.partner_model.create({"name": "Test partner"})
+ self.categ_parent = self.product_categ_model.create(
+ {
+ "name": "Test Parent categ",
+ "manual_sale_min_qty": 10.0,
+ "manual_force_sale_min_qty": False,
+ "manual_sale_max_qty": 100.0,
+ "manual_force_sale_max_qty": False,
+ "manual_sale_multiple_qty": 5.0,
+ }
+ )
+
+ self.categ = self.product_categ_model.create(
+ {
+ "name": "Test categ",
+ "parent_id": self.categ_parent.id,
+ "manual_force_sale_min_qty": False,
+ "manual_force_sale_max_qty": False,
+ }
+ )
+
+ self.product = self.product_model.create({
+ "name": "Test product",
+ "force_sale_min_qty": False,
+ "force_sale_max_qty": False,
+ })
+
+ def test_check_sale_order_min_qty_required(self):
+ line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
+ self.product.manual_sale_min_qty = 10
+ # Create sale order line with Qty less than min Qty
+ with self.assertRaises(ValidationError):
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ line_values["product_uom_qty"] = 12.0
+ # Create sale order line with Qty great then min Qty
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ self.assertFalse(self.sale_order.order_line.is_qty_less_min_qty)
+
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 12.0)
+
+ def test_check_sale_order_min_qty_recommended(self):
+ line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
+ self.product.manual_sale_min_qty = 10
+ # Set Force min Qty to true
+ self.product.manual_force_sale_min_qty = 'force'
+
+ # Create sale order line with Qty less than min Qty
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ self.assertTrue(self.sale_order.order_line.is_qty_less_min_qty)
+
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 5.0)
+
+ def test_check_sale_order_max_qty_required(self):
+ line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
+ self.product.manual_sale_max_qty = 10
+ # Create sale order line with Qty bigger than max Qty
+ with self.assertRaises(ValidationError):
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ line_values["product_uom_qty"] = 12.0
+ # Create sale order line with Qty great then max Qty
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ self.assertFalse(self.sale_order.order_line.is_qty_bigger_max_qty)
+
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 12.0)
+
+ def test_check_sale_order_max_qty_recommended(self):
+ line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
+ self.product.manual_sale_max_qty = 10
+ # Set Force max Qty to true
+ self.product.manual_force_sale_max_qty = 'force'
+
+ # Create sale order line with Qty bigger than max Qty
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ self.assertTrue(self.sale_order.order_line.is_qty_bigger_max_qty)
+
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 5.0)
+
+ def test_check_sale_order_multiple_qty_required(self):
+ line_values = {"product_id": self.product.id, "product_uom_qty": 15.0}
+ self.product.manual_sale_min_qty = 10
+ self.product.manual_sale_multiple_qty = 10
+ # Create sale order line with Qty not multiple Qty
+ with self.assertRaises(ValidationError):
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ line_values["product_uom_qty"] = 20
+ # Create sale order line with Qty multiple Qty
+ self.sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ )
+ self.assertFalse(self.sale_order.order_line.is_qty_not_multiple_qty)
+
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 20)
+
+ def test_check_restricted_qty_category_hierarchy(self):
+ # Check Restricted Qty from parent category
+ self.product.categ_id = self.categ
+ self.assertEqual(self.product.manual_sale_min_qty, 0)
+ self.assertEqual(self.product.manual_sale_multiple_qty, 0)
+ self.product.categ_id.parent_id._compute_sale_restricted_qty()
+
+ self.assertEqual(self.product.sale_min_qty, 10)
+ self.assertEqual(self.product.sale_max_qty, 100)
+ self.assertEqual(self.product.sale_multiple_qty, 5)
+ self.categ_parent.manual_force_sale_min_qty = 'force'
+ self.assertEqual(self.product.manual_force_sale_min_qty, False)
+ self.assertEqual(self.product.force_sale_min_qty, True)
+ # Check Restricted Qty from category
+ self.categ.manual_sale_min_qty = 15
+ self.categ.manual_sale_multiple_qty = 10
+ self.categ.manual_force_sale_min_qty = 'not_force'
+ self.assertEqual(self.product.sale_min_qty, 15)
+ self.assertEqual(self.product.sale_multiple_qty, 10)
+ self.assertEqual(self.product.force_sale_min_qty, False)
+ self.categ.manual_sale_max_qty = 200
+ self.categ.manual_force_sale_max_qty = 'not_force'
+ self.assertEqual(self.product.sale_max_qty, 200)
+ self.assertEqual(self.product.force_sale_max_qty, False)
+
+ def test_check_restricted_qty_tmpl_hierarchy(self):
+ # Check Restricted from product template
+ self.product.product_tmpl_id.manual_sale_min_qty = 20
+ self.product.product_tmpl_id.manual_sale_max_qty = 250
+ self.product.product_tmpl_id.manual_sale_multiple_qty = 30
+ self.product.product_tmpl_id._compute_sale_restricted_qty()
+ self.assertEqual(self.product.manual_sale_min_qty, 0)
+ self.assertEqual(self.product.sale_min_qty, 20)
+ self.assertEqual(self.product.manual_sale_max_qty, 0)
+ self.assertEqual(self.product.sale_max_qty, 250)
+ self.assertEqual(self.product.manual_sale_multiple_qty, 0)
+ self.assertEqual(self.product.sale_multiple_qty, 30)
+ # Check Restricted Qty from product
+ self.product.manual_sale_min_qty = 25
+ self.product.manual_sale_max_qty = 150
+ self.product.manual_sale_multiple_qty = 25
+ self.assertEqual(self.product.sale_min_qty, 25)
+ self.assertEqual(self.product.sale_max_qty, 150)
+ self.assertEqual(self.product.sale_multiple_qty, 25)
diff --git a/sale_restricted_qty/views/product_category_views.xml b/sale_restricted_qty/views/product_category_views.xml
new file mode 100644
index 00000000000..0c578c04135
--- /dev/null
+++ b/sale_restricted_qty/views/product_category_views.xml
@@ -0,0 +1,23 @@
+
+
+
+ product.category
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_restricted_qty/views/product_product_views.xml b/sale_restricted_qty/views/product_product_views.xml
new file mode 100644
index 00000000000..2b33952ed04
--- /dev/null
+++ b/sale_restricted_qty/views/product_product_views.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ product.product.form
+ product.product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_restricted_qty/views/product_template_views.xml b/sale_restricted_qty/views/product_template_views.xml
new file mode 100644
index 00000000000..51b47cfa58a
--- /dev/null
+++ b/sale_restricted_qty/views/product_template_views.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ product.template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_restricted_qty/views/sale_views.xml b/sale_restricted_qty/views/sale_views.xml
new file mode 100644
index 00000000000..bc2beb4d2a2
--- /dev/null
+++ b/sale_restricted_qty/views/sale_views.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ sale.order
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ is_qty_less_min_qty == True and force_sale_min_qty == False
+ is_qty_less_min_qty == True and force_sale_min_qty == True
+ is_qty_not_multiple_qty == True
+
+
+
+
From ce5344dc2f5ab64d2ddeb8316d9475c99f95a473 Mon Sep 17 00:00:00 2001
From: Mourad
Date: Thu, 18 Jun 2020 17:35:08 +0200
Subject: [PATCH 02/19] [FIX] Update Readme & "use_parent" value to
manual_force_sale_min_qty and manual_force_sale_max_qty (imp user UI)
---
sale_restricted_qty/hooks.py | 2 +-
sale_restricted_qty/models/product_category.py | 8 ++++----
sale_restricted_qty/models/product_product.py | 8 ++++----
.../models/product_restricted_qty_mixin.py | 6 ++++++
sale_restricted_qty/models/product_template.py | 8 ++++----
sale_restricted_qty/readme/DESCRIPTION.rst | 8 +++++---
sale_restricted_qty/tests/test_sale.py | 14 +++++++-------
7 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/sale_restricted_qty/hooks.py b/sale_restricted_qty/hooks.py
index 2a0f812af7d..810cf4053f5 100644
--- a/sale_restricted_qty/hooks.py
+++ b/sale_restricted_qty/hooks.py
@@ -16,4 +16,4 @@ def rename_module(cr):
cr,
[("sale_order_min_qty", "sale_restricted_qty")],
merge_modules=True,
- )
+ )
diff --git a/sale_restricted_qty/models/product_category.py b/sale_restricted_qty/models/product_category.py
index bb98cc5d44a..7ea01efffff 100644
--- a/sale_restricted_qty/models/product_category.py
+++ b/sale_restricted_qty/models/product_category.py
@@ -13,16 +13,16 @@ def _get_sale_restricted_qty(self):
force_sale_min_qty = False
if self.manual_force_sale_min_qty == 'force':
force_sale_min_qty = True
- if self.manual_force_sale_min_qty == 'not_force':
+ elif self.manual_force_sale_min_qty == 'not_force':
force_sale_min_qty = False
- if not self.manual_force_sale_min_qty:
+ else:
force_sale_min_qty = self.parent_id.force_sale_min_qty
force_sale_max_qty = False
if self.manual_force_sale_max_qty == 'force':
force_sale_max_qty = True
- if self.manual_force_sale_max_qty == 'not_force':
+ elif self.manual_force_sale_max_qty == 'not_force':
force_sale_max_qty = False
- if not self.manual_force_sale_max_qty:
+ else:
force_sale_max_qty = self.parent_id.force_sale_max_qty
res.update(
{
diff --git a/sale_restricted_qty/models/product_product.py b/sale_restricted_qty/models/product_product.py
index dc43340bddc..2ef51f58c64 100644
--- a/sale_restricted_qty/models/product_product.py
+++ b/sale_restricted_qty/models/product_product.py
@@ -14,16 +14,16 @@ def _get_sale_restricted_qty(self):
force_sale_min_qty = False
if self.manual_force_sale_min_qty == 'force':
force_sale_min_qty = True
- if self.manual_force_sale_min_qty == 'not_force':
+ elif self.manual_force_sale_min_qty == 'not_force':
force_sale_min_qty = False
- if not self.manual_force_sale_min_qty:
+ else:
force_sale_min_qty = self.product_tmpl_id.force_sale_min_qty
force_sale_max_qty = False
if self.manual_force_sale_max_qty == 'force':
force_sale_max_qty = True
- if self.manual_force_sale_max_qty == 'not_force':
+ elif self.manual_force_sale_max_qty == 'not_force':
force_sale_max_qty = False
- if not self.manual_force_sale_max_qty:
+ else:
force_sale_max_qty = self.product_tmpl_id.force_sale_max_qty
res.update(
{
diff --git a/sale_restricted_qty/models/product_restricted_qty_mixin.py b/sale_restricted_qty/models/product_restricted_qty_mixin.py
index 105fc4c41eb..e9a4e83ec9d 100644
--- a/sale_restricted_qty/models/product_restricted_qty_mixin.py
+++ b/sale_restricted_qty/models/product_restricted_qty_mixin.py
@@ -45,10 +45,13 @@ class ProductMinMultipleMixin(models.AbstractModel):
"Product/product Template/product category/parent categoroies ",
)
manual_force_sale_min_qty = fields.Selection([
+ ('use_parent', 'Use Parent Setting'),
('force', 'Yes'),
('not_force', 'No'),
],
string="Manual Force Min Qty",
+ required=True,
+ default="use_parent",
help="If force min qty is checked, the min quantity "
"is only indicative value."
"If is not test we check parent value",
@@ -77,9 +80,12 @@ class ProductMinMultipleMixin(models.AbstractModel):
"Product/product Template/product category/parent categoroies ",
)
manual_force_sale_max_qty = fields.Selection([
+ ('use_parent', 'Use Parent Setting'),
('force', 'Yes'),
('not_force', 'No'),
],
+ required=True,
+ default="use_parent",
string="Manual Force Max Qty",
help="If force max qty is checked, the max quantity "
"is only indicative value."
diff --git a/sale_restricted_qty/models/product_template.py b/sale_restricted_qty/models/product_template.py
index 6de94b4342b..3aa9bebcf49 100644
--- a/sale_restricted_qty/models/product_template.py
+++ b/sale_restricted_qty/models/product_template.py
@@ -14,16 +14,16 @@ def _get_sale_restricted_qty(self):
force_sale_min_qty = False
if self.manual_force_sale_min_qty == 'force':
force_sale_min_qty = True
- if self.manual_force_sale_min_qty == 'not_force':
+ elif self.manual_force_sale_min_qty == 'not_force':
force_sale_min_qty = False
- if not self.manual_force_sale_min_qty:
+ else:
force_sale_min_qty = self.categ_id.force_sale_min_qty
force_sale_max_qty = False
if self.manual_force_sale_max_qty == 'force':
force_sale_max_qty = True
- if self.manual_force_sale_max_qty == 'not_force':
+ elif self.manual_force_sale_max_qty == 'not_force':
force_sale_max_qty = False
- if not self.manual_force_sale_max_qty:
+ else:
force_sale_max_qty = self.categ_id.force_sale_max_qty
res.update(
{
diff --git a/sale_restricted_qty/readme/DESCRIPTION.rst b/sale_restricted_qty/readme/DESCRIPTION.rst
index f1fd4fa94fa..47ad576a40f 100644
--- a/sale_restricted_qty/readme/DESCRIPTION.rst
+++ b/sale_restricted_qty/readme/DESCRIPTION.rst
@@ -1,9 +1,11 @@
-This module allows to define minimum and multiple order quantity on product.
-Minimum order quantity is either required or recommanded.
-If you check "Force min Qty" on product the min qty do not block sale
+This module allows to define min, max and multiple order quantity on product.
+Min and Max order quantity is either required or recommanded.
+If you check "Force mini/max Qty" on product the min/max qty do not block sale
order.
This can be used if the available stock is less then the min qty
or if you want to sell sample products.
+Max qty can be used if we dont want to sale more than available qty
+(end of series product)
Setings are defined on product, if values are not set we use settings from
product template.
diff --git a/sale_restricted_qty/tests/test_sale.py b/sale_restricted_qty/tests/test_sale.py
index 167a49c5258..d634a034324 100644
--- a/sale_restricted_qty/tests/test_sale.py
+++ b/sale_restricted_qty/tests/test_sale.py
@@ -24,9 +24,9 @@ def setUp(self):
{
"name": "Test Parent categ",
"manual_sale_min_qty": 10.0,
- "manual_force_sale_min_qty": False,
+ "manual_force_sale_min_qty": "use_parent",
"manual_sale_max_qty": 100.0,
- "manual_force_sale_max_qty": False,
+ "manual_force_sale_max_qty": "use_parent",
"manual_sale_multiple_qty": 5.0,
}
)
@@ -35,15 +35,15 @@ def setUp(self):
{
"name": "Test categ",
"parent_id": self.categ_parent.id,
- "manual_force_sale_min_qty": False,
- "manual_force_sale_max_qty": False,
+ "manual_force_sale_min_qty": "use_parent",
+ "manual_force_sale_max_qty": "use_parent",
}
)
self.product = self.product_model.create({
"name": "Test product",
- "force_sale_min_qty": False,
- "force_sale_max_qty": False,
+ "force_sale_min_qty": False,
+ "force_sale_max_qty": False,
})
def test_check_sale_order_min_qty_required(self):
@@ -137,7 +137,7 @@ def test_check_restricted_qty_category_hierarchy(self):
self.assertEqual(self.product.sale_max_qty, 100)
self.assertEqual(self.product.sale_multiple_qty, 5)
self.categ_parent.manual_force_sale_min_qty = 'force'
- self.assertEqual(self.product.manual_force_sale_min_qty, False)
+ self.assertEqual(self.product.manual_force_sale_min_qty, "use_parent")
self.assertEqual(self.product.force_sale_min_qty, True)
# Check Restricted Qty from category
self.categ.manual_sale_min_qty = 15
From df86d2ba17017c2d44034358c06fbf58ac921637 Mon Sep 17 00:00:00 2001
From: Mourad
Date: Fri, 19 Jun 2020 12:35:25 +0200
Subject: [PATCH 03/19] [IMP] views & fix is_qty_less_min_qty &
is_qty_bigger_max_qty
---
sale_restricted_qty/models/sale.py | 10 +++---
.../views/product_category_views.xml | 31 +++++++++++++------
.../views/product_product_views.xml | 30 ++++++++++++------
.../views/product_template_views.xml | 30 ++++++++++++------
sale_restricted_qty/views/sale_views.xml | 16 ++++++++++
5 files changed, 84 insertions(+), 33 deletions(-)
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index 86bc1f73595..ae0d690b24d 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -115,12 +115,12 @@ def _compute_is_qty_less_min_qty(self):
product_qty = line.product_uom._compute_quantity(
line.product_uom_qty, line.product_id.uom_id
)
- line.is_qty_less_min_qty = (
+ line.is_qty_less_min_qty = line.sale_min_qty and (
float_compare(
product_qty, line.sale_min_qty, precision_rounding=rounding
)
< 0
- )
+ ) or False
@api.depends("product_uom_qty", "sale_max_qty")
def _compute_is_qty_bigger_max_qty(self):
@@ -129,12 +129,12 @@ def _compute_is_qty_bigger_max_qty(self):
product_qty = line.product_uom._compute_quantity(
line.product_uom_qty, line.product_id.uom_id
)
- line.is_qty_bigger_max_qty = (
+ line.is_qty_bigger_max_qty = line.sale_max_qty and (
float_compare(
product_qty, line.sale_max_qty, precision_rounding=rounding
)
- < 0
- )
+ > 0
+ ) or False
@api.depends("product_uom_qty", "sale_multiple_qty")
def _compute_is_qty_not_multiple_qty(self):
diff --git a/sale_restricted_qty/views/product_category_views.xml b/sale_restricted_qty/views/product_category_views.xml
index 0c578c04135..e1e92773169 100644
--- a/sale_restricted_qty/views/product_category_views.xml
+++ b/sale_restricted_qty/views/product_category_views.xml
@@ -6,16 +6,27 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_restricted_qty/views/product_product_views.xml b/sale_restricted_qty/views/product_product_views.xml
index 2b33952ed04..5389c4c2b7a 100644
--- a/sale_restricted_qty/views/product_product_views.xml
+++ b/sale_restricted_qty/views/product_product_views.xml
@@ -9,15 +9,27 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_restricted_qty/views/product_template_views.xml b/sale_restricted_qty/views/product_template_views.xml
index 51b47cfa58a..18379214104 100644
--- a/sale_restricted_qty/views/product_template_views.xml
+++ b/sale_restricted_qty/views/product_template_views.xml
@@ -7,15 +7,27 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sale_restricted_qty/views/sale_views.xml b/sale_restricted_qty/views/sale_views.xml
index bc2beb4d2a2..6d42932c483 100644
--- a/sale_restricted_qty/views/sale_views.xml
+++ b/sale_restricted_qty/views/sale_views.xml
@@ -11,6 +11,9 @@
+
+
+
+
+
+
+
+ >
is_qty_less_min_qty == True and force_sale_min_qty == Falseis_qty_less_min_qty == True and force_sale_min_qty == True
+ is_qty_bigger_max_qty == True and force_sale_max_qty == False
+ is_qty_bigger_max_qty == True and force_sale_max_qty == Trueis_qty_not_multiple_qty == True
From 1fbc137ad18e4cfc0379eafaac9616f797c79b54 Mon Sep 17 00:00:00 2001
From: Mourad
Date: Fri, 19 Jun 2020 16:11:34 +0200
Subject: [PATCH 04/19] [IMP] add _get_sale_restricted_qty on sale to make qty
overridable by other module & pep8
---
.../models/product_restricted_qty_mixin.py | 4 +-
sale_restricted_qty/models/sale.py | 44 ++++++++++----
sale_restricted_qty/tests/test_sale.py | 59 ++++++++++++++-----
3 files changed, 79 insertions(+), 28 deletions(-)
diff --git a/sale_restricted_qty/models/product_restricted_qty_mixin.py b/sale_restricted_qty/models/product_restricted_qty_mixin.py
index e9a4e83ec9d..9fc9f569fa3 100644
--- a/sale_restricted_qty/models/product_restricted_qty_mixin.py
+++ b/sale_restricted_qty/models/product_restricted_qty_mixin.py
@@ -48,7 +48,7 @@ class ProductMinMultipleMixin(models.AbstractModel):
('use_parent', 'Use Parent Setting'),
('force', 'Yes'),
('not_force', 'No'),
- ],
+ ],
string="Manual Force Min Qty",
required=True,
default="use_parent",
@@ -83,7 +83,7 @@ class ProductMinMultipleMixin(models.AbstractModel):
('use_parent', 'Use Parent Setting'),
('force', 'Yes'),
('not_force', 'No'),
- ],
+ ],
required=True,
default="use_parent",
string="Manual Force Max Qty",
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index ae0d690b24d..ca187dd4cdb 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -12,13 +12,13 @@ class SaleOrderLine(models.Model):
sale_min_qty = fields.Float(
string="Min Qty",
- related="product_id.sale_min_qty",
- readonly=True,
+ compute="_get_sale_restricted_qty",
store=True,
digits=dp.get_precision("Product Unit of Measure"),
)
force_sale_min_qty = fields.Boolean(
- related="product_id.force_sale_min_qty", readonly=True, store=True
+ compute="_get_sale_restricted_qty",
+ readonly=True, store=True
)
is_qty_less_min_qty = fields.Boolean(
string="Qty < Min Qty", compute="_compute_is_qty_less_min_qty"
@@ -26,21 +26,20 @@ class SaleOrderLine(models.Model):
sale_max_qty = fields.Float(
string="Max Qty",
- related="product_id.sale_max_qty",
- readonly=True,
+ compute="_get_sale_restricted_qty",
store=True,
digits=dp.get_precision("Product Unit of Measure"),
)
force_sale_max_qty = fields.Boolean(
- related="product_id.force_sale_max_qty", readonly=True, store=True
+ compute="_get_sale_restricted_qty",
+ readonly=True, store=True
)
is_qty_bigger_max_qty = fields.Boolean(
string="Qty > max Qty", compute="_compute_is_qty_bigger_max_qty"
)
sale_multiple_qty = fields.Float(
string="Multiple Qty",
- related="product_id.sale_multiple_qty",
- readonly=True,
+ compute="_get_sale_restricted_qty",
store=True,
digits=dp.get_precision("Product Unit of Measure"),
)
@@ -48,8 +47,10 @@ class SaleOrderLine(models.Model):
string="Not Multiple Qty", compute="_compute_is_qty_not_multiple_qty"
)
- @api.constrains("product_uom_qty")
+ @api.constrains(
+ "product_uom_qty", "sale_min_qty", "sale_max_qty", "sale_multiple_qty")
def check_constraint_restricted_qty(self):
+
msg = ""
invaild_min_lines = []
line_to_test = self.filtered(
@@ -143,5 +144,28 @@ def _compute_is_qty_not_multiple_qty(self):
line.product_uom_qty, line.product_id.uom_id
)
line.is_qty_not_multiple_qty = (
- line.sale_multiple_qty > 0 and product_qty % line.sale_multiple_qty != 0
+ line.sale_multiple_qty > 0
+ and product_qty % line.sale_multiple_qty != 0
)
+
+ def _get_sale_restricted_qty(self):
+ """Overridable function to change qty values (ex: form stock)"""
+ self.ensure_one()
+ res = {
+ "sale_min_qty": (self.product_id and
+ self.product_id.sale_min_qty or 0),
+ "force_sale_min_qty": (self.product_id and
+ self.product_id.force_sale_min_qty or False),
+ "sale_max_qty": (self.product_id and
+ self.product_id.sale_max_qty or 0),
+ "force_sale_max_qty": (self.product_id and
+ self.product_id.force_sale_max_qty or False),
+ "sale_multiple_qty": (self.product_id and
+ self.product_id.sale_multiple_qty or 0),
+ }
+ return res
+
+ @api.depends("product_id")
+ def _compute_sale_restricted_qty(self):
+ for rec in self:
+ rec.update(rec._get_sale_restricted_qty())
diff --git a/sale_restricted_qty/tests/test_sale.py b/sale_restricted_qty/tests/test_sale.py
index d634a034324..b1bd517f1ab 100644
--- a/sale_restricted_qty/tests/test_sale.py
+++ b/sale_restricted_qty/tests/test_sale.py
@@ -46,19 +46,32 @@ def setUp(self):
"force_sale_max_qty": False,
})
+ def refrech_sale_values(self, sale_order):
+ sale_order.order_line._compute_sale_restricted_qty()
+ sale_order.order_line.product_id_change()
+ sale_order.order_line._compute_is_qty_less_min_qty()
+ sale_order.order_line._compute_is_qty_bigger_max_qty()
+ sale_values = sale_order._convert_to_write(sale_order._cache)
+ return sale_values
+
def test_check_sale_order_min_qty_required(self):
line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
self.product.manual_sale_min_qty = 10
# Create sale order line with Qty less than min Qty
with self.assertRaises(ValidationError):
- self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ sale_order = self.sale_order_model.new(
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ sale_values = self.refrech_sale_values(sale_order)
+ self.sale_order_model.create(sale_values)
line_values["product_uom_qty"] = 12.0
# Create sale order line with Qty great then min Qty
self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ self.sale_order.order_line._compute_sale_restricted_qty()
self.assertFalse(self.sale_order.order_line.is_qty_less_min_qty)
self.assertEqual(self.sale_order.order_line.product_uom_qty, 12.0)
@@ -71,42 +84,51 @@ def test_check_sale_order_min_qty_recommended(self):
# Create sale order line with Qty less than min Qty
self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ self.sale_order.order_line._compute_sale_restricted_qty()
self.assertTrue(self.sale_order.order_line.is_qty_less_min_qty)
self.assertEqual(self.sale_order.order_line.product_uom_qty, 5.0)
def test_check_sale_order_max_qty_required(self):
- line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
+ line_values = {"product_id": self.product.id, "product_uom_qty": 15.0}
self.product.manual_sale_max_qty = 10
# Create sale order line with Qty bigger than max Qty
with self.assertRaises(ValidationError):
- self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
- line_values["product_uom_qty"] = 12.0
+ sale_values = self.refrech_sale_values(sale_order)
+ self.sale_order_model.create(sale_values)
+ line_values["product_uom_qty"] = 2.0
# Create sale order line with Qty great then max Qty
self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ self.sale_order.order_line._compute_sale_restricted_qty()
self.assertFalse(self.sale_order.order_line.is_qty_bigger_max_qty)
- self.assertEqual(self.sale_order.order_line.product_uom_qty, 12.0)
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 2.0)
def test_check_sale_order_max_qty_recommended(self):
- line_values = {"product_id": self.product.id, "product_uom_qty": 5.0}
+ line_values = {"product_id": self.product.id, "product_uom_qty": 15.0}
self.product.manual_sale_max_qty = 10
# Set Force max Qty to true
self.product.manual_force_sale_max_qty = 'force'
# Create sale order line with Qty bigger than max Qty
self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ self.sale_order.order_line._compute_sale_restricted_qty()
self.assertTrue(self.sale_order.order_line.is_qty_bigger_max_qty)
- self.assertEqual(self.sale_order.order_line.product_uom_qty, 5.0)
+ self.assertEqual(self.sale_order.order_line.product_uom_qty, 15.0)
def test_check_sale_order_multiple_qty_required(self):
line_values = {"product_id": self.product.id, "product_uom_qty": 15.0}
@@ -114,14 +136,19 @@ def test_check_sale_order_multiple_qty_required(self):
self.product.manual_sale_multiple_qty = 10
# Create sale order line with Qty not multiple Qty
with self.assertRaises(ValidationError):
- self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ sale_order = self.sale_order_model.create(
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ sale_values = self.refrech_sale_values(sale_order)
+ self.sale_order_model.create(sale_values)
line_values["product_uom_qty"] = 20
# Create sale order line with Qty multiple Qty
self.sale_order = self.sale_order_model.create(
- {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]}
+ {"partner_id": self.partner.id,
+ "order_line": [(0, 0, line_values)]}
)
+ self.sale_order.order_line._compute_sale_restricted_qty()
self.assertFalse(self.sale_order.order_line.is_qty_not_multiple_qty)
self.assertEqual(self.sale_order.order_line.product_uom_qty, 20)
From 9a2059d0cfe1bbf722987d9a19e41af9acfc036d Mon Sep 17 00:00:00 2001
From: Mourad
Date: Tue, 23 Jun 2020 17:37:51 +0200
Subject: [PATCH 05/19] [FIX] _compute_sale_restricted_qty
---
sale_restricted_qty/models/sale.py | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index ca187dd4cdb..46b6d3d471e 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -12,12 +12,12 @@ class SaleOrderLine(models.Model):
sale_min_qty = fields.Float(
string="Min Qty",
- compute="_get_sale_restricted_qty",
+ compute="_compute_sale_restricted_qty",
store=True,
digits=dp.get_precision("Product Unit of Measure"),
)
force_sale_min_qty = fields.Boolean(
- compute="_get_sale_restricted_qty",
+ compute="_compute_sale_restricted_qty",
readonly=True, store=True
)
is_qty_less_min_qty = fields.Boolean(
@@ -26,12 +26,12 @@ class SaleOrderLine(models.Model):
sale_max_qty = fields.Float(
string="Max Qty",
- compute="_get_sale_restricted_qty",
+ compute="_compute_sale_restricted_qty",
store=True,
digits=dp.get_precision("Product Unit of Measure"),
)
force_sale_max_qty = fields.Boolean(
- compute="_get_sale_restricted_qty",
+ compute="_compute_sale_restricted_qty",
readonly=True, store=True
)
is_qty_bigger_max_qty = fields.Boolean(
@@ -39,7 +39,7 @@ class SaleOrderLine(models.Model):
)
sale_multiple_qty = fields.Float(
string="Multiple Qty",
- compute="_get_sale_restricted_qty",
+ compute="_compute_sale_restricted_qty",
store=True,
digits=dp.get_precision("Product Unit of Measure"),
)
@@ -54,7 +54,8 @@ def check_constraint_restricted_qty(self):
msg = ""
invaild_min_lines = []
line_to_test = self.filtered(
- lambda l: not l.product_id.force_sale_min_qty and l.is_qty_less_min_qty
+ lambda sl: not sl.product_id.force_sale_min_qty and
+ sl.is_qty_less_min_qty
)
for line in line_to_test:
invaild_min_lines.append(
@@ -72,7 +73,8 @@ def check_constraint_restricted_qty(self):
)
invaild_max_lines = []
line_to_test = self.filtered(
- lambda l: not l.product_id.force_sale_max_qty and l.is_qty_bigger_max_qty
+ lambda sl: not sl.product_id.force_sale_max_qty and
+ sl.is_qty_bigger_max_qty
)
for line in line_to_test:
invaild_max_lines.append(
@@ -89,7 +91,7 @@ def check_constraint_restricted_qty(self):
',Check "force max quatity" on product'
)
invaild_multiple_lines = []
- line_to_test = self.filtered(lambda l: l.is_qty_not_multiple_qty)
+ line_to_test = self.filtered(lambda sl: sl.is_qty_not_multiple_qty)
for line in line_to_test:
invaild_multiple_lines.append(
_('Product "%s": multiple Quantity %s.')
From a6afa29aee2d248b4fd49180344673371886f48a Mon Sep 17 00:00:00 2001
From: Mourad
Date: Fri, 26 Jun 2020 17:28:06 +0200
Subject: [PATCH 06/19] [FIX] restricted_qty UserError Message
---
sale_restricted_qty/models/sale.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index 46b6d3d471e..cd72200404a 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -60,7 +60,7 @@ def check_constraint_restricted_qty(self):
for line in line_to_test:
invaild_min_lines.append(
_('Product "%s": Min Quantity %s.')
- % (line.product_id.name, line.product_id.sale_min_qty)
+ % (line.product_id.name, line.sale_min_qty)
)
if invaild_min_lines:
@@ -79,7 +79,7 @@ def check_constraint_restricted_qty(self):
for line in line_to_test:
invaild_max_lines.append(
_('Product "%s": max Quantity %s.')
- % (line.product_id.name, line.product_id.sale_max_qty)
+ % (line.product_id.name, line.sale_max_qty)
)
if invaild_max_lines:
@@ -95,7 +95,7 @@ def check_constraint_restricted_qty(self):
for line in line_to_test:
invaild_multiple_lines.append(
_('Product "%s": multiple Quantity %s.')
- % (line.product_id.name, line.product_id.sale_multiple_qty)
+ % (line.product_id.name, line.sale_multiple_qty)
)
if invaild_multiple_lines:
From c7a31c0c3506afaeb5e983a69a32adfedf993f33 Mon Sep 17 00:00:00 2001
From: Mourad
Date: Mon, 29 Jun 2020 14:43:39 +0200
Subject: [PATCH 07/19] [FIX] openupgradelib requirements.txt
---
sale_restricted_qty/__manifest__.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sale_restricted_qty/__manifest__.py b/sale_restricted_qty/__manifest__.py
index a686c872f4d..aed05cb82af 100644
--- a/sale_restricted_qty/__manifest__.py
+++ b/sale_restricted_qty/__manifest__.py
@@ -8,6 +8,9 @@
"author": "Akretion, " "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/sale-workflow/",
"license": "AGPL-3",
+ "external_dependencies": {
+ 'python' : ['openupgradelib'],
+ },
"depends": ["sale_management"],
"data": [
"views/product_category_views.xml",
From b8061e32d121db86235cdc51b4219e5d097c8251 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20BEAU?=
Date: Tue, 29 Sep 2020 16:30:56 +0200
Subject: [PATCH 08/19] [REF] refactor code to make it simplier and inheritable
---
.../models/product_restricted_qty_mixin.py | 1 +
sale_restricted_qty/models/sale.py | 14 ++++++++------
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/sale_restricted_qty/models/product_restricted_qty_mixin.py b/sale_restricted_qty/models/product_restricted_qty_mixin.py
index 9fc9f569fa3..3213b200748 100644
--- a/sale_restricted_qty/models/product_restricted_qty_mixin.py
+++ b/sale_restricted_qty/models/product_restricted_qty_mixin.py
@@ -7,6 +7,7 @@
class ProductMinMultipleMixin(models.AbstractModel):
_name = "product.restricted.qty.mixin"
+ _description = "Product Restrict Qty Mixin"
sale_multiple_qty = fields.Float(
compute="_compute_sale_restricted_qty",
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index cd72200404a..9eda0bb043f 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -111,13 +111,17 @@ def check_constraint_restricted_qty(self):
if msg:
raise ValidationError(msg)
+ def _get_product_qty_in_product_unit(self):
+ self.ensure_one()
+ return self.product_uom._compute_quantity(
+ self.product_uom_qty, self.product_id.uom_id
+ )
+
@api.depends("product_uom_qty", "sale_min_qty")
def _compute_is_qty_less_min_qty(self):
for line in self:
rounding = line.product_uom.rounding
- product_qty = line.product_uom._compute_quantity(
- line.product_uom_qty, line.product_id.uom_id
- )
+ product_qty = line._get_product_qty_in_product_unit()
line.is_qty_less_min_qty = line.sale_min_qty and (
float_compare(
product_qty, line.sale_min_qty, precision_rounding=rounding
@@ -129,9 +133,7 @@ def _compute_is_qty_less_min_qty(self):
def _compute_is_qty_bigger_max_qty(self):
for line in self:
rounding = line.product_uom.rounding
- product_qty = line.product_uom._compute_quantity(
- line.product_uom_qty, line.product_id.uom_id
- )
+ product_qty = line._get_product_qty_in_product_unit()
line.is_qty_bigger_max_qty = line.sale_max_qty and (
float_compare(
product_qty, line.sale_max_qty, precision_rounding=rounding
From 475f9f01347691f995f31f05f8c91cd1b93ba48c Mon Sep 17 00:00:00 2001
From: Mourad
Date: Thu, 3 Dec 2020 15:50:51 +0100
Subject: [PATCH 09/19] [FIX] _compute_is_qty_less bigger multiple qty
---
sale_restricted_qty/README.rst | 23 +-
sale_restricted_qty/__manifest__.py | 2 +-
.../i18n/sale_restricted_qty.pot | 380 ++++++++++++++++++
sale_restricted_qty/models/sale.py | 6 +-
.../static/description/index.html | 20 +-
5 files changed, 412 insertions(+), 19 deletions(-)
create mode 100644 sale_restricted_qty/i18n/sale_restricted_qty.pot
diff --git a/sale_restricted_qty/README.rst b/sale_restricted_qty/README.rst
index 7949177ad2d..265f0cb756c 100644
--- a/sale_restricted_qty/README.rst
+++ b/sale_restricted_qty/README.rst
@@ -14,10 +14,10 @@ Sale order min quantity
: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/12.0/sale_order_min_qty
+ :target: https://github.com/OCA/sale-workflow/tree/12.0/sale_restricted_qty
: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-12-0/sale-workflow-12-0-sale_order_min_qty
+ :target: https://translation.odoo-community.org/projects/sale-workflow-12-0/sale-workflow-12-0-sale_restricted_qty
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/167/12.0
@@ -25,12 +25,19 @@ Sale order min quantity
|badge1| |badge2| |badge3| |badge4| |badge5|
-This module allows to define minimum order quantity on product.
-Minimum order quantity is either required or recommanded.
-If you check "Force min Qty" on product the min qty do not block sale
+This module allows to define min, max and multiple order quantity on product.
+Min and Max order quantity is either required or recommanded.
+If you check "Force mini/max Qty" on product the min/max qty do not block sale
order.
This can be used if the available stock is less then the min qty
-or if you want to sell sample products.
+or if you want to sell sample products.
+Max qty can be used if we dont want to sale more than available qty
+(end of series product)
+
+Setings are defined on product, if values are not set we use settings from
+product template.
+If settings are not set on product template we use settings from
+product category otherwise parent category.
**Table of contents**
@@ -54,7 +61,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 smashing it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -84,6 +91,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_restricted_qty/__manifest__.py b/sale_restricted_qty/__manifest__.py
index aed05cb82af..64d192f8ce0 100644
--- a/sale_restricted_qty/__manifest__.py
+++ b/sale_restricted_qty/__manifest__.py
@@ -3,7 +3,7 @@
{
"name": "Sale order min quantity",
- "version": "12.0.1.0.0",
+ "version": "12.0.2.0.0",
"category": "Sales Management",
"author": "Akretion, " "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/sale-workflow/",
diff --git a/sale_restricted_qty/i18n/sale_restricted_qty.pot b/sale_restricted_qty/i18n/sale_restricted_qty.pot
new file mode 100644
index 00000000000..4578596920f
--- /dev/null
+++ b/sale_restricted_qty/i18n/sale_restricted_qty.pot
@@ -0,0 +1,380 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * sale_restricted_qty
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.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_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:89
+#, python-format
+msgid "\n"
+"* If you want sell quantity bigger than max Quantity,Check \"force max quatity\" on product"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:70
+#, python-format
+msgid "\n"
+"* If you want sell quantity less than Min Quantity,Check \"force min quatity\" on product"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:105
+#, python-format
+msgid "\n"
+"* If you want sell quantity not multiple Quantity,Set multiple quantity to 0 on product or product template or product category"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:86
+#, python-format
+msgid "Check maximum order quantity for this products: * \n"
+""
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:67
+#, python-format
+msgid "Check minimum order quantity for this products: * \n"
+""
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:102
+#, python-format
+msgid "Check multiple order quantity for this products: * \n"
+""
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__force_sale_max_qty
+msgid "Define if user can force sale max qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__force_sale_min_qty
+msgid "Define if user can force sale min qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_max_qty
+msgid "Define sale max qty 'If not set, Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_min_qty
+msgid "Define sale min qty 'If not set, Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__sale_multiple_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_multiple_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_multiple_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_multiple_qty
+msgid "Define sale multiple qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__force_sale_max_qty
+msgid "Force Max Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__force_sale_min_qty
+msgid "Force Min Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__force_sale_max_qty
+msgid "Force Sale Max Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__force_sale_min_qty
+msgid "Force Sale Min Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__id
+msgid "ID"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__manual_force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__manual_force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__manual_force_sale_max_qty
+msgid "If force max qty is checked, the max quantity is only indicative value.If is not test we check parent value"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__manual_force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__manual_force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__manual_force_sale_min_qty
+msgid "If force min qty is checked, the min quantity is only indicative value.If is not test we check parent value"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_force_sale_max_qty
+msgid "Manual Force Max Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_force_sale_min_qty
+msgid "Manual Force Min Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_max_qty
+msgid "Max Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Max Quantity Exceeded"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Max Quantity Recommended"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_sale_max_qty
+msgid "Max Sale Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_min_qty
+msgid "Min Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Min Quantity Recommended"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Min Quantity Required"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_sale_min_qty
+msgid "Min Sale Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_multiple_qty
+msgid "Multiple Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Multiple Quantity Required"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_sale_multiple_qty
+msgid "Multiple Sale Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: selection:product.category,manual_force_sale_max_qty:0
+#: selection:product.category,manual_force_sale_min_qty:0
+#: selection:product.product,manual_force_sale_max_qty:0
+#: selection:product.product,manual_force_sale_min_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
+#: selection:product.template,manual_force_sale_max_qty:0
+#: selection:product.template,manual_force_sale_min_qty:0
+msgid "No"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__is_qty_not_multiple_qty
+msgid "Not Multiple Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_product
+msgid "Product"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:62
+#, python-format
+msgid "Product \"%s\": Min Quantity %s."
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:81
+#, python-format
+msgid "Product \"%s\": max Quantity %s."
+msgstr ""
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:97
+#, python-format
+msgid "Product \"%s\": multiple Quantity %s."
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_category
+msgid "Product Category"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_restricted_qty_mixin
+msgid "Product Restrict Qty Mixin"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_template
+msgid "Product Template"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__is_qty_less_min_qty
+msgid "Qty < Min Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__is_qty_bigger_max_qty
+msgid "Qty > max Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__sale_max_qty
+msgid "Sale Max Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale Max qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale Miltiple qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__sale_min_qty
+msgid "Sale Min Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale Min qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__sale_multiple_qty
+msgid "Sale Multiple Qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale restricted qty"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_sale_order_line
+msgid "Sales Order Line"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: selection:product.category,manual_force_sale_max_qty:0
+#: selection:product.category,manual_force_sale_min_qty:0
+#: selection:product.product,manual_force_sale_max_qty:0
+#: selection:product.product,manual_force_sale_min_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
+#: selection:product.template,manual_force_sale_max_qty:0
+#: selection:product.template,manual_force_sale_min_qty:0
+msgid "Use Parent Setting"
+msgstr ""
+
+#. module: sale_restricted_qty
+#: selection:product.category,manual_force_sale_max_qty:0
+#: selection:product.category,manual_force_sale_min_qty:0
+#: selection:product.product,manual_force_sale_max_qty:0
+#: selection:product.product,manual_force_sale_min_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
+#: selection:product.template,manual_force_sale_max_qty:0
+#: selection:product.template,manual_force_sale_min_qty:0
+msgid "Yes"
+msgstr ""
+
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index 9eda0bb043f..e5860d54797 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -117,7 +117,7 @@ def _get_product_qty_in_product_unit(self):
self.product_uom_qty, self.product_id.uom_id
)
- @api.depends("product_uom_qty", "sale_min_qty")
+ @api.depends("product_id", "product_uom_qty", "sale_min_qty")
def _compute_is_qty_less_min_qty(self):
for line in self:
rounding = line.product_uom.rounding
@@ -129,7 +129,7 @@ def _compute_is_qty_less_min_qty(self):
< 0
) or False
- @api.depends("product_uom_qty", "sale_max_qty")
+ @api.depends("product_id", "product_uom_qty", "sale_max_qty")
def _compute_is_qty_bigger_max_qty(self):
for line in self:
rounding = line.product_uom.rounding
@@ -141,7 +141,7 @@ def _compute_is_qty_bigger_max_qty(self):
> 0
) or False
- @api.depends("product_uom_qty", "sale_multiple_qty")
+ @api.depends("product_id", "product_uom_qty", "sale_multiple_qty")
def _compute_is_qty_not_multiple_qty(self):
for line in self:
product_qty = line.product_uom._compute_quantity(
diff --git a/sale_restricted_qty/static/description/index.html b/sale_restricted_qty/static/description/index.html
index 3bada92d5b5..a86a4ad781e 100644
--- a/sale_restricted_qty/static/description/index.html
+++ b/sale_restricted_qty/static/description/index.html
@@ -367,13 +367,19 @@
Sale order min quantity
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
-
This module allows to define minimum order quantity on product.
-Minimum order quantity is either required or recommanded.
-If you check “Force min Qty” on product the min qty do not block sale
+
+
This module allows to define min, max and multiple order quantity on product.
+Min and Max order quantity is either required or recommanded.
+If you check “Force mini/max Qty” on product the min/max qty do not block sale
order.
This can be used if the available stock is less then the min qty
-or if you want to sell sample products.
+or if you want to sell sample products.
+Max qty can be used if we dont want to sale more than available qty
+(end of series product)
+
Setings are defined on product, if values are not set we use settings from
+product template.
+If settings are not set on product template we use settings from
+product category otherwise parent category.
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 smashing it by providing a detailed and welcomed
-feedback.
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.
From acca562958b9870b7b5060fcd5572a6d60337178 Mon Sep 17 00:00:00 2001
From: Samuele Mariani
Date: Mon, 4 Oct 2021 16:08:31 +0000
Subject: [PATCH 10/19] Added translation using Weblate (Italian)
---
sale_restricted_qty/i18n/it.po | 412 +++++++++++++++++++++++++++++++++
1 file changed, 412 insertions(+)
create mode 100644 sale_restricted_qty/i18n/it.po
diff --git a/sale_restricted_qty/i18n/it.po b/sale_restricted_qty/i18n/it.po
new file mode 100644
index 00000000000..bc3367ea42c
--- /dev/null
+++ b/sale_restricted_qty/i18n/it.po
@@ -0,0 +1,412 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * sale_restricted_qty
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2021-10-05 18:45+0000\n"
+"Last-Translator: Samuele Mariani \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 4.3.2\n"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:89
+#, python-format
+msgid "\n"
+"* If you want sell quantity bigger than max Quantity,Check \"force max quatity\" on product"
+msgstr ""
+"\n"
+"* se vuoi vendere una quantità maggiore della Quantità massima, Seleziona "
+"\"forza quantità massima\" nel prodotto"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:70
+#, python-format
+msgid "\n"
+"* If you want sell quantity less than Min Quantity,Check \"force min quatity\" on product"
+msgstr ""
+"\n"
+"* se vuoi vendere una quantità inferiore alla Quantità Minima, Seleziona "
+"\"forza quantità minima\" nel prodotto"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:105
+#, python-format
+msgid "\n"
+"* If you want sell quantity not multiple Quantity,Set multiple quantity to 0 on product or product template or product category"
+msgstr ""
+"\n"
+"* se vuoi vendere quantità non multiple, Imposta quantità multiple a 0 nella "
+"Prodotto o Modello Prodotto o Categoria Prodotto"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:86
+#, python-format
+msgid "Check maximum order quantity for this products: * \n"
+""
+msgstr "Controlla la quantità di vendita massima per questi prodotti: * \n"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:67
+#, python-format
+msgid "Check minimum order quantity for this products: * \n"
+""
+msgstr "Controlla la quantità di vendita minima per questi prodotti: * \n"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:102
+#, python-format
+msgid "Check multiple order quantity for this products: * \n"
+""
+msgstr "Controlla la quantità di vendita multipla per questi prodotti: * \n"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__force_sale_max_qty
+msgid "Define if user can force sale max qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+"Definisci se l'utente può forzare la quantità massima di vendita 'Se non "
+"impostato', Odoo utilizzerà il valore definito nell'oggetto padre. La "
+"gerarchia è in questo ordine: Prodotto/Modello prodotto/categoria prodotto/"
+"categorie padre "
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__force_sale_min_qty
+msgid "Define if user can force sale min qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+"Definisci se l'utente può forzare la quantità minima di vendita 'Se non "
+"impostato', Odoo utilizzerà il valore definito nell'oggetto padre. La "
+"gerarchia è in questo ordine: Prodotto/Modello prodotto/categoria prodotto/"
+"categorie padre "
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_max_qty
+msgid "Define sale max qty 'If not set, Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+"Definisci la qtà massima di vendita 'Se non impostato', Odoo utilizzerà il "
+"valore definito nell'oggetto padre. La gerarchia è in questo ordine: "
+"Prodotto/Modello prodotto/categoria prodotto/categorie padre "
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_min_qty
+msgid "Define sale min qty 'If not set, Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+"Definisci la qtà minima di vendita 'Se non impostato', Odoo utilizzerà il "
+"valore definito nell'oggetto padre. La gerarchia è in questo ordine: "
+"Prodotto/Modello prodotto/categoria prodotto/categorie padre "
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__sale_multiple_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_multiple_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_multiple_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_multiple_qty
+msgid "Define sale multiple qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgstr ""
+"Definisci la qtà multipla di vendita 'Se non impostato', Odoo utilizzerà il "
+"valore definito nell'oggetto padre. La gerarchia è in questo ordine: "
+"Prodotto/Modello prodotto/categoria prodotto/categorie padre "
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__display_name
+msgid "Display Name"
+msgstr "Nome Visualizzato"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__force_sale_max_qty
+msgid "Force Max Qty"
+msgstr "Forza Qtà Massima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__force_sale_min_qty
+msgid "Force Min Qty"
+msgstr "Forza Qtà Minima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__force_sale_max_qty
+msgid "Force Sale Max Qty"
+msgstr "Forza Qtà Minima di Vendita"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__force_sale_min_qty
+msgid "Force Sale Min Qty"
+msgstr "Forza Qtà Massima di Vendita"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__id
+msgid "ID"
+msgstr "ID"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__manual_force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__manual_force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_max_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__manual_force_sale_max_qty
+msgid "If force max qty is checked, the max quantity is only indicative value.If is not test we check parent value"
+msgstr ""
+"Se forza qtà massima è selezionata, la quantità massima è solo un valore "
+"indicativo. Se non è test controlliamo il valore genitore"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_category__manual_force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__manual_force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_min_qty
+#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__manual_force_sale_min_qty
+msgid "If force min qty is checked, the min quantity is only indicative value.If is not test we check parent value"
+msgstr ""
+"Se forza qtà minima è selezionata, la quantità minima è solo un valore "
+"indicativo. Se non è test controlliamo il valore genitore"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin____last_update
+msgid "Last Modified on"
+msgstr "Ultima Modifica il"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_force_sale_max_qty
+msgid "Manual Force Max Qty"
+msgstr "Forza Manualmente Qtà Massima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_force_sale_min_qty
+msgid "Manual Force Min Qty"
+msgstr "Forza Manualmente Qtà Minima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_max_qty
+msgid "Max Qty"
+msgstr "Quantità Massima"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Max Quantity Exceeded"
+msgstr "Quantità Massima Superata"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Max Quantity Recommended"
+msgstr "Qtà Massima Consigliata"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_sale_max_qty
+msgid "Max Sale Qty"
+msgstr "Qtà di Vendita Massima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_min_qty
+msgid "Min Qty"
+msgstr "Qtà Minima"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Min Quantity Recommended"
+msgstr "Qtà Minima Consigliata"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Min Quantity Required"
+msgstr "Qtà Minima Richiesta"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_sale_min_qty
+msgid "Min Sale Qty"
+msgstr "Qtà di Vendita Minima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_multiple_qty
+msgid "Multiple Qty"
+msgstr "Qtà Multiple"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form
+msgid "Multiple Quantity Required"
+msgstr "Quantità Multipla Richiesta"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__manual_sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__manual_sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__manual_sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__manual_sale_multiple_qty
+msgid "Multiple Sale Qty"
+msgstr "Quantità di Vendita Multipla"
+
+#. module: sale_restricted_qty
+#: selection:product.category,manual_force_sale_max_qty:0
+#: selection:product.category,manual_force_sale_min_qty:0
+#: selection:product.product,manual_force_sale_max_qty:0
+#: selection:product.product,manual_force_sale_min_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
+#: selection:product.template,manual_force_sale_max_qty:0
+#: selection:product.template,manual_force_sale_min_qty:0
+msgid "No"
+msgstr "No"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__is_qty_not_multiple_qty
+msgid "Not Multiple Qty"
+msgstr "Qtà Non Multipla"
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_product
+msgid "Product"
+msgstr "Prodotto"
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:62
+#, python-format
+msgid "Product \"%s\": Min Quantity %s."
+msgstr "Prodotto \"%s\": Quantità Minima %s."
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:81
+#, python-format
+msgid "Product \"%s\": max Quantity %s."
+msgstr "Prodotto \"%s\": Quantità Massima %s."
+
+#. module: sale_restricted_qty
+#: code:addons/sale_restricted_qty/models/sale.py:97
+#, python-format
+msgid "Product \"%s\": multiple Quantity %s."
+msgstr "prodotto \"%s\": Quantità Multipla %s."
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_category
+msgid "Product Category"
+msgstr "Categoria Prodotto"
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_restricted_qty_mixin
+msgid "Product Restrict Qty Mixin"
+msgstr "Mixin Limite di Quantità Prodotto"
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_product_template
+msgid "Product Template"
+msgstr "Modello Prodotto"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__is_qty_less_min_qty
+msgid "Qty < Min Qty"
+msgstr "Qtà < Qtà Minima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__is_qty_bigger_max_qty
+msgid "Qty > max Qty"
+msgstr "Qtà > Qtà Massima"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__sale_max_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__sale_max_qty
+msgid "Sale Max Qty"
+msgstr "Qtà Massima di Vendita"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale Max qty"
+msgstr "qtà Massima di Vendita"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale Miltiple qty"
+msgstr "qtà Multipla di Vendita"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__sale_min_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__sale_min_qty
+msgid "Sale Min Qty"
+msgstr "Qtà Minima di Vendita"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale Min qty"
+msgstr "qtà Minima di Vendita"
+
+#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__sale_multiple_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__sale_multiple_qty
+msgid "Sale Multiple Qty"
+msgstr "Qtà Multipla di Vendita"
+
+#. module: sale_restricted_qty
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.min_order_qty_product_product_form_view
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_category_form
+#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_product_template_form
+msgid "Sale restricted qty"
+msgstr "Quantità di vendita limitata"
+
+#. module: sale_restricted_qty
+#: model:ir.model,name:sale_restricted_qty.model_sale_order_line
+msgid "Sales Order Line"
+msgstr "Riga Ordine di Vendita"
+
+#. module: sale_restricted_qty
+#: selection:product.category,manual_force_sale_max_qty:0
+#: selection:product.category,manual_force_sale_min_qty:0
+#: selection:product.product,manual_force_sale_max_qty:0
+#: selection:product.product,manual_force_sale_min_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
+#: selection:product.template,manual_force_sale_max_qty:0
+#: selection:product.template,manual_force_sale_min_qty:0
+msgid "Use Parent Setting"
+msgstr "Usa Impostazioni Genitore"
+
+#. module: sale_restricted_qty
+#: selection:product.category,manual_force_sale_max_qty:0
+#: selection:product.category,manual_force_sale_min_qty:0
+#: selection:product.product,manual_force_sale_max_qty:0
+#: selection:product.product,manual_force_sale_min_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
+#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
+#: selection:product.template,manual_force_sale_max_qty:0
+#: selection:product.template,manual_force_sale_min_qty:0
+msgid "Yes"
+msgstr "Si"
From 63d6f2b2717cdc93d52061da959888262bb60a16 Mon Sep 17 00:00:00 2001
From: Ashish Hirpara
Date: Fri, 25 Mar 2022 15:27:36 +0530
Subject: [PATCH 11/19] [14.0][MIG] sale_restricted_qty: Migration to 14.0
---
sale_restricted_qty/README.rst | 22 ++-
sale_restricted_qty/__manifest__.py | 10 +-
.../i18n/sale_restricted_qty.pot | 130 +++++++++++-------
.../models/product_category.py | 8 +-
sale_restricted_qty/models/product_product.py | 8 +-
.../models/product_restricted_qty_mixin.py | 37 +++--
.../models/product_template.py | 8 +-
sale_restricted_qty/models/sale.py | 72 +++++-----
sale_restricted_qty/readme/CONTRIBUTORS.rst | 4 +
.../static/description/index.html | 12 +-
sale_restricted_qty/tests/test_sale.py | 58 ++++----
.../views/product_category_views.xml | 60 +++++---
.../views/product_product_views.xml | 65 ++++++---
.../views/product_template_views.xml | 60 +++++---
sale_restricted_qty/views/sale_views.xml | 124 +++++++++++------
15 files changed, 427 insertions(+), 251 deletions(-)
diff --git a/sale_restricted_qty/README.rst b/sale_restricted_qty/README.rst
index 265f0cb756c..be2b9b45883 100644
--- a/sale_restricted_qty/README.rst
+++ b/sale_restricted_qty/README.rst
@@ -14,13 +14,13 @@ Sale order min quantity
: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/12.0/sale_restricted_qty
+ :target: https://github.com/OCA/sale-workflow/tree/14.0/sale_restricted_qty
: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-12-0/sale-workflow-12-0-sale_restricted_qty
+ :target: https://translation.odoo-community.org/projects/sale-workflow-14-0/sale-workflow-14-0-sale_restricted_qty
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
- :target: https://runbot.odoo-community.org/runbot/167/12.0
+ :target: https://runbot.odoo-community.org/runbot/167/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -61,7 +61,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 smashing it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -78,6 +78,10 @@ Contributors
* Mourad EL HADJ MIMOUN
+* `Ooops `_:
+
+ * Ashish Hirpara
+
Maintainers
~~~~~~~~~~~
@@ -91,6 +95,14 @@ 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.
+.. |maintainer-ashishhirapara| image:: https://github.com/ashishhirapara.png?size=40px
+ :target: https://github.com/ashishhirapara
+ :alt: ashishhirapara
+
+Current `maintainer `__:
+
+|maintainer-ashishhirapara|
+
+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_restricted_qty/__manifest__.py b/sale_restricted_qty/__manifest__.py
index 64d192f8ce0..8b0a8471ade 100644
--- a/sale_restricted_qty/__manifest__.py
+++ b/sale_restricted_qty/__manifest__.py
@@ -3,13 +3,15 @@
{
"name": "Sale order min quantity",
- "version": "12.0.2.0.0",
+ "version": "14.0.1.0.1",
"category": "Sales Management",
- "author": "Akretion, " "Odoo Community Association (OCA)",
- "website": "https://github.com/OCA/sale-workflow/",
+ "author": "Akretion, Odoo Community Association (OCA)",
+ "contributors": ["Ashish Hirpara"],
+ "maintainers": ["ashishhirapara"],
+ "website": "https://github.com/OCA/sale-workflow",
"license": "AGPL-3",
"external_dependencies": {
- 'python' : ['openupgradelib'],
+ "python": ["openupgradelib"],
},
"depends": ["sale_management"],
"data": [
diff --git a/sale_restricted_qty/i18n/sale_restricted_qty.pot b/sale_restricted_qty/i18n/sale_restricted_qty.pot
index 4578596920f..c20b2373a25 100644
--- a/sale_restricted_qty/i18n/sale_restricted_qty.pot
+++ b/sale_restricted_qty/i18n/sale_restricted_qty.pot
@@ -1,12 +1,12 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
-# * sale_restricted_qty
+# * sale_restricted_qty
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 12.0\n"
+"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
-"Last-Translator: <>\n"
+"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -14,45 +14,45 @@ msgstr ""
"Plural-Forms: \n"
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:89
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
-msgid "\n"
+msgid ""
+"\n"
"* If you want sell quantity bigger than max Quantity,Check \"force max quatity\" on product"
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:70
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
-msgid "\n"
+msgid ""
+"\n"
"* If you want sell quantity less than Min Quantity,Check \"force min quatity\" on product"
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:105
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
-msgid "\n"
+msgid ""
+"\n"
"* If you want sell quantity not multiple Quantity,Set multiple quantity to 0 on product or product template or product category"
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:86
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
msgid "Check maximum order quantity for this products: * \n"
-""
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:67
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
msgid "Check minimum order quantity for this products: * \n"
-""
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:102
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
msgid "Check multiple order quantity for this products: * \n"
-""
msgstr ""
#. module: sale_restricted_qty
@@ -60,7 +60,10 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__force_sale_max_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_max_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__force_sale_max_qty
-msgid "Define if user can force sale max qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgid ""
+"Define if user can force sale max qty 'If not set', Odoo will use the value "
+"defined in the parent object.Hierarchy is in this order :Product/product "
+"Template/product category/parent categoroies "
msgstr ""
#. module: sale_restricted_qty
@@ -68,7 +71,10 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__force_sale_min_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__force_sale_min_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__force_sale_min_qty
-msgid "Define if user can force sale min qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgid ""
+"Define if user can force sale min qty 'If not set', Odoo will use the value "
+"defined in the parent object.Hierarchy is in this order :Product/product "
+"Template/product category/parent categoroies "
msgstr ""
#. module: sale_restricted_qty
@@ -76,7 +82,10 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_max_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_max_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_max_qty
-msgid "Define sale max qty 'If not set, Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgid ""
+"Define sale max qty 'If not set, Odoo will use the value defined in the "
+"parent object.Hierarchy is in this order :Product/product Template/product "
+"category/parent categoroies "
msgstr ""
#. module: sale_restricted_qty
@@ -84,7 +93,10 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_min_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_min_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_min_qty
-msgid "Define sale min qty 'If not set, Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgid ""
+"Define sale min qty 'If not set, Odoo will use the value defined in the "
+"parent object.Hierarchy is in this order :Product/product Template/product "
+"category/parent categoroies "
msgstr ""
#. module: sale_restricted_qty
@@ -92,11 +104,18 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__sale_multiple_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__sale_multiple_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__sale_multiple_qty
-msgid "Define sale multiple qty 'If not set', Odoo will use the value defined in the parent object.Hierarchy is in this order :Product/product Template/product category/parent categoroies "
+msgid ""
+"Define sale multiple qty 'If not set', Odoo will use the value defined in "
+"the parent object.Hierarchy is in this order :Product/product "
+"Template/product category/parent categoroies "
msgstr ""
#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__display_name
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__display_name
#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__display_name
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__display_name
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__display_name
msgid "Display Name"
msgstr ""
@@ -127,7 +146,11 @@ msgid "Force Sale Min Qty"
msgstr ""
#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__id
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product__id
#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin__id
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template__id
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__id
msgid "ID"
msgstr ""
@@ -136,7 +159,9 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__manual_force_sale_max_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_max_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__manual_force_sale_max_qty
-msgid "If force max qty is checked, the max quantity is only indicative value.If is not test we check parent value"
+msgid ""
+"If force max qty is checked, the max quantity is only indicative value.If is"
+" not test we check parent value"
msgstr ""
#. module: sale_restricted_qty
@@ -144,11 +169,17 @@ msgstr ""
#: model:ir.model.fields,help:sale_restricted_qty.field_product_product__manual_force_sale_min_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_restricted_qty_mixin__manual_force_sale_min_qty
#: model:ir.model.fields,help:sale_restricted_qty.field_product_template__manual_force_sale_min_qty
-msgid "If force min qty is checked, the min quantity is only indicative value.If is not test we check parent value"
+msgid ""
+"If force min qty is checked, the min quantity is only indicative value.If is"
+" not test we check parent value"
msgstr ""
#. module: sale_restricted_qty
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category____last_update
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_product____last_update
#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_restricted_qty_mixin____last_update
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_product_template____last_update
+#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line____last_update
msgid "Last Modified on"
msgstr ""
@@ -233,14 +264,14 @@ msgid "Multiple Sale Qty"
msgstr ""
#. module: sale_restricted_qty
-#: selection:product.category,manual_force_sale_max_qty:0
-#: selection:product.category,manual_force_sale_min_qty:0
-#: selection:product.product,manual_force_sale_max_qty:0
-#: selection:product.product,manual_force_sale_min_qty:0
-#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
-#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
-#: selection:product.template,manual_force_sale_max_qty:0
-#: selection:product.template,manual_force_sale_min_qty:0
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_category__manual_force_sale_max_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_category__manual_force_sale_min_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_product__manual_force_sale_max_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_product__manual_force_sale_min_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_restricted_qty_mixin__manual_force_sale_max_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_restricted_qty_mixin__manual_force_sale_min_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_template__manual_force_sale_max_qty__not_force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_template__manual_force_sale_min_qty__not_force
msgid "No"
msgstr ""
@@ -255,19 +286,19 @@ msgid "Product"
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:62
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
msgid "Product \"%s\": Min Quantity %s."
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:81
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
msgid "Product \"%s\": max Quantity %s."
msgstr ""
#. module: sale_restricted_qty
-#: code:addons/sale_restricted_qty/models/sale.py:97
+#: code:addons/sale_restricted_qty/models/sale.py:0
#, python-format
msgid "Product \"%s\": multiple Quantity %s."
msgstr ""
@@ -355,26 +386,25 @@ msgid "Sales Order Line"
msgstr ""
#. module: sale_restricted_qty
-#: selection:product.category,manual_force_sale_max_qty:0
-#: selection:product.category,manual_force_sale_min_qty:0
-#: selection:product.product,manual_force_sale_max_qty:0
-#: selection:product.product,manual_force_sale_min_qty:0
-#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
-#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
-#: selection:product.template,manual_force_sale_max_qty:0
-#: selection:product.template,manual_force_sale_min_qty:0
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_category__manual_force_sale_max_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_category__manual_force_sale_min_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_product__manual_force_sale_max_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_product__manual_force_sale_min_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_restricted_qty_mixin__manual_force_sale_max_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_restricted_qty_mixin__manual_force_sale_min_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_template__manual_force_sale_max_qty__use_parent
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_template__manual_force_sale_min_qty__use_parent
msgid "Use Parent Setting"
msgstr ""
#. module: sale_restricted_qty
-#: selection:product.category,manual_force_sale_max_qty:0
-#: selection:product.category,manual_force_sale_min_qty:0
-#: selection:product.product,manual_force_sale_max_qty:0
-#: selection:product.product,manual_force_sale_min_qty:0
-#: selection:product.restricted.qty.mixin,manual_force_sale_max_qty:0
-#: selection:product.restricted.qty.mixin,manual_force_sale_min_qty:0
-#: selection:product.template,manual_force_sale_max_qty:0
-#: selection:product.template,manual_force_sale_min_qty:0
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_category__manual_force_sale_max_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_category__manual_force_sale_min_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_product__manual_force_sale_max_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_product__manual_force_sale_min_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_restricted_qty_mixin__manual_force_sale_max_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_restricted_qty_mixin__manual_force_sale_min_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_template__manual_force_sale_max_qty__force
+#: model:ir.model.fields.selection,name:sale_restricted_qty.selection__product_template__manual_force_sale_min_qty__force
msgid "Yes"
msgstr ""
-
diff --git a/sale_restricted_qty/models/product_category.py b/sale_restricted_qty/models/product_category.py
index 7ea01efffff..05815c79a00 100644
--- a/sale_restricted_qty/models/product_category.py
+++ b/sale_restricted_qty/models/product_category.py
@@ -11,16 +11,16 @@ class ProductCategory(models.Model):
def _get_sale_restricted_qty(self):
res = super()._get_sale_restricted_qty()
force_sale_min_qty = False
- if self.manual_force_sale_min_qty == 'force':
+ if self.manual_force_sale_min_qty == "force":
force_sale_min_qty = True
- elif self.manual_force_sale_min_qty == 'not_force':
+ elif self.manual_force_sale_min_qty == "not_force":
force_sale_min_qty = False
else:
force_sale_min_qty = self.parent_id.force_sale_min_qty
force_sale_max_qty = False
- if self.manual_force_sale_max_qty == 'force':
+ if self.manual_force_sale_max_qty == "force":
force_sale_max_qty = True
- elif self.manual_force_sale_max_qty == 'not_force':
+ elif self.manual_force_sale_max_qty == "not_force":
force_sale_max_qty = False
else:
force_sale_max_qty = self.parent_id.force_sale_max_qty
diff --git a/sale_restricted_qty/models/product_product.py b/sale_restricted_qty/models/product_product.py
index 2ef51f58c64..3991b4eb86a 100644
--- a/sale_restricted_qty/models/product_product.py
+++ b/sale_restricted_qty/models/product_product.py
@@ -12,16 +12,16 @@ class ProductProduct(models.Model):
def _get_sale_restricted_qty(self):
res = super()._get_sale_restricted_qty()
force_sale_min_qty = False
- if self.manual_force_sale_min_qty == 'force':
+ if self.manual_force_sale_min_qty == "force":
force_sale_min_qty = True
- elif self.manual_force_sale_min_qty == 'not_force':
+ elif self.manual_force_sale_min_qty == "not_force":
force_sale_min_qty = False
else:
force_sale_min_qty = self.product_tmpl_id.force_sale_min_qty
force_sale_max_qty = False
- if self.manual_force_sale_max_qty == 'force':
+ if self.manual_force_sale_max_qty == "force":
force_sale_max_qty = True
- elif self.manual_force_sale_max_qty == 'not_force':
+ elif self.manual_force_sale_max_qty == "not_force":
force_sale_max_qty = False
else:
force_sale_max_qty = self.product_tmpl_id.force_sale_max_qty
diff --git a/sale_restricted_qty/models/product_restricted_qty_mixin.py b/sale_restricted_qty/models/product_restricted_qty_mixin.py
index 3213b200748..0d0f31cc96f 100644
--- a/sale_restricted_qty/models/product_restricted_qty_mixin.py
+++ b/sale_restricted_qty/models/product_restricted_qty_mixin.py
@@ -2,6 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
+
from odoo.addons import decimal_precision as dp
@@ -45,11 +46,12 @@ class ProductMinMultipleMixin(models.AbstractModel):
"Hierarchy is in this order :"
"Product/product Template/product category/parent categoroies ",
)
- manual_force_sale_min_qty = fields.Selection([
- ('use_parent', 'Use Parent Setting'),
- ('force', 'Yes'),
- ('not_force', 'No'),
- ],
+ manual_force_sale_min_qty = fields.Selection(
+ [
+ ("use_parent", "Use Parent Setting"),
+ ("force", "Yes"),
+ ("not_force", "No"),
+ ],
string="Manual Force Min Qty",
required=True,
default="use_parent",
@@ -80,11 +82,12 @@ class ProductMinMultipleMixin(models.AbstractModel):
"Hierarchy is in this order :"
"Product/product Template/product category/parent categoroies ",
)
- manual_force_sale_max_qty = fields.Selection([
- ('use_parent', 'Use Parent Setting'),
- ('force', 'Yes'),
- ('not_force', 'No'),
- ],
+ manual_force_sale_max_qty = fields.Selection(
+ [
+ ("use_parent", "Use Parent Setting"),
+ ("force", "Yes"),
+ ("not_force", "No"),
+ ],
required=True,
default="use_parent",
string="Manual Force Max Qty",
@@ -97,16 +100,20 @@ def _get_sale_restricted_qty(self):
self.ensure_one()
res = {
"sale_min_qty": self.manual_sale_min_qty,
- "force_sale_min_qty": self.manual_force_sale_min_qty == 'force',
+ "force_sale_min_qty": self.manual_force_sale_min_qty == "force",
"sale_max_qty": self.manual_sale_max_qty,
- "force_sale_max_qty": self.manual_force_sale_max_qty == 'force',
+ "force_sale_max_qty": self.manual_force_sale_max_qty == "force",
"sale_multiple_qty": self.manual_sale_multiple_qty,
}
return res
- @api.depends("manual_force_sale_min_qty", "manual_sale_min_qty",
- "manual_force_sale_max_qty", "manual_sale_max_qty",
- "manual_sale_multiple_qty")
+ @api.depends(
+ "manual_force_sale_min_qty",
+ "manual_sale_min_qty",
+ "manual_force_sale_max_qty",
+ "manual_sale_max_qty",
+ "manual_sale_multiple_qty",
+ )
def _compute_sale_restricted_qty(self):
for rec in self:
rec.update(rec._get_sale_restricted_qty())
diff --git a/sale_restricted_qty/models/product_template.py b/sale_restricted_qty/models/product_template.py
index 3aa9bebcf49..6b95d382d99 100644
--- a/sale_restricted_qty/models/product_template.py
+++ b/sale_restricted_qty/models/product_template.py
@@ -12,16 +12,16 @@ class ProductTemplate(models.Model):
def _get_sale_restricted_qty(self):
res = super()._get_sale_restricted_qty()
force_sale_min_qty = False
- if self.manual_force_sale_min_qty == 'force':
+ if self.manual_force_sale_min_qty == "force":
force_sale_min_qty = True
- elif self.manual_force_sale_min_qty == 'not_force':
+ elif self.manual_force_sale_min_qty == "not_force":
force_sale_min_qty = False
else:
force_sale_min_qty = self.categ_id.force_sale_min_qty
force_sale_max_qty = False
- if self.manual_force_sale_max_qty == 'force':
+ if self.manual_force_sale_max_qty == "force":
force_sale_max_qty = True
- elif self.manual_force_sale_max_qty == 'not_force':
+ elif self.manual_force_sale_max_qty == "not_force":
force_sale_max_qty = False
else:
force_sale_max_qty = self.categ_id.force_sale_max_qty
diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py
index e5860d54797..1cd22ba8e91 100644
--- a/sale_restricted_qty/models/sale.py
+++ b/sale_restricted_qty/models/sale.py
@@ -2,10 +2,11 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
-from odoo.addons import decimal_precision as dp
from odoo.exceptions import ValidationError
from odoo.tools import float_compare
+from odoo.addons import decimal_precision as dp
+
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
@@ -17,8 +18,7 @@ class SaleOrderLine(models.Model):
digits=dp.get_precision("Product Unit of Measure"),
)
force_sale_min_qty = fields.Boolean(
- compute="_compute_sale_restricted_qty",
- readonly=True, store=True
+ compute="_compute_sale_restricted_qty", readonly=True, store=True
)
is_qty_less_min_qty = fields.Boolean(
string="Qty < Min Qty", compute="_compute_is_qty_less_min_qty"
@@ -31,8 +31,7 @@ class SaleOrderLine(models.Model):
digits=dp.get_precision("Product Unit of Measure"),
)
force_sale_max_qty = fields.Boolean(
- compute="_compute_sale_restricted_qty",
- readonly=True, store=True
+ compute="_compute_sale_restricted_qty", readonly=True, store=True
)
is_qty_bigger_max_qty = fields.Boolean(
string="Qty > max Qty", compute="_compute_is_qty_bigger_max_qty"
@@ -48,14 +47,14 @@ class SaleOrderLine(models.Model):
)
@api.constrains(
- "product_uom_qty", "sale_min_qty", "sale_max_qty", "sale_multiple_qty")
+ "product_uom_qty", "sale_min_qty", "sale_max_qty", "sale_multiple_qty"
+ )
def check_constraint_restricted_qty(self):
msg = ""
invaild_min_lines = []
line_to_test = self.filtered(
- lambda sl: not sl.product_id.force_sale_min_qty and
- sl.is_qty_less_min_qty
+ lambda sl: not sl.product_id.force_sale_min_qty and sl.is_qty_less_min_qty
)
for line in line_to_test:
invaild_min_lines.append(
@@ -73,8 +72,7 @@ def check_constraint_restricted_qty(self):
)
invaild_max_lines = []
line_to_test = self.filtered(
- lambda sl: not sl.product_id.force_sale_max_qty and
- sl.is_qty_bigger_max_qty
+ lambda sl: not sl.product_id.force_sale_max_qty and sl.is_qty_bigger_max_qty
)
for line in line_to_test:
invaild_max_lines.append(
@@ -115,31 +113,39 @@ def _get_product_qty_in_product_unit(self):
self.ensure_one()
return self.product_uom._compute_quantity(
self.product_uom_qty, self.product_id.uom_id
- )
+ )
@api.depends("product_id", "product_uom_qty", "sale_min_qty")
def _compute_is_qty_less_min_qty(self):
for line in self:
rounding = line.product_uom.rounding
product_qty = line._get_product_qty_in_product_unit()
- line.is_qty_less_min_qty = line.sale_min_qty and (
- float_compare(
- product_qty, line.sale_min_qty, precision_rounding=rounding
+ line.is_qty_less_min_qty = (
+ line.sale_min_qty
+ and (
+ float_compare(
+ product_qty, line.sale_min_qty, precision_rounding=rounding
+ )
+ < 0
)
- < 0
- ) or False
+ or False
+ )
@api.depends("product_id", "product_uom_qty", "sale_max_qty")
def _compute_is_qty_bigger_max_qty(self):
for line in self:
rounding = line.product_uom.rounding
product_qty = line._get_product_qty_in_product_unit()
- line.is_qty_bigger_max_qty = line.sale_max_qty and (
- float_compare(
- product_qty, line.sale_max_qty, precision_rounding=rounding
+ line.is_qty_bigger_max_qty = (
+ line.sale_max_qty
+ and (
+ float_compare(
+ product_qty, line.sale_max_qty, precision_rounding=rounding
+ )
+ > 0
)
- > 0
- ) or False
+ or False
+ )
@api.depends("product_id", "product_uom_qty", "sale_multiple_qty")
def _compute_is_qty_not_multiple_qty(self):
@@ -148,24 +154,24 @@ def _compute_is_qty_not_multiple_qty(self):
line.product_uom_qty, line.product_id.uom_id
)
line.is_qty_not_multiple_qty = (
- line.sale_multiple_qty > 0
- and product_qty % line.sale_multiple_qty != 0
+ line.sale_multiple_qty > 0 and product_qty % line.sale_multiple_qty != 0
)
def _get_sale_restricted_qty(self):
"""Overridable function to change qty values (ex: form stock)"""
self.ensure_one()
res = {
- "sale_min_qty": (self.product_id and
- self.product_id.sale_min_qty or 0),
- "force_sale_min_qty": (self.product_id and
- self.product_id.force_sale_min_qty or False),
- "sale_max_qty": (self.product_id and
- self.product_id.sale_max_qty or 0),
- "force_sale_max_qty": (self.product_id and
- self.product_id.force_sale_max_qty or False),
- "sale_multiple_qty": (self.product_id and
- self.product_id.sale_multiple_qty or 0),
+ "sale_min_qty": (self.product_id and self.product_id.sale_min_qty or 0),
+ "force_sale_min_qty": (
+ self.product_id and self.product_id.force_sale_min_qty or False
+ ),
+ "sale_max_qty": (self.product_id and self.product_id.sale_max_qty or 0),
+ "force_sale_max_qty": (
+ self.product_id and self.product_id.force_sale_max_qty or False
+ ),
+ "sale_multiple_qty": (
+ self.product_id and self.product_id.sale_multiple_qty or 0
+ ),
}
return res
diff --git a/sale_restricted_qty/readme/CONTRIBUTORS.rst b/sale_restricted_qty/readme/CONTRIBUTORS.rst
index e803c0a3fba..ac5fa9a437c 100644
--- a/sale_restricted_qty/readme/CONTRIBUTORS.rst
+++ b/sale_restricted_qty/readme/CONTRIBUTORS.rst
@@ -1 +1,5 @@
* Mourad EL HADJ MIMOUN
+
+* `Ooops `_:
+
+ * Ashish Hirpara
diff --git a/sale_restricted_qty/static/description/index.html b/sale_restricted_qty/static/description/index.html
index a86a4ad781e..fd9a1082b62 100644
--- a/sale_restricted_qty/static/description/index.html
+++ b/sale_restricted_qty/static/description/index.html
@@ -367,7 +367,7 @@
Sale order min quantity
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
+
This module allows to define min, max and multiple order quantity on product.
Min and Max order quantity is either required or recommanded.
If you check “Force mini/max Qty” on product the min/max qty do not block sale
@@ -416,7 +416,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 smashing it by providing a detailed and welcomed
-feedback.
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.