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 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N 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

+ + +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

+

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

+ +
+

Usage

+
    +
  1. +
    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”
    +
    +
  2. +
  3. +
    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.
    +
    +
  4. +
+
+
+

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

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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/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 @@ + + + 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. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

-

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 +

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

+

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

    @@ -410,7 +416,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.

@@ -434,7 +440,7 @@

Maintainers

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.

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. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runbot

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 @@

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.

@@ -431,6 +431,10 @@

Authors

Contributors

@@ -440,7 +444,9 @@

Maintainers

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.

+

Current 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/tests/test_sale.py b/sale_restricted_qty/tests/test_sale.py index b1bd517f1ab..425c9c8687d 100644 --- a/sale_restricted_qty/tests/test_sale.py +++ b/sale_restricted_qty/tests/test_sale.py @@ -1,12 +1,13 @@ # Copyright 2019 Akretion +# Update (Migration) 2022 Ooops - Ashish Hirpara # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import odoo.tests.common as common from odoo.exceptions import ValidationError +from odoo.tests import tagged -@common.at_install(False) -@common.post_install(True) +@tagged("post_install", "-at_install") class TestSaleOrderLineMinQty(common.TransactionCase): def setUp(self): super(TestSaleOrderLineMinQty, self).setUp() @@ -40,11 +41,13 @@ def setUp(self): } ) - self.product = self.product_model.create({ - "name": "Test product", - "force_sale_min_qty": False, - "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 refrech_sale_values(self, sale_order): sale_order.order_line._compute_sale_restricted_qty() @@ -60,16 +63,21 @@ def test_check_sale_order_min_qty_required(self): # Create sale order line with Qty less than min Qty with self.assertRaises(ValidationError): sale_order = self.sale_order_model.new( - {"partner_id": self.partner.id, - "order_line": [(0, 0, line_values)]} + {"partner_id": self.partner.id, "order_line": [(0, 0, line_values)]} ) sale_values = self.refrech_sale_values(sale_order) + sale_values.update( + { + "partner_shipping_id": sale_values["partner_id"], + "partner_invoice_id": sale_values["partner_id"], + "pricelist_id": 1, + } + ) 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) @@ -80,12 +88,11 @@ 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' + 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)]} + {"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) @@ -98,16 +105,14 @@ def test_check_sale_order_max_qty_required(self): # Create sale order line with Qty bigger than max Qty with self.assertRaises(ValidationError): 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)]} ) 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) @@ -118,12 +123,11 @@ def test_check_sale_order_max_qty_recommended(self): 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' + 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) @@ -137,16 +141,14 @@ def test_check_sale_order_multiple_qty_required(self): # Create sale order line with Qty not multiple Qty with self.assertRaises(ValidationError): 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)]} ) 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) @@ -163,18 +165,18 @@ def test_check_restricted_qty_category_hierarchy(self): 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.categ_parent.manual_force_sale_min_qty = "force" 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 self.categ.manual_sale_multiple_qty = 10 - self.categ.manual_force_sale_min_qty = 'not_force' + 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.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) diff --git a/sale_restricted_qty/views/product_category_views.xml b/sale_restricted_qty/views/product_category_views.xml index e1e92773169..41d857c5c7f 100644 --- a/sale_restricted_qty/views/product_category_views.xml +++ b/sale_restricted_qty/views/product_category_views.xml @@ -1,31 +1,55 @@ - + product.category - + - + - - - - - - diff --git a/sale_restricted_qty/views/product_product_views.xml b/sale_restricted_qty/views/product_product_views.xml index 5389c4c2b7a..7c6122fb1c7 100644 --- a/sale_restricted_qty/views/product_product_views.xml +++ b/sale_restricted_qty/views/product_product_views.xml @@ -1,34 +1,57 @@ - + - + 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 index 18379214104..fd49ebffe74 100644 --- a/sale_restricted_qty/views/product_template_views.xml +++ b/sale_restricted_qty/views/product_template_views.xml @@ -1,32 +1,56 @@ - + product.template - + - + - - - - - - diff --git a/sale_restricted_qty/views/sale_views.xml b/sale_restricted_qty/views/sale_views.xml index 6d42932c483..7459d72133f 100644 --- a/sale_restricted_qty/views/sale_views.xml +++ b/sale_restricted_qty/views/sale_views.xml @@ -1,56 +1,92 @@ - + sale.order - + - - - - - - - - - - From acb6e7af214ef6ef20d3bff788bcb89ab0cc7f9b Mon Sep 17 00:00:00 2001 From: Francesco Foresti Date: Fri, 8 Apr 2022 15:01:09 +0000 Subject: [PATCH 12/19] Translated using Weblate (Italian) Currently translated at 100.0% (54 of 54 strings) Translation: sale-workflow-14.0/sale-workflow-14.0-sale_restricted_qty Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-14-0/sale-workflow-14-0-sale_restricted_qty/it/ --- sale_restricted_qty/i18n/it.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sale_restricted_qty/i18n/it.po b/sale_restricted_qty/i18n/it.po index bc3367ea42c..ff10808710e 100644 --- a/sale_restricted_qty/i18n/it.po +++ b/sale_restricted_qty/i18n/it.po @@ -6,8 +6,8 @@ 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" +"PO-Revision-Date: 2022-04-08 17:05+0000\n" +"Last-Translator: Francesco Foresti \n" "Language-Team: none\n" "Language: it\n" "MIME-Version: 1.0\n" @@ -385,7 +385,7 @@ 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" +msgstr "Riga ordine di vendita" #. module: sale_restricted_qty #: selection:product.category,manual_force_sale_max_qty:0 From 142e9ac9bc80a693decccb3233eb5f8697920190 Mon Sep 17 00:00:00 2001 From: Ilyas Date: Thu, 27 Oct 2022 12:57:10 +0600 Subject: [PATCH 13/19] [IMP] sale_restricted_qty: show sale_min sale_max optionally --- sale_restricted_qty/__manifest__.py | 2 +- sale_restricted_qty/views/sale_views.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sale_restricted_qty/__manifest__.py b/sale_restricted_qty/__manifest__.py index 8b0a8471ade..7036e63614a 100644 --- a/sale_restricted_qty/__manifest__.py +++ b/sale_restricted_qty/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Sale order min quantity", - "version": "14.0.1.0.1", + "version": "14.0.1.1.0", "category": "Sales Management", "author": "Akretion, Odoo Community Association (OCA)", "contributors": ["Ashish Hirpara"], diff --git a/sale_restricted_qty/views/sale_views.xml b/sale_restricted_qty/views/sale_views.xml index 7459d72133f..cae2196f01b 100644 --- a/sale_restricted_qty/views/sale_views.xml +++ b/sale_restricted_qty/views/sale_views.xml @@ -62,10 +62,10 @@ expr="//field[@name='order_line']/tree/field[@name='product_uom_qty']" position="after" > - + - + From 97cc911e3fd152a7d035693b8267ee104b3a89ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Thu, 1 Dec 2022 14:01:27 +0100 Subject: [PATCH 14/19] [IMP] sale_restricted_qty: Change dp.get_precision digits --- sale_restricted_qty/__manifest__.py | 2 +- .../models/product_restricted_qty_mixin.py | 14 ++++++-------- sale_restricted_qty/models/sale.py | 8 +++----- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/sale_restricted_qty/__manifest__.py b/sale_restricted_qty/__manifest__.py index 7036e63614a..3af3de87e4d 100644 --- a/sale_restricted_qty/__manifest__.py +++ b/sale_restricted_qty/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Sale order min quantity", - "version": "14.0.1.1.0", + "version": "14.0.1.1.1", "category": "Sales Management", "author": "Akretion, Odoo Community Association (OCA)", "contributors": ["Ashish Hirpara"], diff --git a/sale_restricted_qty/models/product_restricted_qty_mixin.py b/sale_restricted_qty/models/product_restricted_qty_mixin.py index 0d0f31cc96f..22ecd557434 100644 --- a/sale_restricted_qty/models/product_restricted_qty_mixin.py +++ b/sale_restricted_qty/models/product_restricted_qty_mixin.py @@ -3,8 +3,6 @@ from odoo import api, fields, models -from odoo.addons import decimal_precision as dp - class ProductMinMultipleMixin(models.AbstractModel): _name = "product.restricted.qty.mixin" @@ -18,10 +16,10 @@ class ProductMinMultipleMixin(models.AbstractModel): " 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"), + digits="Product Unit of Measure", ) manual_sale_multiple_qty = fields.Float( - string="Multiple Sale Qty", digits=dp.get_precision("Product Unit of Measure") + string="Multiple Sale Qty", digits="Product Unit of Measure" ) sale_min_qty = fields.Float( compute="_compute_sale_restricted_qty", @@ -31,10 +29,10 @@ class ProductMinMultipleMixin(models.AbstractModel): " 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"), + digits="Product Unit of Measure", ) manual_sale_min_qty = fields.Float( - string="Min Sale Qty", digits=dp.get_precision("Product Unit of Measure") + string="Min Sale Qty", digits="Product Unit of Measure" ) force_sale_min_qty = fields.Boolean( compute="_compute_sale_restricted_qty", @@ -67,10 +65,10 @@ class ProductMinMultipleMixin(models.AbstractModel): " 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"), + digits="Product Unit of Measure", ) manual_sale_max_qty = fields.Float( - string="Max Sale Qty", digits=dp.get_precision("Product Unit of Measure") + string="Max Sale Qty", digits="Product Unit of Measure" ) force_sale_max_qty = fields.Boolean( compute="_compute_sale_restricted_qty", diff --git a/sale_restricted_qty/models/sale.py b/sale_restricted_qty/models/sale.py index 1cd22ba8e91..2ae03714d87 100644 --- a/sale_restricted_qty/models/sale.py +++ b/sale_restricted_qty/models/sale.py @@ -5,8 +5,6 @@ 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" @@ -15,7 +13,7 @@ class SaleOrderLine(models.Model): string="Min Qty", compute="_compute_sale_restricted_qty", store=True, - digits=dp.get_precision("Product Unit of Measure"), + digits="Product Unit of Measure", ) force_sale_min_qty = fields.Boolean( compute="_compute_sale_restricted_qty", readonly=True, store=True @@ -28,7 +26,7 @@ class SaleOrderLine(models.Model): string="Max Qty", compute="_compute_sale_restricted_qty", store=True, - digits=dp.get_precision("Product Unit of Measure"), + digits="Product Unit of Measure", ) force_sale_max_qty = fields.Boolean( compute="_compute_sale_restricted_qty", readonly=True, store=True @@ -40,7 +38,7 @@ class SaleOrderLine(models.Model): string="Multiple Qty", compute="_compute_sale_restricted_qty", store=True, - digits=dp.get_precision("Product Unit of Measure"), + digits="Product Unit of Measure", ) is_qty_not_multiple_qty = fields.Boolean( string="Not Multiple Qty", compute="_compute_is_qty_not_multiple_qty" From 67981ee154f58b2e53981281e8637e263061943e Mon Sep 17 00:00:00 2001 From: Francesco Foresti Date: Tue, 3 Jan 2023 11:45:48 +0000 Subject: [PATCH 15/19] Translated using Weblate (Italian) Currently translated at 100.0% (54 of 54 strings) Translation: sale-workflow-14.0/sale-workflow-14.0-sale_restricted_qty Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-14-0/sale-workflow-14-0-sale_restricted_qty/it/ --- sale_restricted_qty/i18n/it.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sale_restricted_qty/i18n/it.po b/sale_restricted_qty/i18n/it.po index ff10808710e..031356fef83 100644 --- a/sale_restricted_qty/i18n/it.po +++ b/sale_restricted_qty/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2022-04-08 17:05+0000\n" +"PO-Revision-Date: 2023-01-03 11:48+0000\n" "Last-Translator: Francesco Foresti \n" "Language-Team: none\n" "Language: it\n" @@ -14,7 +14,7 @@ msgstr "" "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" +"X-Generator: Weblate 4.14.1\n" #. module: sale_restricted_qty #: code:addons/sale_restricted_qty/models/sale.py:89 @@ -409,4 +409,4 @@ msgstr "Usa Impostazioni Genitore" #: selection:product.template,manual_force_sale_max_qty:0 #: selection:product.template,manual_force_sale_min_qty:0 msgid "Yes" -msgstr "Si" +msgstr "Sì" From ea2e5dd924a69cf590599247fbf6e3f1761b1ebe Mon Sep 17 00:00:00 2001 From: Claude R Perrin Date: Sun, 11 Jun 2023 13:35:57 +0000 Subject: [PATCH 16/19] Added translation using Weblate (French) --- sale_restricted_qty/i18n/fr.po | 444 +++++++++++++++++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 sale_restricted_qty/i18n/fr.po diff --git a/sale_restricted_qty/i18n/fr.po b/sale_restricted_qty/i18n/fr.po new file mode 100644 index 00000000000..8e9595c6e21 --- /dev/null +++ b/sale_restricted_qty/i18n/fr.po @@ -0,0 +1,444 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_restricted_qty +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-06-17 11:09+0000\n" +"Last-Translator: Claude R Perrin \n" +"Language-Team: none\n" +"Language: fr\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.17\n" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "" +"\n" +"* If you want sell quantity bigger than max Quantity,Check \"force max quatity\" on product" +msgstr "" +"\n" +"* Si vous voulez vendre une quantité supérieure à la quantité maximale, " +"cochez « Forcer Qté Max » sur l'article" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "" +"\n" +"* If you want sell quantity less than Min Quantity,Check \"force min quatity\" on product" +msgstr "" +"\n" +"* Si vous souhaitez vendre une quantité inférieure à la quantité minimale, " +"cochez « Forcer Qté Min » sur l'article" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, 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" +"* Si vous souhaitez vendre la quantité et non plusieurs, définissez la " +"quantité multiple sur 0 sur l'article ou le modèle d'article ou la catégorie " +"d'article" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "Check maximum order quantity for this products: * \n" +msgstr "Vérifiez la quantité maximale de commande pour ces articles : * \n" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "Check minimum order quantity for this products: * \n" +msgstr "Vérifiez la quantité minimale de commande pour ces articles : * \n" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "Check multiple order quantity for this products: * \n" +msgstr "Vérifiez la quantité de commande multiple pour ces articles : * \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 "" +"Définir si l’utilisateur peut forcer la qté max de commande 'Si ce n’est pas " +"défini', Odoo utilisera la valeur définie dans l’objet parent. La hiérarchie " +"suit cet ordre :Produit/Modèle de produit/catégorie de produit/catégories " +"parentes " + +#. 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 "" +"Définir si l’utilisateur peut forcer la qté min de commande 'Si ce n’est pas " +"défini', Odoo utilisera la valeur définie dans l’objet parent. La hiérarchie " +"suit cet ordre :Produit/Modèle de produit/catégorie de produit/catégories " +"parentes " + +#. 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 "" +"Définir la vente max qté 'S’elle n’est pas définie, Odoo utilisera la valeur " +"définie dans l’objet parent. La hiérarchie suite cet ordre :Produit/Modèle " +"de produit/catégorie de produit/catégories parentes " + +#. 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 "" +"Définir la vente min qté 'Si elle n’est pas définie, Odoo utilisera la " +"valeur définie dans l’objet parent. La hiérarchie suit cet ordre :Produit/" +"Modèle de produit/catégorie de produit/catégories parentes " + +#. 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 "" +"Définir la vente multiple 'Si elle n’est pas défini', Odoo utilisera la " +"valeur définie dans l’objet parent. La hiérarchie suit cet ordre :Produit/" +"Modèle de produit/catégorie de produit/catégories parentes " + +#. 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 "Nom affiché" + +#. 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 "Forcer qté Max" + +#. 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 "Forcer qté Min" + +#. 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 "Forcer qté max vendable" + +#. 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 "Forcer qté min vendable" + +#. 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 "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 "" +"Si forcer qté max est cochée, la quantité max n’est qu’une valeur " +"indicative. Si c’est pas coché, nous vérifions la valeur parente" + +#. 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 "" +"Si forcer qté min est cochée, la quantité min n’est qu’indicative. Si ce n’" +"est pas coché, nous vérifions la valeur parente" + +#. 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 "Dernière modification le" + +#. 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 "Force manuellement Qté max" + +#. 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 "Force manuellement Qté min" + +#. module: sale_restricted_qty +#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_max_qty +msgid "Max Qty" +msgstr "Qté max" + +#. module: sale_restricted_qty +#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form +msgid "Max Quantity Exceeded" +msgstr "Quantité maximale dépassée" + +#. module: sale_restricted_qty +#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form +msgid "Max Quantity Recommended" +msgstr "Quantité maximale recommandée" + +#. 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é max vendable" + +#. 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é Min" + +#. module: sale_restricted_qty +#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form +msgid "Min Quantity Recommended" +msgstr "Quantité minimale recommandée" + +#. module: sale_restricted_qty +#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form +msgid "Min Quantity Required" +msgstr "Quantité minimale requise" + +#. 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 "Qty min vendable" + +#. module: sale_restricted_qty +#: model:ir.model.fields,field_description:sale_restricted_qty.field_sale_order_line__sale_multiple_qty +msgid "Multiple Qty" +msgstr "Quantité multiple" + +#. module: sale_restricted_qty +#: model_terms:ir.ui.view,arch_db:sale_restricted_qty.view_order_form +msgid "Multiple Quantity Required" +msgstr "Quantité multiple requise" + +#. 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 "Qty vente multiple" + +#. module: sale_restricted_qty +#: 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 "Non" + +#. 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 "Pas de qté multiples" + +#. module: sale_restricted_qty +#: model:ir.model,name:sale_restricted_qty.model_product_product +msgid "Product" +msgstr "Article" + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "Product \"%s\": Min Quantity %s." +msgstr "Article « %s » : quantité minimale %s." + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "Product \"%s\": max Quantity %s." +msgstr "Article « %s » : quantité maximale %s." + +#. module: sale_restricted_qty +#: code:addons/sale_restricted_qty/models/sale.py:0 +#, python-format +msgid "Product \"%s\": multiple Quantity %s." +msgstr "Articles « %s » : quantités multiples %s." + +#. module: sale_restricted_qty +#: model:ir.model,name:sale_restricted_qty.model_product_category +msgid "Product Category" +msgstr "Catégorie d'articles" + +#. module: sale_restricted_qty +#: model:ir.model,name:sale_restricted_qty.model_product_restricted_qty_mixin +msgid "Product Restrict Qty Mixin" +msgstr "Mixin limitations quantités vendables" + +#. module: sale_restricted_qty +#: model:ir.model,name:sale_restricted_qty.model_product_template +msgid "Product Template" +msgstr "Modèle d'article" + +#. 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é min" + +#. 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é max" + +#. 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é max vendable" + +#. 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é max vendable" + +#. 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é multiple par commande" + +#. 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é min vendable" + +#. 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é min vendable" + +#. 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é multiple vendable" + +#. 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 "Limitations des quantités vendables" + +#. module: sale_restricted_qty +#: model:ir.model,name:sale_restricted_qty.model_sale_order_line +msgid "Sales Order Line" +msgstr "Ligne de bon de commande" + +#. module: sale_restricted_qty +#: 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 "Utiliser le paramètre parent" + +#. module: sale_restricted_qty +#: 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 "Oui" From 4ba9d5c1e3e10dea3a5545ea99bd11a8ae00aeeb Mon Sep 17 00:00:00 2001 From: mymage Date: Wed, 19 Apr 2023 12:19:00 +0000 Subject: [PATCH 17/19] Translated using Weblate (Italian) Currently translated at 100.0% (54 of 54 strings) Translation: sale-workflow-14.0/sale-workflow-14.0-sale_restricted_qty Translate-URL: https://translation.odoo-community.org/projects/sale-workflow-14-0/sale-workflow-14-0-sale_restricted_qty/it/ --- sale_restricted_qty/README.rst | 15 ++++--- sale_restricted_qty/i18n/it.po | 10 ++--- .../static/description/index.html | 40 ++++++++++--------- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/sale_restricted_qty/README.rst b/sale_restricted_qty/README.rst index be2b9b45883..4c6e334f40a 100644 --- a/sale_restricted_qty/README.rst +++ b/sale_restricted_qty/README.rst @@ -2,10 +2,13 @@ Sale order min quantity ======================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:9d34c5ce0bf0071dd40afa3f708f0f3d3f2098e4ca0d4b26d65bb1a8a40a23e0 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ Sale order min quantity .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :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/14.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=14.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module allows to define min, max and multiple order quantity on product. Min and Max order quantity is either required or recommanded. @@ -60,7 +63,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 +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/sale_restricted_qty/i18n/it.po b/sale_restricted_qty/i18n/it.po index 031356fef83..c6df581e4b1 100644 --- a/sale_restricted_qty/i18n/it.po +++ b/sale_restricted_qty/i18n/it.po @@ -6,15 +6,15 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2023-01-03 11:48+0000\n" -"Last-Translator: Francesco Foresti \n" +"PO-Revision-Date: 2023-06-20 14:09+0000\n" +"Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.14.1\n" +"X-Generator: Weblate 4.17\n" #. module: sale_restricted_qty #: code:addons/sale_restricted_qty/models/sale.py:89 @@ -127,7 +127,7 @@ 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 "Nome Visualizzato" +msgstr "Nome visualizzato" #. module: sale_restricted_qty #: model:ir.model.fields,field_description:sale_restricted_qty.field_product_category__force_sale_max_qty @@ -308,7 +308,7 @@ 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" +msgstr "Categoria prodotto" #. module: sale_restricted_qty #: model:ir.model,name:sale_restricted_qty.model_product_restricted_qty_mixin diff --git a/sale_restricted_qty/static/description/index.html b/sale_restricted_qty/static/description/index.html index fd9a1082b62..8cf3afe2190 100644 --- a/sale_restricted_qty/static/description/index.html +++ b/sale_restricted_qty/static/description/index.html @@ -1,20 +1,20 @@ - + - + Sale order min quantity