diff --git a/controllers/BatteriesApiController.php b/controllers/BatteriesApiController.php
index 0029f04e..0a681473 100644
--- a/controllers/BatteriesApiController.php
+++ b/controllers/BatteriesApiController.php
@@ -27,7 +27,7 @@ class BatteriesApiController extends BaseApiController
}
$chargeCycleId = $this->BatteriesService->TrackChargeCycle($args['batteryId'], $trackedTime);
- return $this->ApiResponse(array('charge_cycle_id' => $chargeCycleId));
+ return $this->ApiResponse($this->Database->battery_charge_cycles($chargeCycleId));
}
catch (\Exception $ex)
{
diff --git a/controllers/ChoresApiController.php b/controllers/ChoresApiController.php
index 16fd58b1..9805a107 100644
--- a/controllers/ChoresApiController.php
+++ b/controllers/ChoresApiController.php
@@ -33,7 +33,7 @@ class ChoresApiController extends BaseApiController
}
$choreExecutionId = $this->ChoresService->TrackChore($args['choreId'], $trackedTime, $doneBy);
- return $this->ApiResponse(array('chore_execution_id' => $choreExecutionId));
+ return $this->ApiResponse($this->Database->chores_log($choreExecutionId));
}
catch (\Exception $ex)
{
diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php
index 5f8ff54d..2469b06a 100644
--- a/controllers/StockApiController.php
+++ b/controllers/StockApiController.php
@@ -79,7 +79,7 @@ class StockApiController extends BaseApiController
}
$bookingId = $this->StockService->AddProduct($args['productId'], $requestBody['amount'], $bestBeforeDate, $transactionType, date('Y-m-d'), $price, $locationId);
- return $this->ApiResponse(array('booking_id' => $bookingId));
+ return $this->ApiResponse($this->Database->stock_log($bookingId));
}
catch (\Exception $ex)
{
@@ -128,7 +128,7 @@ class StockApiController extends BaseApiController
}
$bookingId = $this->StockService->ConsumeProduct($args['productId'], $requestBody['amount'], $spoiled, $transactionType, $specificStockEntryId, $recipeId);
- return $this->ApiResponse(array('booking_id' => $bookingId));
+ return $this->ApiResponse($this->Database->stock_log($bookingId));
}
catch (\Exception $ex)
{
@@ -159,7 +159,7 @@ class StockApiController extends BaseApiController
}
$bookingId = $this->StockService->InventoryProduct($args['productId'], $requestBody['new_amount'], $bestBeforeDate);
- return $this->ApiResponse(array('booking_id' => $bookingId));
+ return $this->ApiResponse($this->Database->stock_log($bookingId));
}
catch (\Exception $ex)
{
@@ -190,7 +190,7 @@ class StockApiController extends BaseApiController
}
$bookingId = $this->StockService->OpenProduct($args['productId'], $requestBody['amount'], $specificStockEntryId);
- return $this->ApiResponse(array('booking_id' => $bookingId));
+ return $this->ApiResponse($this->Database->stock_log($bookingId));
}
catch (\Exception $ex)
{
diff --git a/grocy.openapi.json b/grocy.openapi.json
index a9aac3d5..ae358859 100644
--- a/grocy.openapi.json
+++ b/grocy.openapi.json
@@ -1022,7 +1022,8 @@
"properties": {
"amount": {
"type": "number",
- "format": "double"
+ "format": "double",
+ "description": "The amount to add - please note that when tare weight handling for the product is enabled, this needs to be the amount including the container weight (gross), the amount to be posted will be automatically calculated based on what is in stock and the defined tare weight"
},
"best_before_date": {
"type": "string",
@@ -1059,7 +1060,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StockBookingResponse"
+ "$ref": "#/components/schemas/StockLogEntry"
}
}
}
@@ -1103,7 +1104,7 @@
"properties": {
"amount": {
"type": "double",
- "description": "The amount to remove"
+ "description": "The amount to remove - please note that when tare weight handling for the product is enabled, this needs to be the amount including the container weight (gross), the amount to be posted will be automatically calculated based on what is in stock and the defined tare weight"
},
"transaction_type": {
"$ref": "#/components/internalSchemas/StockTransactionType"
@@ -1137,7 +1138,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StockBookingResponse"
+ "$ref": "#/components/schemas/StockLogEntry"
}
}
}
@@ -1181,7 +1182,7 @@
"properties": {
"new_amount": {
"type": "integer",
- "description": "The new current amount for the given product"
+ "description": "The new current amount for the given product - please note that when tare weight handling for the product is enabled, this needs to be the amount including the container weight (gross), the amount to be posted will be automatically calculated based on what is in stock and the defined tare weight"
},
"best_before_date": {
"type": "string",
@@ -1199,7 +1200,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StockBookingResponse"
+ "$ref": "#/components/schemas/StockLogEntry"
}
}
}
@@ -1263,7 +1264,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/StockBookingResponse"
+ "$ref": "#/components/schemas/StockLogEntry"
}
}
}
@@ -1568,12 +1569,7 @@
"content": {
"application/json": {
"schema": {
- "type": "object",
- "properties": {
- "chore_execution_id": {
- "type": "integer"
- }
- }
+ "$ref": "#/components/schemas/ChoreLogEntry"
}
}
}
@@ -1729,12 +1725,7 @@
"content": {
"application/json": {
"schema": {
- "type": "object",
- "properties": {
- "charge_cycle_id": {
- "type": "integer"
- }
- }
+ "$ref": "#/components/schemas/BatteryChargeCycleEntry"
}
}
}
@@ -2345,7 +2336,7 @@
}
}
},
- "BatteryChargeCycle": {
+ "BatteryChargeCycleEntry": {
"type": "object",
"properties": {
"id": {
@@ -2462,14 +2453,6 @@
"error_message": "The error message..."
}
},
- "StockBookingResponse": {
- "type": "object",
- "properties": {
- "booking_id": {
- "type": "integer"
- }
- }
- },
"CurrentStockResponse": {
"type": "object",
"properties": {
diff --git a/localization/en/strings.php b/localization/en/strings.php
index f2e4c377..79aee3d6 100644
--- a/localization/en/strings.php
+++ b/localization/en/strings.php
@@ -335,5 +335,11 @@ return array(
'This is for statistical purposes only' => 'This is for statistical purposes only',
'You have to select a recipe' => 'You have to select a recipe',
'Key type' => 'Key type',
- 'Export as iCal' => 'Export as iCal'
+ 'Export as iCal' => 'Export as iCal',
+ 'Allow partial units in stock' => 'Allow partial units in stock',
+ 'Enable tare weight handling' => 'Enable tare weight handling',
+ 'This is useful e.g. for flour in jars - on purchase/consume/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below' => 'This is useful e.g. for flour in jars - on purchase/consume/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below',
+ 'Tare weight' => 'Tare weight',
+ 'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated' => 'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated',
+ 'You have to select a location' => 'You have to select a location'
);
diff --git a/migrations/0057.sql b/migrations/0057.sql
new file mode 100644
index 00000000..491d3275
--- /dev/null
+++ b/migrations/0057.sql
@@ -0,0 +1,5 @@
+ALTER TABLE products
+ADD enable_tare_weight_handling TINYINT NOT NULL DEFAULT 0;
+
+ALTER TABLE products
+ADD tare_weight REAL NOT NULL DEFAULT 0;
diff --git a/public/js/grocy.js b/public/js/grocy.js
index 0a27099f..9f60a332 100644
--- a/public/js/grocy.js
+++ b/public/js/grocy.js
@@ -502,7 +502,7 @@ $("#about-dialog-link").on("click", function()
});
});
-$(".local-number-format[data-format='currency']").each(function ()
+$(".locale-number-format[data-format='currency']").each(function ()
{
- $(this).text(parseFloat($(this).text()).toLocaleString(undefined, { minimumFractionDigits: 2 }));
+ $(this).text(parseFloat($(this).text()).toLocaleString(undefined, { style: "currency", currency: Grocy.Currency }));
});
diff --git a/public/viewjs/batterytracking.js b/public/viewjs/batterytracking.js
index 6fe88df1..4f95bb1a 100644
--- a/public/viewjs/batterytracking.js
+++ b/public/viewjs/batterytracking.js
@@ -12,7 +12,7 @@
function(result)
{
Grocy.FrontendHelpers.EndUiBusy("batterytracking-form");
- toastr.success(L('Tracked charge cycle of battery #1 on #2', batteryDetails.battery.name, $('#tracked_time').find('input').val()) + '
' + L("Undo") + '');
+ toastr.success(L('Tracked charge cycle of battery #1 on #2', batteryDetails.battery.name, $('#tracked_time').find('input').val()) + '
' + L("Undo") + '');
$('#battery_id').val('');
$('#battery_id_text_input').focus();
diff --git a/public/viewjs/choretracking.js b/public/viewjs/choretracking.js
index 9776dd58..87378082 100644
--- a/public/viewjs/choretracking.js
+++ b/public/viewjs/choretracking.js
@@ -12,7 +12,7 @@
function(result)
{
Grocy.FrontendHelpers.EndUiBusy("choretracking-form");
- toastr.success(L('Tracked execution of chore #1 on #2', choreDetails.chore.name, Grocy.Components.DateTimePicker.GetValue()) + '
' + L("Undo") + '');
+ toastr.success(L('Tracked execution of chore #1 on #2', choreDetails.chore.name, Grocy.Components.DateTimePicker.GetValue()) + '
' + L("Undo") + '');
$('#chore_id').val('');
$('#chore_id_text_input').focus();
diff --git a/public/viewjs/consume.js b/public/viewjs/consume.js
index ab0c06f6..01661008 100644
--- a/public/viewjs/consume.js
+++ b/public/viewjs/consume.js
@@ -39,9 +39,14 @@
}
Grocy.FrontendHelpers.EndUiBusy("consume-form");
- toastr.success(L('Removed #1 #2 of #3 from stock', jsonForm.amount, Pluralize(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + L("Undo") + '');
+ toastr.success(L('Removed #1 #2 of #3 from stock', Math.abs(result.amount), Pluralize(Math.abs(result.amount), productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + L("Undo") + '');
+ $("#amount").attr("min", "1");
+ $("#amount").attr("max", "999999");
+ $("#amount").attr("step", "1");
+ $("#amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '1'));
$('#amount').val(1);
+ $("#tare-weight-handling-info").addClass("d-none");
Grocy.Components.ProductPicker.Clear();
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_RECIPES)
{
@@ -100,7 +105,7 @@ $('#save-mark-as-open-button').on('click', function(e)
}
Grocy.FrontendHelpers.EndUiBusy("consume-form");
- toastr.success(L('Marked #1 #2 of #3 as opened', jsonForm.amount, Pluralize(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + L("Undo") + '');
+ toastr.success(L('Marked #1 #2 of #3 as opened', jsonForm.amount, Pluralize(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + L("Undo") + '');
$('#amount').val(1);
Grocy.Components.ProductPicker.Clear();
@@ -146,13 +151,25 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
{
$("#amount").attr("min", "0.01");
$("#amount").attr("step", "0.01");
- $("#amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', 0.01.toLocaleString()));
+ $("#amount").parent().find(".invalid-feedback").text(L('The amount must be between #1 and #2', 0.01.toLocaleString(), parseFloat(productDetails.stock_amount).toLocaleString()));
}
else
{
$("#amount").attr("min", "1");
$("#amount").attr("step", "1");
- $("#amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '1'));
+ $("#amount").parent().find(".invalid-feedback").text(L('The amount must be between #1 and #2', "1", parseFloat(productDetails.stock_amount).toLocaleString()));
+ }
+
+ if (productDetails.product.enable_tare_weight_handling == 1)
+ {
+ $("#amount").attr("min", productDetails.product.tare_weight);
+ $('#amount').attr('max', parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight));
+ $("#amount").parent().find(".invalid-feedback").text(L('The amount must be between #1 and #2', parseFloat(productDetails.product.tare_weight).toLocaleString(), (parseFloat(productDetails.stock_amount) + parseFloat(productDetails.product.tare_weight)).toLocaleString()));
+ $("#tare-weight-handling-info").removeClass("d-none");
+ }
+ else
+ {
+ $("#tare-weight-handling-info").addClass("d-none");
}
if ((productDetails.stock_amount || 0) === 0)
diff --git a/public/viewjs/inventory.js b/public/viewjs/inventory.js
index 75c42c65..2498d3fb 100644
--- a/public/viewjs/inventory.js
+++ b/public/viewjs/inventory.js
@@ -6,7 +6,7 @@
Grocy.FrontendHelpers.BeginUiBusy("inventory-form");
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
- function (productDetails)
+ function(productDetails)
{
var jsonData = { };
jsonData.new_amount = jsonForm.new_amount;
@@ -38,7 +38,7 @@
}
Grocy.FrontendHelpers.EndUiBusy("inventory-form");
- toastr.success(L('Stock amount of #1 is now #2 #3', productDetails.product.name, jsonForm.new_amount, Pluralize(jsonForm.new_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)) + '
' + L("Undo") + '');
+ toastr.success(L('Stock amount of #1 is now #2 #3', productDetails.product.name, productDetails.stock_amount, Pluralize(productDetails.stock_amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)) + '
' + L("Undo") + '');
if (addBarcode !== undefined)
{
@@ -47,6 +47,10 @@
else
{
$('#inventory-change-info').addClass('d-none');
+ $("#tare-weight-handling-info").addClass("d-none");
+ $("#new_amount").attr("min", "0");
+ $("#new_amount").attr("step", "1");
+ $("#new_amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '0'));
$('#new_amount').val('');
Grocy.Components.DateTimePicker.Clear();
Grocy.Components.ProductPicker.SetValue('');
@@ -91,9 +95,20 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
}
else
{
- $("#new_amount").attr("min", "1");
+ $("#new_amount").attr("min", "0");
$("#new_amount").attr("step", "1");
- $("#new_amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '1'));
+ $("#new_amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '0'));
+ }
+
+ if (productDetails.product.enable_tare_weight_handling == 1)
+ {
+ $("#new_amount").attr("min", productDetails.product.tare_weight);
+ $("#new_amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', parseFloat(productDetails.product.tare_weight).toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: 2 })));
+ $("#tare-weight-handling-info").removeClass("d-none");
+ }
+ else
+ {
+ $("#tare-weight-handling-info").addClass("d-none");
}
$('#new_amount').focus();
@@ -177,26 +192,31 @@ $('#new_amount').on('keyup', function(e)
Grocy.Api.Get('stock/products/' + productId,
function(productDetails)
{
- var productStockAmount = parseInt(productDetails.stock_amount || '0');
+ var productStockAmount = parseFloat(productDetails.stock_amount || parseFloat('0'));
+
+ var containerWeight = parseFloat("0");
+ if (productDetails.product.enable_tare_weight_handling == 1)
+ {
+ containerWeight = parseFloat(productDetails.product.tare_weight);
+ }
- if (newAmount > productStockAmount)
- {
- var amountToAdd = newAmount - productDetails.stock_amount;
- $('#inventory-change-info').text(L('This means #1 will be added to stock', amountToAdd.toString() + ' ' + productDetails.quantity_unit_stock.name));
- $('#inventory-change-info').removeClass('d-none');
- Grocy.Components.DateTimePicker.GetInputElement().attr('required', '');
- }
- else if (newAmount < productStockAmount)
- {
- var amountToRemove = productStockAmount - newAmount;
- $('#inventory-change-info').text(L('This means #1 will be removed from stock', amountToRemove.toString() + ' ' + productDetails.quantity_unit_stock.name));
- $('#inventory-change-info').removeClass('d-none');
- Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required');
- }
- else
+ var estimatedBookingAmount = Math.abs(newAmount - productStockAmount - containerWeight);
+ $('#inventory-change-info').removeClass('d-none');
+
+ if (productDetails.product.enable_tare_weight_handling == 1 && newAmount < containerWeight)
{
$('#inventory-change-info').addClass('d-none');
}
+ else if (newAmount > productStockAmount + containerWeight)
+ {
+ $('#inventory-change-info').text(L('This means #1 will be added to stock', estimatedBookingAmount.toLocaleString() + ' ' + Pluralize(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)));
+ Grocy.Components.DateTimePicker.GetInputElement().attr('required', '');
+ }
+ else if (newAmount < productStockAmount + containerWeight)
+ {
+ $('#inventory-change-info').text(L('This means #1 will be removed from stock', estimatedBookingAmount.toLocaleString() + ' ' + Pluralize(estimatedBookingAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)));
+ Grocy.Components.DateTimePicker.GetInputElement().removeAttr('required');
+ }
Grocy.FrontendHelpers.ValidateForm('inventory-form');
},
diff --git a/public/viewjs/productform.js b/public/viewjs/productform.js
index dc2d45a3..8a9b9711 100644
--- a/public/viewjs/productform.js
+++ b/public/viewjs/productform.js
@@ -169,6 +169,8 @@ $('.input-group-qu').on('change', function(e)
$('#qu-conversion-info').addClass('d-none');
}
+ $("#tare_weight_qu_info").text($("#qu_id_stock option:selected").text());
+
Grocy.FrontendHelpers.ValidateForm('product-form');
});
@@ -199,6 +201,20 @@ $('#product-form input').keydown(function(event)
}
});
+$("#enable_tare_weight_handling").on("click", function()
+{
+ if (this.checked)
+ {
+ $("#tare_weight").removeAttr("disabled");
+ }
+ else
+ {
+ $("#tare_weight").attr("disabled", "");
+ }
+
+ Grocy.FrontendHelpers.ValidateForm("product-form");
+});
+
Grocy.DeleteProductPictureOnSave = false;
$('#delete-current-product-picture-button').on('click', function (e)
{
diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js
index 017bd7ef..b7b6ff8a 100644
--- a/public/viewjs/purchase.js
+++ b/public/viewjs/purchase.js
@@ -48,7 +48,7 @@
);
}
- var successMessage = L('Added #1 #2 of #3 to stock', amount, Pluralize(amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + L("Undo") + '';
+ var successMessage = L('Added #1 #2 of #3 to stock', result.amount, Pluralize(result.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name) + '
' + L("Undo") + '';
if (addBarcode !== undefined)
{
@@ -64,8 +64,13 @@
{
Grocy.FrontendHelpers.EndUiBusy("purchase-form");
toastr.success(successMessage);
+
+ $("#amount").attr("min", "1");
+ $("#amount").attr("step", "1");
+ $("#amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '1'));
$('#amount').val(0);
$('#price').val('');
+ $("#tare-weight-handling-info").addClass("d-none");
Grocy.Components.LocationPicker.Clear();
Grocy.Components.DateTimePicker.Clear();
Grocy.Components.ProductPicker.SetValue('');
@@ -97,7 +102,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
Grocy.Components.ProductCard.Refresh(productId);
Grocy.Api.Get('stock/products/' + productId,
- function(productDetails)
+ function (productDetails)
{
$('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name);
$('#price').val(productDetails.last_price);
@@ -116,6 +121,18 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
$("#amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', '1'));
}
+ if (productDetails.product.enable_tare_weight_handling == 1)
+ {
+ var minAmount = parseFloat(productDetails.product.tare_weight) + parseFloat(productDetails.stock_amount) + 1;
+ $("#amount").attr("min", minAmount);
+ $("#amount").parent().find(".invalid-feedback").text(L('The amount cannot be lower than #1', minAmount.toLocaleString()));
+ $("#tare-weight-handling-info").removeClass("d-none");
+ }
+ else
+ {
+ $("#tare-weight-handling-info").addClass("d-none");
+ }
+
if (productDetails.product.default_best_before_days.toString() !== '0')
{
if (productDetails.product.default_best_before_days == -1)
diff --git a/services/DemoDataGeneratorService.php b/services/DemoDataGeneratorService.php
index c8000785..52907578 100644
--- a/services/DemoDataGeneratorService.php
+++ b/services/DemoDataGeneratorService.php
@@ -63,7 +63,7 @@ class DemoDataGeneratorService extends BaseService
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id) VALUES ('{$localizationService->LocalizeForSqlString('Salami')}', 2, 3, 3, 1, 6); --18
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id) VALUES ('{$localizationService->LocalizeForSqlString('Toast')}', 4, 5, 5, 1, 2); --19
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id) VALUES ('{$localizationService->LocalizeForSqlString('Minced meat')}', 2, 3, 3, 1, 4); --20
- INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id) VALUES ('{$localizationService->LocalizeForSqlString('Flour')}', 2, 3, 3, 1, 3); --21
+ INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id, enable_tare_weight_handling, tare_weight) VALUES ('{$localizationService->LocalizeForSqlString('Flour')}', 3, 8, 8, 1, 3, 1, 500); --21
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock, product_group_id) VALUES ('{$localizationService->LocalizeForSqlString('Sugar')}', 3, 3, 3, 1, 3); --22
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('{$localizationService->LocalizeForSqlString('Milk')}', 2, 10, 10, 1); --23
@@ -191,15 +191,15 @@ class DemoDataGeneratorService extends BaseService
$stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
$stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
$stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
- $stockService->AddProduct(21, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
- $stockService->AddProduct(21, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(21, 1500, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(21, 2500, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
$stockService->AddProduct(22, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
$stockService->AddProduct(22, 1, date('Y-m-d', strtotime('+200 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
$stockService->AddProduct(23, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
$stockService->AddProduct(23, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
$stockService->AddMissingProductsToShoppingList();
+ $stockService->OpenProduct(3, 1);
$stockService->OpenProduct(6, 1);
- $stockService->OpenProduct(21, 1);
$stockService->OpenProduct(22, 1);
$choresService = new ChoresService();
diff --git a/services/StockService.php b/services/StockService.php
index 843fedc5..8a4600f8 100644
--- a/services/StockService.php
+++ b/services/StockService.php
@@ -130,6 +130,20 @@ class StockService extends BaseService
throw new \Exception('Product does not exist');
}
+ // Tare weight handling
+ // The given amount is the new total amount including the container weight (gross)
+ // The amount to be posted needs to be the given amount - stock amount - tare weight
+ $productDetails = (object)$this->GetProductDetails($productId);
+ if ($productDetails->product->enable_tare_weight_handling == 1)
+ {
+ if ($amount <= $productDetails->product->tare_weight + $productDetails->stock_amount)
+ {
+ throw new \Exception('The amount cannot be lower or equal than the defined tare weight + current stock amount');
+ }
+
+ $amount = $amount - $productDetails->stock_amount - $productDetails->product->tare_weight;
+ }
+
if ($transactionType === self::TRANSACTION_TYPE_PURCHASE || $transactionType === self::TRANSACTION_TYPE_INVENTORY_CORRECTION)
{
$stockId = uniqid();
@@ -174,6 +188,20 @@ class StockService extends BaseService
throw new \Exception('Product does not exist');
}
+ // Tare weight handling
+ // The given amount is the new total amount including the container weight (gross)
+ // The amount to be posted needs to be the absolute value of the given amount - stock amount - tare weight
+ $productDetails = (object)$this->GetProductDetails($productId);
+ if ($productDetails->product->enable_tare_weight_handling == 1)
+ {
+ if ($amount < $productDetails->product->tare_weight)
+ {
+ throw new \Exception('The amount cannot be lower than the defined tare weight');
+ }
+
+ $amount = abs($amount - $productDetails->stock_amount - $productDetails->product->tare_weight);
+ }
+
if ($transactionType === self::TRANSACTION_TYPE_CONSUME || $transactionType === self::TRANSACTION_TYPE_INVENTORY_CORRECTION)
{
$productStockAmount = $this->Database->stock()->where('product_id', $productId)->sum('amount');
@@ -258,19 +286,37 @@ class StockService extends BaseService
{
throw new \Exception('Product does not exist');
}
-
- $productStockAmount = $this->Database->stock()->where('product_id', $productId)->sum('amount');
- if ($newAmount > $productStockAmount)
+ $productDetails = (object)$this->GetProductDetails($productId);
+
+ // Tare weight handling
+ // The given amount is the new total amount including the container weight (gross)
+ // So assume that the amount in stock is the amount also including the container weight
+ $containerWeight = 0;
+ if ($productDetails->product->enable_tare_weight_handling == 1)
{
- $productDetails = $this->GetProductDetails($productId);
- $amountToAdd = $newAmount - $productStockAmount;
- $this->AddProduct($productId, $amountToAdd, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION, date('Y-m-d'), $productDetails['last_price']);
+ $containerWeight = $productDetails->product->tare_weight;
}
- else if ($newAmount < $productStockAmount)
+
+ if ($newAmount > $productDetails->stock_amount + $containerWeight)
{
- $amountToRemove = $productStockAmount - $newAmount;
- $this->ConsumeProduct($productId, $amountToRemove, false, self::TRANSACTION_TYPE_INVENTORY_CORRECTION);
+ $bookingAmount = $newAmount - $productDetails->stock_amount;
+ if ($productDetails->product->enable_tare_weight_handling == 1)
+ {
+ $bookingAmount = $newAmount;
+ }
+
+ $this->AddProduct($productId, $bookingAmount, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION, date('Y-m-d'), $productDetails->last_price);
+ }
+ else if ($newAmount < $productDetails->stock_amount + $containerWeight)
+ {
+ $bookingAmount = $productDetails->stock_amount - $newAmount;
+ if ($productDetails->product->enable_tare_weight_handling == 1)
+ {
+ $bookingAmount = $newAmount;
+ }
+
+ $this->ConsumeProduct($productId, $bookingAmount, false, self::TRANSACTION_TYPE_INVENTORY_CORRECTION);
}
return $this->Database->lastInsertId();
diff --git a/views/components/numberpicker.blade.php b/views/components/numberpicker.blade.php
index 97ff57a9..ffdb131b 100644
--- a/views/components/numberpicker.blade.php
+++ b/views/components/numberpicker.blade.php
@@ -12,10 +12,11 @@
@php if(empty($additionalGroupCssClasses)) { $additionalGroupCssClasses = ''; } @endphp
@php if(empty($additionalAttributes)) { $additionalAttributes = ''; } @endphp
@php if(empty($additionalHtmlElements)) { $additionalHtmlElements = ''; } @endphp
+@php if(empty($additionalHtmlContextHelp)) { $additionalHtmlContextHelp = ''; } @endphp
@php if(!isset($isRequired)) { $isRequired = true; } @endphp
- {{ $totalRecipeCosts }} {{ GROCY_CURRENCY }} + {{ $totalRecipeCosts }} {{ GROCY_CURRENCY }}