From 61ed756dd01294a0428f4ae8ad47dde8c901158a Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sun, 3 Apr 2022 21:15:05 +0200 Subject: [PATCH] Implemented "default consume location" handling (closes #1365) --- changelog/67_UNRELEASED_xxxx-xx-xx.md | 7 ++++ localization/strings.pot | 6 +++ migrations/0187.sql | 55 +++++++++++++++++++++++++++ public/viewjs/consume.js | 20 ++++++++-- services/StockService.php | 11 ++---- views/productform.blade.php | 23 +++++++++++ 6 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 migrations/0187.sql diff --git a/changelog/67_UNRELEASED_xxxx-xx-xx.md b/changelog/67_UNRELEASED_xxxx-xx-xx.md index 9f80f089..daa87536 100644 --- a/changelog/67_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/67_UNRELEASED_xxxx-xx-xx.md @@ -31,6 +31,13 @@ - Product card, stock overiew and stock entries page optimizations regarding displaying prices: - Prices are now shown per default purchase quantity unit, instead of per stock QU and when clicking/hovering, a tooltip shows the price per stock QU - The price history chart is now based on the value per purchase QU, instead of per stock QU +- New product option "Default consume location" (not mandatory, defaults to not set / empty) + - When set, stock entries at that location will be consumed first + - => This will be automatically taken into account when consuming from the stock overview page and all other places where no specific location can be selected + - => On the consume page the location is preselected in the following order: + - 1. The new default consume location, if the product currently has any stock there, otherwise + - 2. The products default location, if the product currently has any stock there, otherwise + - 3. The first location where the product currently has any stock - New product option "Disable own stock" (defaults to disabled) - When enabled, the corresponding product can't have own stock, means it will not be selectable on purchase (useful for parent products which are just used as a summary/total view of the child products) - The location content sheet can now optionally list also out of stock products (at the products default location, new checkbox "Show only in-stock products " at the top of the page, defaults to enabled) diff --git a/localization/strings.pot b/localization/strings.pot index f3675f1e..8e64193d 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -2350,3 +2350,9 @@ msgstr "" msgid "Edit meal plan entry" msgstr "" + +msgid "Default consume location" +msgstr "" + +msgid "Stock entries at this location will be consumed first" +msgstr "" diff --git a/migrations/0187.sql b/migrations/0187.sql new file mode 100644 index 00000000..a2b189df --- /dev/null +++ b/migrations/0187.sql @@ -0,0 +1,55 @@ +ALTER TABLE products +ADD default_consume_location_id INTEGER; + +DROP VIEW stock_next_use; +CREATE VIEW stock_next_use +AS + +/* + The default consume rule is: + Opened first, then first due first, then first in first out + Apart from that products at their default consume location should be consumed first + + This orders the stock entries by that + => Highest "priority" per product = the stock entry to use next +*/ + +SELECT + (ROW_NUMBER() OVER(PARTITION BY s.product_id ORDER BY CASE WHEN IFNULL(p.default_consume_location_id, -1) = s.location_id THEN 0 ELSE 1 END ASC, s.open DESC, s.best_before_date ASC, s.purchased_date ASC)) * -1 AS priority, + s.* +FROM stock s +JOIN products p + ON p.id = s.product_id; + +CREATE TRIGGER stock_next_use_INS INSTEAD OF INSERT ON stock_next_use +BEGIN + INSERT INTO stock + (product_id, amount, best_before_date, purchased_date, stock_id, + price, open, opened_date, location_id, shopping_location_id, note) + VALUES + (NEW.product_id, NEW.amount, NEW.best_before_date, NEW.purchased_date, NEW.stock_id, + NEW.price, NEW.open, NEW.opened_date, NEW.location_id, NEW.shopping_location_id, NEW.note); +END; + +CREATE TRIGGER stock_next_use_UPD INSTEAD OF UPDATE ON stock_next_use +BEGIN + UPDATE stock + SET product_id = NEW.product_id, + amount = NEW.amount, + best_before_date = NEW.best_before_date, + purchased_date = NEW.purchased_date, + stock_id = NEW.stock_id, + price = NEW.price, + open = NEW.open, + opened_date = NEW.opened_date, + location_id = NEW.location_id, + shopping_location_id = NEW.shopping_location_id, + note = NEW.note + WHERE id = NEW.id; +END; + +CREATE TRIGGER stock_next_use_DEL INSTEAD OF DELETE ON stock_next_use +BEGIN + DELETE FROM stock + WHERE id = OLD.id; +END; diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js index 15cb7699..2a733a2b 100644 --- a/public/viewjs/consume.js +++ b/public/viewjs/consume.js @@ -372,22 +372,30 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e) RefreshLocaleNumberInput(); $(".input-group-productamountpicker").trigger("change"); + var defaultLocationId = productDetails.location.id; + if (productDetails.product.default_consume_location_id != null && !productDetails.product.default_consume_location_id.isEmpty()) + { + defaultLocationId = productDetails.product.default_consume_location_id; + } + $("#location_id").find("option").remove().end().append(""); Grocy.Api.Get("stock/products/" + productId + '/locations?include_sub_products=true', function(stockLocations) { var setDefault = 0; + var stockAmountAtDefaultLocation = 0; stockLocations.forEach(stockLocation => { - if (productDetails.location.id == stockLocation.location_id) + if (stockLocation.location_id == defaultLocationId) { $("#location_id").append($("