diff --git a/stock_orderpoint_generator/models/orderpoint_template.py b/stock_orderpoint_generator/models/orderpoint_template.py index d50047d12..893f1a542 100644 --- a/stock_orderpoint_generator/models/orderpoint_template.py +++ b/stock_orderpoint_generator/models/orderpoint_template.py @@ -46,6 +46,7 @@ class OrderpointTemplate(models.Model): ("median", "Most frequent"), ("avg", "Average"), ("min", "Minimum"), + ("delivered", "Delivered"), ], default="max", help="Select a criteria to auto compute the minimum", @@ -60,6 +61,7 @@ class OrderpointTemplate(models.Model): ("median", "Most frequent"), ("avg", "Average"), ("min", "Minimum"), + ("delivered", "Delivered"), ], help="Select a criteria to auto compute the maximum", ) @@ -118,7 +120,13 @@ class OrderpointTemplate(models.Model): self, products, location_id, from_date, to_date, criteria ): """Returns a dict with product ids as keys and the resulting - calculation of historic moves according to criteria""" + calculation of historic moves according to criteria. If the + creteria is delivered we just search how many items were + delivered in the given period of time""" + if criteria == "delivered": + return products._get_delivered_to_customer_dict( + location_id, from_date, to_date + ) stock_qty_history = products._compute_historic_quantities_dict( location_id=location_id, from_date=from_date, to_date=to_date ) diff --git a/stock_orderpoint_generator/models/product.py b/stock_orderpoint_generator/models/product.py index afc1debdb..f76ea12e5 100644 --- a/stock_orderpoint_generator/models/product.py +++ b/stock_orderpoint_generator/models/product.py @@ -4,6 +4,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) from collections import OrderedDict +from datetime import timedelta from odoo import api, fields, models @@ -49,8 +50,13 @@ class ProductProduct(models.Model): # default the compute the stock value anyway to default the value # for products with no moves for the given period initial_stock = {} + # Compute the second before the given date so we don't duplicate + # history values in case the given hour is the same than the one + # of the first move + from_date_stock = from_date - timedelta(seconds=1) + to_date_stock = to_date + timedelta(seconds=1) initial_stock = self.with_context(location=location)._compute_quantities_dict( - False, False, False, to_date=from_date or to_date + False, False, False, to_date=from_date_stock or to_date_stock ) product_moves_dict = {} for move in moves: @@ -76,23 +82,47 @@ class ProductProduct(models.Model): # we can compute the stock historical values from the moves # sequence so we can exploit it statisticaly product_moves = OrderedDict(sorted(product_moves.items())) - stock = False + product_moves_dict[product.id]["stock_history"] = [ + prod_initial_stock.get("qty_available", 0) + ] + stock = 0 first_item = product_moves[next(iter(product_moves))] if from_date: stock = prod_initial_stock.get("qty_available") - if not stock: - stock = first_item["prod_qty"] - first_item["stock"] = stock + first_item["stock"] = stock + first_item["prod_qty"] + stock = first_item["stock"] iter_moves = iter(product_moves) next(iter_moves, None) for date in iter_moves: stock += product_moves[date]["prod_qty"] product_moves[date]["stock"] = stock - product_moves_dict[product.id]["stock_history"] = [ + product_moves_dict[product.id]["stock_history"] += [ v["stock"] for k, v in product_moves.items() ] return product_moves_dict + def _get_delivered_to_customer_dict( + self, location=False, from_date=False, to_date=False + ): + """Returns a dict of products with their delivered qtys for the + given dates and locations + """ + domain = [ + ("product_id", "in", self.ids), + ("state", "=", "done"), + ("location_dest_id.usage", "=", "customer"), + ] + if location: + domain += [("location_id", "child_of", location.id)] + if from_date: + domain += [("date", ">=", from_date)] + if to_date: + domain += [("date", "<=", to_date)] + move_lines = self.env["stock.move.line"].read_group( + domain, ["product_id", "qty_done"], ["product_id"] + ) + return {p["product_id"][0]: p["qty_done"] for p in move_lines} + def _compute_historic_quantities_dict( self, location_id=False, from_date=False, to_date=False ): diff --git a/stock_orderpoint_generator/tests/test_orderpoint_generator.py b/stock_orderpoint_generator/tests/test_orderpoint_generator.py index d07aee154..672ab6c2c 100644 --- a/stock_orderpoint_generator/tests/test_orderpoint_generator.py +++ b/stock_orderpoint_generator/tests/test_orderpoint_generator.py @@ -318,7 +318,7 @@ class TestOrderpointGenerator(SavepointCase): self.template.write( { "auto_min_qty": True, - "auto_min_date_start": "2019-01-01 00:00:00", + "auto_min_date_start": "2019-01-01 01:30:00", "auto_min_date_end": "2019-02-01 00:00:00", "auto_min_qty_criteria": "max", } @@ -368,7 +368,7 @@ class TestOrderpointGenerator(SavepointCase): # Auto min max over a shorter period self.template.write( { - "auto_max_date_start": "2019-01-01 02:00:00", + "auto_max_date_start": "2019-01-01 02:30:00", "auto_max_date_end": "2019-01-01 03:00:00", "auto_min_date_start": "2019-01-01 04:00:00", "auto_min_date_end": "2019-01-01 06:00:00", @@ -378,6 +378,13 @@ class TestOrderpointGenerator(SavepointCase): wizard.action_configure() orderpoint_auto_dict.update({"product_min_qty": 55, "product_max_qty": 50}) self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict) + # Check delivered + self.template.auto_min_qty_criteria = "delivered" + self.template.auto_max_qty_criteria = "delivered" + wizard = self.wizard_over_products(self.p1, self.template) + wizard.action_configure() + orderpoint_auto_dict.update({"product_min_qty": 3, "product_max_qty": 5}) + self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict) def test_auto_qty_multi_products(self): """Each product has a different history"""