mirror of
https://github.com/grocy/grocy.git
synced 2025-04-28 17:23:56 +00:00
Add something for product price tracking (references #22)
This commit is contained in:
parent
f4eb5196f7
commit
c64eb27ca1
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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": {
|
||||
|
@ -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',
|
||||
|
5
migrations/0029.sql
Normal file
5
migrations/0029.sql
Normal file
@ -0,0 +1,5 @@
|
||||
ALTER TABLE stock
|
||||
ADD price DECIMAL(15, 2);
|
||||
|
||||
ALTER TABLE stock_log
|
||||
ADD price DECIMAL(15, 2);
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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')
|
||||
{
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/node_modules/chart.js//dist/Chart.min.js?v=', true) }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/viewjs/components/productcard.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@ -11,6 +12,11 @@
|
||||
<strong>{{ $L('Stock quantity unit') }}:</strong> <span id="productcard-product-stock-qu-name"></span><br>
|
||||
<strong>{{ $L('Stock amount') }}:</strong> <span id="productcard-product-stock-amount"></span> <span id="productcard-product-stock-qu-name2"></span><br>
|
||||
<strong>{{ $L('Last purchased') }}:</strong> <span id="productcard-product-last-purchased"></span> <time id="productcard-product-last-purchased-timeago" class="timeago timeago-contextual"></time><br>
|
||||
<strong>{{ $L('Last used') }}:</strong> <span id="productcard-product-last-used"></span> <time id="productcard-product-last-used-timeago" class="timeago timeago-contextual"></time>
|
||||
<strong>{{ $L('Last used') }}:</strong> <span id="productcard-product-last-used"></span> <time id="productcard-product-last-used-timeago" class="timeago timeago-contextual"></time><br>
|
||||
<strong>{{ $L('Last price') }}:</strong> <span id="productcard-product-last-price"></span>
|
||||
|
||||
<h5 class="mt-3">{{ $L('Price history') }}</h5>
|
||||
<canvas id="productcard-product-price-history-chart" class="w-100 d-none"></canvas>
|
||||
<span id="productcard-no-price-data-hint" class="font-italic d-none">{{ $L('No price history available') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,6 +39,7 @@
|
||||
Grocy.LocalizationStrings = {!! json_encode($localizationStrings) !!};
|
||||
Grocy.ActiveNav = '@yield('activeNav', '')';
|
||||
Grocy.Culture = '{{ GROCY_CULTURE }}';
|
||||
Grocy.Currency = '{{ GROCY_CURRENCY }}';
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
@ -30,10 +30,16 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="amount">{{ $L('Amount') }} <span id="amount_qu_unit" class="small text-muted"></span></label>
|
||||
<input type="number" class="form-control" id="amount" name="amount" value="1" min="1" required>
|
||||
<input type="number" class="form-control" id="amount" name="amount" value="1" min="1" step="1.00" required>
|
||||
<div class="invalid-feedback">{{ $L('The amount cannot be lower than #1', '1') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="price">{{ $L('Price') }} <span id="amount_qu_unit" class="small text-muted">{{ $L('in #1 per purchase quantity unit', GROCY_CURRENCY) }}</span></label>
|
||||
<input type="number" class="form-control" id="price" name="price" min="0" step="0.01">
|
||||
<div class="invalid-feedback">{{ $L('The price cannot be lower than #1', '0') }}</div>
|
||||
</div>
|
||||
|
||||
<button id="save-purchase-button" type="submit" class="btn btn-success">{{ $L('OK') }}</button>
|
||||
|
||||
</form>
|
||||
|
13
yarn.lock
13
yarn.lock
@ -10,7 +10,7 @@
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.2.0.tgz#50cd9856774351c56c0b1b0db4efe122d7913e58"
|
||||
|
||||
"TagManager@https://github.com/max-favilli/tagmanager.git#3.0.2":
|
||||
"TagManager@https://github.com/max-favilli/tagmanager.git#3.0.2", "tagmanager@https://github.com/max-favilli/tagmanager.git#3.0.2":
|
||||
version "3.0.1"
|
||||
resolved "https://github.com/max-favilli/tagmanager.git#df9eb9935c8585a392dfc00602f890caf233fa94"
|
||||
dependencies:
|
||||
@ -39,13 +39,20 @@ chart.js@2.7.1:
|
||||
chartjs-color "~2.2.0"
|
||||
moment "~2.18.0"
|
||||
|
||||
chart.js@^2.7.2:
|
||||
version "2.7.2"
|
||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.7.2.tgz#3c9fde4dc5b95608211bdefeda7e5d33dffa5714"
|
||||
dependencies:
|
||||
chartjs-color "^2.1.0"
|
||||
moment "^2.10.2"
|
||||
|
||||
chartjs-color-string@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz#8d3752d8581d86687c35bfe2cb80ac5213ceb8c1"
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
|
||||
chartjs-color@~2.2.0:
|
||||
chartjs-color@^2.1.0, chartjs-color@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.2.0.tgz#84a2fb755787ed85c39dd6dd8c7b1d88429baeae"
|
||||
dependencies:
|
||||
@ -161,7 +168,7 @@ moment-timezone@^0.5.11:
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0", moment@^2.22.2:
|
||||
"moment@>= 2.9.0", moment@^2.10.2, moment@^2.22.2:
|
||||
version "2.22.2"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user