From 171bbf8ad43435fc9bcca1c3823400da3b269b04 Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Thu, 29 Mar 2018 13:50:01 +0200 Subject: [PATCH 1/7] refactor module name --- stock_coverage/README.md | 3 + stock_coverage/__init__.py | 1 + stock_coverage/__openerp__.py | 23 ++++ stock_coverage/data/cron.xml | 14 ++ stock_coverage/models/__init__.py | 1 + stock_coverage/models/product_template.py | 124 ++++++++++++++++++ stock_coverage/tests/__init__.py | 1 + stock_coverage/tests/test_stock_coverage.py | 101 ++++++++++++++ .../views/product_template_view.xml | 40 ++++++ 9 files changed, 308 insertions(+) create mode 100644 stock_coverage/README.md create mode 100644 stock_coverage/__init__.py create mode 100644 stock_coverage/__openerp__.py create mode 100644 stock_coverage/data/cron.xml create mode 100644 stock_coverage/models/__init__.py create mode 100644 stock_coverage/models/product_template.py create mode 100644 stock_coverage/tests/__init__.py create mode 100644 stock_coverage/tests/test_stock_coverage.py create mode 100644 stock_coverage/views/product_template_view.xml diff --git a/stock_coverage/README.md b/stock_coverage/README.md new file mode 100644 index 0000000..778224d --- /dev/null +++ b/stock_coverage/README.md @@ -0,0 +1,3 @@ +- initializing the computed fields + - daily cron + - manual trigger diff --git a/stock_coverage/__init__.py b/stock_coverage/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/stock_coverage/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_coverage/__openerp__.py b/stock_coverage/__openerp__.py new file mode 100644 index 0000000..7348736 --- /dev/null +++ b/stock_coverage/__openerp__.py @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +{ + 'name': 'Product - Stock Coverage', + 'version': '9.0.1', + 'category': 'Product', + 'description': """ +Shows figures in the product form related to stock coverage +There are settings in Inventory/settings to define the calculation range and +the display range. + """, + 'author': 'coop it easy', + 'website': 'coopiteasy.be', + 'license': 'AGPL-3', + 'depends': [ + 'product', + 'point_of_sale', + 'stock' + ], + 'data': [ + 'views/product_template_view.xml', + 'data/cron.xml', + ], +} diff --git a/stock_coverage/data/cron.xml b/stock_coverage/data/cron.xml new file mode 100644 index 0000000..12775ac --- /dev/null +++ b/stock_coverage/data/cron.xml @@ -0,0 +1,14 @@ + + + + Stock Coverage - Update Article Consumption + 24 + hours + -1 + + product.template + _batch_compute_total_consumption + () + + + diff --git a/stock_coverage/models/__init__.py b/stock_coverage/models/__init__.py new file mode 100644 index 0000000..e8fa8f6 --- /dev/null +++ b/stock_coverage/models/__init__.py @@ -0,0 +1 @@ +from . import product_template diff --git a/stock_coverage/models/product_template.py b/stock_coverage/models/product_template.py new file mode 100644 index 0000000..60be025 --- /dev/null +++ b/stock_coverage/models/product_template.py @@ -0,0 +1,124 @@ +# -*- encoding: utf-8 -*- +from openerp import models, fields, api +import datetime as dt + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + consumption_calculation_method = fields.Selection( + selection=[('sales_history', 'Sales History')], + string='Consumption Calculation Method', + default='sales_history', + ) + calculation_range = fields.Integer( + 'Calculation range (days)', + default=365, # todo sensible defaults, 14, 28? + ) + + average_consumption = fields.Float( + string='Average Consumption', + compute='_compute_average_daily_consumption', + readonly=True, + digits=(100, 2), + ) + + total_consumption = fields.Float( + string='Total Consumption', + default=0, + readonly=True, + digits=(100, 2), + ) + + estimated_stock_coverage = fields.Float( + string='Estimated Stock Coverage (days)', + compute='_compute_estimated_stock_coverage', + default=0, + digits=(100, 2), + readonly=True, + ) + + @api.multi + @api.depends('calculation_range') + def _compute_average_daily_consumption(self): + for template in self: + if template.calculation_range > 0: + avg = template.total_consumption / template.calculation_range + else: + avg = 0 + template.average_consumption = avg + + return True + + @api.multi + @api.onchange('calculation_range') + def _compute_total_consumption(self): + for template in self: + products = ( + self.env['product.product'] + .search([('product_tmpl_id', '=', template.id)])) + + today = dt.date.today() + pol_date_limit = ( + today - dt.timedelta(days=template.calculation_range)) + + order_lines = ( + self.env['pos.order.line'] + .search([ + ('product_id', 'in', products.ids), + ('create_date', '>', + fields.Datetime.to_string(pol_date_limit)) + ]) + ) + + if order_lines: + order_lines = order_lines.filtered( + lambda oi: oi.order_id.state in ['done', 'invoiced', 'paid']) # noqa + res = sum(order_lines.mapped('qty')) + else: + res = 0 + template.total_consumption = res + return True + + @api.multi + @api.depends('calculation_range') + def _compute_estimated_stock_coverage(self): + for product_template in self: + qty = product_template.qty_available + avg = product_template.average_consumption + if avg > 0: + product_template.estimated_stock_coverage = qty / avg + else: + # todo what would be a good default value? (not float(inf)) + product_template.estimated_stock_coverage = 9999 + + return True + + @api.model + def _batch_compute_total_consumption(self): + products = ( + self.env['product.template'] + .search([('active', '=', True)]) + ) + + query = """ + select + template.id as product_template_id, + sum(pol.qty) as total_consumption + from pos_order_line pol + join pos_order po ON pol.order_id = po.id + join product_product product ON pol.product_id = product.id + join product_template template ON product.product_tmpl_id = template.id + where po.state in ('done', 'invoiced', 'paid') + and template.active + and pol.create_date + BETWEEN date_trunc('day', now()) - calculation_range * interval '1 days' + and date_trunc('day', now()) + group by product_template_id + """ + + self.env.cr.execute(query) + results = {pid: qty for pid, qty in self.env.cr.fetchall()} + + for product in products: + product.total_consumption = results.get(product.id, product.total_consumption) diff --git a/stock_coverage/tests/__init__.py b/stock_coverage/tests/__init__.py new file mode 100644 index 0000000..fc398f2 --- /dev/null +++ b/stock_coverage/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_coverage diff --git a/stock_coverage/tests/test_stock_coverage.py b/stock_coverage/tests/test_stock_coverage.py new file mode 100644 index 0000000..43777db --- /dev/null +++ b/stock_coverage/tests/test_stock_coverage.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +from collections import namedtuple + +from openerp.tests.common import TransactionCase +from openerp.addons.stock.product import product_template as ProductTemplate +from openerp.addons.stock.product import product_product as ProductProduct +import datetime as dt + +# fixme setup tests based on demo data, test on a clean database + +_datetimes = map( + lambda d: d.strftime('%Y-%m-%d %H:%M:%S'), + (dt.datetime.now() - dt.timedelta(days=d) for d in range(0, 24, 2))) + +_quantities = [0.64, 6.45, 9.65, 1.76, 9.14, 3.99, + 6.92, 2.25, 6.91, 1.44, 6.52, 1.44] + + +class TestProductTemplate(TransactionCase): + + def setUp(self, *args, **kwargs): + result = super(TestProductTemplate, self).setUp(*args, **kwargs) + + test_product_template = ( + self.env['product.template'] + .create({'name': 'test product template', + 'calculation_range': 14, + 'consumption_calculation_method': 'sales_history', + 'product_template_id': 0, + }) + ) + + pid = ( + self.env['product.product'] + .search([('product_tmpl_id', '=', test_product_template.id)]) + .ids + ).pop() + + for date, qty in zip(_datetimes, _quantities): + (self.env['pos.order.line'] + .create({'create_date': date, + 'qty': qty, + 'product_id': pid, + }) + ) + + def _product_available(*args, **kwargs): + products = ( + self.env['product.product'] + .search([ + ('product_tmpl_id', '=', test_product_template.id)]) + ) + mock_data = { + 'qty_available': 53.2, + 'incoming_qty': 14, + 'outgoing_qty': 4.1, + 'virtual_available': 53.2 + 14 - 4.1, + } + return {pid: mock_data for pid in products.ids} + + # mock area + # ProductTemplate._product_available = _product_available + # ProductProduct._product_available = _product_available + # Order = namedtuple('Order', ['id', 'state']) + # PosOrderLine.order_id = Order('1', 'done') + + test_product_template._compute_total_consumption() + self.product_template_id = test_product_template.id + + return result + + def test_create(self): + """Create a simple product template""" + Template = self.env['product.template'] + product = Template.create({'name': 'Test create product'}) + self.assertEqual(product.name, 'Test create product') + + def test_compute_average_daily_consumption(self): + """Test computed field average_daily_consumption""" + ProductTemplate = self.env['product.template'] + product_template = ProductTemplate.browse(self.product_template_id) + + computed_value = product_template.average_consumption + expected_value = 4.08 + self.assertAlmostEqual(computed_value, expected_value, 7) + + def test_compute_total_consumption(self): + """Test total consumption was computed in setup""" + ProductTemplate = self.env['product.template'] + product_template = ProductTemplate.browse(self.product_template_id) + computed_value = product_template.total_consumption + expected_value = 57.11 + self.assertAlmostEqual(computed_value, expected_value) + + # def test_compute_estimated_stock_coverage(self): + # """Test computed field estimated_stock_coverage""" + # ProductTemplate = self.env['product.template'] + # product_template = ProductTemplate.browse(self.product_template_id) + # computed_value = product_template.estimated_stock_coverage + # expected_value = 13.04 + # self.assertAlmostEqual(computed_value, expected_value) diff --git a/stock_coverage/views/product_template_view.xml b/stock_coverage/views/product_template_view.xml new file mode 100644 index 0000000..5eb634e --- /dev/null +++ b/stock_coverage/views/product_template_view.xml @@ -0,0 +1,40 @@ + + + + + template.consumption.form + product.template + + + + + + + + + + + + + + + + + template.consumption.tree + product.template + + + + + + + + + + + + + + + + From 9fede5a91c82fbad939a4145167a21812b1c914b Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Thu, 29 Mar 2018 13:52:52 +0200 Subject: [PATCH 2/7] store and compute total_consumption --- stock_coverage/README.md | 1 + stock_coverage/models/product_template.py | 15 ++++++++------- stock_coverage/tests/test_stock_coverage.py | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/stock_coverage/README.md b/stock_coverage/README.md index 778224d..5082494 100644 --- a/stock_coverage/README.md +++ b/stock_coverage/README.md @@ -1,3 +1,4 @@ +# todo - initializing the computed fields - daily cron - manual trigger diff --git a/stock_coverage/models/product_template.py b/stock_coverage/models/product_template.py index 60be025..951c7ac 100644 --- a/stock_coverage/models/product_template.py +++ b/stock_coverage/models/product_template.py @@ -26,6 +26,8 @@ class ProductTemplate(models.Model): total_consumption = fields.Float( string='Total Consumption', default=0, + compute='_compute_total_consumption', + store=True, readonly=True, digits=(100, 2), ) @@ -39,7 +41,7 @@ class ProductTemplate(models.Model): ) @api.multi - @api.depends('calculation_range') + @api.depends('total_consumption') def _compute_average_daily_consumption(self): for template in self: if template.calculation_range > 0: @@ -51,7 +53,7 @@ class ProductTemplate(models.Model): return True @api.multi - @api.onchange('calculation_range') + @api.depends('calculation_range') def _compute_total_consumption(self): for template in self: products = ( @@ -73,15 +75,14 @@ class ProductTemplate(models.Model): if order_lines: order_lines = order_lines.filtered( - lambda oi: oi.order_id.state in ['done', 'invoiced', 'paid']) # noqa - res = sum(order_lines.mapped('qty')) + lambda ol: ol.order_id.state in ['done', 'invoiced', 'paid']) # noqa + template.total_consumption = sum(order_lines.mapped('qty')) else: - res = 0 - template.total_consumption = res + template.total_consumption = 0 return True @api.multi - @api.depends('calculation_range') + @api.depends('total_consumption') def _compute_estimated_stock_coverage(self): for product_template in self: qty = product_template.qty_available diff --git a/stock_coverage/tests/test_stock_coverage.py b/stock_coverage/tests/test_stock_coverage.py index 43777db..d8696ea 100644 --- a/stock_coverage/tests/test_stock_coverage.py +++ b/stock_coverage/tests/test_stock_coverage.py @@ -58,7 +58,7 @@ class TestProductTemplate(TransactionCase): } return {pid: mock_data for pid in products.ids} - # mock area + # mock area fixme # ProductTemplate._product_available = _product_available # ProductProduct._product_available = _product_available # Order = namedtuple('Order', ['id', 'state']) @@ -92,7 +92,7 @@ class TestProductTemplate(TransactionCase): expected_value = 57.11 self.assertAlmostEqual(computed_value, expected_value) - # def test_compute_estimated_stock_coverage(self): + # def test_compute_estimated_stock_coverage(self): fixme # """Test computed field estimated_stock_coverage""" # ProductTemplate = self.env['product.template'] # product_template = ProductTemplate.browse(self.product_template_id) From 58f92089ad152fa51405318910f8f937273e385f Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Fri, 8 Jun 2018 11:33:21 +0200 Subject: [PATCH 3/7] flake stock coverage --- stock_coverage/__openerp__.py | 1 + stock_coverage/models/product_template.py | 12 ++++++------ stock_coverage/tests/test_stock_coverage.py | 7 +------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/stock_coverage/__openerp__.py b/stock_coverage/__openerp__.py index 7348736..a7640b8 100644 --- a/stock_coverage/__openerp__.py +++ b/stock_coverage/__openerp__.py @@ -13,6 +13,7 @@ the display range. 'license': 'AGPL-3', 'depends': [ 'product', + 'purchase', 'point_of_sale', 'stock' ], diff --git a/stock_coverage/models/product_template.py b/stock_coverage/models/product_template.py index 951c7ac..a4aa50b 100644 --- a/stock_coverage/models/product_template.py +++ b/stock_coverage/models/product_template.py @@ -13,7 +13,7 @@ class ProductTemplate(models.Model): ) calculation_range = fields.Integer( 'Calculation range (days)', - default=365, # todo sensible defaults, 14, 28? + default=14, ) average_consumption = fields.Float( @@ -68,9 +68,8 @@ class ProductTemplate(models.Model): self.env['pos.order.line'] .search([ ('product_id', 'in', products.ids), - ('create_date', '>', - fields.Datetime.to_string(pol_date_limit)) - ]) + ('create_date', '>', fields.Datetime.to_string(pol_date_limit)) # noqa + ]) ) if order_lines: @@ -116,10 +115,11 @@ class ProductTemplate(models.Model): BETWEEN date_trunc('day', now()) - calculation_range * interval '1 days' and date_trunc('day', now()) group by product_template_id - """ + """ # noqa self.env.cr.execute(query) results = {pid: qty for pid, qty in self.env.cr.fetchall()} for product in products: - product.total_consumption = results.get(product.id, product.total_consumption) + product.total_consumption = results.get(product.id, + product.total_consumption) diff --git a/stock_coverage/tests/test_stock_coverage.py b/stock_coverage/tests/test_stock_coverage.py index d8696ea..290b6af 100644 --- a/stock_coverage/tests/test_stock_coverage.py +++ b/stock_coverage/tests/test_stock_coverage.py @@ -1,12 +1,7 @@ # -*- coding: utf-8 -*- -from collections import namedtuple -from openerp.tests.common import TransactionCase -from openerp.addons.stock.product import product_template as ProductTemplate -from openerp.addons.stock.product import product_product as ProductProduct import datetime as dt - -# fixme setup tests based on demo data, test on a clean database +from openerp.tests.common import TransactionCase _datetimes = map( lambda d: d.strftime('%Y-%m-%d %H:%M:%S'), From 2de207e51a0d1d321adc858bc447cf424f9a5b3f Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Sun, 29 Mar 2020 12:03:44 +0200 Subject: [PATCH 4/7] [REF] blacken stock coverage --- stock_coverage/__openerp__.py | 26 +++---- stock_coverage/models/product_template.py | 73 ++++++++++--------- stock_coverage/tests/test_stock_coverage.py | 81 ++++++++++++--------- 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/stock_coverage/__openerp__.py b/stock_coverage/__openerp__.py index a7640b8..4f07560 100644 --- a/stock_coverage/__openerp__.py +++ b/stock_coverage/__openerp__.py @@ -1,24 +1,16 @@ # -*- encoding: utf-8 -*- { - 'name': 'Product - Stock Coverage', - 'version': '9.0.1', - 'category': 'Product', - 'description': """ + "name": "Product - Stock Coverage", + "version": "9.0.1", + "category": "Product", + "description": """ Shows figures in the product form related to stock coverage There are settings in Inventory/settings to define the calculation range and the display range. """, - 'author': 'coop it easy', - 'website': 'coopiteasy.be', - 'license': 'AGPL-3', - 'depends': [ - 'product', - 'purchase', - 'point_of_sale', - 'stock' - ], - 'data': [ - 'views/product_template_view.xml', - 'data/cron.xml', - ], + "author": "coop it easy", + "website": "coopiteasy.be", + "license": "AGPL-3", + "depends": ["product", "purchase", "point_of_sale", "stock"], + "data": ["views/product_template_view.xml", "data/cron.xml"], } diff --git a/stock_coverage/models/product_template.py b/stock_coverage/models/product_template.py index a4aa50b..342f3a7 100644 --- a/stock_coverage/models/product_template.py +++ b/stock_coverage/models/product_template.py @@ -7,41 +7,38 @@ class ProductTemplate(models.Model): _inherit = "product.template" consumption_calculation_method = fields.Selection( - selection=[('sales_history', 'Sales History')], - string='Consumption Calculation Method', - default='sales_history', - ) - calculation_range = fields.Integer( - 'Calculation range (days)', - default=14, + selection=[("sales_history", "Sales History")], + string="Consumption Calculation Method", + default="sales_history", ) + calculation_range = fields.Integer("Calculation range (days)", default=14) average_consumption = fields.Float( - string='Average Consumption', - compute='_compute_average_daily_consumption', + string="Average Consumption", + compute="_compute_average_daily_consumption", readonly=True, digits=(100, 2), ) total_consumption = fields.Float( - string='Total Consumption', + string="Total Consumption", default=0, - compute='_compute_total_consumption', + compute="_compute_total_consumption", store=True, readonly=True, digits=(100, 2), ) estimated_stock_coverage = fields.Float( - string='Estimated Stock Coverage (days)', - compute='_compute_estimated_stock_coverage', + string="Estimated Stock Coverage (days)", + compute="_compute_estimated_stock_coverage", default=0, digits=(100, 2), readonly=True, ) @api.multi - @api.depends('total_consumption') + @api.depends("total_consumption") def _compute_average_daily_consumption(self): for template in self: if template.calculation_range > 0: @@ -53,35 +50,41 @@ class ProductTemplate(models.Model): return True @api.multi - @api.depends('calculation_range') + @api.depends("calculation_range") def _compute_total_consumption(self): for template in self: - products = ( - self.env['product.product'] - .search([('product_tmpl_id', '=', template.id)])) + products = self.env["product.product"].search( + [("product_tmpl_id", "=", template.id)] + ) today = dt.date.today() - pol_date_limit = ( - today - dt.timedelta(days=template.calculation_range)) - - order_lines = ( - self.env['pos.order.line'] - .search([ - ('product_id', 'in', products.ids), - ('create_date', '>', fields.Datetime.to_string(pol_date_limit)) # noqa - ]) + pol_date_limit = today - dt.timedelta( + days=template.calculation_range + ) + + order_lines = self.env["pos.order.line"].search( + [ + ("product_id", "in", products.ids), + ( + "create_date", + ">", + fields.Datetime.to_string(pol_date_limit), + ), # noqa + ] ) if order_lines: order_lines = order_lines.filtered( - lambda ol: ol.order_id.state in ['done', 'invoiced', 'paid']) # noqa - template.total_consumption = sum(order_lines.mapped('qty')) + lambda ol: ol.order_id.state + in ["done", "invoiced", "paid"] + ) # noqa + template.total_consumption = sum(order_lines.mapped("qty")) else: template.total_consumption = 0 return True @api.multi - @api.depends('total_consumption') + @api.depends("total_consumption") def _compute_estimated_stock_coverage(self): for product_template in self: qty = product_template.qty_available @@ -96,10 +99,7 @@ class ProductTemplate(models.Model): @api.model def _batch_compute_total_consumption(self): - products = ( - self.env['product.template'] - .search([('active', '=', True)]) - ) + products = self.env["product.template"].search([("active", "=", True)]) query = """ select @@ -121,5 +121,6 @@ class ProductTemplate(models.Model): results = {pid: qty for pid, qty in self.env.cr.fetchall()} for product in products: - product.total_consumption = results.get(product.id, - product.total_consumption) + product.total_consumption = results.get( + product.id, product.total_consumption + ) diff --git a/stock_coverage/tests/test_stock_coverage.py b/stock_coverage/tests/test_stock_coverage.py index 290b6af..49849b5 100644 --- a/stock_coverage/tests/test_stock_coverage.py +++ b/stock_coverage/tests/test_stock_coverage.py @@ -4,53 +4,62 @@ import datetime as dt from openerp.tests.common import TransactionCase _datetimes = map( - lambda d: d.strftime('%Y-%m-%d %H:%M:%S'), - (dt.datetime.now() - dt.timedelta(days=d) for d in range(0, 24, 2))) - -_quantities = [0.64, 6.45, 9.65, 1.76, 9.14, 3.99, - 6.92, 2.25, 6.91, 1.44, 6.52, 1.44] + lambda d: d.strftime("%Y-%m-%d %H:%M:%S"), + (dt.datetime.now() - dt.timedelta(days=d) for d in range(0, 24, 2)), +) + +_quantities = [ + 0.64, + 6.45, + 9.65, + 1.76, + 9.14, + 3.99, + 6.92, + 2.25, + 6.91, + 1.44, + 6.52, + 1.44, +] class TestProductTemplate(TransactionCase): - def setUp(self, *args, **kwargs): result = super(TestProductTemplate, self).setUp(*args, **kwargs) - test_product_template = ( - self.env['product.template'] - .create({'name': 'test product template', - 'calculation_range': 14, - 'consumption_calculation_method': 'sales_history', - 'product_template_id': 0, - }) + test_product_template = self.env["product.template"].create( + { + "name": "test product template", + "calculation_range": 14, + "consumption_calculation_method": "sales_history", + "product_template_id": 0, + } ) pid = ( - self.env['product.product'] - .search([('product_tmpl_id', '=', test_product_template.id)]) - .ids + self.env["product.product"] + .search([("product_tmpl_id", "=", test_product_template.id)]) + .ids ).pop() for date, qty in zip(_datetimes, _quantities): - (self.env['pos.order.line'] - .create({'create_date': date, - 'qty': qty, - 'product_id': pid, - }) - ) + ( + self.env["pos.order.line"].create( + {"create_date": date, "qty": qty, "product_id": pid} + ) + ) def _product_available(*args, **kwargs): - products = ( - self.env['product.product'] - .search([ - ('product_tmpl_id', '=', test_product_template.id)]) + products = self.env["product.product"].search( + [("product_tmpl_id", "=", test_product_template.id)] ) mock_data = { - 'qty_available': 53.2, - 'incoming_qty': 14, - 'outgoing_qty': 4.1, - 'virtual_available': 53.2 + 14 - 4.1, - } + "qty_available": 53.2, + "incoming_qty": 14, + "outgoing_qty": 4.1, + "virtual_available": 53.2 + 14 - 4.1, + } return {pid: mock_data for pid in products.ids} # mock area fixme @@ -66,13 +75,13 @@ class TestProductTemplate(TransactionCase): def test_create(self): """Create a simple product template""" - Template = self.env['product.template'] - product = Template.create({'name': 'Test create product'}) - self.assertEqual(product.name, 'Test create product') + Template = self.env["product.template"] + product = Template.create({"name": "Test create product"}) + self.assertEqual(product.name, "Test create product") def test_compute_average_daily_consumption(self): """Test computed field average_daily_consumption""" - ProductTemplate = self.env['product.template'] + ProductTemplate = self.env["product.template"] product_template = ProductTemplate.browse(self.product_template_id) computed_value = product_template.average_consumption @@ -81,7 +90,7 @@ class TestProductTemplate(TransactionCase): def test_compute_total_consumption(self): """Test total consumption was computed in setup""" - ProductTemplate = self.env['product.template'] + ProductTemplate = self.env["product.template"] product_template = ProductTemplate.browse(self.product_template_id) computed_value = product_template.total_consumption expected_value = 57.11 From 146651cddf77a0229a5ea83b58f03f303246576a Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Sun, 29 Mar 2020 12:04:32 +0200 Subject: [PATCH 5/7] [REF] beesdoo_stock_coverage --- {stock_coverage => beesdoo_stock_coverage}/README.md | 0 {stock_coverage => beesdoo_stock_coverage}/__init__.py | 0 {stock_coverage => beesdoo_stock_coverage}/__openerp__.py | 0 {stock_coverage => beesdoo_stock_coverage}/data/cron.xml | 0 {stock_coverage => beesdoo_stock_coverage}/models/__init__.py | 0 .../models/product_template.py | 0 {stock_coverage => beesdoo_stock_coverage}/tests/__init__.py | 0 .../tests/test_stock_coverage.py | 0 .../views/product_template_view.xml | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {stock_coverage => beesdoo_stock_coverage}/README.md (100%) rename {stock_coverage => beesdoo_stock_coverage}/__init__.py (100%) rename {stock_coverage => beesdoo_stock_coverage}/__openerp__.py (100%) rename {stock_coverage => beesdoo_stock_coverage}/data/cron.xml (100%) rename {stock_coverage => beesdoo_stock_coverage}/models/__init__.py (100%) rename {stock_coverage => beesdoo_stock_coverage}/models/product_template.py (100%) rename {stock_coverage => beesdoo_stock_coverage}/tests/__init__.py (100%) rename {stock_coverage => beesdoo_stock_coverage}/tests/test_stock_coverage.py (100%) rename {stock_coverage => beesdoo_stock_coverage}/views/product_template_view.xml (100%) diff --git a/stock_coverage/README.md b/beesdoo_stock_coverage/README.md similarity index 100% rename from stock_coverage/README.md rename to beesdoo_stock_coverage/README.md diff --git a/stock_coverage/__init__.py b/beesdoo_stock_coverage/__init__.py similarity index 100% rename from stock_coverage/__init__.py rename to beesdoo_stock_coverage/__init__.py diff --git a/stock_coverage/__openerp__.py b/beesdoo_stock_coverage/__openerp__.py similarity index 100% rename from stock_coverage/__openerp__.py rename to beesdoo_stock_coverage/__openerp__.py diff --git a/stock_coverage/data/cron.xml b/beesdoo_stock_coverage/data/cron.xml similarity index 100% rename from stock_coverage/data/cron.xml rename to beesdoo_stock_coverage/data/cron.xml diff --git a/stock_coverage/models/__init__.py b/beesdoo_stock_coverage/models/__init__.py similarity index 100% rename from stock_coverage/models/__init__.py rename to beesdoo_stock_coverage/models/__init__.py diff --git a/stock_coverage/models/product_template.py b/beesdoo_stock_coverage/models/product_template.py similarity index 100% rename from stock_coverage/models/product_template.py rename to beesdoo_stock_coverage/models/product_template.py diff --git a/stock_coverage/tests/__init__.py b/beesdoo_stock_coverage/tests/__init__.py similarity index 100% rename from stock_coverage/tests/__init__.py rename to beesdoo_stock_coverage/tests/__init__.py diff --git a/stock_coverage/tests/test_stock_coverage.py b/beesdoo_stock_coverage/tests/test_stock_coverage.py similarity index 100% rename from stock_coverage/tests/test_stock_coverage.py rename to beesdoo_stock_coverage/tests/test_stock_coverage.py diff --git a/stock_coverage/views/product_template_view.xml b/beesdoo_stock_coverage/views/product_template_view.xml similarity index 100% rename from stock_coverage/views/product_template_view.xml rename to beesdoo_stock_coverage/views/product_template_view.xml From 306c7ffddde9cc22dc5411810be44e5dee2f4a28 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Sun, 29 Mar 2020 15:26:24 +0200 Subject: [PATCH 6/7] [MIG] beesdoo_stock_coverage: v12 --- beesdoo_stock_coverage/README.md | 4 - beesdoo_stock_coverage/README.rst | 58 +++ beesdoo_stock_coverage/__manifest__.py | 16 + beesdoo_stock_coverage/__openerp__.py | 16 - beesdoo_stock_coverage/data/cron.xml | 29 +- .../models/product_template.py | 162 +++---- .../readme/CONTRIBUTORS.rst | 2 + beesdoo_stock_coverage/readme/DESCRIPTION.rst | 1 + .../static/description/index.html | 414 ++++++++++++++++++ .../tests/test_stock_coverage.py | 117 +---- .../views/product_template_view.xml | 38 +- 11 files changed, 589 insertions(+), 268 deletions(-) delete mode 100644 beesdoo_stock_coverage/README.md create mode 100644 beesdoo_stock_coverage/README.rst create mode 100644 beesdoo_stock_coverage/__manifest__.py delete mode 100644 beesdoo_stock_coverage/__openerp__.py create mode 100644 beesdoo_stock_coverage/readme/CONTRIBUTORS.rst create mode 100644 beesdoo_stock_coverage/readme/DESCRIPTION.rst create mode 100644 beesdoo_stock_coverage/static/description/index.html diff --git a/beesdoo_stock_coverage/README.md b/beesdoo_stock_coverage/README.md deleted file mode 100644 index 5082494..0000000 --- a/beesdoo_stock_coverage/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# todo -- initializing the computed fields - - daily cron - - manual trigger diff --git a/beesdoo_stock_coverage/README.rst b/beesdoo_stock_coverage/README.rst new file mode 100644 index 0000000..b235324 --- /dev/null +++ b/beesdoo_stock_coverage/README.rst @@ -0,0 +1,58 @@ +======================== +Product - Stock Coverage +======================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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-odoo-cae%2Fodoo--addons--hr--incubator-lightgray.png?logo=github + :target: https://github.com/odoo-cae/odoo-addons-hr-incubator/tree/12.0/beesdoo_stock_coverage + :alt: odoo-cae/odoo-addons-hr-incubator + +|badge1| |badge2| |badge3| + +Compute estimated stock coverage based on product sales over a date range. + +**Table of contents** + +.. contents:: + :local: + +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 +~~~~~~~ + +* Coop IT Easy SCRLfs + +Contributors +~~~~~~~~~~~~ + +* Robin Keunen + + +Maintainers +~~~~~~~~~~~ + +This module is part of the `odoo-cae/odoo-addons-hr-incubator `_ project on GitHub. + +You are welcome to contribute. diff --git a/beesdoo_stock_coverage/__manifest__.py b/beesdoo_stock_coverage/__manifest__.py new file mode 100644 index 0000000..acfaf6f --- /dev/null +++ b/beesdoo_stock_coverage/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Product - Stock Coverage", + "version": "12.0.0.0.1", + "category": "Product", + "summary": "Compute estimated stock coverage based on product sales over a date range.", + "author": "Coop IT Easy SCRLfs", + "website": "https://www.coopiteasy.be", + "license": "AGPL-3", + "depends": ["point_of_sale", "sale", "l10n_be"], # fixme + "data": ["views/product_template_view.xml", "data/cron.xml"], + "installable": True, + "application": False, +} diff --git a/beesdoo_stock_coverage/__openerp__.py b/beesdoo_stock_coverage/__openerp__.py deleted file mode 100644 index 4f07560..0000000 --- a/beesdoo_stock_coverage/__openerp__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- encoding: utf-8 -*- -{ - "name": "Product - Stock Coverage", - "version": "9.0.1", - "category": "Product", - "description": """ -Shows figures in the product form related to stock coverage -There are settings in Inventory/settings to define the calculation range and -the display range. - """, - "author": "coop it easy", - "website": "coopiteasy.be", - "license": "AGPL-3", - "depends": ["product", "purchase", "point_of_sale", "stock"], - "data": ["views/product_template_view.xml", "data/cron.xml"], -} diff --git a/beesdoo_stock_coverage/data/cron.xml b/beesdoo_stock_coverage/data/cron.xml index 12775ac..a067a34 100644 --- a/beesdoo_stock_coverage/data/cron.xml +++ b/beesdoo_stock_coverage/data/cron.xml @@ -1,14 +1,17 @@ - - - - Stock Coverage - Update Article Consumption - 24 - hours - -1 - - product.template - _batch_compute_total_consumption - () - - + + + + + Stock Coverage - Update Article Sales Statistics + 1 + days + -1 + + code + + model.cron_compute_stock_coverage() + diff --git a/beesdoo_stock_coverage/models/product_template.py b/beesdoo_stock_coverage/models/product_template.py index 342f3a7..0821c24 100644 --- a/beesdoo_stock_coverage/models/product_template.py +++ b/beesdoo_stock_coverage/models/product_template.py @@ -1,126 +1,64 @@ -# -*- encoding: utf-8 -*- -from openerp import models, fields, api -import datetime as dt +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError class ProductTemplate(models.Model): _inherit = "product.template" - consumption_calculation_method = fields.Selection( - selection=[("sales_history", "Sales History")], - string="Consumption Calculation Method", - default="sales_history", - ) - calculation_range = fields.Integer("Calculation range (days)", default=14) - - average_consumption = fields.Float( - string="Average Consumption", - compute="_compute_average_daily_consumption", - readonly=True, - digits=(100, 2), + @api.multi + @api.constrains("computation_range") + def _check_computation_range(self): + for template in self: + if template.computation_range <= 0: + raise ValidationError( + _("Computation range must be greater than 0.") + ) + + computation_range = fields.Integer("Computation range (days)", default=14) + range_sales = fields.Float( + string="Sales over Range", compute="_compute_stock_coverage" ) - - total_consumption = fields.Float( - string="Total Consumption", - default=0, - compute="_compute_total_consumption", - store=True, - readonly=True, - digits=(100, 2), + daily_sales = fields.Float( + string="Daily Sales", compute="_compute_stock_coverage" ) - - estimated_stock_coverage = fields.Float( - string="Estimated Stock Coverage (days)", - compute="_compute_estimated_stock_coverage", - default=0, - digits=(100, 2), - readonly=True, + stock_coverage = fields.Float( + string="Stock Coverage (days)", compute="_compute_stock_coverage" ) @api.multi - @api.depends("total_consumption") - def _compute_average_daily_consumption(self): - for template in self: - if template.calculation_range > 0: - avg = template.total_consumption / template.calculation_range - else: - avg = 0 - template.average_consumption = avg - - return True - - @api.multi - @api.depends("calculation_range") - def _compute_total_consumption(self): + def _compute_stock_coverage(self): + query = """ + select template.id as product_template_id, + sum(pol.qty) as total_sales, + sum(pol.qty) / template.computation_range as daily_sales + from pos_order_line pol + join pos_order po ON pol.order_id = po.id + join product_product product ON pol.product_id = product.id + join product_template template ON product.product_tmpl_id = template.id + where po.state in ('done', 'invoiced', 'paid') + and template.active + and pol.create_date + BETWEEN date_trunc('day', now()) - template.computation_range * interval '1 days' + and date_trunc('day', now()) + and template.id in %(template_ids)s + group by product_template_id + """ + self.env.cr.execute(query, {"template_ids": tuple(self.ids)}) + results = {pid: (qty, avg) for pid, qty, avg in self.env.cr.fetchall()} for template in self: - products = self.env["product.product"].search( - [("product_tmpl_id", "=", template.id)] - ) - - today = dt.date.today() - pol_date_limit = today - dt.timedelta( - days=template.calculation_range - ) - - order_lines = self.env["pos.order.line"].search( - [ - ("product_id", "in", products.ids), - ( - "create_date", - ">", - fields.Datetime.to_string(pol_date_limit), - ), # noqa - ] - ) - - if order_lines: - order_lines = order_lines.filtered( - lambda ol: ol.order_id.state - in ["done", "invoiced", "paid"] - ) # noqa - template.total_consumption = sum(order_lines.mapped("qty")) - else: - template.total_consumption = 0 - return True - - @api.multi - @api.depends("total_consumption") - def _compute_estimated_stock_coverage(self): - for product_template in self: - qty = product_template.qty_available - avg = product_template.average_consumption - if avg > 0: - product_template.estimated_stock_coverage = qty / avg + qty, avg = results.get(template.id, (0, 0)) + template.range_sales = qty + template.daily_sales = avg + if avg != 0: + template.stock_coverage = template.virtual_available / avg else: - # todo what would be a good default value? (not float(inf)) - product_template.estimated_stock_coverage = 9999 - - return True + template.stock_coverage = 9999 @api.model - def _batch_compute_total_consumption(self): - products = self.env["product.template"].search([("active", "=", True)]) - - query = """ - select - template.id as product_template_id, - sum(pol.qty) as total_consumption - from pos_order_line pol - join pos_order po ON pol.order_id = po.id - join product_product product ON pol.product_id = product.id - join product_template template ON product.product_tmpl_id = template.id - where po.state in ('done', 'invoiced', 'paid') - and template.active - and pol.create_date - BETWEEN date_trunc('day', now()) - calculation_range * interval '1 days' - and date_trunc('day', now()) - group by product_template_id - """ # noqa - - self.env.cr.execute(query) - results = {pid: qty for pid, qty in self.env.cr.fetchall()} - - for product in products: - product.total_consumption = results.get( - product.id, product.total_consumption - ) + def cron_compute_stock_coverage(self): + templates = self.env["product.template"].search([]) + templates._compute_stock_coverage() diff --git a/beesdoo_stock_coverage/readme/CONTRIBUTORS.rst b/beesdoo_stock_coverage/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..1798654 --- /dev/null +++ b/beesdoo_stock_coverage/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Robin Keunen + diff --git a/beesdoo_stock_coverage/readme/DESCRIPTION.rst b/beesdoo_stock_coverage/readme/DESCRIPTION.rst new file mode 100644 index 0000000..fac23c2 --- /dev/null +++ b/beesdoo_stock_coverage/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Compute estimated stock coverage based on product sales over a date range. diff --git a/beesdoo_stock_coverage/static/description/index.html b/beesdoo_stock_coverage/static/description/index.html new file mode 100644 index 0000000..919df7c --- /dev/null +++ b/beesdoo_stock_coverage/static/description/index.html @@ -0,0 +1,414 @@ + + + + + + +Product - Stock Coverage + + + +
+

Product - Stock Coverage

+ + +

Beta License: AGPL-3 odoo-cae/odoo-addons-hr-incubator

+

Compute estimated stock coverage based on product sales over a date range.

+

Table of contents

+ +
+

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

+
    +
  • Coop IT Easy SCRLfs
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the odoo-cae/odoo-addons-hr-incubator project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/beesdoo_stock_coverage/tests/test_stock_coverage.py b/beesdoo_stock_coverage/tests/test_stock_coverage.py index 49849b5..847cc0b 100644 --- a/beesdoo_stock_coverage/tests/test_stock_coverage.py +++ b/beesdoo_stock_coverage/tests/test_stock_coverage.py @@ -1,105 +1,22 @@ -# -*- coding: utf-8 -*- +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -import datetime as dt -from openerp.tests.common import TransactionCase -_datetimes = map( - lambda d: d.strftime("%Y-%m-%d %H:%M:%S"), - (dt.datetime.now() - dt.timedelta(days=d) for d in range(0, 24, 2)), -) - -_quantities = [ - 0.64, - 6.45, - 9.65, - 1.76, - 9.14, - 3.99, - 6.92, - 2.25, - 6.91, - 1.44, - 6.52, - 1.44, -] +from odoo.tests.common import TransactionCase class TestProductTemplate(TransactionCase): - def setUp(self, *args, **kwargs): - result = super(TestProductTemplate, self).setUp(*args, **kwargs) - - test_product_template = self.env["product.template"].create( - { - "name": "test product template", - "calculation_range": 14, - "consumption_calculation_method": "sales_history", - "product_template_id": 0, - } - ) - - pid = ( - self.env["product.product"] - .search([("product_tmpl_id", "=", test_product_template.id)]) - .ids - ).pop() - - for date, qty in zip(_datetimes, _quantities): - ( - self.env["pos.order.line"].create( - {"create_date": date, "qty": qty, "product_id": pid} - ) - ) - - def _product_available(*args, **kwargs): - products = self.env["product.product"].search( - [("product_tmpl_id", "=", test_product_template.id)] - ) - mock_data = { - "qty_available": 53.2, - "incoming_qty": 14, - "outgoing_qty": 4.1, - "virtual_available": 53.2 + 14 - 4.1, - } - return {pid: mock_data for pid in products.ids} - - # mock area fixme - # ProductTemplate._product_available = _product_available - # ProductProduct._product_available = _product_available - # Order = namedtuple('Order', ['id', 'state']) - # PosOrderLine.order_id = Order('1', 'done') - - test_product_template._compute_total_consumption() - self.product_template_id = test_product_template.id - - return result - - def test_create(self): - """Create a simple product template""" - Template = self.env["product.template"] - product = Template.create({"name": "Test create product"}) - self.assertEqual(product.name, "Test create product") - - def test_compute_average_daily_consumption(self): - """Test computed field average_daily_consumption""" - ProductTemplate = self.env["product.template"] - product_template = ProductTemplate.browse(self.product_template_id) - - computed_value = product_template.average_consumption - expected_value = 4.08 - self.assertAlmostEqual(computed_value, expected_value, 7) - - def test_compute_total_consumption(self): - """Test total consumption was computed in setup""" - ProductTemplate = self.env["product.template"] - product_template = ProductTemplate.browse(self.product_template_id) - computed_value = product_template.total_consumption - expected_value = 57.11 - self.assertAlmostEqual(computed_value, expected_value) - - # def test_compute_estimated_stock_coverage(self): fixme - # """Test computed field estimated_stock_coverage""" - # ProductTemplate = self.env['product.template'] - # product_template = ProductTemplate.browse(self.product_template_id) - # computed_value = product_template.estimated_stock_coverage - # expected_value = 13.04 - # self.assertAlmostEqual(computed_value, expected_value) + def setUp(self): + super(TestProductTemplate, self).setUp() + self.product1 = self.browse_ref( + "point_of_sale.whiteboard_pen" + ).product_tmpl_id + + def test_compute_stock_coverage(self): + self.product1._compute_stock_coverage() + print("***") + print(self.product1.range_sales) + print(self.product1.daily_sales) + print(self.product1.stock_coverage) + print("***") diff --git a/beesdoo_stock_coverage/views/product_template_view.xml b/beesdoo_stock_coverage/views/product_template_view.xml index 5eb634e..9447a7f 100644 --- a/beesdoo_stock_coverage/views/product_template_view.xml +++ b/beesdoo_stock_coverage/views/product_template_view.xml @@ -1,40 +1,32 @@ - - template.consumption.form + + product.template.product.form product.template - - - - - - - - + + + + + + - - template.consumption.tree + + product.template.product.tree product.template - - - - - - - - - - - + + + + + From e3acfebf19cf50ca9f1bd6c5ec67879c81e9d643 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Sun, 29 Mar 2020 16:10:10 +0200 Subject: [PATCH 7/7] [ADD] beesdoo_stock_coverage: tests --- beesdoo_stock_coverage/__manifest__.py | 2 +- .../models/product_template.py | 6 +- .../tests/test_stock_coverage.py | 104 +++++++++++++++--- 3 files changed, 93 insertions(+), 19 deletions(-) diff --git a/beesdoo_stock_coverage/__manifest__.py b/beesdoo_stock_coverage/__manifest__.py index acfaf6f..953b1c7 100644 --- a/beesdoo_stock_coverage/__manifest__.py +++ b/beesdoo_stock_coverage/__manifest__.py @@ -9,7 +9,7 @@ "author": "Coop IT Easy SCRLfs", "website": "https://www.coopiteasy.be", "license": "AGPL-3", - "depends": ["point_of_sale", "sale", "l10n_be"], # fixme + "depends": ["point_of_sale", "sale_stock"], "data": ["views/product_template_view.xml", "data/cron.xml"], "installable": True, "application": False, diff --git a/beesdoo_stock_coverage/models/product_template.py b/beesdoo_stock_coverage/models/product_template.py index 0821c24..4af6842 100644 --- a/beesdoo_stock_coverage/models/product_template.py +++ b/beesdoo_stock_coverage/models/product_template.py @@ -41,9 +41,9 @@ class ProductTemplate(models.Model): join product_template template ON product.product_tmpl_id = template.id where po.state in ('done', 'invoiced', 'paid') and template.active - and pol.create_date - BETWEEN date_trunc('day', now()) - template.computation_range * interval '1 days' - and date_trunc('day', now()) + and po.date_order + BETWEEN now() - template.computation_range * interval '1 days' + and now() and template.id in %(template_ids)s group by product_template_id """ diff --git a/beesdoo_stock_coverage/tests/test_stock_coverage.py b/beesdoo_stock_coverage/tests/test_stock_coverage.py index 847cc0b..8719d60 100644 --- a/beesdoo_stock_coverage/tests/test_stock_coverage.py +++ b/beesdoo_stock_coverage/tests/test_stock_coverage.py @@ -1,22 +1,96 @@ -# Copyright 2020 Coop IT Easy SCRL fs -# Robin Keunen -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - +# Copyright (C) 2019 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# @author: Robin Keunen +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields +from odoo.tools import float_compare from odoo.tests.common import TransactionCase +from datetime import timedelta -class TestProductTemplate(TransactionCase): +class TestModule(TransactionCase): def setUp(self): - super(TestProductTemplate, self).setUp() - self.product1 = self.browse_ref( - "point_of_sale.whiteboard_pen" - ).product_tmpl_id + super(TestModule, self).setUp() + + # Get Registry + self.PosOrder = self.env["pos.order"] + self.AccountPayment = self.env["account.payment"] + + # Get Object + self.pos_product = self.env.ref("product.product_product_25") + self.pos_template = self.pos_product.product_tmpl_id + self.pricelist = self.env.ref("product.list0") + self.partner = self.env.ref("base.res_partner_12") + # Create a new pos config and open it + self.pos_config = self.env.ref("point_of_sale.pos_config_main").copy() + self.pos_config.open_session_cb() + + # Test Section def test_compute_stock_coverage(self): - self.product1._compute_stock_coverage() - print("***") - print(self.product1.range_sales) - print(self.product1.daily_sales) - print(self.product1.stock_coverage) - print("***") + self._create_order() + self.pos_template._compute_stock_coverage() + self.assertEquals(1.0, self.pos_template.range_sales) + self.assertEqual( + float_compare( + 0.0714, self.pos_template.daily_sales, precision_digits=2 + ), + 0, + ) + self.assertEquals(210.0, self.pos_template.stock_coverage) + + def _create_order(self): + date = fields.Date.today() - timedelta(days=1) + date_str = fields.Date.to_string(date) + order_data = { + "id": u"0006-001-0010", + "to_invoice": True, + "data": { + "pricelist_id": self.pricelist.id, + "user_id": 1, + "name": "Order 0006-001-0010", + "partner_id": self.partner.id, + "amount_paid": 0.9, + "pos_session_id": self.pos_config.current_session_id.id, + "lines": [ + [ + 0, + 0, + { + "product_id": self.pos_product.id, + "price_unit": 0.9, + "qty": 1, + "price_subtotal": 0.9, + "price_subtotal_incl": 0.9, + }, + ] + ], + "statement_ids": [ + [ + 0, + 0, + { + "journal_id": self.pos_config.journal_ids[0].id, + "amount": 0.9, + "name": fields.Datetime.now(), + "account_id": self.env.user.partner_id.property_account_receivable_id.id, + "statement_id": self.pos_config.current_session_id.statement_ids[ + 0 + ].id, + }, + ] + ], + "creation_date": date_str, + "amount_tax": 0, + "fiscal_position_id": False, + "uid": u"00001-001-0001", + "amount_return": 0, + "sequence_number": 1, + "amount_total": 0.9, + }, + } + + result = self.PosOrder.create_from_ui([order_data]) + order = self.PosOrder.browse(result[0]) + return order