diff --git a/GrocyDbMigrator.php b/GrocyDbMigrator.php new file mode 100644 index 00000000..e99975e3 --- /dev/null +++ b/GrocyDbMigrator.php @@ -0,0 +1,79 @@ +query("SELECT COUNT(*) FROM migrations WHERE migration = $migrationId")->fetchColumn() == 0) + { + $pdo->exec(utf8_encode($sql)); + $pdo->exec('INSERT INTO migrations (migration) VALUES (' . $migrationId . ')'); + } + } +} diff --git a/GrocyDemoDataGenerator.php b/GrocyDemoDataGenerator.php new file mode 100644 index 00000000..1ccea703 --- /dev/null +++ b/GrocyDemoDataGenerator.php @@ -0,0 +1,24 @@ +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()."'); + ")); + } +} diff --git a/GrocyLogicStock.php b/GrocyLogicStock.php new file mode 100644 index 00000000..cd729c30 --- /dev/null +++ b/GrocyLogicStock.php @@ -0,0 +1,89 @@ +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; + } +} diff --git a/GrocyPhpHelper.php b/GrocyPhpHelper.php new file mode 100644 index 00000000..9fb0dda9 --- /dev/null +++ b/GrocyPhpHelper.php @@ -0,0 +1,17 @@ +{$propertyName} == $propertyValue) + { + return $object; + } + } + + return null; + } +} diff --git a/bower.json b/bower.json index a6ae2ccb..8d2433e7 100644 --- a/bower.json +++ b/bower.json @@ -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" } } diff --git a/grocy.js b/grocy.js index 6d62ca57..a5060c4a 100644 --- a/grocy.js +++ b/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(''); + } +}; diff --git a/grocy.php b/grocy.php index a866df1b..4ffeaeae 100644 --- a/grocy.php +++ b/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'); } } diff --git a/grocy.phpproj b/grocy.phpproj index 517a3f87..36db974b 100644 --- a/grocy.phpproj +++ b/grocy.phpproj @@ -17,7 +17,11 @@ - + + + + + @@ -44,6 +48,7 @@ + diff --git a/index.php b/index.php index f34b0466..6ec0dedd 100644 --- a/index.php +++ b/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'); }); }); diff --git a/style.css b/style.css index 83815612..d2a0e0ca 100644 --- a/style.css +++ b/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; +} \ No newline at end of file diff --git a/version.txt b/version.txt index 6c6aa7cb..341cf11f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.1.0 \ No newline at end of file +0.2.0 \ No newline at end of file diff --git a/views/consumption.js b/views/consumption.js index 134fd2e6..20cbde67 100644 --- a/views/consumption.js +++ b/views/consumption.js @@ -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'); }); diff --git a/views/consumption.php b/views/consumption.php index bbc42d89..57b06296 100644 --- a/views/consumption.php +++ b/views/consumption.php @@ -38,7 +38,7 @@

Stock amount:
- Last purchased:
- Last used: + Last purchased:
+ Last used:

\ No newline at end of file diff --git a/views/dashboard.js b/views/dashboard.js new file mode 100644 index 00000000..ff920ae3 --- /dev/null +++ b/views/dashboard.js @@ -0,0 +1,4 @@ +$(function() +{ + $('#current-stock-table').DataTable({ 'paging': false }); +}); diff --git a/views/dashboard.php b/views/dashboard.php index 6cfd429d..f2d35b6f 100644 --- a/views/dashboard.php +++ b/views/dashboard.php @@ -3,7 +3,7 @@

Current stock

- +
@@ -15,13 +15,13 @@ diff --git a/views/layout.php b/views/layout.php index b7c861a5..6e70c025 100644 --- a/views/layout.php +++ b/views/layout.php @@ -9,14 +9,14 @@ - - <?php echo $title; ?> | grocy - + <?php echo $title; ?> | grocy + + @@ -27,20 +27,37 @@ @@ -98,11 +115,16 @@ + + + + + - + - + diff --git a/views/locationform.js b/views/locationform.js index 9216710c..661ee560 100644 --- a/views/locationform.js +++ b/views/locationform.js @@ -33,4 +33,6 @@ $(function() { $('#name').focus(); + $('#location-form').validator(); + $('#location-form').validator('validate'); }); diff --git a/views/locationform.php b/views/locationform.php index 652fadf9..c2c0e270 100644 --- a/views/locationform.php +++ b/views/locationform.php @@ -10,7 +10,8 @@
- + +
diff --git a/views/locations.js b/views/locations.js index c41027b5..e23f5d56 100644 --- a/views/locations.js +++ b/views/locations.js @@ -30,3 +30,15 @@ } }); }); + +$(function() +{ + $('#locations-table').DataTable({ + 'pageLength': 50, + 'order': [[1, 'asc']], + 'columnDefs': [ + { 'orderable': false, 'targets': 0 } + ] + }); +}); + diff --git a/views/locations.php b/views/locations.php index a50a8083..c0e17216 100644 --- a/views/locations.php +++ b/views/locations.php @@ -7,7 +7,7 @@
-
Product
- product_id)->name; ?> + product_id)->name; ?> amount; ?> - best_before_date; ?> + best_before_date; ?>
+
diff --git a/views/productform.js b/views/productform.js index 266dbf67..d423ec11 100644 --- a/views/productform.js +++ b/views/productform.js @@ -33,4 +33,6 @@ $(function() { $('#name').focus(); + $('#product-form').validator(); + $('#product-form').validator('validate'); }); diff --git a/views/productform.php b/views/productform.php index 2110ff2a..508da690 100644 --- a/views/productform.php +++ b/views/productform.php @@ -10,7 +10,8 @@
- + +
@@ -27,31 +28,35 @@
- +
- +
- + + + +
- + +
diff --git a/views/products.js b/views/products.js index fc2fdf14..91b97aa4 100644 --- a/views/products.js +++ b/views/products.js @@ -30,3 +30,14 @@ } }); }); + +$(function() +{ + $('#products-table').DataTable({ + 'pageLength': 50, + 'order': [[1, 'asc']], + 'columnDefs': [ + { 'orderable': false, 'targets': 0 } + ] + }); +}); diff --git a/views/products.php b/views/products.php index 50aa6803..359eb7c1 100644 --- a/views/products.php +++ b/views/products.php @@ -7,7 +7,7 @@
-
#
+
@@ -34,13 +34,13 @@ name; ?>
# - location_id)->name; ?> + location_id)->name; ?> - qu_id_purchase)->name; ?> + qu_id_purchase)->name; ?> - qu_id_stock)->name; ?> + qu_id_stock)->name; ?> qu_factor_purchase_to_stock; ?> diff --git a/views/purchase.js b/views/purchase.js index f8d4dd94..773ccf47 100644 --- a/views/purchase.js +++ b/views/purchase.js @@ -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'); }); diff --git a/views/purchase.php b/views/purchase.php index 7becac08..d7ea0dee 100644 --- a/views/purchase.php +++ b/views/purchase.php @@ -43,7 +43,7 @@

Stock amount:
- Last purchased:
- Last used: + Last purchased:
+ Last used:

\ No newline at end of file diff --git a/views/quantityunitform.js b/views/quantityunitform.js index c571e08f..488efa4d 100644 --- a/views/quantityunitform.js +++ b/views/quantityunitform.js @@ -33,4 +33,6 @@ $(function() { $('#name').focus(); + $('#quantityunit-form').validator(); + $('#quantityunit-form').validator('validate'); }); diff --git a/views/quantityunitform.php b/views/quantityunitform.php index 0f330673..7c2d34da 100644 --- a/views/quantityunitform.php +++ b/views/quantityunitform.php @@ -10,7 +10,8 @@
- + +
diff --git a/views/quantityunits.js b/views/quantityunits.js index 9788c925..2a4ec1ed 100644 --- a/views/quantityunits.js +++ b/views/quantityunits.js @@ -30,3 +30,14 @@ } }); }); + +$(function() +{ + $('#quantityunits-table').DataTable({ + 'pageLength': 50, + 'order': [[1, 'asc']], + 'columnDefs': [ + { 'orderable': false, 'targets': 0 } + ] + }); +}); diff --git a/views/quantityunits.php b/views/quantityunits.php index 40815edf..0b562f2b 100644 --- a/views/quantityunits.php +++ b/views/quantityunits.php @@ -7,7 +7,7 @@
- +
#