mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 09:39:57 +00:00
General improvements
This commit is contained in:
parent
b7852d78d0
commit
bd29f5da25
79
GrocyDbMigrator.php
Normal file
79
GrocyDbMigrator.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
class GrocyDbMigrator
|
||||
{
|
||||
public static function MigrateDb(PDO $pdo)
|
||||
{
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 1, "
|
||||
CREATE TABLE products (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
location_id INTEGER NOT NULL,
|
||||
qu_id_purchase INTEGER NOT NULL,
|
||||
qu_id_stock INTEGER NOT NULL,
|
||||
qu_factor_purchase_to_stock REAL NOT NULL,
|
||||
barcode TEXT UNIQUE,
|
||||
created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 2, "
|
||||
CREATE TABLE locations (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 3, "
|
||||
CREATE TABLE quantity_units (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 4, "
|
||||
CREATE TABLE stock (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
product_id INTEGER NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
best_before_date DATE,
|
||||
purchased_date DATE DEFAULT (datetime('now', 'localtime')),
|
||||
stock_id TEXT NOT NULL
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 5, "
|
||||
CREATE TABLE consumptions (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
product_id INTEGER NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
best_before_date DATE,
|
||||
purchased_date DATE,
|
||||
used_date DATE DEFAULT (datetime('now', 'localtime')),
|
||||
spoiled INTEGER NOT NULL DEFAULT 0,
|
||||
stock_id TEXT NOT NULL
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 6, "
|
||||
INSERT INTO locations (name, description) VALUES ('DefaultLocation', 'This is the first default location, edit or delete it');
|
||||
INSERT INTO quantity_units (name, description) VALUES ('DefaultQuantityUnit', 'This is the first default quantity unit, edit or delete it');
|
||||
INSERT INTO products (name, description, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('DefaultProduct1', 'This is the first default product, edit or delete it', 1, 1, 1, 1);
|
||||
INSERT INTO products (name, description, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('DefaultProduct2', 'This is the second default product, edit or delete it', 1, 1, 1, 1);"
|
||||
);
|
||||
}
|
||||
|
||||
private static function ExecuteMigrationWhenNeeded(PDO $pdo, int $migrationId, string $sql)
|
||||
{
|
||||
if ($pdo->query("SELECT COUNT(*) FROM migrations WHERE migration = $migrationId")->fetchColumn() == 0)
|
||||
{
|
||||
$pdo->exec(utf8_encode($sql));
|
||||
$pdo->exec('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||
}
|
||||
}
|
||||
}
|
24
GrocyDemoDataGenerator.php
Normal file
24
GrocyDemoDataGenerator.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
class GrocyDemoDataGenerator
|
||||
{
|
||||
public static function PopulateDemoData(PDO $pdo)
|
||||
{
|
||||
$pdo->exec(utf8_encode("
|
||||
UPDATE locations SET name = 'Vorratskammer', description = '' WHERE id = 1;
|
||||
INSERT INTO locations (name) VALUES ('Süßigkeitenschrank');
|
||||
INSERT INTO locations (name) VALUES ('Konvervenschrank');
|
||||
|
||||
UPDATE quantity_units SET name = 'Stück' WHERE id = 1;
|
||||
INSERT INTO quantity_units (name) VALUES ('Packung');
|
||||
|
||||
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Gummibärchen', 2, 2, 2, 1);
|
||||
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 ('Eier', 1, 2, 1, 10);
|
||||
|
||||
INSERT INTO stock (product_id, amount, best_before_date, stock_id) VALUES (3, 5, date('now', '+180 day'), '".uniqid()."');
|
||||
INSERT INTO stock (product_id, amount, best_before_date, stock_id) VALUES (4, 5, date('now', '+180 day'), '".uniqid()."');
|
||||
INSERT INTO stock (product_id, amount, best_before_date, stock_id) VALUES (5, 5, date('now', '+25 day'), '".uniqid()."');
|
||||
"));
|
||||
}
|
||||
}
|
89
GrocyLogicStock.php
Normal file
89
GrocyLogicStock.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
class GrocyLogicStock
|
||||
{
|
||||
public static function GetCurrentStock()
|
||||
{
|
||||
$db = Grocy::GetDbConnectionRaw();
|
||||
return $db->query('SELECT product_id, SUM(amount) AS amount, MIN(best_before_date) AS best_before_date from stock GROUP BY product_id ORDER BY MIN(best_before_date) DESC')->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public static function GetProductDetails(int $productId)
|
||||
{
|
||||
$db = Grocy::GetDbConnection();
|
||||
|
||||
$product = $db->products($productId);
|
||||
$productStockAmount = $db->stock()->where('product_id', $productId)->sum('amount');
|
||||
$productLastPurchased = $db->stock()->where('product_id', $productId)->max('purchased_date');
|
||||
$productLastUsed = $db->consumptions()->where('product_id', $productId)->max('used_date');
|
||||
$quPurchase = $db->quantity_units($product->qu_id_purchase);
|
||||
$quStock = $db->quantity_units($product->qu_id_stock);
|
||||
|
||||
return array(
|
||||
'product' => $product,
|
||||
'last_purchased' => $productLastPurchased,
|
||||
'last_used' => $productLastUsed,
|
||||
'stock_amount' => $productStockAmount,
|
||||
'quantity_unit_purchase' => $quPurchase,
|
||||
'quantity_unit_stock' => $quStock
|
||||
);
|
||||
}
|
||||
|
||||
public static function ConsumeProduct(int $productId, int $amount, bool $spoiled)
|
||||
{
|
||||
$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
|
||||
{
|
||||
$consumptionRow = $db->consumptions()->createRow(array(
|
||||
'product_id' => $stockEntry->product_id,
|
||||
'amount' => $stockEntry->amount,
|
||||
'best_before_date' => $stockEntry->best_before_date,
|
||||
'purchased_date' => $stockEntry->purchased_date,
|
||||
'spoiled' => $spoiled,
|
||||
'stock_id' => $stockEntry->stock_id
|
||||
));
|
||||
$consumptionRow->save();
|
||||
|
||||
$amount -= $stockEntry->amount;
|
||||
$stockEntry->delete();
|
||||
}
|
||||
else //Stock entry amount is > than need amount -> split the stock entry resp. update the amount
|
||||
{
|
||||
$consumptionRow = $db->consumptions()->createRow(array(
|
||||
'product_id' => $stockEntry->product_id,
|
||||
'amount' => $amount,
|
||||
'best_before_date' => $stockEntry->best_before_date,
|
||||
'purchased_date' => $stockEntry->purchased_date,
|
||||
'spoiled' => $spoiled,
|
||||
'stock_id' => $stockEntry->stock_id
|
||||
));
|
||||
$consumptionRow->save();
|
||||
|
||||
$restStockAmount = $stockEntry->amount - $amount;
|
||||
$amount = 0;
|
||||
|
||||
$stockEntry->update(array(
|
||||
'amount' => $restStockAmount
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
17
GrocyPhpHelper.php
Normal file
17
GrocyPhpHelper.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
class GrocyPhpHelper
|
||||
{
|
||||
public static function FindObjectInArrayByPropertyValue($array, $propertyName, $propertyValue)
|
||||
{
|
||||
foreach($array as $object)
|
||||
{
|
||||
if($object->{$propertyName} == $propertyValue)
|
||||
{
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -9,6 +9,11 @@
|
||||
"bootstrap-validator": "0.11.9",
|
||||
"bootstrap-datepicker": "1.6.4",
|
||||
"moment": "2.18.1",
|
||||
"bootstrap-combobox": "1.1.8"
|
||||
"bootstrap-combobox": "1.1.8",
|
||||
"datatables.net": "1.10.13",
|
||||
"datatables.net-bs": "2.1.1",
|
||||
"datatables.net-responsive": "2.1.1",
|
||||
"datatables.net-responsive-bs": "2.1.1",
|
||||
"jquery-timeago": "1.5.4"
|
||||
}
|
||||
}
|
||||
|
17
grocy.js
17
grocy.js
@ -2,8 +2,11 @@
|
||||
|
||||
$(function()
|
||||
{
|
||||
var menuItem = $('.nav-sidebar').find("[data-nav-for-page='" + Grocy.ContentPage + "']");
|
||||
var menuItem = $('.nav').find("[data-nav-for-page='" + Grocy.ContentPage + "']");
|
||||
menuItem.addClass('active');
|
||||
|
||||
$.timeago.settings.allowFuture = true;
|
||||
$('time.timeago').timeago();
|
||||
});
|
||||
|
||||
Grocy.FetchJson = function(url, success, error)
|
||||
@ -33,7 +36,7 @@ Grocy.FetchJson = function(url, success, error)
|
||||
|
||||
xhr.open('GET', url, true);
|
||||
xhr.send();
|
||||
}
|
||||
};
|
||||
|
||||
Grocy.PostJson = function(url, jsonData, success, error)
|
||||
{
|
||||
@ -63,4 +66,12 @@ Grocy.PostJson = function(url, jsonData, success, error)
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader('Content-type', 'application/json');
|
||||
xhr.send(JSON.stringify(jsonData));
|
||||
}
|
||||
};
|
||||
|
||||
Grocy.EmptyElementWhenMatches = function(selector, text)
|
||||
{
|
||||
if ($(selector).text() === text)
|
||||
{
|
||||
$(selector).text('');
|
||||
}
|
||||
};
|
||||
|
130
grocy.php
130
grocy.php
@ -8,22 +8,22 @@ class Grocy
|
||||
/**
|
||||
* @return PDO
|
||||
*/
|
||||
private static function GetDbConnectionRaw()
|
||||
public static function GetDbConnectionRaw()
|
||||
{
|
||||
if (self::$DbConnectionRaw == null)
|
||||
{
|
||||
$newDb = !file_exists('data/grocy.db');
|
||||
$pdo = new PDO('sqlite:data/grocy.db');
|
||||
$newDb = !file_exists(__DIR__ . '/data/grocy.db');
|
||||
$pdo = new PDO('sqlite:' . __DIR__ . '/data/grocy.db');
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
if ($newDb)
|
||||
{
|
||||
$pdo->exec('PRAGMA encoding = "UTF-8"');
|
||||
$pdo->exec("CREATE TABLE migrations (migration INTEGER NOT NULL UNIQUE, execution_time_timestamp DATETIME DEFAULT (datetime('now', 'localtime')), PRIMARY KEY(migration)) WITHOUT ROWID");
|
||||
self::MigrateDb();
|
||||
GrocyDbMigrator::MigrateDb($pdo);
|
||||
|
||||
if (self::IsDemoInstallation())
|
||||
{
|
||||
self::PopulateDemoData();
|
||||
GrocyDemoDataGenerator::PopulateDemoData($pdo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,124 +46,8 @@ class Grocy
|
||||
return self::$DbConnection;
|
||||
}
|
||||
|
||||
public static function MigrateDb()
|
||||
{
|
||||
$pdo = self::GetDbConnectionRaw();
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 1, "
|
||||
CREATE TABLE products (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
location_id INTEGER NOT NULL,
|
||||
qu_id_purchase INTEGER NOT NULL,
|
||||
qu_id_stock INTEGER NOT NULL,
|
||||
qu_factor_purchase_to_stock REAL NOT NULL,
|
||||
barcode TEXT UNIQUE,
|
||||
created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 2, "
|
||||
CREATE TABLE locations (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 3, "
|
||||
CREATE TABLE quantity_units (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 4, "
|
||||
CREATE TABLE stock (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
product_id INTEGER NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
best_before_date DATE,
|
||||
purchased_date DATE DEFAULT (datetime('now', 'localtime'))
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 5, "
|
||||
CREATE TABLE consumptions (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
product_id INTEGER NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
used_date DATE DEFAULT (datetime('now', 'localtime')),
|
||||
best_before_date DATE,
|
||||
purchased_date DATE,
|
||||
spoiled INTEGER NOT NULL DEFAULT 0
|
||||
)"
|
||||
);
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, 6, "
|
||||
INSERT INTO locations (name, description) VALUES ('DefaultLocation', 'This is the first default location, edit or delete it');
|
||||
INSERT INTO quantity_units (name, description) VALUES ('DefaultQuantityUnit', 'This is the first default quantity unit, edit or delete it');
|
||||
INSERT INTO products (name, description, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('DefaultProduct1', 'This is the first default product, edit or delete it', 1, 1, 1, 1);
|
||||
INSERT INTO products (name, description, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('DefaultProduct2', 'This is the second default product, edit or delete it', 1, 1, 1, 1);"
|
||||
);
|
||||
}
|
||||
|
||||
public static function PopulateDemoData()
|
||||
{
|
||||
$pdo = self::GetDbConnectionRaw();
|
||||
|
||||
self::ExecuteMigrationWhenNeeded($pdo, -1, utf8_encode("
|
||||
UPDATE locations SET name = 'Vorratskammer', description = '' WHERE id = 1;
|
||||
INSERT INTO locations (name) VALUES ('Süßigkeitenschrank');
|
||||
INSERT INTO locations (name) VALUES ('Konvervenschrank');
|
||||
|
||||
UPDATE quantity_units SET name = 'Stück' WHERE id = 1;
|
||||
INSERT INTO quantity_units (name) VALUES ('Packung');
|
||||
|
||||
INSERT INTO products (name, location_id, qu_id_purchase, qu_id_stock, qu_factor_purchase_to_stock) VALUES ('Gummibärchen', 2, 2, 2, 1);
|
||||
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 ('Eier', 1, 2, 1, 10);
|
||||
|
||||
INSERT INTO stock (product_id, amount, best_before_date) VALUES (3, 5, date('now', '+180 day'));
|
||||
INSERT INTO stock (product_id, amount, best_before_date) VALUES (4, 5, date('now', '+180 day'));
|
||||
INSERT INTO stock (product_id, amount, best_before_date) VALUES (5, 5, date('now', '+25 day'));
|
||||
"));
|
||||
}
|
||||
|
||||
private static function ExecuteMigrationWhenNeeded(PDO $pdo, int $migrationId, string $sql)
|
||||
{
|
||||
if ($pdo->query("SELECT COUNT(*) FROM migrations WHERE migration = $migrationId")->fetchColumn() == 0)
|
||||
{
|
||||
$pdo->exec($sql);
|
||||
$pdo->exec('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')');
|
||||
}
|
||||
}
|
||||
|
||||
public static function FindObjectInArrayByPropertyValue($array, $propertyName, $propertyValue)
|
||||
{
|
||||
foreach($array as $object)
|
||||
{
|
||||
if($object->{$propertyName} == $propertyValue)
|
||||
{
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function GetCurrentStock()
|
||||
{
|
||||
$db = self::GetDbConnectionRaw();
|
||||
return $db->query('SELECT product_id, SUM(amount) AS amount, MIN(best_before_date) AS best_before_date from stock GROUP BY product_id ORDER BY MIN(best_before_date) DESC')->fetchAll(PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public static function IsDemoInstallation()
|
||||
{
|
||||
return file_exists('data/demo.txt');
|
||||
return file_exists(__DIR__ . '/data/demo.txt');
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,11 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="config-dist.php" />
|
||||
<Compile Include="grocy.php" />
|
||||
<Compile Include="Grocy.php" />
|
||||
<Compile Include="GrocyLogicStock.php" />
|
||||
<Compile Include="GrocyDemoDataGenerator.php" />
|
||||
<Compile Include="GrocyPhpHelper.php" />
|
||||
<Compile Include="GrocyDbMigrator.php" />
|
||||
<Compile Include="index.php" />
|
||||
<Compile Include="views\consumption.php" />
|
||||
<Compile Include="views\purchase.php" />
|
||||
@ -44,6 +48,7 @@
|
||||
<Content Include="style.css" />
|
||||
<Content Include="version.txt" />
|
||||
<Content Include="views\consumption.js" />
|
||||
<Content Include="views\dashboard.js" />
|
||||
<Content Include="views\purchase.js" />
|
||||
<Content Include="views\quantityunitform.js" />
|
||||
<Content Include="views\locationform.js" />
|
||||
|
102
index.php
102
index.php
@ -6,9 +6,17 @@ use Slim\Views\PhpRenderer;
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once 'config.php';
|
||||
require_once 'grocy.php';
|
||||
require_once 'Grocy.php';
|
||||
require_once 'GrocyDbMigrator.php';
|
||||
require_once 'GrocyDemoDataGenerator.php';
|
||||
require_once 'GrocyLogicStock.php';
|
||||
require_once 'GrocyPhpHelper.php';
|
||||
|
||||
$app = new \Slim\App;
|
||||
$app = new \Slim\App(new \Slim\Container([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
]));
|
||||
$container = $app->getContainer();
|
||||
$container['renderer'] = new PhpRenderer('./views');
|
||||
|
||||
@ -32,7 +40,7 @@ $app->get('/', function(Request $request, Response $response)
|
||||
'title' => 'Dashboard',
|
||||
'contentPage' => 'dashboard.php',
|
||||
'products' => $db->products(),
|
||||
'currentStock' => Grocy::GetCurrentStock()
|
||||
'currentStock' => GrocyLogicStock::GetCurrentStock()
|
||||
]);
|
||||
});
|
||||
|
||||
@ -217,95 +225,33 @@ $app->group('/api', function()
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
});
|
||||
|
||||
$this->get('/get-product-statistics/{productId}', function(Request $request, Response $response, $args)
|
||||
$this->get('/stock/get-product-details/{productId}', function(Request $request, Response $response, $args)
|
||||
{
|
||||
$db = Grocy::GetDbConnection();
|
||||
$product = $db->products($args['productId']);
|
||||
$productStockAmount = $db->stock()->where('product_id', $args['productId'])->sum('amount');
|
||||
$productLastPurchased = $db->stock()->where('product_id', $args['productId'])->max('purchased_date');
|
||||
$productLastUsed = $db->consumptions()->where('product_id', $args['productId'])->max('used_date');
|
||||
$quPurchase = $db->quantity_units($product->qu_id_purchase);
|
||||
$quStock = $db->quantity_units($product->qu_id_stock);
|
||||
|
||||
echo json_encode(array(
|
||||
'product' => $product,
|
||||
'last_purchased' => $productLastPurchased,
|
||||
'last_used' => $productLastUsed,
|
||||
'stock_amount' => $productStockAmount,
|
||||
'quantity_unit_purchase' => $quPurchase,
|
||||
'quantity_unit_stock' => $quStock
|
||||
));
|
||||
|
||||
echo json_encode(GrocyLogicStock::GetProductDetails($args['productId']));
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
});
|
||||
|
||||
$this->get('/get-current-stock', function(Request $request, Response $response)
|
||||
$this->get('/stock/get-current-stock', function(Request $request, Response $response)
|
||||
{
|
||||
echo json_encode(Grocy::GetCurrentStock());
|
||||
|
||||
echo json_encode(GrocyLogicStock::GetCurrentStock());
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
});
|
||||
|
||||
$this->get('/consume-product/{productId}/{amount}', function(Request $request, Response $response, $args)
|
||||
$this->get('/stock/consume-product/{productId}/{amount}', function(Request $request, Response $response, $args)
|
||||
{
|
||||
$db = Grocy::GetDbConnection();
|
||||
$productStockAmount = $db->stock()->where('product_id', $args['productId'])->sum('amount');
|
||||
$potentialStockEntries = $db->stock()->where('product_id', $args['productId'])->orderBy('purchased_date', 'ASC')->fetchAll(); //FIFO
|
||||
$amount = $args['amount'];
|
||||
|
||||
if ($amount > $productStockAmount)
|
||||
{
|
||||
echo json_encode(array('success' => false));
|
||||
return $response->withStatus(400)->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
$spoiled = 0;
|
||||
$spoiled = false;
|
||||
if (isset($request->getQueryParams()['spoiled']) && !empty($request->getQueryParams()['spoiled']) && $request->getQueryParams()['spoiled'] == '1')
|
||||
{
|
||||
$spoiled = 1;
|
||||
$spoiled = true;
|
||||
}
|
||||
|
||||
foreach ($potentialStockEntries as $stockEntry)
|
||||
{
|
||||
if ($amount == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
echo json_encode(array('success' => GrocyLogicStock::ConsumeProduct($args['productId'], $args['amount'], $spoiled)));
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
});
|
||||
|
||||
if ($amount >= $stockEntry->amount) //Take the whole stock entry
|
||||
{
|
||||
$newRow = $db->consumptions()->createRow(array(
|
||||
'product_id' => $stockEntry->product_id,
|
||||
'amount' => $stockEntry->amount,
|
||||
'best_before_date' => $stockEntry->best_before_date,
|
||||
'purchased_date' => $stockEntry->purchased_date,
|
||||
'spoiled' => $spoiled
|
||||
));
|
||||
$newRow->save();
|
||||
|
||||
$stockEntry->delete();
|
||||
}
|
||||
else //Split the stock entry resp. update the amount
|
||||
{
|
||||
$newRow = $db->consumptions()->createRow(array(
|
||||
'product_id' => $stockEntry->product_id,
|
||||
'amount' => $amount,
|
||||
'best_before_date' => $stockEntry->best_before_date,
|
||||
'purchased_date' => $stockEntry->purchased_date,
|
||||
'spoiled' => $spoiled
|
||||
));
|
||||
$newRow->save();
|
||||
|
||||
$restStockAmount = $stockEntry->amount - $amount;
|
||||
$stockEntry->update(array(
|
||||
'amount' => $restStockAmount
|
||||
));
|
||||
}
|
||||
|
||||
$amount -= $stockEntry->amount;
|
||||
}
|
||||
|
||||
echo json_encode(array('success' => true));
|
||||
$this->get('/helper/uniqid', function(Request $request, Response $response)
|
||||
{
|
||||
echo json_encode(array('uniqid' => uniqid()));
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
});
|
||||
});
|
||||
|
17
style.css
17
style.css
@ -26,6 +26,10 @@
|
||||
min-width: 210px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
#navbar-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-sidebar {
|
||||
@ -70,7 +74,7 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a.discrete-link {
|
||||
.discrete-link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@ -90,3 +94,14 @@ a.discrete-link {
|
||||
white-space: nowrap;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.dataTables_info,
|
||||
.dataTables_length,
|
||||
.dataTables_filter {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.timeago-contextual {
|
||||
font-style: italic;
|
||||
font-size: 0.8em;
|
||||
}
|
@ -1 +1 @@
|
||||
0.1.0
|
||||
0.2.0
|
@ -10,12 +10,13 @@
|
||||
spoiled = 1;
|
||||
}
|
||||
|
||||
Grocy.FetchJson('/api/consume-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?spoiled=' + spoiled,
|
||||
Grocy.FetchJson('/api/stock/consume-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?spoiled=' + spoiled,
|
||||
function(result)
|
||||
{
|
||||
$('#product_id').val(null);
|
||||
$('#product_id_text_input').focus();
|
||||
$('#product_id_text_input').val('');
|
||||
$('#product_id_text_input').trigger('change');
|
||||
$('#amount').val(1);
|
||||
$('#product_name').focus();
|
||||
$('#consumption-form').validator('validate');
|
||||
},
|
||||
function(xhr)
|
||||
@ -29,22 +30,30 @@ $('#product_id').on('change', function(e)
|
||||
{
|
||||
var productId = $(e.target).val();
|
||||
|
||||
Grocy.FetchJson('/api/get-product-statistics/' + productId,
|
||||
function(productStatistics)
|
||||
{
|
||||
$('#selected-product-name').text(productStatistics.product.name);
|
||||
$('#selected-product-stock-amount').text(productStatistics.stock_amount || '0');
|
||||
$('#selected-product-stock-qu-name').text(productStatistics.quantity_unit_stock.name);
|
||||
$('#selected-product-stock-qu-name2').text(productStatistics.quantity_unit_stock.name);
|
||||
$('#selected-product-last-purchased').text(productStatistics.last_purchased || 'never');
|
||||
$('#selected-product-last-used').text(productStatistics.last_used || 'never');
|
||||
$('#amount').attr('max', productStatistics.stock_amount);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
if (productId)
|
||||
{
|
||||
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
|
||||
function(productStatistics)
|
||||
{
|
||||
$('#selected-product-name').text(productStatistics.product.name);
|
||||
$('#selected-product-stock-amount').text(productStatistics.stock_amount || '0');
|
||||
$('#selected-product-stock-qu-name').text(productStatistics.quantity_unit_stock.name);
|
||||
$('#selected-product-stock-qu-name2').text(productStatistics.quantity_unit_stock.name);
|
||||
$('#selected-product-last-purchased').text((productStatistics.last_purchased || 'never').substring(0, 10));
|
||||
$('#selected-product-last-purchased-timeago').text($.timeago(productStatistics.last_purchased || ''));
|
||||
$('#selected-product-last-used').text((productStatistics.last_used || 'never').substring(0, 10));
|
||||
$('#selected-product-last-used-timeago').text($.timeago(productStatistics.last_used || ''));
|
||||
$('#amount').attr('max', productStatistics.stock_amount);
|
||||
|
||||
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()
|
||||
@ -61,10 +70,11 @@ $(function()
|
||||
$('.datepicker').val(moment().format('YYYY-MM-DD'));
|
||||
$('.datepicker').trigger('change');
|
||||
|
||||
$('.combobox').combobox();
|
||||
$('#product_id').focus();
|
||||
$('#product_id').val(null);
|
||||
$('#product_name').trigger('change');
|
||||
$('#purchase-form').validator();
|
||||
$('#purchase-form').validator('validate');
|
||||
$('.combobox').combobox({ appendId: '_text_input' });
|
||||
$('#product_id_text_input').focus();
|
||||
$('#product_id_text_input').val('');
|
||||
$('#product_id_text_input').trigger('change');
|
||||
|
||||
$('#consumption-form').validator();
|
||||
$('#consumption-form').validator('validate');
|
||||
});
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
<p>
|
||||
<strong>Stock amount:</strong> <span id="selected-product-stock-amount"></span> <span id="selected-product-stock-qu-name2"></span><br />
|
||||
<strong>Last purchased:</strong> <span id="selected-product-last-purchased"></span><br />
|
||||
<strong>Last used:</strong> <span id="selected-product-last-used"></span>
|
||||
<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>
|
4
views/dashboard.js
Normal file
4
views/dashboard.js
Normal file
@ -0,0 +1,4 @@
|
||||
$(function()
|
||||
{
|
||||
$('#current-stock-table').DataTable({ 'paging': false });
|
||||
});
|
@ -3,7 +3,7 @@
|
||||
|
||||
<h3>Current stock</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<table id="current-stock-table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
@ -15,13 +15,13 @@
|
||||
<?php foreach ($currentStock as $currentStockEntry) : ?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo Grocy::FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name; ?>
|
||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo $currentStockEntry->amount; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo $currentStockEntry->best_before_date; ?>
|
||||
<?php echo $currentStockEntry->best_before_date; ?> <time class="timeago timeago-contextual" datetime="<?php echo $currentStockEntry->best_before_date; ?>"></time>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
@ -9,14 +9,14 @@
|
||||
|
||||
<meta name="author" content="Bernd Bestel (bernd@berrnd.de)" />
|
||||
|
||||
<title>
|
||||
<?php echo $title; ?> | grocy
|
||||
</title>
|
||||
<title><?php echo $title; ?> | grocy</title>
|
||||
|
||||
<link href="/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
|
||||
<link href="/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker3.min.css" rel="stylesheet" />
|
||||
<link href="/bower_components/bootstrap-combobox/css/bootstrap-combobox.css" rel="stylesheet" />
|
||||
<link href="/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/bower_components/datatables.net-responsive-bs/css/responsive.bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
||||
@ -27,20 +27,37 @@
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<!--<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" >
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-mobile" >
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>-->
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">grocy</a>
|
||||
</div>
|
||||
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<!--<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a href="#">About</a>
|
||||
<div id="navbar-mobile" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li data-nav-for-page="dashboard.php">
|
||||
<a class="discrete-link" href="/"><i class="fa fa-tachometer fa-fw"></i> Dashboard</a>
|
||||
</li>
|
||||
</ul>-->
|
||||
<li data-nav-for-page="purchase.php">
|
||||
<a class="discrete-link" href="/purchase"><i class="fa fa-shopping-cart fa-fw"></i> Record purchase</a>
|
||||
</li>
|
||||
<li data-nav-for-page="consumption.php">
|
||||
<a class="discrete-link" href="/consumption"><i class="fa fa-cutlery fa-fw"></i> Record consumption</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li data-nav-for-page="products.php">
|
||||
<a class="discrete-link" href="/products"><i class="fa fa-product-hunt fa-fw"></i> Manage products</a>
|
||||
</li>
|
||||
<li data-nav-for-page="locations.php">
|
||||
<a class="discrete-link" href="/locations"><i class="fa fa-map-marker fa-fw"></i> Manage locations</a>
|
||||
</li>
|
||||
<li data-nav-for-page="quantityunits.php">
|
||||
<a class="discrete-link" href="/quantityunits"><i class="fa fa-balance-scale fa-fw"></i> Manage quantity units</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@ -98,11 +115,16 @@
|
||||
<script src="/bower_components/moment/min/moment.min.js"></script>
|
||||
<script src="/bower_components/bootstrap-validator/dist/validator.min.js"></script>
|
||||
<script src="/bower_components/bootstrap-combobox/js/bootstrap-combobox.js"></script>
|
||||
<script src="/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
<script src="/bower_components/datatables.net-responsive/js/dataTables.responsive.min.js"></script>
|
||||
<script src="/bower_components/datatables.net-responsive-bs/js/responsive.bootstrap.min.js"></script>
|
||||
<script src="/bower_components/jquery-timeago/jquery.timeago.js"></script>
|
||||
|
||||
<?php if (file_exists('views/' . str_replace('.php', '.js', $contentPage))) : ?>
|
||||
<?php if (file_exists(__DIR__ . '/' . str_replace('.php', '.js', $contentPage))) : ?>
|
||||
<script src="/views/<?php echo str_replace('.php', '.js', $contentPage); ?>"></script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (file_exists('data/add_before_end_body.html')) include 'data/add_before_end_body.html' ?>
|
||||
<?php if (file_exists(__DIR__ . '/../data/add_before_end_body.html')) include __DIR__ . '/../data/add_before_end_body.html' ?>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -33,4 +33,6 @@
|
||||
$(function()
|
||||
{
|
||||
$('#name').focus();
|
||||
$('#location-form').validator();
|
||||
$('#location-form').validator('validate');
|
||||
});
|
||||
|
@ -10,7 +10,8 @@
|
||||
<form id="location-form">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php if ($mode == 'edit') echo $location->name; ?>" />
|
||||
<input type="text" class="form-control" required id="name" name="name" value="<?php if ($mode == 'edit') echo $location->name; ?>" />
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
|
@ -30,3 +30,15 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(function()
|
||||
{
|
||||
$('#locations-table').DataTable({
|
||||
'pageLength': 50,
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<table id="locations-table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
|
@ -33,4 +33,6 @@
|
||||
$(function()
|
||||
{
|
||||
$('#name').focus();
|
||||
$('#product-form').validator();
|
||||
$('#product-form').validator('validate');
|
||||
});
|
||||
|
@ -10,7 +10,8 @@
|
||||
<form id="product-form">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php if ($mode == 'edit') echo $product->name; ?>">
|
||||
<input type="text" class="form-control" required id="name" name="name" value="<?php if ($mode == 'edit') echo $product->name; ?>">
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="barcode">Barcode</label>
|
||||
@ -27,31 +28,35 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location_id">Location</label>
|
||||
<select class="form-control" id="location_id" name="location_id">
|
||||
<select required class="form-control" id="location_id" name="location_id">
|
||||
<?php foreach ($locations as $location) : ?>
|
||||
<option <?php if ($mode == 'edit' && $location->id == $product->location_id) echo 'selected="selected"'; ?> value="<?php echo $location->id; ?>"><?php echo $location->name; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="qu_id_purchase">Quantity unit purchase</label>
|
||||
<select class="form-control" id="qu_id_purchase" name="qu_id_purchase">
|
||||
<select required class="form-control" id="qu_id_purchase" name="qu_id_purchase">
|
||||
<?php foreach ($quantityunits as $quantityunit) : ?>
|
||||
<option <?php if ($mode == 'edit' && $quantityunit->id == $product->qu_id_purchase) echo 'selected="selected"'; ?> value="<?php echo $quantityunit->id; ?>"><?php echo $quantityunit->name; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="qu_id_stock">Quantity unit stock</label>
|
||||
<select class="form-control" id="qu_id_stock" name="qu_id_stock">
|
||||
<?php foreach ($quantityunits as $quantityunit) : ?>
|
||||
<option <?php if ($mode == 'edit' && $quantityunit->id == $product->qu_id_stock) echo 'selected="selected"'; ?> value="<?php echo $quantityunit->id; ?>"><?php echo $quantityunit->name; ?></option>
|
||||
<?php endforeach; ?>
|
||||
<select required class="form-control" id="qu_id_stock" name="qu_id_stock">
|
||||
<?php foreach ($quantityunits as $quantityunit) : ?>
|
||||
<option <?php if ($mode == 'edit' && $quantityunit->id == $product->qu_id_stock) echo 'selected="selected"'; ?> value="<?php echo $quantityunit->id; ?>"><?php echo $quantityunit->name; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="qu_factor_purchase_to_stock">Factor purchase to stock quantity unit</label>
|
||||
<input type="text" class="form-control" id="qu_factor_purchase_to_stock" name="qu_factor_purchase_to_stock" value="<?php if ($mode == 'edit') echo $product->qu_factor_purchase_to_stock; else echo '1'; ?>">
|
||||
<input required min="1" type="number" class="form-control" id="qu_factor_purchase_to_stock" name="qu_factor_purchase_to_stock" value="<?php if ($mode == 'edit') echo $product->qu_factor_purchase_to_stock; else echo '1'; ?>">
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<button id="save-product-button" type="submit" class="btn btn-default">Save</button>
|
||||
</form>
|
||||
|
@ -30,3 +30,14 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(function()
|
||||
{
|
||||
$('#products-table').DataTable({
|
||||
'pageLength': 50,
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<table id="products-table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
@ -34,13 +34,13 @@
|
||||
<?php echo $product->name; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo Grocy::FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name; ?>
|
||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($locations, 'id', $product->location_id)->name; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo Grocy::FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name; ?>
|
||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_purchase)->name; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo Grocy::FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_stock)->name; ?>
|
||||
<?php echo GrocyPhpHelper::FindObjectInArrayByPropertyValue($quantityunits, 'id', $product->qu_id_stock)->name; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php echo $product->qu_factor_purchase_to_stock; ?>
|
||||
|
@ -10,13 +10,26 @@
|
||||
{
|
||||
jsonForm.amount = jsonForm.amount * product.qu_factor_purchase_to_stock;
|
||||
|
||||
Grocy.PostJson('/api/add-object/stock', jsonForm,
|
||||
function(result)
|
||||
Grocy.FetchJson('/api/helper/uniqid',
|
||||
function (uniqidResponse)
|
||||
{
|
||||
$('#product_id').val(null);
|
||||
$('#amount').val(1);
|
||||
$('#product_id').focus();
|
||||
$('#purchase-form').validator('validate');
|
||||
jsonForm.amount = jsonForm.amount * product.qu_factor_purchase_to_stock;
|
||||
jsonForm.stock_id = uniqidResponse.uniqid;
|
||||
|
||||
Grocy.PostJson('/api/add-object/stock', jsonForm,
|
||||
function(result)
|
||||
{
|
||||
$('#product_id_text_input').focus();
|
||||
$('#product_id_text_input').val('');
|
||||
$('#product_id_text_input').trigger('change');
|
||||
$('#amount').val(1);
|
||||
$('#purchase-form').validator('validate');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
@ -35,21 +48,29 @@ $('#product_id').on('change', function(e)
|
||||
{
|
||||
var productId = $(e.target).val();
|
||||
|
||||
Grocy.FetchJson('/api/get-product-statistics/' + productId,
|
||||
function(productStatistics)
|
||||
{
|
||||
$('#selected-product-name').text(productStatistics.product.name);
|
||||
$('#selected-product-stock-amount').text(productStatistics.stock_amount || '0');
|
||||
$('#selected-product-stock-qu-name').text(productStatistics.quantity_unit_stock.name);
|
||||
$('#selected-product-purchase-qu-name').text(productStatistics.quantity_unit_purchase.name);
|
||||
$('#selected-product-last-purchased').text(productStatistics.last_purchased || 'never');
|
||||
$('#selected-product-last-used').text(productStatistics.last_used || 'never');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
if (productId)
|
||||
{
|
||||
Grocy.FetchJson('/api/stock/get-product-details/' + productId,
|
||||
function(productStatistics)
|
||||
{
|
||||
$('#selected-product-name').text(productStatistics.product.name);
|
||||
$('#selected-product-stock-amount').text(productStatistics.stock_amount || '0');
|
||||
$('#selected-product-stock-qu-name').text(productStatistics.quantity_unit_stock.name);
|
||||
$('#selected-product-purchase-qu-name').text(productStatistics.quantity_unit_purchase.name);
|
||||
$('#selected-product-last-purchased').text((productStatistics.last_purchased || 'never').substring(0, 10));
|
||||
$('#selected-product-last-purchased-timeago').text($.timeago(productStatistics.last_purchased || ''));
|
||||
$('#selected-product-last-used').text((productStatistics.last_used || 'never').substring(0, 10));
|
||||
$('#selected-product-last-used-timeago').text($.timeago(productStatistics.last_used || ''));
|
||||
|
||||
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()
|
||||
@ -66,10 +87,11 @@ $(function()
|
||||
$('.datepicker').val(moment().format('YYYY-MM-DD'));
|
||||
$('.datepicker').trigger('change');
|
||||
|
||||
$('.combobox').combobox();
|
||||
$('#product_id').focus();
|
||||
$('#product_id').val(null);
|
||||
$('#product_name').trigger('change');
|
||||
$('.combobox').combobox({ appendId: '_text_input' });
|
||||
$('#product_id_text_input').focus();
|
||||
$('#product_id_text_input').val('');
|
||||
$('#product_id_text_input').trigger('change');
|
||||
|
||||
$('#purchase-form').validator();
|
||||
$('#purchase-form').validator('validate');
|
||||
});
|
||||
|
@ -43,7 +43,7 @@
|
||||
|
||||
<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><br />
|
||||
<strong>Last used:</strong> <span id="selected-product-last-used"></span>
|
||||
<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>
|
@ -33,4 +33,6 @@
|
||||
$(function()
|
||||
{
|
||||
$('#name').focus();
|
||||
$('#quantityunit-form').validator();
|
||||
$('#quantityunit-form').validator('validate');
|
||||
});
|
||||
|
@ -10,7 +10,8 @@
|
||||
<form id="quantityunit-form">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php if ($mode == 'edit') echo $quantityunit->name; ?>" />
|
||||
<input type="text" class="form-control" required id="name" name="name" value="<?php if ($mode == 'edit') echo $quantityunit->name; ?>" />
|
||||
<div class="help-block with-errors"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
|
@ -30,3 +30,14 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(function()
|
||||
{
|
||||
$('#quantityunits-table').DataTable({
|
||||
'pageLength': 50,
|
||||
'order': [[1, 'asc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<table id="quantityunits-table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
|
Loading…
x
Reference in New Issue
Block a user