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($("