diff --git a/config-dist.php b/config-dist.php
index bdfef11d..75c9cae3 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -7,6 +7,11 @@ Setting('MODE', 'production');
# one of the other available localization files in the "/localization" directory
Setting('CULTURE', 'en');
+# To keep it simpel, grocy does not handle any currency conversions,
+# this here is used to format all money values,
+# so can be anything (e. g. "USD" OR "$", doesn't matter...)
+Setting('CURRENCY', '$');
+
# The base url of your installation,
# should be just "/" when running directly under the root of a (sub)domain
# or for example "https:/example.com/grocy" when using a subdirectory
diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php
index 0a9061fe..8892eeef 100644
--- a/controllers/StockApiController.php
+++ b/controllers/StockApiController.php
@@ -26,6 +26,18 @@ class StockApiController extends BaseApiController
}
}
+ public function ProductPriceHistory(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
+ {
+ try
+ {
+ return $this->ApiResponse($this->StockService->GetProductPriceHistory($args['productId']));
+ }
+ catch (\Exception $ex)
+ {
+ return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage());
+ }
+ }
+
public function AddProduct(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
$bestBeforeDate = date('Y-m-d');
@@ -34,6 +46,12 @@ class StockApiController extends BaseApiController
$bestBeforeDate = $request->getQueryParams()['bestbeforedate'];
}
+ $price = null;
+ if (isset($request->getQueryParams()['price']) && !empty($request->getQueryParams()['price']) && is_numeric($request->getQueryParams()['price']))
+ {
+ $price = $request->getQueryParams()['price'];
+ }
+
$transactionType = StockService::TRANSACTION_TYPE_PURCHASE;
if (isset($request->getQueryParams()['transactiontype']) && !empty($request->getQueryParams()['transactiontype']))
{
@@ -42,7 +60,7 @@ class StockApiController extends BaseApiController
try
{
- $this->StockService->AddProduct($args['productId'], $args['amount'], $bestBeforeDate, $transactionType);
+ $this->StockService->AddProduct($args['productId'], $args['amount'], $bestBeforeDate, $transactionType, date('Y-m-d'), $price);
return $this->VoidApiActionResponse($response);
}
catch (\Exception $ex)
diff --git a/grocy.openapi.json b/grocy.openapi.json
index 0aaf59ce..403c536b 100644
--- a/grocy.openapi.json
+++ b/grocy.openapi.json
@@ -571,6 +571,16 @@
"type": "date"
}
},
+ {
+ "in": "query",
+ "name": "price",
+ "required": false,
+ "description": "The price per purchase quantity unit in configured currency",
+ "schema": {
+ "type": "number",
+ "format": "double"
+ }
+ },
{
"in": "query",
"name": "transactiontype",
@@ -774,6 +784,50 @@
}
}
},
+ "/stock/get-product-price-history/{productId}": {
+ "get": {
+ "description": "Returns the price history of the given product",
+ "tags": [
+ "Stock"
+ ],
+ "parameters": [
+ {
+ "in": "path",
+ "name": "productId",
+ "required": true,
+ "description": "A valid product id",
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "An array of ProductPriceHistory objects",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ProductPriceHistory"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "A VoidApiActionResponse object (possible errors are: Not existing product)",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorExampleVoidApiActionResponse"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/stock/get-current-stock": {
"get": {
"description": "Returns all products which are currently in stock incl. the next expiring date per product",
@@ -1272,6 +1326,19 @@
}
}
},
+ "ProductPriceHistory": {
+ "type": "object",
+ "properties": {
+ "date": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "price": {
+ "type": "number",
+ "format": "double"
+ }
+ }
+ },
"ExternalBarcodeLookupResponse": {
"type": "object",
"properties": {
diff --git a/localization/de.php b/localization/de.php
index 5cfecd83..db943ea8 100644
--- a/localization/de.php
+++ b/localization/de.php
@@ -188,6 +188,12 @@ return array(
'Habits analysis' => 'Gewohnheiten Analyse',
'0 means suggestions for the next charge cycle are disabled' => '0 bedeutet dass Vorschläge für den nächsten Ladezyklus deaktiviert sind',
'Charge cycle interval (days)' => 'Ladezyklusintervall (Tage)',
+ 'Last price' => 'Letzter Preis',
+ 'Price history' => 'Preisentwicklung',
+ 'No price history available' => 'Keine Preisdaten verfügbar',
+ 'Price' => 'Preis',
+ 'in #1 per purchase quantity unit' => 'in #1 pro Einkaufsmengeneinheit',
+ 'The price cannot be lower than #1' => 'Der Preis darf nicht niedriger als #1 sein',
//Constants
'manually' => 'Manuell',
diff --git a/migrations/0029.sql b/migrations/0029.sql
new file mode 100644
index 00000000..793ee9bc
--- /dev/null
+++ b/migrations/0029.sql
@@ -0,0 +1,5 @@
+ALTER TABLE stock
+ADD price DECIMAL(15, 2);
+
+ALTER TABLE stock_log
+ADD price DECIMAL(15, 2);
diff --git a/package.json b/package.json
index 078dd4b3..1d07b659 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"tagmanager": "https://github.com/max-favilli/tagmanager.git#3.0.2",
"tempusdominus-bootstrap-4": "^5.0.1",
"timeago": "^1.6.3",
- "toastr": "^2.1.4"
+ "toastr": "^2.1.4",
+ "chart.js": "^2.7.2"
}
}
diff --git a/public/viewjs/components/productcard.js b/public/viewjs/components/productcard.js
index 45ad1d7e..6b0e7c1b 100644
--- a/public/viewjs/components/productcard.js
+++ b/public/viewjs/components/productcard.js
@@ -14,6 +14,15 @@ Grocy.Components.ProductCard.Refresh = function(productId)
$('#productcard-product-last-used').text((productDetails.last_used || L('never')).substring(0, 10));
$('#productcard-product-last-used-timeago').text($.timeago(productDetails.last_used || ''));
+ if (productDetails.last_price !== null)
+ {
+ $('#productcard-product-last-price').text(Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + Grocy.Currency);
+ }
+ else
+ {
+ $('#productcard-product-last-price').text(L('Unknown'));
+ }
+
EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', L('timeago_nan'));
EmptyElementWhenMatches('#productcard-product-last-used-timeago', L('timeago_nan'));
},
@@ -22,4 +31,90 @@ Grocy.Components.ProductCard.Refresh = function(productId)
console.error(xhr);
}
);
+
+ Grocy.Api.Get('stock/get-product-price-history/' + productId,
+ function(priceHistoryDataPoints)
+ {
+ if (priceHistoryDataPoints.length > 0)
+ {
+ $("#productcard-product-price-history-chart").removeClass("d-none");
+ $("#productcard-no-price-data-hint").addClass("d-none");
+
+ Grocy.Components.ProductCard.ReInitPriceHistoryChart();
+ priceHistoryDataPoints.forEach((dataPoint) =>
+ {
+ Grocy.Components.ProductCard.PriceHistoryChart.data.labels.push(moment(dataPoint.date).toDate());
+
+ var dataset = Grocy.Components.ProductCard.PriceHistoryChart.data.datasets[0];
+ dataset.data.push(dataPoint.price);
+ });
+ Grocy.Components.ProductCard.PriceHistoryChart.update();
+ }
+ else
+ {
+ $("#productcard-product-price-history-chart").addClass("d-none");
+ $("#productcard-no-price-data-hint").removeClass("d-none");
+ }
+ },
+ function(xhr)
+ {
+ console.error(xhr);
+ }
+ );
};
+
+Grocy.Components.ProductCard.ReInitPriceHistoryChart = function()
+{
+ if (typeof Grocy.Components.ProductCard.PriceHistoryChart !== "undefined")
+ {
+ Grocy.Components.ProductCard.PriceHistoryChart.destroy();
+ }
+
+ var format = 'YYYY-MM-DD';
+ Grocy.Components.ProductCard.PriceHistoryChart = new Chart(document.getElementById("productcard-product-price-history-chart"), {
+ type: "line",
+ data: {
+ labels: [ //Date objects
+ new Date()
+ // Will be populated in Grocy.Components.ProductCard.Refresh
+ ],
+ datasets: [{
+ data: [
+ 0
+ // Will be populated in Grocy.Components.ProductCard.Refresh
+ ],
+ fill: false,
+ borderColor: '#17a2b8'
+ }]
+ },
+ options: {
+ scales: {
+ xAxes: [{
+ type: 'time',
+ time: {
+ parser: format,
+ round: 'day',
+ tooltipFormat: format,
+ unit: 'day',
+ unitStepSize: 10,
+ displayFormats: {
+ 'day': format
+ }
+ },
+ ticks: {
+ autoSkip: true,
+ maxRotation: 0
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ },
+ legend: {
+ display: false
+ }
+ }
+ });
+}
diff --git a/public/viewjs/purchase.js b/public/viewjs/purchase.js
index e9fb9056..e48efa14 100644
--- a/public/viewjs/purchase.js
+++ b/public/viewjs/purchase.js
@@ -9,7 +9,13 @@
{
var amount = jsonForm.amount * productDetails.product.qu_factor_purchase_to_stock;
- Grocy.Api.Get('stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + Grocy.Components.DateTimePicker.GetValue(),
+ var price = "";
+ if (!jsonForm.price.toString().isEmpty())
+ {
+ price = parseFloat(jsonForm.price).toFixed(2);
+ }
+
+ Grocy.Api.Get('stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + Grocy.Components.DateTimePicker.GetValue() + '&price=' + price,
function(result)
{
var addBarcode = GetUriParam('addbarcodetoselection');
@@ -43,6 +49,7 @@
else
{
$('#amount').val(0);
+ $('#price').val('');
Grocy.Components.DateTimePicker.SetValue('');
Grocy.Components.ProductPicker.SetValue('');
Grocy.Components.ProductPicker.GetInputElement().focus();
@@ -74,6 +81,7 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
function(productDetails)
{
$('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name);
+ $('#price').val(productDetails.last_price);
if (productDetails.product.default_best_before_days.toString() !== '0')
{
diff --git a/routes.php b/routes.php
index c01f2d3a..d7d6f046 100644
--- a/routes.php
+++ b/routes.php
@@ -82,6 +82,7 @@ $app->group('/api', function()
$this->get('/stock/consume-product/{productId}/{amount}', '\Grocy\Controllers\StockApiController:ConsumeProduct');
$this->get('/stock/inventory-product/{productId}/{newAmount}', '\Grocy\Controllers\StockApiController:InventoryProduct');
$this->get('/stock/get-product-details/{productId}', '\Grocy\Controllers\StockApiController:ProductDetails');
+ $this->get('/stock/get-product-price-history/{productId}', '\Grocy\Controllers\StockApiController:ProductPriceHistory');
$this->get('/stock/get-current-stock', '\Grocy\Controllers\StockApiController:CurrentStock');
$this->get('/stock/add-missing-products-to-shoppinglist', '\Grocy\Controllers\StockApiController:AddMissingProductsToShoppingList');
$this->get('/stock/clear-shopping-list', '\Grocy\Controllers\StockApiController:ClearShoppingList');
diff --git a/services/DemoDataGeneratorService.php b/services/DemoDataGeneratorService.php
index d1c9be40..95b33cdf 100644
--- a/services/DemoDataGeneratorService.php
+++ b/services/DemoDataGeneratorService.php
@@ -89,19 +89,71 @@ class DemoDataGeneratorService extends BaseService
$this->DatabaseService->ExecuteDbStatement($sql);
$stockService = new StockService();
- $stockService->AddProduct(3, 5, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(4, 5, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(5, 5, date('Y-m-d', strtotime('+20 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(6, 5, date('Y-m-d', strtotime('+600 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(7, 5, date('Y-m-d', strtotime('+800 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(8, 5, date('Y-m-d', strtotime('+900 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(9, 5, date('Y-m-d', strtotime('+14 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(10, 5, date('Y-m-d', strtotime('+21 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(11, 5, date('Y-m-d', strtotime('+10 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(12, 5, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(13, 5, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(14, 5, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE);
- $stockService->AddProduct(15, 5, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE);
+ $stockService->AddProduct(3, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(3, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(3, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(3, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(3, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(4, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(4, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(4, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(4, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(4, 1, date('Y-m-d', strtotime('+180 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(5, 1, date('Y-m-d', strtotime('+20 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(5, 1, date('Y-m-d', strtotime('+20 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(5, 1, date('Y-m-d', strtotime('+20 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(5, 1, date('Y-m-d', strtotime('+20 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(5, 1, date('Y-m-d', strtotime('+20 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(6, 1, date('Y-m-d', strtotime('+600 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(6, 1, date('Y-m-d', strtotime('+600 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(6, 1, date('Y-m-d', strtotime('+600 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(6, 1, date('Y-m-d', strtotime('+600 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(6, 1, date('Y-m-d', strtotime('+600 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(7, 1, date('Y-m-d', strtotime('+800 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(7, 1, date('Y-m-d', strtotime('+800 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(7, 1, date('Y-m-d', strtotime('+800 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(7, 1, date('Y-m-d', strtotime('+800 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(7, 1, date('Y-m-d', strtotime('+800 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(8, 1, date('Y-m-d', strtotime('+900 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(8, 1, date('Y-m-d', strtotime('+900 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(8, 1, date('Y-m-d', strtotime('+900 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(8, 1, date('Y-m-d', strtotime('+900 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(8, 1, date('Y-m-d', strtotime('+900 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(9, 1, date('Y-m-d', strtotime('+14 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(9, 1, date('Y-m-d', strtotime('+14 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(9, 1, date('Y-m-d', strtotime('+14 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(9, 1, date('Y-m-d', strtotime('+14 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(9, 1, date('Y-m-d', strtotime('+14 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(10, 1, date('Y-m-d', strtotime('+21 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(10, 1, date('Y-m-d', strtotime('+21 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(10, 1, date('Y-m-d', strtotime('+21 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(10, 1, date('Y-m-d', strtotime('+21 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(10, 1, date('Y-m-d', strtotime('+21 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(11, 1, date('Y-m-d', strtotime('+10 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(11, 1, date('Y-m-d', strtotime('+10 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(11, 1, date('Y-m-d', strtotime('+10 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(11, 1, date('Y-m-d', strtotime('+10 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(11, 1, date('Y-m-d', strtotime('+10 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(12, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(12, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(12, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(12, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(12, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(13, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(13, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(13, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(13, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(13, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(14, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(14, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $stockService->AddProduct(14, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-30 days')), $this->RandomPrice());
+ $stockService->AddProduct(14, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-40 days')), $this->RandomPrice());
+ $stockService->AddProduct(14, 1, date('Y-m-d', strtotime('+2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-50 days')), $this->RandomPrice());
+ $stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-10 days')), $this->RandomPrice());
+ $stockService->AddProduct(15, 1, date('Y-m-d', strtotime('-2 days')), StockService::TRANSACTION_TYPE_PURCHASE, date('Y-m-d', strtotime('-20 days')), $this->RandomPrice());
+ $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->AddMissingProductsToShoppingList();
$habitsService = new HabitsService();
@@ -126,9 +178,8 @@ class DemoDataGeneratorService extends BaseService
}
}
- public function RecreateDemo()
+ private function RandomPrice()
{
- unlink(GROCY_DATAPATH . '/grocy.db');
- $this->PopulateDemoData();
+ return mt_rand(2 * 100, 25 * 100) / 100;
}
}
diff --git a/services/StockService.php b/services/StockService.php
index 9f545eb1..5e717673 100644
--- a/services/StockService.php
+++ b/services/StockService.php
@@ -33,6 +33,7 @@ class StockService extends BaseService
$productLastUsed = $this->Database->stock_log()->where('product_id', $productId)->where('transaction_type', self::TRANSACTION_TYPE_CONSUME)->max('used_date');
$quPurchase = $this->Database->quantity_units($product->qu_id_purchase);
$quStock = $this->Database->quantity_units($product->qu_id_stock);
+ $lastPrice = $this->Database->stock_log()->where('product_id = :1 AND transaction_type = :2', $productId, self::TRANSACTION_TYPE_PURCHASE)->orderBy('row_created_timestamp', 'DESC')->limit(1)->fetch()->price;
return array(
'product' => $product,
@@ -40,11 +41,31 @@ class StockService extends BaseService
'last_used' => $productLastUsed,
'stock_amount' => $productStockAmount,
'quantity_unit_purchase' => $quPurchase,
- 'quantity_unit_stock' => $quStock
+ 'quantity_unit_stock' => $quStock,
+ 'last_price' => $lastPrice
);
}
- public function AddProduct(int $productId, int $amount, string $bestBeforeDate, $transactionType)
+ public function GetProductPriceHistory(int $productId)
+ {
+ if (!$this->ProductExists($productId))
+ {
+ throw new \Exception('Product does not exist');
+ }
+
+ $returnData = array();
+ $rows = $this->Database->stock_log()->where('product_id = :1 AND transaction_type = :2', $productId, self::TRANSACTION_TYPE_PURCHASE)->whereNOT('price', null)->orderBy('purchased_date', 'DESC');
+ foreach ($rows as $row)
+ {
+ $returnData[] = array(
+ 'date' => $row->purchased_date,
+ 'price' => $row->price
+ );
+ }
+ return $returnData;
+ }
+
+ public function AddProduct(int $productId, int $amount, string $bestBeforeDate, $transactionType, $purchasedDate, $price)
{
if (!$this->ProductExists($productId))
{
@@ -59,9 +80,10 @@ class StockService extends BaseService
'product_id' => $productId,
'amount' => $amount,
'best_before_date' => $bestBeforeDate,
- 'purchased_date' => date('Y-m-d'),
+ 'purchased_date' => $purchasedDate,
'stock_id' => $stockId,
- 'transaction_type' => $transactionType
+ 'transaction_type' => $transactionType,
+ 'price' => $price
));
$logRow->save();
@@ -69,8 +91,9 @@ class StockService extends BaseService
'product_id' => $productId,
'amount' => $amount,
'best_before_date' => $bestBeforeDate,
- 'purchased_date' => date('Y-m-d'),
+ 'purchased_date' => $purchasedDate,
'stock_id' => $stockId,
+ 'price' => $price
));
$stockRow->save();
@@ -116,7 +139,8 @@ class StockService extends BaseService
'used_date' => date('Y-m-d'),
'spoiled' => $spoiled,
'stock_id' => $stockEntry->stock_id,
- 'transaction_type' => $transactionType
+ 'transaction_type' => $transactionType,
+ 'price' => $stockEntry->price
));
$logRow->save();
@@ -133,7 +157,8 @@ class StockService extends BaseService
'used_date' => date('Y-m-d'),
'spoiled' => $spoiled,
'stock_id' => $stockEntry->stock_id,
- 'transaction_type' => $transactionType
+ 'transaction_type' => $transactionType,
+ 'price' => $stockEntry->price
));
$logRow->save();
@@ -165,8 +190,9 @@ class StockService extends BaseService
if ($newAmount > $productStockAmount)
{
+ $productDetails = $this->GetProductDetails($productId);
$amountToAdd = $newAmount - $productStockAmount;
- $this->AddProduct($productId, $amountToAdd, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION);
+ $this->AddProduct($productId, $amountToAdd, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION, date('Y-m-d'), $productDetails['last_price']);
}
else if ($newAmount < $productStockAmount)
{
diff --git a/views/components/productcard.blade.php b/views/components/productcard.blade.php
index 81df9c6f..db23546e 100644
--- a/views/components/productcard.blade.php
+++ b/views/components/productcard.blade.php
@@ -1,4 +1,5 @@
@push('componentScripts')
+
@endpush
@@ -11,6 +12,11 @@
{{ $L('Stock quantity unit') }}:
{{ $L('Stock amount') }}:
{{ $L('Last purchased') }}:
- {{ $L('Last used') }}:
+ {{ $L('Last used') }}:
+ {{ $L('Last price') }}:
+
+