From dd90188d8aef857031a8530af7ed237150be4f4d Mon Sep 17 00:00:00 2001 From: ps-tubtim Date: Thu, 19 Dec 2019 13:26:19 +0700 Subject: [PATCH] [IMP] stock_available: black, isort --- stock_available/__manifest__.py | 26 ++-- stock_available/models/product_product.py | 46 ++++--- stock_available/models/product_template.py | 62 +++++---- stock_available/models/res_config_settings.py | 59 +++++---- stock_available/tests/test_stock_available.py | 121 +++++++++--------- 5 files changed, 170 insertions(+), 144 deletions(-) diff --git a/stock_available/__manifest__.py b/stock_available/__manifest__.py index 608de28a4..81f644ff0 100644 --- a/stock_available/__manifest__.py +++ b/stock_available/__manifest__.py @@ -3,18 +3,18 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - 'name': 'Stock available to promise', - 'version': '12.0.1.0.1', - 'author': 'Numérigraphe, Sodexis, Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/stock-logistics-warehouse', - 'development_status': 'Production/Stable', - 'category': 'Warehouse', - 'depends': ['stock'], - 'license': 'AGPL-3', - 'data': [ - 'views/product_template_view.xml', - 'views/product_product_view.xml', - 'views/res_config_settings_views.xml', + "name": "Stock available to promise", + "version": "13.0.1.0.0", + "author": "Numérigraphe, Sodexis, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/stock-logistics-warehouse", + "development_status": "Production/Stable", + "category": "Warehouse", + "depends": ["stock"], + "license": "AGPL-3", + "data": [ + "views/product_template_view.xml", + "views/product_product_view.xml", + "views/res_config_settings_views.xml", ], - 'installable': True, + "installable": True, } diff --git a/stock_available/models/product_product.py b/stock_available/models/product_product.py index f65cd0516..4832fbc37 100644 --- a/stock_available/models/product_product.py +++ b/stock_available/models/product_product.py @@ -3,6 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import api, fields, models + from odoo.addons import decimal_precision as dp from odoo.addons.stock.models.product import OPERATORS @@ -13,27 +14,28 @@ class ProductProduct(models.Model): Useful implementations need to be installed through the Settings menu or by installing one of the modules stock_available_* """ - _inherit = 'product.product' + + _inherit = "product.product" @api.multi def _compute_available_quantities_dict(self): stock_dict = self._compute_quantities_dict( - self._context.get('lot_id'), - self._context.get('owner_id'), - self._context.get('package_id'), - self._context.get('from_date'), - self._context.get('to_date')) + self._context.get("lot_id"), + self._context.get("owner_id"), + self._context.get("package_id"), + self._context.get("from_date"), + self._context.get("to_date"), + ) res = {} for product in self: res[product.id] = { - 'immediately_usable_qty': stock_dict[product.id][ - 'virtual_available'], - 'potential_qty': 0.0 + "immediately_usable_qty": stock_dict[product.id]["virtual_available"], + "potential_qty": 0.0, } return res, stock_dict @api.multi - @api.depends('virtual_available') + @api.depends("virtual_available") def _compute_available_quantities(self): res, _ = self._compute_available_quantities_dict() for product in self: @@ -42,20 +44,22 @@ class ProductProduct(models.Model): product[key] = value immediately_usable_qty = fields.Float( - digits=dp.get_precision('Product Unit of Measure'), - compute='_compute_available_quantities', + digits=dp.get_precision("Product Unit of Measure"), + compute="_compute_available_quantities", search="_search_immediately_usable_qty", - string='Available to promise', + string="Available to promise", help="Stock for this Product that can be safely proposed " - "for sale to Customers.\n" - "The definition of this value can be configured to suit " - "your needs.") + "for sale to Customers.\n" + "The definition of this value can be configured to suit " + "your needs.", + ) potential_qty = fields.Float( - compute='_compute_available_quantities', - digits=dp.get_precision('Product Unit of Measure'), - string='Potential', + compute="_compute_available_quantities", + digits=dp.get_precision("Product Unit of Measure"), + string="Potential", help="Quantity of this Product that could be produced using " - "the materials already at hand.") + "the materials already at hand.", + ) @api.model def _search_immediately_usable_qty(self, operator, value): @@ -73,4 +77,4 @@ class ProductProduct(models.Model): for product in products: if OPERATORS[operator](product.immediately_usable_qty, value): product_ids.append(product.id) - return [('id', 'in', product_ids)] + return [("id", "in", product_ids)] diff --git a/stock_available/models/product_template.py b/stock_available/models/product_template.py index 1483f8a8b..13c4a0068 100644 --- a/stock_available/models/product_template.py +++ b/stock_available/models/product_template.py @@ -2,17 +2,20 @@ # Copyright 2016 Sodexis # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models, fields, api +from odoo import api, fields, models + from odoo.addons import decimal_precision as dp from odoo.addons.stock.models.product import OPERATORS class ProductTemplate(models.Model): - _inherit = 'product.template' + _inherit = "product.template" @api.multi - @api.depends('product_variant_ids.immediately_usable_qty', - 'product_variant_ids.potential_qty') + @api.depends( + "product_variant_ids.immediately_usable_qty", + "product_variant_ids.potential_qty", + ) def _compute_available_quantities(self): res = self._compute_available_quantities_dict() for product in self: @@ -23,40 +26,49 @@ class ProductTemplate(models.Model): @api.multi def _compute_available_quantities_dict(self): variants_dict, _ = self.mapped( - 'product_variant_ids')._compute_available_quantities_dict() + "product_variant_ids" + )._compute_available_quantities_dict() res = {} for template in self: immediately_usable_qty = sum( - [variants_dict[p.id]["immediately_usable_qty"] - - variants_dict[p.id]["potential_qty"] for p in - template.product_variant_ids]) + [ + variants_dict[p.id]["immediately_usable_qty"] + - variants_dict[p.id]["potential_qty"] + for p in template.product_variant_ids + ] + ) potential_qty = max( - [variants_dict[p.id]["potential_qty"] for p in - template.product_variant_ids] or [0.0]) + [ + variants_dict[p.id]["potential_qty"] + for p in template.product_variant_ids + ] + or [0.0] + ) res[template.id] = { - "immediately_usable_qty": immediately_usable_qty + - potential_qty, + "immediately_usable_qty": immediately_usable_qty + potential_qty, "potential_qty": potential_qty, } return res immediately_usable_qty = fields.Float( - digits=dp.get_precision('Product Unit of Measure'), - compute='_compute_available_quantities', + digits=dp.get_precision("Product Unit of Measure"), + compute="_compute_available_quantities", search="_search_immediately_usable_qty", - string='Available to promise', + string="Available to promise", help="Stock for this Product that can be safely proposed " - "for sale to Customers.\n" - "The definition of this value can be configured to suit " - "your needs") + "for sale to Customers.\n" + "The definition of this value can be configured to suit " + "your needs", + ) potential_qty = fields.Float( - compute='_compute_available_quantities', - digits=dp.get_precision('Product Unit of Measure'), - string='Potential', + compute="_compute_available_quantities", + digits=dp.get_precision("Product Unit of Measure"), + string="Potential", help="Quantity of this Product that could be produced using " - "the materials already at hand. " - "If the product has several variants, this will be the biggest " - "quantity that can be made for a any single variant.") + "the materials already at hand. " + "If the product has several variants, this will be the biggest " + "quantity that can be made for a any single variant.", + ) @api.model def _search_immediately_usable_qty(self, operator, value): @@ -74,4 +86,4 @@ class ProductTemplate(models.Model): for product in products: if OPERATORS[operator](product.immediately_usable_qty, value): product_ids.append(product.id) - return [('id', 'in', product_ids)] + return [("id", "in", product_ids)] diff --git a/stock_available/models/res_config_settings.py b/stock_available/models/res_config_settings.py index 3067791a2..824a04e72 100644 --- a/stock_available/models/res_config_settings.py +++ b/stock_available/models/res_config_settings.py @@ -8,62 +8,67 @@ from odoo import api, fields, models class ResConfigSettings(models.TransientModel): """Add options to easily install the submodules""" - _inherit = 'res.config.settings' + + _inherit = "res.config.settings" @api.model def _get_stock_available_mrp_based_on(self): """Gets the available languages for the selection.""" - pdct_fields = self.env['ir.model.fields'].search( - [('model', '=', 'product.product'), - ('ttype', '=', 'float')]) + pdct_fields = self.env["ir.model.fields"].search( + [("model", "=", "product.product"), ("ttype", "=", "float")] + ) return [ (field.name, field.field_description) for field in sorted(pdct_fields, key=lambda f: f.field_description) ] module_stock_available_immediately = fields.Boolean( - string='Exclude incoming goods', + string="Exclude incoming goods", help="This will subtract incoming quantities from the quantities " - "available to promise.\n" - "This installs the module stock_available_immediately.") + "available to promise.\n" + "This installs the module stock_available_immediately.", + ) module_stock_available_sale = fields.Boolean( - string='Exclude goods already in sale quotations', + string="Exclude goods already in sale quotations", help="This will subtract quantities from the sale quotations from " - "the quantities available to promise.\n" - "This installs the modules stock_available_sale.\n" - "If the modules sale and sale_delivery_date are not " - "installed, this will install them too") + "the quantities available to promise.\n" + "This installs the modules stock_available_sale.\n" + "If the modules sale and sale_delivery_date are not " + "installed, this will install them too", + ) module_stock_available_mrp = fields.Boolean( - string='Include the production potential', + string="Include the production potential", help="This will add the quantities of goods that can be " - "immediately manufactured, to the quantities available to " - "promise.\n" - "This installs the module stock_available_mrp.\n" - "If the module mrp is not installed, this will install it " - "too") + "immediately manufactured, to the quantities available to " + "promise.\n" + "This installs the module stock_available_mrp.\n" + "If the module mrp is not installed, this will install it " + "too", + ) stock_available_mrp_based_on = fields.Selection( _get_stock_available_mrp_based_on, - string='based on', + string="based on", help="Choose the field of the product which will be used to compute " - "potential.\nIf empty, Quantity On Hand is used.\n" - "Only the quantity fields have meaning for computing stock", + "potential.\nIf empty, Quantity On Hand is used.\n" + "Only the quantity fields have meaning for computing stock", ) @api.model def get_values(self): res = super(ResConfigSettings, self).get_values() - res.update(stock_available_mrp_based_on=self.env[ - 'ir.config_parameter'].sudo().get_param( - 'stock_available_mrp_based_on', - 'qty_available') + res.update( + stock_available_mrp_based_on=self.env["ir.config_parameter"] + .sudo() + .get_param("stock_available_mrp_based_on", "qty_available") ) return res @api.multi def set_values(self): super(ResConfigSettings, self).set_values() - self.env['ir.config_parameter'].sudo().set_param( - 'stock_available_mrp_based_on', self.stock_available_mrp_based_on) + self.env["ir.config_parameter"].sudo().set_param( + "stock_available_mrp_based_on", self.stock_available_mrp_based_on + ) diff --git a/stock_available/tests/test_stock_available.py b/stock_available/tests/test_stock_available.py index 976dd7c5f..5ffa91c0d 100644 --- a/stock_available/tests/test_stock_available.py +++ b/stock_available/tests/test_stock_available.py @@ -8,71 +8,74 @@ from odoo.tests.common import TransactionCase class TestStockLogisticsWarehouse(TransactionCase): def test_res_config(self): """Test the config file""" - stock_setting = self.env['res.config.settings'].create({}) + stock_setting = self.env["res.config.settings"].create({}) - self.assertEquals( - stock_setting.stock_available_mrp_based_on, - 'qty_available') - stock_setting.stock_available_mrp_based_on = 'immediately_usable_qty' + self.assertEquals(stock_setting.stock_available_mrp_based_on, "qty_available") + stock_setting.stock_available_mrp_based_on = "immediately_usable_qty" stock_setting.set_values() self.assertEquals( - stock_setting.stock_available_mrp_based_on, - 'immediately_usable_qty') + stock_setting.stock_available_mrp_based_on, "immediately_usable_qty" + ) def test01_stock_levels(self): """checking that immediately_usable_qty actually reflects \ the variations in stock, both on product and template""" - moveObj = self.env['stock.move'] - productObj = self.env['product.product'] - templateObj = self.env['product.template'] - supplier_location = self.env.ref('stock.stock_location_suppliers') - stock_location = self.env.ref('stock.stock_location_stock') - customer_location = self.env.ref('stock.stock_location_customers') - uom_unit = self.env.ref('uom.product_uom_unit') + moveObj = self.env["stock.move"] + productObj = self.env["product.product"] + templateObj = self.env["product.template"] + supplier_location = self.env.ref("stock.stock_location_suppliers") + stock_location = self.env.ref("stock.stock_location_stock") + customer_location = self.env.ref("stock.stock_location_customers") + uom_unit = self.env.ref("uom.product_uom_unit") # Create product template - templateAB = templateObj.create( - {'name': 'templAB', - 'uom_id': uom_unit.id, - }) + templateAB = templateObj.create({"name": "templAB", "uom_id": uom_unit.id}) # Create product A and B productA = productObj.create( - {'name': 'product A', - 'standard_price': 1, - 'type': 'product', - 'uom_id': uom_unit.id, - 'default_code': 'A', - 'product_tmpl_id': templateAB.id, - }) + { + "name": "product A", + "standard_price": 1, + "type": "product", + "uom_id": uom_unit.id, + "default_code": "A", + "product_tmpl_id": templateAB.id, + } + ) productB = productObj.create( - {'name': 'product B', - 'standard_price': 1, - 'type': 'product', - 'uom_id': uom_unit.id, - 'default_code': 'B', - 'product_tmpl_id': templateAB.id, - }) + { + "name": "product B", + "standard_price": 1, + "type": "product", + "uom_id": uom_unit.id, + "default_code": "B", + "product_tmpl_id": templateAB.id, + } + ) # Create a stock move from INCOMING to STOCK stockMoveInA = moveObj.create( - {'location_id': supplier_location.id, - 'location_dest_id': stock_location.id, - 'name': 'MOVE INCOMING -> STOCK ', - 'product_id': productA.id, - 'product_uom': productA.uom_id.id, - 'product_uom_qty': 2, - }) + { + "location_id": supplier_location.id, + "location_dest_id": stock_location.id, + "name": "MOVE INCOMING -> STOCK ", + "product_id": productA.id, + "product_uom": productA.uom_id.id, + "product_uom_qty": 2, + } + ) stockMoveInB = moveObj.create( - {'location_id': supplier_location.id, - 'location_dest_id': stock_location.id, - 'name': 'MOVE INCOMING -> STOCK ', - 'product_id': productB.id, - 'product_uom': productB.uom_id.id, - 'product_uom_qty': 3, - }) + { + "location_id": supplier_location.id, + "location_dest_id": stock_location.id, + "name": "MOVE INCOMING -> STOCK ", + "product_id": productB.id, + "product_uom": productB.uom_id.id, + "product_uom_qty": 3, + } + ) def compare_product_usable_qty(product, value): """ @@ -87,16 +90,16 @@ class TestStockLogisticsWarehouse(TransactionCase): product.refresh() self.assertEqual(product.immediately_usable_qty, value) # Now check search function - domain = [('immediately_usable_qty', '=', value)] + domain = [("immediately_usable_qty", "=", value)] results = self.env[product._name].search(domain) self.assertIn(product.id, results.ids) - domain = [('immediately_usable_qty', '!=', value)] + domain = [("immediately_usable_qty", "!=", value)] results = self.env[product._name].search(domain) self.assertNotIn(product.id, results.ids) - domain = [('immediately_usable_qty', '>', value-1)] + domain = [("immediately_usable_qty", ">", value - 1)] results = self.env[product._name].search(domain) self.assertIn(product.id, results.ids) - domain = [('immediately_usable_qty', '<', value+1)] + domain = [("immediately_usable_qty", "<", value + 1)] results = self.env[product._name].search(domain) self.assertIn(product.id, results.ids) @@ -123,14 +126,16 @@ class TestStockLogisticsWarehouse(TransactionCase): # Create a stock move from STOCK to CUSTOMER stockMoveOutA = moveObj.create( - {'location_id': stock_location.id, - 'location_dest_id': customer_location.id, - 'name': ' STOCK --> CUSTOMER ', - 'product_id': productA.id, - 'product_uom': productA.uom_id.id, - 'product_uom_qty': 1, - 'state': 'confirmed', - }) + { + "location_id": stock_location.id, + "location_dest_id": customer_location.id, + "name": " STOCK --> CUSTOMER ", + "product_id": productA.id, + "product_uom": productA.uom_id.id, + "product_uom_qty": 1, + "state": "confirmed", + } + ) stockMoveOutA._action_done() compare_product_usable_qty(productA, 1)