mirror of
https://github.com/grocy/grocy.git
synced 2025-10-12 16:44:55 +00:00
This is 1.0
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -198,6 +198,5 @@ FakesAssemblies/
|
|||||||
/bower_components
|
/bower_components
|
||||||
/vendor
|
/vendor
|
||||||
/.release
|
/.release
|
||||||
/config.php
|
|
||||||
/composer.phar
|
/composer.phar
|
||||||
/composer.lock
|
/composer.lock
|
||||||
|
@@ -14,6 +14,8 @@ class GrocyDbMigrator
|
|||||||
qu_id_stock INTEGER NOT NULL,
|
qu_id_stock INTEGER NOT NULL,
|
||||||
qu_factor_purchase_to_stock REAL NOT NULL,
|
qu_factor_purchase_to_stock REAL NOT NULL,
|
||||||
barcode TEXT,
|
barcode TEXT,
|
||||||
|
min_stock_amount INTEGER NOT NULL DEFAULT 0,
|
||||||
|
default_best_before_days INTEGER NOT NULL DEFAULT 0,
|
||||||
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||||
)"
|
)"
|
||||||
);
|
);
|
||||||
|
@@ -6,15 +6,31 @@ class GrocyDemoDataGenerator
|
|||||||
{
|
{
|
||||||
$sql = "
|
$sql = "
|
||||||
UPDATE locations SET name = 'Vorratskammer', description = '' WHERE id = 1;
|
UPDATE locations SET name = 'Vorratskammer', description = '' WHERE id = 1;
|
||||||
INSERT INTO locations (name) VALUES ('S<><53>igkeitenschrank');
|
INSERT INTO locations (name) VALUES ('S<><53>igkeitenschrank'); --2
|
||||||
INSERT INTO locations (name) VALUES ('Konvervenschrank');
|
INSERT INTO locations (name) VALUES ('Konservenschrank'); --3
|
||||||
|
INSERT INTO locations (name) VALUES ('K<>hlschrank'); --4
|
||||||
|
|
||||||
UPDATE quantity_units SET name = 'St<53>ck' WHERE id = 1;
|
UPDATE quantity_units SET name = 'St<53>ck' WHERE id = 1;
|
||||||
INSERT INTO quantity_units (name) VALUES ('Packung');
|
INSERT INTO quantity_units (name) VALUES ('Packung'); --2
|
||||||
|
INSERT INTO quantity_units (name) VALUES ('Glas'); --3
|
||||||
|
INSERT INTO quantity_units (name) VALUES ('Dose'); --4
|
||||||
|
INSERT INTO quantity_units (name) VALUES ('Becher'); --5
|
||||||
|
INSERT INTO quantity_units (name) VALUES ('Bund'); --6
|
||||||
|
|
||||||
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Gummib<69>rchen', 2, 2, 2, 1);
|
DELETE FROM products WHERE id IN (1, 2);
|
||||||
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Chips', 2, 2, 2, 1);
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Gummib<EFBFBD>rchen', 2, 2, 2, 1); --3
|
||||||
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Eier', 1, 2, 1, 10);
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Chips', 2, 2, 2, 1); --4
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Eier', 1, 2, 1, 10); --5
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Nudeln', 1, 2, 2, 1); --6
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Essiggurken', 3, 3, 3, 1); --7
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Gulaschsuppe', 3, 4, 4, 1); --8
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Joghurt', 4, 5, 5, 1); --9
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('K<>se', 4, 2, 2, 1); --10
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Aufschnitt', 4, 2, 2, 1); --11
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Paprika', 4, 1, 1, 1); --12
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Gurke', 4, 1, 1, 1); --13
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Radieschen', 4, 6, 6, 1); --14
|
||||||
|
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Tomate', 4, 1, 1, 1); --15
|
||||||
";
|
";
|
||||||
|
|
||||||
if ($pdo->exec(utf8_encode($sql)) === false)
|
if ($pdo->exec(utf8_encode($sql)) === false)
|
||||||
@@ -24,6 +40,16 @@ class GrocyDemoDataGenerator
|
|||||||
|
|
||||||
GrocyLogicStock::AddProduct(3, 5, date('Y-m-d', strtotime('+180 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
GrocyLogicStock::AddProduct(3, 5, date('Y-m-d', strtotime('+180 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
GrocyLogicStock::AddProduct(4, 5, date('Y-m-d', strtotime('+180 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
GrocyLogicStock::AddProduct(4, 5, date('Y-m-d', strtotime('+180 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
GrocyLogicStock::AddProduct(5, 5, date('Y-m-d', strtotime('+25 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
GrocyLogicStock::AddProduct(5, 5, date('Y-m-d', strtotime('+20 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(6, 5, date('Y-m-d', strtotime('+600 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(7, 5, date('Y-m-d', strtotime('+800 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(8, 5, date('Y-m-d', strtotime('+900 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(9, 5, date('Y-m-d', strtotime('+14 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(10, 5, date('Y-m-d', strtotime('+21 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(11, 5, date('Y-m-d', strtotime('+10 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(12, 5, date('Y-m-d', strtotime('+2 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(13, 5, date('Y-m-d', strtotime('-2 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(14, 5, date('Y-m-d', strtotime('+2 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
|
GrocyLogicStock::AddProduct(15, 5, date('Y-m-d', strtotime('-2 days')), GrocyLogicStock::TRANSACTION_TYPE_PURCHASE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,88 +35,121 @@ class GrocyLogicStock
|
|||||||
|
|
||||||
public static function AddProduct(int $productId, int $amount, string $bestBeforeDate, $transactionType)
|
public static function AddProduct(int $productId, int $amount, string $bestBeforeDate, $transactionType)
|
||||||
{
|
{
|
||||||
$db = Grocy::GetDbConnection();
|
if ($transactionType === self::TRANSACTION_TYPE_CONSUME || $transactionType === self::TRANSACTION_TYPE_PURCHASE || $transactionType === self::TRANSACTION_TYPE_INVENTORY_CORRECTION)
|
||||||
$stockId = uniqid();
|
{
|
||||||
|
$db = Grocy::GetDbConnection();
|
||||||
|
$stockId = uniqid();
|
||||||
|
|
||||||
$logRow = $db->stock_log()->createRow(array(
|
$logRow = $db->stock_log()->createRow(array(
|
||||||
'product_id' => $productId,
|
'product_id' => $productId,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
'best_before_date' => $bestBeforeDate,
|
'best_before_date' => $bestBeforeDate,
|
||||||
'purchased_date' => date('Y-m-d'),
|
'purchased_date' => date('Y-m-d'),
|
||||||
'stock_id' => $stockId,
|
'stock_id' => $stockId,
|
||||||
'transaction_type' => $transactionType
|
'transaction_type' => $transactionType
|
||||||
));
|
));
|
||||||
$logRow->save();
|
$logRow->save();
|
||||||
|
|
||||||
$stockRow = $db->stock()->createRow(array(
|
$stockRow = $db->stock()->createRow(array(
|
||||||
'product_id' => $productId,
|
'product_id' => $productId,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
'best_before_date' => $bestBeforeDate,
|
'best_before_date' => $bestBeforeDate,
|
||||||
'purchased_date' => date('Y-m-d'),
|
'purchased_date' => date('Y-m-d'),
|
||||||
'stock_id' => $stockId,
|
'stock_id' => $stockId,
|
||||||
));
|
));
|
||||||
$stockRow->save();
|
$stockRow->save();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Transaction type $transactionType is not valid (GrocyLogicStock.AddProduct)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ConsumeProduct(int $productId, int $amount, bool $spoiled, $transactionType)
|
public static function ConsumeProduct(int $productId, int $amount, bool $spoiled, $transactionType)
|
||||||
{
|
{
|
||||||
$db = Grocy::GetDbConnection();
|
if ($transactionType === self::TRANSACTION_TYPE_CONSUME || $transactionType === self::TRANSACTION_TYPE_PURCHASE || $transactionType === self::TRANSACTION_TYPE_INVENTORY_CORRECTION)
|
||||||
|
|
||||||
$productStockAmount = $db->stock()->where('product_id', $productId)->sum('amount');
|
|
||||||
$potentialStockEntries = $db->stock()->where('product_id', $productId)->orderBy('purchased_date', 'ASC')->fetchAll(); //FIFO
|
|
||||||
|
|
||||||
if ($amount > $productStockAmount)
|
|
||||||
{
|
{
|
||||||
return false;
|
$db = Grocy::GetDbConnection();
|
||||||
|
|
||||||
|
$productStockAmount = $db->stock()->where('product_id', $productId)->sum('amount');
|
||||||
|
$potentialStockEntries = $db->stock()->where('product_id', $productId)->orderBy('purchased_date', 'ASC')->fetchAll(); //FIFO
|
||||||
|
|
||||||
|
if ($amount > $productStockAmount)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($potentialStockEntries as $stockEntry)
|
||||||
|
{
|
||||||
|
if ($amount == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($amount >= $stockEntry->amount) //Take the whole stock entry
|
||||||
|
{
|
||||||
|
$logRow = $db->stock_log()->createRow(array(
|
||||||
|
'product_id' => $stockEntry->product_id,
|
||||||
|
'amount' => $stockEntry->amount * -1,
|
||||||
|
'best_before_date' => $stockEntry->best_before_date,
|
||||||
|
'purchased_date' => $stockEntry->purchased_date,
|
||||||
|
'used_date' => date('Y-m-d'),
|
||||||
|
'spoiled' => $spoiled,
|
||||||
|
'stock_id' => $stockEntry->stock_id,
|
||||||
|
'transaction_type' => $transactionType
|
||||||
|
));
|
||||||
|
$logRow->save();
|
||||||
|
|
||||||
|
$amount -= $stockEntry->amount;
|
||||||
|
$stockEntry->delete();
|
||||||
|
}
|
||||||
|
else //Stock entry amount is > than needed amount -> split the stock entry resp. update the amount
|
||||||
|
{
|
||||||
|
$logRow = $db->stock_log()->createRow(array(
|
||||||
|
'product_id' => $stockEntry->product_id,
|
||||||
|
'amount' => $amount * -1,
|
||||||
|
'best_before_date' => $stockEntry->best_before_date,
|
||||||
|
'purchased_date' => $stockEntry->purchased_date,
|
||||||
|
'used_date' => date('Y-m-d'),
|
||||||
|
'spoiled' => $spoiled,
|
||||||
|
'stock_id' => $stockEntry->stock_id,
|
||||||
|
'transaction_type' => $transactionType
|
||||||
|
));
|
||||||
|
$logRow->save();
|
||||||
|
|
||||||
|
$restStockAmount = $stockEntry->amount - $amount;
|
||||||
|
$amount = 0;
|
||||||
|
|
||||||
|
$stockEntry->update(array(
|
||||||
|
'amount' => $restStockAmount
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
foreach ($potentialStockEntries as $stockEntry)
|
|
||||||
{
|
{
|
||||||
if ($amount == 0)
|
throw new Exception("Transaction type $transactionType is not valid (GrocyLogicStock.ConsumeProduct)");
|
||||||
{
|
}
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ($amount >= $stockEntry->amount) //Take the whole stock entry
|
public static function InventoryProduct(int $productId, int $newAmount, string $bestBeforeDate)
|
||||||
{
|
{
|
||||||
$logRow = $db->stock_log()->createRow(array(
|
$db = Grocy::GetDbConnection();
|
||||||
'product_id' => $stockEntry->product_id,
|
$productStockAmount = $db->stock()->where('product_id', $productId)->sum('amount');
|
||||||
'amount' => $stockEntry->amount * -1,
|
|
||||||
'best_before_date' => $stockEntry->best_before_date,
|
|
||||||
'purchased_date' => $stockEntry->purchased_date,
|
|
||||||
'used_date' => date('Y-m-d'),
|
|
||||||
'spoiled' => $spoiled,
|
|
||||||
'stock_id' => $stockEntry->stock_id,
|
|
||||||
'transaction_type' => $transactionType
|
|
||||||
));
|
|
||||||
$logRow->save();
|
|
||||||
|
|
||||||
$amount -= $stockEntry->amount;
|
if ($newAmount > $productStockAmount)
|
||||||
$stockEntry->delete();
|
{
|
||||||
}
|
$amountToAdd = $newAmount - $productStockAmount;
|
||||||
else //Stock entry amount is > than needed amount -> split the stock entry resp. update the amount
|
self::AddProduct($productId, $amountToAdd, $bestBeforeDate, self::TRANSACTION_TYPE_INVENTORY_CORRECTION);
|
||||||
{
|
}
|
||||||
$logRow = $db->stock_log()->createRow(array(
|
else if ($newAmount < $productStockAmount)
|
||||||
'product_id' => $stockEntry->product_id,
|
{
|
||||||
'amount' => $amount * -1,
|
$amountToRemove = $productStockAmount - $newAmount;
|
||||||
'best_before_date' => $stockEntry->best_before_date,
|
self::ConsumeProduct($productId, $amountToRemove, false, self::TRANSACTION_TYPE_INVENTORY_CORRECTION);
|
||||||
'purchased_date' => $stockEntry->purchased_date,
|
|
||||||
'used_date' => date('Y-m-d H:i:s'),
|
|
||||||
'spoiled' => $spoiled,
|
|
||||||
'stock_id' => $stockEntry->stock_id,
|
|
||||||
'transaction_type' => $transactionType
|
|
||||||
));
|
|
||||||
$logRow->save();
|
|
||||||
|
|
||||||
$restStockAmount = $stockEntry->amount - $amount;
|
|
||||||
$amount = 0;
|
|
||||||
|
|
||||||
$stockEntry->update(array(
|
|
||||||
'amount' => $restStockAmount
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@@ -14,4 +14,36 @@ class GrocyPhpHelper
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function FindAllObjectsInArrayByPropertyValue($array, $propertyName, $propertyValue, $operator = '==')
|
||||||
|
{
|
||||||
|
$returnArray = array();
|
||||||
|
|
||||||
|
foreach($array as $object)
|
||||||
|
{
|
||||||
|
switch($operator)
|
||||||
|
{
|
||||||
|
case '==':
|
||||||
|
if($object->{$propertyName} == $propertyValue)
|
||||||
|
{
|
||||||
|
$returnArray[] = $object;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
if($object->{$propertyName} > $propertyValue)
|
||||||
|
{
|
||||||
|
$returnArray[] = $object;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
if($object->{$propertyName} < $propertyValue)
|
||||||
|
{
|
||||||
|
$returnArray[] = $object;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $returnArray;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,10 +11,7 @@ For now my main focus is on stock management, ERP your fridge!
|
|||||||
Public demo of the latest version → [https://grocy.projectdemos.berrnd.org](https://grocy.projectdemos.berrnd.org)
|
Public demo of the latest version → [https://grocy.projectdemos.berrnd.org](https://grocy.projectdemos.berrnd.org)
|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
Just unpack the [latest release](https://github.com/berrnd/grocy/releases/latest) on your PHP enabled webserver, copy `config-dist.php` to `config.php`, edit it to your needs, ensure that the `data` directory is writable and you're ready to go. Alternatively clone this repository and install Composer and Bower dependencies manually.
|
Just unpack the [latest release](https://github.com/berrnd/grocy/releases/latest) on your PHP enabled webserver, copy `config-dist.php` to `data/config.php`, edit it to your needs, ensure that the `data` directory is writable and you're ready to go. Alternatively clone this repository and install Composer and Bower dependencies manually.
|
||||||
|
|
||||||
## Todo
|
|
||||||
A lot...
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
@@ -8,4 +8,4 @@ for /f "tokens=*" %%a in ('type version.txt') do set version=%%a
|
|||||||
|
|
||||||
del "%releasePath%\grocy_%version%.zip"
|
del "%releasePath%\grocy_%version%.zip"
|
||||||
"build_tools\7za.exe" a -r "%releasePath%\grocy_%version%.zip" "%projectPath%\*" -xr!.* -xr!build_tools -xr!build.bat -xr!composer.json -xr!composer.lock -xr!composer.phar -xr!grocy.phpproj -xr!grocy.phpproj.user -xr!grocy.sln
|
"build_tools\7za.exe" a -r "%releasePath%\grocy_%version%.zip" "%projectPath%\*" -xr!.* -xr!build_tools -xr!build.bat -xr!composer.json -xr!composer.lock -xr!composer.phar -xr!grocy.phpproj -xr!grocy.phpproj.user -xr!grocy.sln
|
||||||
"build_tools\7za.exe" d "%releasePath%\grocy_%version%.zip" data\add_before_end_body.html data\demo.txt data\grocy.db data\.gitignore config.php bower.json
|
"build_tools\7za.exe" d "%releasePath%\grocy_%version%.zip" data\add_before_end_body.html data\demo.txt data\config.php data\grocy.db data\.gitignore bower.json
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
<Compile Include="GrocyDbMigrator.php" />
|
<Compile Include="GrocyDbMigrator.php" />
|
||||||
<Compile Include="index.php" />
|
<Compile Include="index.php" />
|
||||||
<Compile Include="views\consumption.php" />
|
<Compile Include="views\consumption.php" />
|
||||||
|
<Compile Include="views\inventory.php" />
|
||||||
<Compile Include="views\purchase.php" />
|
<Compile Include="views\purchase.php" />
|
||||||
<Compile Include="views\quantityunitform.php" />
|
<Compile Include="views\quantityunitform.php" />
|
||||||
<Compile Include="views\locationform.php" />
|
<Compile Include="views\locationform.php" />
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
<Content Include="version.txt" />
|
<Content Include="version.txt" />
|
||||||
<Content Include="views\consumption.js" />
|
<Content Include="views\consumption.js" />
|
||||||
<Content Include="views\dashboard.js" />
|
<Content Include="views\dashboard.js" />
|
||||||
|
<Content Include="views\inventory.js" />
|
||||||
<Content Include="views\purchase.js" />
|
<Content Include="views\purchase.js" />
|
||||||
<Content Include="views\quantityunitform.js" />
|
<Content Include="views\quantityunitform.js" />
|
||||||
<Content Include="views\locationform.js" />
|
<Content Include="views\locationform.js" />
|
||||||
|
34
index.php
34
index.php
@@ -4,13 +4,13 @@ use \Psr\Http\Message\ServerRequestInterface as Request;
|
|||||||
use \Psr\Http\Message\ResponseInterface as Response;
|
use \Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Slim\Views\PhpRenderer;
|
use Slim\Views\PhpRenderer;
|
||||||
|
|
||||||
require_once 'vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
require_once 'config.php';
|
require_once __DIR__ . '/data/config.php';
|
||||||
require_once 'Grocy.php';
|
require_once __DIR__ . '/Grocy.php';
|
||||||
require_once 'GrocyDbMigrator.php';
|
require_once __DIR__ . '/GrocyDbMigrator.php';
|
||||||
require_once 'GrocyDemoDataGenerator.php';
|
require_once __DIR__ . '/GrocyDemoDataGenerator.php';
|
||||||
require_once 'GrocyLogicStock.php';
|
require_once __DIR__ . '/GrocyLogicStock.php';
|
||||||
require_once 'GrocyPhpHelper.php';
|
require_once __DIR__ . '/GrocyPhpHelper.php';
|
||||||
|
|
||||||
$app = new \Slim\App(new \Slim\Container([
|
$app = new \Slim\App(new \Slim\Container([
|
||||||
'settings' => [
|
'settings' => [
|
||||||
@@ -62,6 +62,15 @@ $app->get('/consumption', function(Request $request, Response $response) use($db
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$app->get('/inventory', function(Request $request, Response $response) use($db)
|
||||||
|
{
|
||||||
|
return $this->renderer->render($response, '/layout.php', [
|
||||||
|
'title' => 'Inventory',
|
||||||
|
'contentPage' => 'inventory.php',
|
||||||
|
'products' => $db->products()
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
$app->get('/products', function(Request $request, Response $response) use($db)
|
$app->get('/products', function(Request $request, Response $response) use($db)
|
||||||
{
|
{
|
||||||
return $this->renderer->render($response, '/layout.php', [
|
return $this->renderer->render($response, '/layout.php', [
|
||||||
@@ -228,6 +237,17 @@ $app->group('/api', function() use($db, $app)
|
|||||||
echo json_encode(array('success' => GrocyLogicStock::ConsumeProduct($args['productId'], $args['amount'], $spoiled, $transactionType)));
|
echo json_encode(array('success' => GrocyLogicStock::ConsumeProduct($args['productId'], $args['amount'], $spoiled, $transactionType)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->get('/stock/inventory-product/{productId}/{newAmount}', function(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
$bestBeforeDate = date('Y-m-d');
|
||||||
|
if (isset($request->getQueryParams()['bestbeforedate']) && !empty($request->getQueryParams()['bestbeforedate']))
|
||||||
|
{
|
||||||
|
$bestBeforeDate = $request->getQueryParams()['bestbeforedate'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(array('success' => GrocyLogicStock::InventoryProduct($args['productId'], $args['newAmount'], $bestBeforeDate)));
|
||||||
|
});
|
||||||
|
|
||||||
$this->get('/stock/get-product-details/{productId}', function(Request $request, Response $response, $args)
|
$this->get('/stock/get-product-details/{productId}', function(Request $request, Response $response, $args)
|
||||||
{
|
{
|
||||||
echo json_encode(GrocyLogicStock::GetProductDetails($args['productId']));
|
echo json_encode(GrocyLogicStock::GetProductDetails($args['productId']));
|
||||||
|
11
style.css
11
style.css
@@ -106,6 +106,15 @@
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disabled {
|
.disabled,
|
||||||
|
.no-real-button {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning-bg {
|
||||||
|
background-color: #fcf8e3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-bg {
|
||||||
|
background-color: #f2dede !important;
|
||||||
|
}
|
||||||
|
@@ -1 +1 @@
|
|||||||
0.4.0
|
1.0.0
|
@@ -45,22 +45,22 @@ $('#product_id').on('change', function(e)
|
|||||||
if (productId)
|
if (productId)
|
||||||
{
|
{
|
||||||
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
|
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
|
||||||
function(productStatistics)
|
function (productDetails)
|
||||||
{
|
{
|
||||||
$('#selected-product-name').text(productStatistics.product.name);
|
$('#selected-product-name').text(productDetails.product.name);
|
||||||
$('#selected-product-stock-amount').text(productStatistics.stock_amount || '0');
|
$('#selected-product-stock-amount').text(productDetails.stock_amount || '0');
|
||||||
$('#selected-product-stock-qu-name').text(productStatistics.quantity_unit_stock.name);
|
$('#selected-product-stock-qu-name').text(productDetails.quantity_unit_stock.name);
|
||||||
$('#selected-product-stock-qu-name2').text(productStatistics.quantity_unit_stock.name);
|
$('#selected-product-stock-qu-name2').text(productDetails.quantity_unit_stock.name);
|
||||||
$('#selected-product-last-purchased').text((productStatistics.last_purchased || 'never').substring(0, 10));
|
$('#selected-product-last-purchased').text((productDetails.last_purchased || 'never').substring(0, 10));
|
||||||
$('#selected-product-last-purchased-timeago').text($.timeago(productStatistics.last_purchased || ''));
|
$('#selected-product-last-purchased-timeago').text($.timeago(productDetails.last_purchased || ''));
|
||||||
$('#selected-product-last-used').text((productStatistics.last_used || 'never').substring(0, 10));
|
$('#selected-product-last-used').text((productDetails.last_used || 'never').substring(0, 10));
|
||||||
$('#selected-product-last-used-timeago').text($.timeago(productStatistics.last_used || ''));
|
$('#selected-product-last-used-timeago').text($.timeago(productDetails.last_used || ''));
|
||||||
$('#amount').attr('max', productStatistics.stock_amount);
|
$('#amount').attr('max', productDetails.stock_amount);
|
||||||
|
|
||||||
Grocy.EmptyElementWhenMatches('#selected-product-last-purchased-timeago', 'NaN years ago');
|
Grocy.EmptyElementWhenMatches('#selected-product-last-purchased-timeago', 'NaN years ago');
|
||||||
Grocy.EmptyElementWhenMatches('#selected-product-last-used-timeago', 'NaN years ago');
|
Grocy.EmptyElementWhenMatches('#selected-product-last-used-timeago', 'NaN years ago');
|
||||||
|
|
||||||
if ((productStatistics.stock_amount || 0) === 0)
|
if ((productDetails.stock_amount || 0) === 0)
|
||||||
{
|
{
|
||||||
$('#product_id').val('');
|
$('#product_id').val('');
|
||||||
$('#product_id_text_input').val('');
|
$('#product_id_text_input').val('');
|
||||||
@@ -69,6 +69,7 @@ $('#product_id').on('change', function(e)
|
|||||||
$('#product_id_text_input').closest('.form-group').addClass('has-error');
|
$('#product_id_text_input').closest('.form-group').addClass('has-error');
|
||||||
$('#product-error').text('This product is not in stock.');
|
$('#product-error').text('This product is not in stock.');
|
||||||
$('#product-error').show();
|
$('#product-error').show();
|
||||||
|
$('#product_id_text_input').focus();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -89,25 +90,19 @@ $('#product_id').on('change', function(e)
|
|||||||
$(function()
|
$(function()
|
||||||
{
|
{
|
||||||
$('.combobox').combobox({
|
$('.combobox').combobox({
|
||||||
appendId: '_text_input',
|
appendId: '_text_input'
|
||||||
matcher: function(text)
|
});
|
||||||
|
|
||||||
|
$('#product_id_text_input').on('change', function(e)
|
||||||
|
{
|
||||||
|
var input = $('#product_id_text_input').val().toString();
|
||||||
|
var possibleOptionElement = $("#product_id option[data-additional-searchdata*='" + input + "']").first();
|
||||||
|
|
||||||
|
if (possibleOptionElement.length > 0)
|
||||||
{
|
{
|
||||||
var input = $('#product_id_text_input').val();
|
$('#product_id').val(possibleOptionElement.val());
|
||||||
var optionElement = $("#product_id option:contains('" + text + "')").first();
|
$('#product_id').data('combobox').refresh();
|
||||||
var additionalSearchdata = optionElement.data('additional-searchdata');
|
$('#product_id').trigger('change');
|
||||||
|
|
||||||
if (text.contains(input))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (additionalSearchdata !== null && additionalSearchdata.length > 0)
|
|
||||||
{
|
|
||||||
return additionalSearchdata.contains(input);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -120,6 +115,14 @@ $(function()
|
|||||||
$('#consumption-form').validator();
|
$('#consumption-form').validator();
|
||||||
$('#consumption-form').validator('validate');
|
$('#consumption-form').validator('validate');
|
||||||
|
|
||||||
|
$('#amount').on('focus', function(e)
|
||||||
|
{
|
||||||
|
if ($('#product_id_text_input').val().length === 0)
|
||||||
|
{
|
||||||
|
$('#product_id_text_input').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#consumption-form input').keydown(function(event)
|
$('#consumption-form input').keydown(function(event)
|
||||||
{
|
{
|
||||||
if (event.keyCode === 13) //Enter
|
if (event.keyCode === 13) //Enter
|
||||||
|
@@ -1,7 +1,14 @@
|
|||||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
|
||||||
|
|
||||||
<h1 class="page-header">Dashboard</h1>
|
<h1 class="page-header">Dashboard</h1>
|
||||||
|
|
||||||
<h3>Current stock</h3>
|
<h3>Stock overview</h3>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p class="btn btn-lg btn-warning no-real-button"><strong><?php echo count(GrocyPhpHelper::FindAllObjectsInArrayByPropertyValue($currentStock, 'best_before_date', date('Y-m-d', strtotime('+5 days')), '<')); ?></strong> products expiring within the next 5 days</p>
|
||||||
|
<p class="btn btn-lg btn-danger no-real-button"><strong><?php echo count(GrocyPhpHelper::FindAllObjectsInArrayByPropertyValue($currentStock, 'best_before_date', date('Y-m-d', strtotime('-1 days')), '<')); ?></strong> products are already expired</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="current-stock-table" class="table table-striped">
|
<table id="current-stock-table" class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -13,7 +20,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ($currentStock as $currentStockEntry) : ?>
|
<?php foreach ($currentStock as $currentStockEntry) : ?>
|
||||||
<tr>
|
<tr class="<?php if ($currentStockEntry->best_before_date < date('Y-m-d', strtotime('-1 days'))) echo 'error-bg'; else if ($currentStockEntry->best_before_date < date('Y-m-d', strtotime('+5 days'))) echo 'warning-bg'; ?>">
|
||||||
<td>
|
<td>
|
||||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name; ?>
|
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name; ?>
|
||||||
</td>
|
</td>
|
||||||
@@ -21,11 +28,13 @@
|
|||||||
<?php echo $currentStockEntry->amount; ?>
|
<?php echo $currentStockEntry->amount; ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php echo $currentStockEntry->best_before_date; ?> <time class="timeago timeago-contextual" datetime="<?php echo $currentStockEntry->best_before_date; ?>"></time>
|
<?php echo $currentStockEntry->best_before_date; ?>
|
||||||
|
<time class="timeago timeago-contextual" datetime="<?php echo $currentStockEntry->best_before_date; ?>"></time>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
237
views/inventory.js
Normal file
237
views/inventory.js
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
$('#save-inventory-button').on('click', function(e)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var jsonForm = $('#inventory-form').serializeJSON();
|
||||||
|
|
||||||
|
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id,
|
||||||
|
function (productDetails)
|
||||||
|
{
|
||||||
|
Grocy.FetchJson('/api/stock/inventory-product/' + jsonForm.product_id + '/' + jsonForm.new_amount + '?bestbeforedate=' + $('#best_before_date').val(),
|
||||||
|
function (result) {
|
||||||
|
toastr.success('Stock amount of ' + productDetails.product.name + ' is now ' + jsonForm.new_amount.toString() + ' ' + productDetails.quantity_unit_stock.name);
|
||||||
|
|
||||||
|
$('#new_amount').val('');
|
||||||
|
$('#best_before_date').val('');
|
||||||
|
$('#product_id').val('');
|
||||||
|
$('#product_id_text_input').focus();
|
||||||
|
$('#product_id_text_input').val('');
|
||||||
|
$('#product_id_text_input').trigger('change');
|
||||||
|
$('#inventory-form').validator('validate');
|
||||||
|
},
|
||||||
|
function (xhr) {
|
||||||
|
console.error(xhr);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function(xhr)
|
||||||
|
{
|
||||||
|
console.error(xhr);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#product_id').on('change', function(e)
|
||||||
|
{
|
||||||
|
var productId = $(e.target).val();
|
||||||
|
|
||||||
|
if (productId)
|
||||||
|
{
|
||||||
|
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
|
||||||
|
function(productDetails)
|
||||||
|
{
|
||||||
|
$('#selected-product-name').text(productDetails.product.name);
|
||||||
|
$('#selected-product-stock-amount').text(productDetails.stock_amount || '0');
|
||||||
|
$('#selected-product-stock-qu-name').text(productDetails.quantity_unit_stock.name);
|
||||||
|
$('#selected-product-purchase-qu-name').text(productDetails.quantity_unit_purchase.name);
|
||||||
|
$('#selected-product-last-purchased').text((productDetails.last_purchased || 'never').substring(0, 10));
|
||||||
|
$('#selected-product-last-purchased-timeago').text($.timeago(productDetails.last_purchased || ''));
|
||||||
|
$('#selected-product-last-used').text((productDetails.last_used || 'never').substring(0, 10));
|
||||||
|
$('#selected-product-last-used-timeago').text($.timeago(productDetails.last_used || ''));
|
||||||
|
$('#new_amount').attr('not-equal', productDetails.stock_amount);
|
||||||
|
$('#new_amount_qu_unit').text(productDetails.quantity_unit_stock.name);
|
||||||
|
|
||||||
|
Grocy.EmptyElementWhenMatches('#selected-product-last-purchased-timeago', 'NaN years ago');
|
||||||
|
Grocy.EmptyElementWhenMatches('#selected-product-last-used-timeago', 'NaN years ago');
|
||||||
|
},
|
||||||
|
function(xhr)
|
||||||
|
{
|
||||||
|
console.error(xhr);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function()
|
||||||
|
{
|
||||||
|
$('.datepicker').datepicker(
|
||||||
|
{
|
||||||
|
format: 'yyyy-mm-dd',
|
||||||
|
startDate: '+0d',
|
||||||
|
todayHighlight: true,
|
||||||
|
autoclose: true,
|
||||||
|
calendarWeeks: true,
|
||||||
|
orientation: 'bottom auto',
|
||||||
|
weekStart: 1,
|
||||||
|
showOnFocus: false
|
||||||
|
});
|
||||||
|
$('.datepicker').trigger('change');
|
||||||
|
|
||||||
|
$('.combobox').combobox({
|
||||||
|
appendId: '_text_input'
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#product_id_text_input').on('change', function(e)
|
||||||
|
{
|
||||||
|
var input = $('#product_id_text_input').val().toString();
|
||||||
|
var possibleOptionElement = $("#product_id option[data-additional-searchdata*='" + input + "']").first();
|
||||||
|
|
||||||
|
if (possibleOptionElement.length > 0)
|
||||||
|
{
|
||||||
|
$('#product_id').val(possibleOptionElement.val());
|
||||||
|
$('#product_id').data('combobox').refresh();
|
||||||
|
$('#product_id').trigger('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#new_amount').val('');
|
||||||
|
$('#best_before_date').val('');
|
||||||
|
$('#product_id').val('');
|
||||||
|
$('#product_id_text_input').focus();
|
||||||
|
$('#product_id_text_input').val('');
|
||||||
|
$('#product_id_text_input').trigger('change');
|
||||||
|
|
||||||
|
$('#inventory-form').validator({
|
||||||
|
custom: {
|
||||||
|
'isodate': function($el)
|
||||||
|
{
|
||||||
|
if ($el.val().length !== 0 && !moment($el.val(), 'YYYY-MM-DD', true).isValid())
|
||||||
|
{
|
||||||
|
return 'Wrong date format, needs to be YYYY-MM-DD';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'notequal': function($el)
|
||||||
|
{
|
||||||
|
if ($el.val().length !== 0 && $el.val().toString() === $el.attr('not-equal').toString())
|
||||||
|
{
|
||||||
|
return 'This value cannot be equal to ' + $el.attr('not-equal').toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#inventory-form').validator('validate');
|
||||||
|
|
||||||
|
$('#new_amount').on('focus', function(e)
|
||||||
|
{
|
||||||
|
if ($('#product_id_text_input').val().length === 0)
|
||||||
|
{
|
||||||
|
$('#product_id_text_input').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#inventory-form input').keydown(function(event)
|
||||||
|
{
|
||||||
|
if (event.keyCode === 13) //Enter
|
||||||
|
{
|
||||||
|
if ($('#inventory-form').validator('validate').has('.has-error').length !== 0) //There is at least one validation error
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#best_before_date-datepicker-button').on('click', function(e)
|
||||||
|
{
|
||||||
|
$('.datepicker').datepicker('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#best_before_date').on('change', function(e)
|
||||||
|
{
|
||||||
|
var value = $('#best_before_date').val();
|
||||||
|
if (value.length === 8 && $.isNumeric(value))
|
||||||
|
{
|
||||||
|
value = value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3');
|
||||||
|
$('#best_before_date').val(value);
|
||||||
|
$('#inventory-form').validator('validate');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#best_before_date').on('keypress', function(e)
|
||||||
|
{
|
||||||
|
var element = $(e.target);
|
||||||
|
var value = element.val();
|
||||||
|
var dateObj = moment(element.val(), 'YYYY-MM-DD', true);
|
||||||
|
|
||||||
|
$('.datepicker').datepicker('hide');
|
||||||
|
|
||||||
|
if (value.length === 0)
|
||||||
|
{
|
||||||
|
element.val(moment().format('YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
else if (dateObj.isValid())
|
||||||
|
{
|
||||||
|
if (e.keyCode === 38) //Up
|
||||||
|
{
|
||||||
|
element.val(dateObj.add(-1, 'days').format('YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
else if (e.keyCode === 40) //Down
|
||||||
|
{
|
||||||
|
element.val(dateObj.add(1, 'days').format('YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
else if (e.keyCode === 37) //Left
|
||||||
|
{
|
||||||
|
element.val(dateObj.add(-1, 'weeks').format('YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
else if (e.keyCode === 39) //Right
|
||||||
|
{
|
||||||
|
element.val(dateObj.add(1, 'weeks').format('YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#inventory-form').validator('validate');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#new_amount').on('change', function(e)
|
||||||
|
{
|
||||||
|
if ($('#product_id').parent().hasClass('has-error'))
|
||||||
|
{
|
||||||
|
$('#inventory-change-info').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var productId = $('#product_id').val();
|
||||||
|
var newAmount = $('#new_amount').val();
|
||||||
|
|
||||||
|
if (productId)
|
||||||
|
{
|
||||||
|
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
|
||||||
|
function(productDetails)
|
||||||
|
{
|
||||||
|
var productStockAmount = productDetails.stock_amount || '0';
|
||||||
|
|
||||||
|
if (newAmount > productStockAmount)
|
||||||
|
{
|
||||||
|
var amountToAdd = newAmount - productDetails.stock_amount;
|
||||||
|
$('#inventory-change-info').text('This means ' + amountToAdd.toString() + ' ' + productDetails.quantity_unit_stock.name + ' will be added to stock');
|
||||||
|
$('#inventory-change-infoo').show();
|
||||||
|
}
|
||||||
|
else if (newAmount < productStockAmount)
|
||||||
|
{
|
||||||
|
var amountToRemove = productStockAmount - newAmount;
|
||||||
|
$('#inventory-change-info').text('This means ' + amountToRemove.toString() + ' ' + productDetails.quantity_unit_stock.name + ' will be removed from stock');
|
||||||
|
$('#inventory-change-info').show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$('#inventory-change-info').hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(xhr)
|
||||||
|
{
|
||||||
|
console.error(xhr);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
49
views/inventory.php
Normal file
49
views/inventory.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<div class="col-sm-4 col-sm-offset-3 col-md-3 col-md-offset-2 main">
|
||||||
|
<h1 class="page-header">Inventory</h1>
|
||||||
|
|
||||||
|
<form id="inventory-form">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="product_id">Product <i class="fa fa-barcode"></i></label>
|
||||||
|
<select class="form-control combobox" id="product_id" name="product_id" required>
|
||||||
|
<option value=""></option>
|
||||||
|
<?php foreach ($products as $product) : ?>
|
||||||
|
<option data-additional-searchdata="<?php echo $product->barcode; ?>" value="<?php echo $product->id; ?>"><?php echo $product->name; ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="new_amount">New amount <span id="new_amount_qu_unit" class="small text-muted"></span></label>
|
||||||
|
<input type="number" data-notequal="notequal" class="form-control" id="new_amount" name="new_amount" min="0" not-equal="-1" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
<div id="inventory-change-info" class="help-block text-muted"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="best_before_date">Best before <span class="small text-muted">This will apply to added products</span></label>
|
||||||
|
<div class="input-group date">
|
||||||
|
<input type="text" data-isodate="isodate" class="form-control datepicker" id="best_before_date" name="best_before_date" autocomplete="off">
|
||||||
|
<div id="best_before_date-datepicker-button" class="input-group-addon">
|
||||||
|
<i class="fa fa-calendar"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="save-inventory-button" type="submit" class="btn btn-default">OK</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-5 col-lg-3 main well">
|
||||||
|
<h3>Product overview <strong><span id="selected-product-name"></span></strong></h3>
|
||||||
|
<h4><strong>Purchase quantity:</strong> <span id="selected-product-purchase-qu-name"></span></h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Stock amount:</strong> <span id="selected-product-stock-amount"></span> <span id="selected-product-stock-qu-name"></span><br />
|
||||||
|
<strong>Last purchased:</strong> <span id="selected-product-last-purchased"></span> <time id="selected-product-last-purchased-timeago" class="timeago timeago-contextual"></time><br />
|
||||||
|
<strong>Last used:</strong> <span id="selected-product-last-used"></span> <time id="selected-product-last-used-timeago" class="timeago timeago-contextual"></time>
|
||||||
|
</p>
|
||||||
|
</div>
|
@@ -49,6 +49,9 @@
|
|||||||
<li data-nav-for-page="consumption.php">
|
<li data-nav-for-page="consumption.php">
|
||||||
<a class="discrete-link" href="/consumption"><i class="fa fa-cutlery fa-fw"></i> Record consumption</a>
|
<a class="discrete-link" href="/consumption"><i class="fa fa-cutlery fa-fw"></i> Record consumption</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li data-nav-for-page="inventory.php">
|
||||||
|
<a class="discrete-link" href="/inventory"><i class="fa fa-list fa-fw"></i> Inventory</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
@@ -82,6 +85,9 @@
|
|||||||
<li data-nav-for-page="consumption.php">
|
<li data-nav-for-page="consumption.php">
|
||||||
<a class="discrete-link" href="/consumption"><i class="fa fa-cutlery fa-fw"></i> Record consumption</a>
|
<a class="discrete-link" href="/consumption"><i class="fa fa-cutlery fa-fw"></i> Record consumption</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li data-nav-for-page="inventory.php">
|
||||||
|
<a class="discrete-link" href="/inventory"><i class="fa fa-list fa-fw"></i> Inventory</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="nav nav-sidebar">
|
<ul class="nav nav-sidebar">
|
||||||
|
@@ -37,6 +37,18 @@
|
|||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="min_stock_amount">Minimum stock amount</label>
|
||||||
|
<input required min="0" type="number" class="form-control" id="min_stock_amount" name="min_stock_amount" value="<?php if ($mode == 'edit') echo $product->min_stock_amount; else echo '0'; ?>">
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="default_best_before_days">Default best before days<br /><span class="small text-muted">For purchases this amount of days will be added to today for the best before date suggestion</span></label>
|
||||||
|
<input required min="0" type="number" class="form-control" id="default_best_before_days" name="default_best_before_days" value="<?php if ($mode == 'edit') echo $product->default_best_before_days; else echo '0'; ?>">
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="qu_id_purchase">Quantity unit purchase</label>
|
<label for="qu_id_purchase">Quantity unit purchase</label>
|
||||||
<select required class="form-control input-group-qu" id="qu_id_purchase" name="qu_id_purchase">
|
<select required class="form-control input-group-qu" id="qu_id_purchase" name="qu_id_purchase">
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Location</th>
|
<th>Location</th>
|
||||||
|
<th>Min. stock amount</th>
|
||||||
<th>QU purchase</th>
|
<th>QU purchase</th>
|
||||||
<th>QU stock</th>
|
<th>QU stock</th>
|
||||||
<th>QU factor</th>
|
<th>QU factor</th>
|
||||||
@@ -37,6 +38,9 @@
|
|||||||
<td>
|
<td>
|
||||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name; ?>
|
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name; ?>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php echo $product->min_stock_amount; ?>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name; ?>
|
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name; ?>
|
||||||
</td>
|
</td>
|
||||||
|
@@ -7,11 +7,11 @@
|
|||||||
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id,
|
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id,
|
||||||
function (productDetails)
|
function (productDetails)
|
||||||
{
|
{
|
||||||
jsonForm.amount = jsonForm.amount * productDetails.product.qu_factor_purchase_to_stock;
|
var amount = jsonForm.amount * productDetails.product.qu_factor_purchase_to_stock;
|
||||||
|
|
||||||
Grocy.FetchJson('/api/stock/add-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?bestbeforedate=' + $('#best_before_date').val(),
|
Grocy.FetchJson('/api/stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + $('#best_before_date').val(),
|
||||||
function (result) {
|
function (result) {
|
||||||
toastr.success('Added ' + jsonForm.amount + ' ' + productDetails.quantity_unit_stock.name + ' of ' + productDetails.product.name + ' to stock');
|
toastr.success('Added ' + amount + ' ' + productDetails.quantity_unit_stock.name + ' of ' + productDetails.product.name + ' to stock');
|
||||||
|
|
||||||
$('#amount').val(1);
|
$('#amount').val(1);
|
||||||
$('#best_before_date').val('');
|
$('#best_before_date').val('');
|
||||||
@@ -50,6 +50,12 @@ $('#product_id').on('change', function(e)
|
|||||||
$('#selected-product-last-purchased-timeago').text($.timeago(productDetails.last_purchased || ''));
|
$('#selected-product-last-purchased-timeago').text($.timeago(productDetails.last_purchased || ''));
|
||||||
$('#selected-product-last-used').text((productDetails.last_used || 'never').substring(0, 10));
|
$('#selected-product-last-used').text((productDetails.last_used || 'never').substring(0, 10));
|
||||||
$('#selected-product-last-used-timeago').text($.timeago(productDetails.last_used || ''));
|
$('#selected-product-last-used-timeago').text($.timeago(productDetails.last_used || ''));
|
||||||
|
$('#new_amount_qu_unit').text(productDetails.quantity_unit_stock.name);
|
||||||
|
|
||||||
|
if (productDetails.product.default_best_before_days.toString() !== '0')
|
||||||
|
{
|
||||||
|
$('#best_before_date').val(moment().add(productDetails.product.default_best_before_days, 'days').format('YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
|
||||||
Grocy.EmptyElementWhenMatches('#selected-product-last-purchased-timeago', 'NaN years ago');
|
Grocy.EmptyElementWhenMatches('#selected-product-last-purchased-timeago', 'NaN years ago');
|
||||||
Grocy.EmptyElementWhenMatches('#selected-product-last-used-timeago', 'NaN years ago');
|
Grocy.EmptyElementWhenMatches('#selected-product-last-used-timeago', 'NaN years ago');
|
||||||
@@ -78,25 +84,19 @@ $(function()
|
|||||||
$('.datepicker').trigger('change');
|
$('.datepicker').trigger('change');
|
||||||
|
|
||||||
$('.combobox').combobox({
|
$('.combobox').combobox({
|
||||||
appendId: '_text_input',
|
appendId: '_text_input'
|
||||||
matcher: function(text)
|
});
|
||||||
|
|
||||||
|
$('#product_id_text_input').on('change', function(e)
|
||||||
|
{
|
||||||
|
var input = $('#product_id_text_input').val().toString();
|
||||||
|
var possibleOptionElement = $("#product_id option[data-additional-searchdata*='" + input + "']").first();
|
||||||
|
|
||||||
|
if (possibleOptionElement.length > 0)
|
||||||
{
|
{
|
||||||
var input = $('#product_id_text_input').val();
|
$('#product_id').val(possibleOptionElement.val());
|
||||||
var optionElement = $("#product_id option:contains('" + text + "')").first();
|
$('#product_id').data('combobox').refresh();
|
||||||
var additionalSearchdata = optionElement.data('additional-searchdata');
|
$('#product_id').trigger('change');
|
||||||
|
|
||||||
if (text.contains(input))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (additionalSearchdata !== null && additionalSearchdata.length > 0)
|
|
||||||
{
|
|
||||||
return additionalSearchdata.contains(input);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -120,6 +120,14 @@ $(function()
|
|||||||
});
|
});
|
||||||
$('#purchase-form').validator('validate');
|
$('#purchase-form').validator('validate');
|
||||||
|
|
||||||
|
$('#best_before_date').on('focus', function(e)
|
||||||
|
{
|
||||||
|
if ($('#product_id_text_input').val().length === 0)
|
||||||
|
{
|
||||||
|
$('#product_id_text_input').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#purchase-form input').keydown(function(event)
|
$('#purchase-form input').keydown(function(event)
|
||||||
{
|
{
|
||||||
if (event.keyCode === 13) //Enter
|
if (event.keyCode === 13) //Enter
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="amount">Amount</label>
|
<label for="amount">Amount <span id="new_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" required>
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user