From 8964202a1a3a39ef9e4a7121b13cd2be7c6c6865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andrade?= Date: Tue, 18 Feb 2020 11:03:20 +0200 Subject: [PATCH 1/8] Add basic stock management --- action.php | 60 ++++++++++++++++++++++++++++++++ database.sql | 22 +++++++++++- langs/en/actions.json | 4 ++- langs/en/items.json | 6 +++- langs/en/messages.json | 4 ++- langs/messages.php | 8 +++++ lib/getitemtable.php | 4 +++ pages.php | 22 ++++++++++++ pages/addstock.php | 77 ++++++++++++++++++++++++++++++++++++++++++ pages/edititem.php | 14 ++++++-- pages/removestock.php | 77 ++++++++++++++++++++++++++++++++++++++++++ static/js/items.js | 4 +-- 12 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 pages/addstock.php create mode 100644 pages/removestock.php diff --git a/action.php b/action.php index 7619cf0..2e6f7fc 100644 --- a/action.php +++ b/action.php @@ -363,4 +363,64 @@ switch ($VARS['action']) { session_destroy(); header('Location: index.php?logout=1'); die("Logged out."); + case "addstock": + $insert = true; + + if (empty($VARS['stock'])) { + $VARS['stock'] = 1; + } else if (!is_numeric($VARS['stock'])) { + returnToSender('field_nan'); + } + + $user = $_SESSION['uid']; + + $data = [ + 'itemid' => $VARS['itemid'], + 'stock' => $VARS['stock'], + 'text1' => $VARS['text1'], + 'userid' => $user + ]; + + $database->insert('stock', $data); + + $currentqty = $database->get('items', 'qty', ['itemid' => $VARS['itemid']]); + $newqty = $currentqty + $VARS['stock']; + + $data = [ + 'qty' => $newqty + ]; + + $database->update('items', $data, ['itemid' => $VARS['itemid']]); + + returnToSender("stock_added"); + case "removestock": + $insert = true; + + if (empty($VARS['stock'])) { + $VARS['stock'] = -1; + } else if (!is_numeric($VARS['stock'])) { + returnToSender('field_nan'); + } + + $user = $_SESSION['uid']; + + $data = [ + 'itemid' => $VARS['itemid'], + 'stock' => -$VARS['stock'], + 'text1' => $VARS['text1'], + 'userid' => $user + ]; + + $database->insert('stock', $data); + + $currentqty = $database->get('items', 'qty', ['itemid' => $VARS['itemid']]); + $newqty = $currentqty - $VARS['stock']; + + $data = [ + 'qty' => $newqty + ]; + + $database->update('items', $data, ['itemid' => $VARS['itemid']]); + + returnToSender("stock_removed"); } diff --git a/database.sql b/database.sql index 5cac0af..14196f7 100644 --- a/database.sql +++ b/database.sql @@ -70,6 +70,27 @@ CREATE TABLE IF NOT EXISTS `items` ( ENGINE = InnoDB; +-- ----------------------------------------------------- +-- Table `stock` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `stock` ( + `stockid` INT NOT NULL AUTO_INCREMENT, + `timestamp` TIMESTAMP, + `itemid` INT NOT NULL, + `stock` INT NOT NULL, + `text1` TEXT(500) NOT NULL, + `userid` INT NOT NULL, + PRIMARY KEY (`stockid`), + -- INDEX `fk_items_stock_idx` (`stockid` ASC), + UNIQUE INDEX `stockid_UNIQUE` (`stockid` ASC), + CONSTRAINT `fk_items_stock` + FOREIGN KEY (`itemid`) + REFERENCES `items` (`itemid`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB; + + -- ----------------------------------------------------- -- Table `labels` -- ----------------------------------------------------- @@ -148,4 +169,3 @@ INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (8, 'itemtext3', 'Text Va INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (9, 'catname', 'Category Name'); COMMIT; - diff --git a/langs/en/actions.json b/langs/en/actions.json index 3da53a6..0079195 100644 --- a/langs/en/actions.json +++ b/langs/en/actions.json @@ -4,5 +4,7 @@ "save": "Save", "delete": "Delete", "view": "View", - "show all items": "Show All Items" + "show all items": "Show All Items", + "addstock": "Add", + "removestock": "Remove" } diff --git a/langs/en/items.json b/langs/en/items.json index 15c6549..4e06df1 100644 --- a/langs/en/items.json +++ b/langs/en/items.json @@ -9,5 +9,9 @@ "cloning item": "Copying {oitem} {nitem}", "itemid": "Item ID", "id": "ID", - "Edit Item": "Edit Item" + "Edit Item": "Edit Item", + "Adding stock": "Adding stock", + "adding stock": "Adding stock for {item}", + "Removing stock": "Removing stock", + "removing stock": "Removing stock from {item}" } diff --git a/langs/en/messages.json b/langs/en/messages.json index c359087..036b246 100644 --- a/langs/en/messages.json +++ b/langs/en/messages.json @@ -17,5 +17,7 @@ "only showing understocked": "Only showing understocked items.", "missing name": "You need to enter a name.", "use the dropdowns": "Whoops, you need to use the category and location autocomplete boxes.", - "make categories and locations": "Please create at least one category and location before adding an item." + "make categories and locations": "Please create at least one category and location before adding an item.", + "stock added": "Stock added.", + "stock removed": "Stock removed." } diff --git a/langs/messages.php b/langs/messages.php index 3155be1..637c3b2 100644 --- a/langs/messages.php +++ b/langs/messages.php @@ -96,5 +96,13 @@ define("MESSAGES", [ "upload_success" => [ "string" => "Image uploaded.", "type" => "success" + ], + "stock_added" => [ + "string" => "stock added", + "type" => "success" + ], + "stock_removed" => [ + "string" => "stock removed", + "type" => "success" ] ]); diff --git a/lib/getitemtable.php b/lib/getitemtable.php index afe32b5..ab3f296 100644 --- a/lib/getitemtable.php +++ b/lib/getitemtable.php @@ -118,8 +118,12 @@ for ($i = 0; $i < count($items); $i++) { $user = new User($_SESSION['uid']); if ($user->hasPermission("INV_EDIT")) { $items[$i]["editbtn"] = ' ' . $Strings->get("edit", false) . ''; + $items[$i]["addstockbtn"] = ' ' . $Strings->get("addstock", false) . ''; + $items[$i]["removestockbtn"] = ' ' . $Strings->get("removestock", false) . ''; } else { $items[$i]["editbtn"] = ''; + $items[$i]["addstockbtn"] = ''; + $items[$i]["removestockbtn"] = ''; } $items[$i]["viewbtn"] = ' ' . $Strings->get("view", false) . ''; if (is_null($items[$i]['userid'])) { diff --git a/pages.php b/pages.php index e324a56..e4b8bcd 100644 --- a/pages.php +++ b/pages.php @@ -99,5 +99,27 @@ define("PAGES", [ ], "404" => [ "title" => "404 error" + ], + "addstock" => [ + "title" => "Add stock", + "navbar" => false, + "styles" => [ + "static/css/easy-autocomplete.min.css" + ], + "scripts" => [ + "static/js/jquery.easy-autocomplete.min.js", + "static/js/edititem.js" + ], + ], + "removestock" => [ + "title" => "Remove stock", + "navbar" => false, + "styles" => [ + "static/css/easy-autocomplete.min.css" + ], + "scripts" => [ + "static/js/jquery.easy-autocomplete.min.js", + "static/js/edititem.js" + ], ] ]); diff --git a/pages/addstock.php b/pages/addstock.php new file mode 100644 index 0000000..f6e4381 --- /dev/null +++ b/pages/addstock.php @@ -0,0 +1,77 @@ +count("locations") == 0 || $database->count("categories") == 0) { + header('Location: app.php?page=items&msg=noloccat'); + die(); +} + +$itemdata = [ + 'text1' => '', + 'qty' => '']; + +if (!empty($VARS['id'])) { + if ($database->has('items', ['itemid' => $VARS['id']])) { + $itemdata = $database->select( + 'items', [ + 'name', + 'qty', + ], [ + 'itemid' => $VARS['id'] + ])[0]; + } else { + // item id is invalid, redirect to a page that won't cause an error when pressing Save + header('Location: app.php?page=addstock'); + die(); + } +} +?> + +
+
+

+ build("adding stock", ['item' => "" . htmlspecialchars($itemdata['name']) . ""]); ?> +

+
+ +
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + + + '; + } else { + echo ''; + } + ?> + + +
+
diff --git a/pages/edititem.php b/pages/edititem.php index f4da79e..c8f402d 100644 --- a/pages/edititem.php +++ b/pages/edititem.php @@ -164,7 +164,17 @@ if (!empty($VARS['id'])) {
- + + + + +
@@ -263,4 +273,4 @@ if (!empty($VARS['id'])) { ?>
- \ No newline at end of file + diff --git a/pages/removestock.php b/pages/removestock.php new file mode 100644 index 0000000..86d8551 --- /dev/null +++ b/pages/removestock.php @@ -0,0 +1,77 @@ +count("locations") == 0 || $database->count("categories") == 0) { + header('Location: app.php?page=items&msg=noloccat'); + die(); +} + +$itemdata = [ + 'text1' => '', + 'qty' => '']; + +if (!empty($VARS['id'])) { + if ($database->has('items', ['itemid' => $VARS['id']])) { + $itemdata = $database->select( + 'items', [ + 'name', + 'qty', + ], [ + 'itemid' => $VARS['id'] + ])[0]; + } else { + // item id is invalid, redirect to a page that won't cause an error when pressing Save + header('Location: app.php?page=removestock'); + die(); + } +} +?> + +
+
+

+ build("removing stock", ['item' => "" . htmlspecialchars($itemdata['name']) . ""]); ?> +

+
+ +
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + + + '; + } else { + echo ''; + } + ?> + + +
+
diff --git a/static/js/items.js b/static/js/items.js index 298fa14..009bbca 100644 --- a/static/js/items.js +++ b/static/js/items.js @@ -49,7 +49,7 @@ var itemtable = $('#itemtable').DataTable({ json.items.forEach(function (row) { json.data.push([ "", - "" + row.viewbtn + " " + row.editbtn + "", + "" + row.viewbtn + " " + row.editbtn + " " + row.addstockbtn + " " + row.removestockbtn + "" row.name, row.catname, row.locname + " (" + row.loccode + ")", @@ -74,4 +74,4 @@ $(document).ready(function () { $(searchInput).trigger("input"); $(searchInput).trigger("change"); } -}); \ No newline at end of file +}); From 0e2c2c17a2fb610fa96bd487dfcf3a8ab35b831d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andrade?= Date: Tue, 18 Feb 2020 13:38:48 +0200 Subject: [PATCH 2/8] UI tweaks --- langs/en/items.json | 2 -- lib/getitemtable.php | 14 +++++++------- pages.php | 26 +++++++++++++------------- pages/categories.php | 12 ++++++------ pages/items.php | 4 ++-- pages/locations.php | 12 ++++++------ static/js/categories.js | 6 +++--- static/js/items.js | 10 +++++----- static/js/locations.js | 6 +++--- 9 files changed, 45 insertions(+), 47 deletions(-) diff --git a/langs/en/items.json b/langs/en/items.json index 4e06df1..8089631 100644 --- a/langs/en/items.json +++ b/langs/en/items.json @@ -10,8 +10,6 @@ "itemid": "Item ID", "id": "ID", "Edit Item": "Edit Item", - "Adding stock": "Adding stock", "adding stock": "Adding stock for {item}", - "Removing stock": "Removing stock", "removing stock": "Removing stock from {item}" } diff --git a/lib/getitemtable.php b/lib/getitemtable.php index ab3f296..ab1cf80 100644 --- a/lib/getitemtable.php +++ b/lib/getitemtable.php @@ -32,25 +32,25 @@ if ($VARS['order'][0]['dir'] == 'asc') { $sortby = "ASC"; } switch ($VARS['order'][0]['column']) { - case 2: + case 1: $order = ["name" => $sortby]; break; - case 3: + case 2: $order = ["catname" => $sortby]; break; - case 4: + case 3: $order = ["locname" => $sortby]; break; - case 5: + case 4: $order = ["code1" => $sortby]; break; - case 6: + case 5: $order = ["code2" => $sortby]; break; - case 7: + case 6: $order = ["qty" => $sortby]; break; - case 8: + case 7: $order = ["want" => $sortby]; break; // Note: We're not going to sort by assigned user. It's too hard. Maybe later. diff --git a/pages.php b/pages.php index e4b8bcd..f11026d 100644 --- a/pages.php +++ b/pages.php @@ -24,19 +24,6 @@ define("PAGES", [ "static/js/items.js" ], ], - "locations" => [ - "title" => "Locations", - "navbar" => true, - "icon" => "fas fa-map-marker", - "styles" => [ - "static/css/datatables.min.css", - "static/css/tables.css" - ], - "scripts" => [ - "static/js/datatables.min.js", - "static/js/locations.js" - ], - ], "categories" => [ "title" => "Categories", "navbar" => true, @@ -50,6 +37,19 @@ define("PAGES", [ "static/js/categories.js" ], ], + "locations" => [ + "title" => "Locations", + "navbar" => true, + "icon" => "fas fa-map-marker", + "styles" => [ + "static/css/datatables.min.css", + "static/css/tables.css" + ], + "scripts" => [ + "static/js/datatables.min.js", + "static/js/locations.js" + ], + ], "item" => [ "title" => "Item", "navbar" => false diff --git a/pages/categories.php b/pages/categories.php index d76f598..d21df2b 100644 --- a/pages/categories.php +++ b/pages/categories.php @@ -14,9 +14,9 @@ redirectifnotloggedin(); - get('actions'); ?> get('category'); ?> get('item count'); ?> + get('actions'); ?> @@ -30,11 +30,11 @@ redirectifnotloggedin(); ?> - - get("edit"); ?> - + + get("edit"); ?> + - get('actions'); ?> get('category'); ?> get('item count'); ?> + get('actions'); ?> - \ No newline at end of file + diff --git a/pages/items.php b/pages/items.php index 2ea748e..7f898e2 100644 --- a/pages/items.php +++ b/pages/items.php @@ -24,7 +24,6 @@ redirectifnotloggedin(); - get('actions'); ?> get('name'); ?> get('category'); ?> get('location'); ?> @@ -33,6 +32,7 @@ redirectifnotloggedin(); get('qty'); ?> get('want'); ?> get('assigned to'); ?> + get('actions'); ?> @@ -40,7 +40,6 @@ redirectifnotloggedin(); - get('actions'); ?> get('name'); ?> get('category'); ?> get('location'); ?> @@ -49,6 +48,7 @@ redirectifnotloggedin(); get('qty'); ?> get('want'); ?> get('assigned to'); ?> + get('actions'); ?> diff --git a/pages/locations.php b/pages/locations.php index ca0cf1f..d3bee39 100644 --- a/pages/locations.php +++ b/pages/locations.php @@ -15,10 +15,10 @@ redirectifnotloggedin(); - get('actions'); ?> get('location'); ?> get('code'); ?> get('item count'); ?> + get('actions'); ?> @@ -33,12 +33,12 @@ redirectifnotloggedin(); ?> - - get("edit"); ?> - + + get("edit"); ?> + - get('actions'); ?> get('location'); ?> get('code'); ?> get('item count'); ?> + get('actions'); ?> - \ No newline at end of file + diff --git a/static/js/categories.js b/static/js/categories.js index 67d6fdd..9384b99 100644 --- a/static/js/categories.js +++ b/static/js/categories.js @@ -24,11 +24,11 @@ $('#cattable').DataTable({ orderable: false }, { - targets: 1, + targets: 3, orderable: false } ], order: [ - [2, 'asc'] + [1, 'asc'] ] -}); \ No newline at end of file +}); diff --git a/static/js/items.js b/static/js/items.js index 009bbca..977dfb9 100644 --- a/static/js/items.js +++ b/static/js/items.js @@ -24,16 +24,16 @@ var itemtable = $('#itemtable').DataTable({ orderable: false }, { - targets: 1, + targets: 8, orderable: false }, { - targets: 8, + targets: 9, orderable: false } ], order: [ - [2, 'asc'] + [1, 'asc'] ], serverSide: true, ajax: { @@ -49,7 +49,6 @@ var itemtable = $('#itemtable').DataTable({ json.items.forEach(function (row) { json.data.push([ "", - "" + row.viewbtn + " " + row.editbtn + " " + row.addstockbtn + " " + row.removestockbtn + "" row.name, row.catname, row.locname + " (" + row.loccode + ")", @@ -57,7 +56,8 @@ var itemtable = $('#itemtable').DataTable({ row.code2, row.qty, row.want, - row.username + row.username, + "" + row.viewbtn + " " + row.editbtn + " " + row.addstockbtn + " " + row.removestockbtn + "" ]); }); return JSON.stringify(json); diff --git a/static/js/locations.js b/static/js/locations.js index c27465b..eceaaeb 100644 --- a/static/js/locations.js +++ b/static/js/locations.js @@ -24,11 +24,11 @@ $('#loctable').DataTable({ orderable: false }, { - targets: 1, + targets: 4, orderable: false } ], order: [ - [2, 'asc'] + [1, 'asc'] ] -}); \ No newline at end of file +}); From a960d5143f59bd493c8650c118285e109f273dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andrade?= Date: Mon, 9 Mar 2020 13:18:36 +0200 Subject: [PATCH 3/8] Add stock transactions to item page --- langs/en/labels.json | 6 +++++- pages/item.php | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/langs/en/labels.json b/langs/en/labels.json index 1dca287..ea663b3 100644 --- a/langs/en/labels.json +++ b/langs/en/labels.json @@ -18,5 +18,9 @@ "Notes": "Notes", "Comments": "Comments", "Cost": "Cost", - "Price": "Price" + "Price": "Price", + "date": "Date", + "amount": "Stock amount", + "description": "Description", + "changed by": "Changed by" } diff --git a/pages/item.php b/pages/item.php index 47be2fa..eabb20e 100644 --- a/pages/item.php +++ b/pages/item.php @@ -169,5 +169,44 @@ $item = $database->get( } ?> + +
+ +
+ + + + + + + + + + + select('stock', [ + 'timestamp', + 'stock', + 'text1', + 'userid' + ], [ + 'itemid' => $item['itemid'] + ] + ); + foreach ($stockentries as $stockentry) { + $user = new User($stockentry['userid']) + ?> + + + + + + + + +
get('date'); ?> get('amount'); ?> get('description'); ?> get('changed by'); ?>
getName() . " (" . $user->getUsername() . ")"; ?>
+
- \ No newline at end of file + From 5c0bea68a9cdc79abbf9ca11450f7793fb1ebc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andrade?= Date: Tue, 10 Mar 2020 09:02:08 +0200 Subject: [PATCH 4/8] Add stock table to EER diagram --- database.mwb | Bin 12030 -> 12943 bytes database.sql | 57 ++++++++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/database.mwb b/database.mwb index fb8479efd5ea6021815d965ac6d8ff85d8525dc2..567965dfc032f4ed51f7e7d4c347e271373d56ab 100644 GIT binary patch literal 12943 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 literal 12030 zcmZ{Kb8u$Ox9*#XZQGc5GO=yjwv#uuZQI5qnRvp9ZQHha^L^)>`klIU@7~q5y1IM) zu~&7k{XD%Mc_~mZGynhq1z@ZEDFb>Up_fGk090Xv0+7B&jqQwpHYT>t^fs=B^lmoR z=iXXQyX^G`uWvA+kNam?$(evl^mSe>lXj0k=Tgz9coQ zkxf_T875YFM_^Jv(IquaCN>ttl0FZr<%ztNk$eL4~+dlscBzDDSvFBkjM zGbB#bvB14L%A16Pk6YaSG1g%R7W{IooDz*#X<{lKf$(0c&>nbxEY+DtiNOOPPnLmy z>5O>vcbFW=9lo6?44?d4T6Aa^9?m2^BZ%i5!zRN(57Dt07JLlFZhF1{uXE@IWKATT zlF^_?aXlZ7thgE~wXAWscEbH*@9QWVNIy*qCfeSdN_7HZY$Gg6hY8_XPp+urTKK;l z(eivEf1xRhUQ+NslqW;b1CS<`h!n1S695Ee?#$dK5i`O8(S1#OV$8HbKzpXZl6=D4+7Dt+PqatM&)-|yS5JqzBfgBUhk;Z+RZ1m#vu_?Zp7*__ zX-vLjh2wmm%1F89=WkZ2V{!l?t>6spAMNkR)1`J)^uId|JwKY$TT++|an!*{3qw<_ zH$Eg~OZZZ{OhRD~t9z<8XR}v;Tk*z+$;U zxf>yWPM?i>lD^H7 z&IEXi!kzJ#f)7;UwXO`Sp_LZ{%;gyK| z8;pbq^0}^@y35h^m1MOoV2+>!(%P1CH}0lQ>=kFKDZ&E_$VrFSCBY<053A3 z{LenuS1t(oWJT7H@MiIlY|nxEuc^zSCd zU%k;gD0tt@fG)-(gtuj!_a>-!dqFDsU-!d8rlD61aL`(B;2{HD_`Q5*?l_-qVZKYD zIG-#Z!#jk^fxh;!SCdx;;*6}$E29Y8gdq6?UGzJI{ywQ!a-jMkejMH#PuqGufdTV` znCUu#U4YCI#4^#YuU_}`(W1oN->)b{`#2s!&gX6NR=-=1vI@ZpMEh_B`XJOZE*vfL z+!>*Y;YIu4yzfm-DhK);5abi~6S3%sdzOf0RUGw{hOFO4Auan`JT-}SP2}gE9~D4C z-+^X^@~uv*Ahw+dV7+~e=&cA5LdNSzx-;dQVWT~83kFy;=K6c)_e)O+&Rtn+f$A;# zG0dSOqI8>C6FlMlo#`(1mqUBXk?#@G+S|>q7n&N3i-n-~@6VWAwCY^<;~iX_N622g zc76k@a$(^-yMR6qV9Tf|x7|)J7c06(;URjKnHiQ^-|pPUMe1~1oie|)$}e&{qr7XQ zUZB5#6r#WM&;j^*A$|x4j;C@Ow?;^YTIZ(t^a1*eM|t6W+QEGM>79=#o(%K?b0PSk zuW*ZzRi_SuBgYC6}*eyytBd#JHa{xa<=+2RXUwfVvYmMg3*%+}nj`qK3E7 zT-y<<(2E6~C1ZrkuR^9BM3}5!@#p=Np^@5i7@MW-G_ZGUIo1HVg!g+Zo9JyL!V!L! z%DjqOCLTW?Z%KQtJPn+rbUj>DgV8_brnqy5Io>}Me5o-AWS!BYjUQ~pIV>lUMyD;M6sBC%(-Xx>gIdCX2;D&qf)I&Hia&Hf#pnR9H)>}`hn=?Zu>uBSS=GVHJj56cO-YI8r+m7L z80%gGtbSbKzL4ryPq8k&!7`r(WK>Bn%%Y{g%m6pK2l4 z?+p#I3*%-=y@M~-AilrmAynmSKJ9>?3vN>ZOwi1k&9Q}L82v5wn zu}nQ}42FDhS2c8+y_nX`P>SSPNQ+RPJhHm^CANOYnB)TXx@n~N+pVbxlBlYKpfV*i z#%1&c_Dd7vw#M5~VO>R&;ejA?4*bSB>~NeNkdf@WaWo!@Ph61@^Ko3&`}K5)Q2hfX zBPZGFY24Y{$=zA(0!VuP2h}bw$>b|R9x=ldh$h=N7HoK3f$jk(=U?6+TKcXIh&Civ zYU&G|`iTfjD}>Uv?D|%|O{Ji$_`huAx*t4pia}W+j8irgPBptJ@_N2QEeY&ms^HvHaAPx5Hfl0IOO zck6(|8WuC#hFp4mT76>K(LA9L(TBvdNhX(mHp_B8dz;45_Xj@s(jif=h8U@z4gzWR z@XblZn?b@L=*mc?nNl~7UC@F6JDYE3v9U9$7T4r{v7bR!CQd5wAB;Ip0#jW`?=ahU1kw zVrcj;yX`KYDN8*Dn5MKq2Di7&3~|CepGM40)fIpZtq{akXdhSRhpC@T-Fp4adq5u@ zjpbQazE6CMwKx(sBnq#|wBG?#C{?AQ|blv{c}S9CmiAXz!v4{)yl_FQW*Q6^fV2Doj#07in2ob5*< zcE2su-~Gh{t$5oS-ljJCe_W_{4gUH;C`WH;kEXe3A!otn?H6c$Kz>BeND%APiHywB z8g65hn{cIogK(nZlD|xwokkvClm#issmFz1;0ycJb~C}A=}R?9?@|})nT)dJX_b`Q zrHlTm)GE1F(X~?>WfAo2qzo{#DQGw8vcMRLk`idJre{tt)3MPKsV-7uWz?~LaZ_3< zT@9`Qyq3xQd;lW9mhA!|Uum_B2>*6|{Tkt1VXtbR^1wjcp=<~tPIB@`JVWZu@o*}? zjzJ<8pDmPx)%#n zUi3fWj04|40;~Rm5X5Bukmm$=lVe>{$A?<1Hf+NxvPGUFpEiRMHHN`A?y1yuoFi5OY_iKBYZCjMCie6;tf5^_Nao^fy^(Q24ar-ebP zVAZr;>J;L!E?`}_D^I8cpdMDdZRfdF`{v~NwoB6B4JeBwp2rk|fbd^ch8upm=8}gy zCW4U>wkWmbfS0a}Y|diV$Set@^Bz5ZVZL-nh-~7#&G`!RLC3_O#s0YLp*BQmr_0Nq zHnsp@A5^?p1KTv>x1@Tt5WBa(GoYRe-3m0^Nds_x}qvoJzn|E~_ zW2l;+I!Dr%XsH{Np`HT^I_!~>^wGl~w2jGPTim96*ULjvr0}5xe^Z~$mWO=r=Jm8a zF088+S~7c$k^MPW9#Rh|qgc*KoTB$9s;@h3je+RF|0V#LfdkeK6@WnY@n0q8=g0r22HC=ybx( zk+P}nBI+x&>*+OIvp}=;GA^gX-XxxCE-PG(%O`R#)L6MT^aOX)P$A|DA!hu9@DO9+ zP-9NAM3~{GJ(N!7RC&E#wI0m5|Jl1dAkK1tc^(kj!D${4mv&12c6_#VRQ^Y>sefkt zUJ3=OJinb8%K0sWyXMT1d-)8CR5J>RB z|6sD;5L<<$?g#yWpQrTD4*nLLqWV^MtHgIgwq?_`kw>B2dWE^7dtq}G4{@y&0Q^kLTcQ)5cGS~FU`0?}4P473q5t51n+r39Dt zsg-)M$p11d*D7ULBA+RQ7x0DyzLDKDGofMw`-H9Vyo58UdyV!n1PfWaf&Mi;9YQbO zR<5p2MS#xbiO2F``JDsoF#|`v2vgu6;g+yd_+Sjg?_+u<{|%^0(_o@+ys3lA+zoTp z30ONYI>ju%={m3%sL*104*#Qz#6on>9gXd!^K z1>AmEy}?^$?R#Wq)wl^EKH52NPOkF*RRtW*d^>N(bH`crex(cbZ=!X#DxFW<8lKkd z;H|dp(q8heUI@-=YYw@nt}7vth5O^pu%q0BwlThe!|h4C+B`)RGv5<1lMW% zk8L|~1KAF8-p&ASiCtR~QK1;J7ITWff_4eWSzB1$-2tYuraPuOMkd;=cP!DoCRtE1F z;pOoXV7&mtZRbZ7p(9zNQ3_Qa*p!yoG;oXTR?cz z9_TZ*fd8$jOo+eL@jEY#CfH5f3NaDb5R6da(rIak*7crNOr`&?2L7WZI4FGhpdEg5 zYb*5t1X+hur4ym!(d(`2~d->torSXN~x zmBbfj4AW4SQnp>gi`p7Q-rTipJw58~D%WWGrNi*Mx_QADDdu$fv0wDaHI_a?`Tc)k ztT@y54#t&`ndhlW%BQF{7a<}tC?CT24Q8#jJ&P!=k}stsWIT)%cNM0yB&Lc~3A}@2 zANMbM?jD%PQ!}oWtkLnjgM7YmPDPm|bnl1TZQIq~tcsQeCU2r@y!O^FByY|nZ*oIN zXz4tU8|Be`@6xgaqW@B?p0}DiS%?f;U$k7G{tP7aGBldvb<&W1bw=Z%>eRRtrOt)}paJD`Prc0q; z`IZL3HzW#}ZL&gj-_2XT&%!7^s({~Z5r_pa--x%X=9}>yjn$Z6w@UC9OjOP<49_S) z%8W;<@&;j*%^%SGs2B5CEx9&kj!rjtoo2?j&z?84_?4?DU+!MOK|GAyz5 zy2vs@K@*|eGt=Sa{kh$B@M_%AyxJ|uO|fV!EI7ZUlkuIp3mA6B;hXevGh3B~IVkk3a;e#po=rw6!Cl{Y$=XDgbf<}I}b;585KA$FNhsO^0R2bS2Vp~o4W9hKQM{6JU>}1 z9Tc6K3x9V|IlC=(KJEl^3M@kTcdp_~*~l=UY-IE8!ehCY^Rt!}VXu2uiN{0tGT*jCL^MzD9g+3<}{4tafX%t2n#Op`M&rh$f;{P}ANYQ>{haXSb7hr+ifTF7dGC$Gf~va4tMbEK8~Jcq3~R(D+?fXi%YO|1x4RM|`Z7z1G{x)dt+~!wcI9-}n2o60N+|EqoVr=lJsr zydzPQ{kV{|aiN;s-gzNgeelenweBWPu-2OGbDlj2c(%<;vORkDtJmh)NmsL-$?STN z1>WXhq|L2p7xW!a*bBVAAA$;HYV%rv>l(8vf}Of!Ym}WjY!|2<9?%Q8zD;f4F0QTw z5cjB~3W1Na*oMSQ9nc;K&vg}~zPK|asFGp!SVIrtTxxaF8dkCetjx)&+lN(PJ+a~LQU ziwBC&$60U_RA`1z{n1H}U9FMd^%R|TwQ9azlT!r$ZfAkv=@A($|K|wkl3@JHUTgB! z07>z77r3mQEEtJV3Uz42QpS3?mzoVcy|?#!N$=y#?eMG_-Me0Yp6{Bhz4(BAvm53~ zVOU(mq+?Z8z9=8ETaciF@9C9Fnu+GrvZAnIQl$4?{xWH z`@(5dzj%BME+6NdDA$#n+T<$It)FQHN_uy+e0Wh-O7=G_5S%772P0S>n_hCh&E7xm zQ>$vP?46gg)=_~UJy|%s84r`-dt#kcbM~Aom9`~z|2&$di_D^j33k+^LR-@cW^R_t zx10=E%u`4;D?Qv~fK9&9Dobi@rfCe$e>zAiU!wug-3_g1@vQVRdbu+X;V2)EKGz1b z3KRyjp4$=0j*gQ4Hc~uDn@Ca1IBQ`==fG;%%e9HlO(74jqVs!kba)q0Y^SfO_2GGX zza3A#t(;{0+^{159`n(x(w|~tUoF#rG4au2Rc=W6`SfQ{SK4a9lEsSerzI9LbDl|f z;D(ZXXQ{orkB5iDfcROdWB)F(3~AM5%zQ4so_7~_I|tvgT!-OoDNwRQmI>#zT(05k z5=*W!r5L$Zpg4k2i`S&W?d5iOo%NC}j4aC|57>LGU0|U)8{(7B=)+)(N^iTJlM;z~ zWU%e%fJtnuz&zhah+6P7Mk*I776 z`q-}^YJ^F9t&>~VAM&Z#Vvzt_Yo7x$MVU z=;ERTO-$vt;7T&Fs3`Nm>pO{=`N&Z9FmMuNol(%mFy z2K@$}w$1OZ5=6eLd$Oc+a?|!JI0Y{ctc9J+oAc9j?}U!X!pd*5Q09Vg%X~YxZf?)@B}A%X>d-zIN#D&xw?Nfh|_S-N@A+LX`>6--#R zcfG$VCN8lGvXPlvr#0|7lxj&n9pn##Kr9S`*RYRfgCUgXW_IKr*rZe@ej~>ML!w&e zm6%}OLu$_Si6nMXr`Tq^89ed{0f(Riy~HHZ_b0X&=IBg4Uv|?2eH27a;K8royCVq( z%APjOzxB+;ncy;{B7^!VEoy{YZj;DZ4(2ry(0JBkAQaA3?xRtap1W!le6wcMqI10X z5Tp~+!bX6BiEcpZtmDxy*5(Y*+#cNwIAlrL25`~qcx6`-Dqw(vvQiLjV_L4_UDINk zFpY>pwUq4M1MdC^BH1E7=QOZJ;gGdN-81JgNgB-!Fj*QR{F=aH5v|5Alx~uQ%qNtT zi5w`4F;bo;Nc6xs|Khrp%a7#-kKClfF8Vl~9W+rmUqsEMzf(zdJ6V)ES}{|M8Z)iL z2uw9wz&uHlOR^e-Dy1;#oU`xBTTPOZMY2(h8YR!~rE~Eglr7-)g|bz@C{CYx7*38& ztvUHmVebkMNzl0k0NV8%BLJkP0h}B#?-Xmzik3%Q0g4ptuzi3OKNHarLe+!nB={ps z6qsXn%z+DTGr7Dm=Bw_b;N0n=w&ub_u1BEZXnwnc@CovZN@-B&0(x**pF66d;Sf3E ztH2B`JpMY*H=%&+f}$WzBE2fFPl8pz@`A_{T|W)zpq1I(5(#kBERh)>oh;4-=~J_C zknVc*&WMP8)A3^xPZN96>wMflJvLLAR+wJS4Gf25eMTH>%oHO4gu5{Go!g94!t;=L;An8;` znD!8ZZRC&Uj1*p(iM6#KX1x3$_||>#w`AJpVx1sjyy;jgoB|e@_^tCCd>g}gYgVm| zh8(X4gQXHRnfCkK;UFu?cHPNx6ayC7{e?!39a5d;ad;Q$-*ZQy*2T3iGU>^o?x+l7 z3h%J}&!5I6L|4*l+39O32FfzA&d1t!$FfPMdhjtQEW5J#Tp!2tOP!O7o_Tu528|!n z>bj5zClYas@n0eEUX2Y`N|{gKQ*EwJ1wk$!gpbd&+CU%Z(u>E!)G`fPrvTEibhFWm zpBhd3nl%=o;C_dzHSk`mV86tbS-~5aI+~W#2%}DQa;r==aYwahn}latc#sc&>&Yg@YKATO0bohLr#;L`Cj228-9)tAc@2IY z`bi1FkSk{iZY2S)>-Ac<(+8Z_VTf^OCKNuRu5nYty9uj<#7+jrNsE zyf!cBf8V)sW)o;#s@sVh9%F1oT3&(5q>ZQrNp9B$YwFBMUHBGW>A&VCEHB$NC$?ub z6Jt{AUZb*MBG)0#bDv9wjX|n;vo$yo{zixWJ@5<{T5GK z#KSjoB^K`xV&)C1NRn{gZ(ym zJGP5Zzbsz-SZ70ee}mNbPEk=Q{;dw6*mPWL&I}nWOri^&#dJ8qdw&sblVSY>z5ODZ zE@J5u=3Kpnae|CFlJO;DcQ`vq z`lb1anY7%heKv(TEmfgT+OvZ>{#ZcC6h#J8b_7(1lc8I`XnUSZto|h7Rxlt2*s=_h zc?S2eIKF~8$qK)g>IrxDL?7q<&(6^rUo`=3L21+-bweUW)3a?qdEB407Dah8Y;lU@E>EI%|e#m9cJRO#vswU;_0E5pbF`-MOQ$QD*C@v zeQQU@C0@kZ8du=mz@t$fc~C8<#D47^4p&bIXOHXsp*( zHK=S`#v-it6^~{F*;8KN~l&jCrAK0Nlzy3HDJ}AA5)8AF?w)%PbWIze7XckFrM^W|%;- z?SiQIjOV(AsApo^BvOkwIXj2ysx&jWjI~nXg2c`yb(p^tDr@%Mv|m(-Y=Fl$aARYn z{&vhZc~M=>3Xn?TonV#?*~jcOQs&!|RziD#vALVa9Fg(7J!#g%Kv*qkio`9k5%qMy z8Q32^=VOo!91UW0dcXT_lJ#TomSYn67JSdeG3K3EP9$taLUP%6(nv6ukHP~=fm0?? zVIg6_J!|^+eHsm#V$bAbR8!FvL(+hBqzQJbWar1jPYE-}crr!pz07yM61UBnBQcgd zZyLGEFq2Mrq!PqoKQsdVn5lclj3C%xV)6JE?RV^Bg@?lA=5rTx$*HRZ-BT;MG^X{F!4@mDe!p7#iAdMGge&6V6{raQ0 zf@G%s&fK|yb-oc?Yl+gf**AQDkttnNy}aq8BbJ`s0;3OiJc?TXj##1V-e4mB8Z)qs zDxX*dYgQeW3Bm}&;MKuY5J|*|+bkKr=IzOg)|LV}NRxZW$3l^eqhZj9*Dv#Sg60*_ zgq1u)r>y*+`ekUYq|A2zO{u7bqRAj;-FMN3pvuXBme#Ti^XU`4Z53F?(Wwdq0>YtE>83@jKN=!k&9_ww1<& zS7+V!@>CtjT^=HlA4E`z!E%X;*AK5*9p`e9_ey#Slhdeyw@8?o=EIx3t{Y&s_QsWM-LArJ$u)=zysradG3`k*Iwsj8zGeX~w@K#;tx8_eo`Ttfj z0sy_4KV<}re%eEPDH#80U|-GJ&d7?Hk(GrJ@IULI0MlRVY9(!pkN;V~4gkP@8Gd6! z24e$f1A1e_^K=(IS3j}0~nS*FiysYJP z4NK~n$Oh=+crTV2CMe#1sP^^aUyQt<=2O|ocXgKTpOQN z&6l-oiqHwF?Zb-vRmh`F)rcU!XsuMe?bIGI}7afGsv znsVUA4x1DWqc>`0rA64`V!g{DycT|Lc>!%cv7^_yEb9ksg9j;TVZH+E2fpXudN#?; z)SPsWH=b-?L$#7hmDf|pksYDuE#3Exg#$@f70qQ$oed3eu2I73+JFlIMvm<^{X>IY ztUYSK=Hi!O45F`Zh~1yo-DNjfrCjFHg!@wYB-4}3IYRT>A#eB7Ld7-jiAhmqWu@h2 z0a|5oZz4S&+=g6SR0+>$??__RGk>0jp1d)LPBdNC2IjyZ6DNA)upwFSNHT<9sWwo@ z&ZK6YZ!D~=?^kWF3tHy>&ud zdh$wehX5xhNw*bbkLE>Ab{k%`f}~S1c^?gEi|KR81haWU+Wu6)!${=GOIr+t(np%x zQ!{W#iiZ_Tl|gZOk*SzaOQ#W{Vm*uG=+o@VZ$-tjJPDJ#$s4A^AV02vwpRLxf!(DA z%{{{2@6qOEF{fS;+3^82hW<*qgTxb6&}n*&KlzdtZa;jQzTslSU%zdxG*;=98h+}p zznhixcD22LLtE*=x^;pm)1VHA@HTHlMqCH2Q z-Vl?J`PF?!c$M`pc5>s7!+Lh6W3o~RZ;|z=jWao?T^kwz6(L}7rAlm(aLgyWkf2-J z%744E_r5=5{^{w-jAvKz znA#7TuWt|PpP#;DB>8A?Z3!alt}k_j!okM)2ltq!c{tR8Kjfi|CZ2vucyk}3d0Nio zjgA6{OE@({**-Rh%){<6<)uKrp@IJYRp4J~@!zM({~z;zYr_BI{r}Fq|K$q+H2IHw zDa-%K!~gO4f9K%;c-(w>{I85GF9rTjJ_mq)z2#pPj2-~s0?4>4DM%Bk**RJnn%Ekd v6Uo>an^+SWI2xH-xR?+z(KEu>{4+x5WMpn)W8lO|WMS*_rD?ZwbcgvrUBwJ% diff --git a/database.sql b/database.sql index 14196f7..0d30dd3 100644 --- a/database.sql +++ b/database.sql @@ -1,5 +1,5 @@ -- MySQL Script generated by MySQL Workbench --- Sat 22 Sep 2018 02:40:11 AM MDT +-- Tue 10 Mar 2020 08:54:46 EET -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering @@ -55,14 +55,14 @@ CREATE TABLE IF NOT EXISTS `items` ( `price` DECIMAL(10,2) NULL, PRIMARY KEY (`itemid`), INDEX `fk_items_categories_idx` (`catid` ASC), - INDEX `fk_items_locations1_idx` (`locid` ASC), + INDEX `fk_items_locations_idx` (`locid` ASC), UNIQUE INDEX `itemid_UNIQUE` (`itemid` ASC), CONSTRAINT `fk_items_categories` FOREIGN KEY (`catid`) REFERENCES `categories` (`catid`) ON DELETE NO ACTION ON UPDATE NO ACTION, - CONSTRAINT `fk_items_locations1` + CONSTRAINT `fk_items_locations` FOREIGN KEY (`locid`) REFERENCES `locations` (`locid`) ON DELETE NO ACTION @@ -70,27 +70,6 @@ CREATE TABLE IF NOT EXISTS `items` ( ENGINE = InnoDB; --- ----------------------------------------------------- --- Table `stock` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `stock` ( - `stockid` INT NOT NULL AUTO_INCREMENT, - `timestamp` TIMESTAMP, - `itemid` INT NOT NULL, - `stock` INT NOT NULL, - `text1` TEXT(500) NOT NULL, - `userid` INT NOT NULL, - PRIMARY KEY (`stockid`), - -- INDEX `fk_items_stock_idx` (`stockid` ASC), - UNIQUE INDEX `stockid_UNIQUE` (`stockid` ASC), - CONSTRAINT `fk_items_stock` - FOREIGN KEY (`itemid`) - REFERENCES `items` (`itemid`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB; - - -- ----------------------------------------------------- -- Table `labels` -- ----------------------------------------------------- @@ -110,8 +89,8 @@ CREATE TABLE IF NOT EXISTS `permissions` ( `itemid` INT NOT NULL, `canedit` TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (`userid`, `itemid`), - INDEX `fk_permissions_items1_idx` (`itemid` ASC), - CONSTRAINT `fk_permissions_items1` + INDEX `fk_permissions_items_idx` (`itemid` ASC), + CONSTRAINT `fk_permissions_items` FOREIGN KEY (`itemid`) REFERENCES `items` (`itemid`) ON DELETE NO ACTION @@ -141,8 +120,29 @@ CREATE TABLE IF NOT EXISTS `images` ( `primary` TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (`imageid`, `itemid`), UNIQUE INDEX `imageid_UNIQUE` (`imageid` ASC), - INDEX `fk_images_items1_idx` (`itemid` ASC), - CONSTRAINT `fk_images_items1` + INDEX `fk_images_items_idx` (`itemid` ASC), + CONSTRAINT `fk_images_items` + FOREIGN KEY (`itemid`) + REFERENCES `items` (`itemid`) + ON DELETE NO ACTION + ON UPDATE NO ACTION) +ENGINE = InnoDB; + + +-- ----------------------------------------------------- +-- Table `stock` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `stock` ( + `stockid` INT NOT NULL AUTO_INCREMENT, + `itemid` INT NOT NULL, + `stock` INT NOT NULL, + `text1` TEXT(500) NOT NULL, + `userid` INT NOT NULL, + `timestamp` TIMESTAMP NOT NULL, + PRIMARY KEY (`stockid`, `itemid`), + UNIQUE INDEX `stockid_UNIQUE` (`stockid` ASC), + INDEX `fk_stock_items_idx` (`itemid` ASC), + CONSTRAINT `fk_stock_items` FOREIGN KEY (`itemid`) REFERENCES `items` (`itemid`) ON DELETE NO ACTION @@ -169,3 +169,4 @@ INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (8, 'itemtext3', 'Text Va INSERT INTO `labels` (`rowid`, `name`, `value`) VALUES (9, 'catname', 'Category Name'); COMMIT; + From 841c03847eb4aa7efc591a4a6ab92fcbb384982e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andrade?= Date: Tue, 10 Mar 2020 10:48:35 +0200 Subject: [PATCH 5/8] Add stock management mode setting --- lib/getitemtable.php | 9 ++++-- pages/edititem.php | 14 ++++---- pages/item.php | 75 +++++++++++++++++++++++-------------------- settings.template.php | 4 ++- 4 files changed, 57 insertions(+), 45 deletions(-) diff --git a/lib/getitemtable.php b/lib/getitemtable.php index ab1cf80..6aee0d5 100644 --- a/lib/getitemtable.php +++ b/lib/getitemtable.php @@ -118,8 +118,13 @@ for ($i = 0; $i < count($items); $i++) { $user = new User($_SESSION['uid']); if ($user->hasPermission("INV_EDIT")) { $items[$i]["editbtn"] = ' ' . $Strings->get("edit", false) . ''; - $items[$i]["addstockbtn"] = ' ' . $Strings->get("addstock", false) . ''; - $items[$i]["removestockbtn"] = ' ' . $Strings->get("removestock", false) . ''; + if ($SETTINGS['stock_management']) { + $items[$i]["addstockbtn"] = ' ' . $Strings->get("addstock", false) . ''; + $items[$i]["removestockbtn"] = ' ' . $Strings->get("removestock", false) . ''; + } else { + $items[$i]["addstockbtn"] = ''; + $items[$i]["removestockbtn"] = ''; + } } else { $items[$i]["editbtn"] = ''; $items[$i]["addstockbtn"] = ''; diff --git a/pages/edititem.php b/pages/edititem.php index c8f402d..1b1d8ee 100644 --- a/pages/edititem.php +++ b/pages/edititem.php @@ -165,14 +165,14 @@ if (!empty($VARS['id'])) {
- - + + - - + +
diff --git a/pages/item.php b/pages/item.php index eabb20e..9a61cf5 100644 --- a/pages/item.php +++ b/pages/item.php @@ -169,44 +169,49 @@ $item = $database->get( } ?> + +
-
- -
- - - - - - - - - - - select('stock', [ - 'timestamp', - 'stock', - 'text1', - 'userid' - ], [ - 'itemid' => $item['itemid'] - ] - ); - foreach ($stockentries as $stockentry) { - $user = new User($stockentry['userid']) - ?> +
+
get('date'); ?> get('amount'); ?> get('description'); ?> get('changed by'); ?>
+ - - - - + + + + + + - -
getName() . " (" . $user->getUsername() . ")"; ?> get('date'); ?> get('amount'); ?> get('description'); ?> get('changed by'); ?>
-
+ $stockentries = $database->select('stock', [ + 'timestamp', + 'stock', + 'text1', + 'userid' + ], [ + 'itemid' => $item['itemid'] + ] + ); + foreach ($stockentries as $stockentry) { + $user = new User($stockentry['userid']) + ?> + + + + + getName() . " (" . $user->getUsername() . ")"; ?> + + + + + + diff --git a/settings.template.php b/settings.template.php index 4350bd3..d68964a 100644 --- a/settings.template.php +++ b/settings.template.php @@ -56,5 +56,7 @@ $SETTINGS = [ // Base URL for building links relative to the location of the app. // Only used when there's no good context for the path. // The default is almost definitely fine. - "url" => "." + "url" => ".", + // Enable stock management mode. + "stock_management" => false, ]; From 488988f7d461becf4bf89229b64016e107070c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Andrade?= Date: Wed, 11 Mar 2020 09:00:00 +0200 Subject: [PATCH 6/8] Add stock movements report --- langs/en/items.json | 1 + langs/en/reports.json | 3 ++- lib/reports.php | 48 ++++++++++++++++++++++++++++++++++++++++++- pages/edititem.php | 2 +- pages/export.php | 9 +++++++- 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/langs/en/items.json b/langs/en/items.json index 8089631..d824c10 100644 --- a/langs/en/items.json +++ b/langs/en/items.json @@ -10,6 +10,7 @@ "itemid": "Item ID", "id": "ID", "Edit Item": "Edit Item", + "stockid": "Stock ID", "adding stock": "Adding stock for {item}", "removing stock": "Removing stock from {item}" } diff --git a/langs/en/reports.json b/langs/en/reports.json index bb824a3..c76c7f1 100644 --- a/langs/en/reports.json +++ b/langs/en/reports.json @@ -5,5 +5,6 @@ "choose an option": "Choose an option", "csv file": "CSV text file", "ods file": "ODS spreadsheet", - "html file": "HTML web page" + "html file": "HTML web page", + "Stock": "Stock movements" } diff --git a/lib/reports.php b/lib/reports.php index 182272d..2283294 100644 --- a/lib/reports.php +++ b/lib/reports.php @@ -151,6 +151,49 @@ function getLocationReport(): Report { return $report; } +function getStockReport($filter = []): Report { + global $database, $Strings; + $stock = $database->select( + "stock", [ + "[>]items" => ["itemid"] + ], [ + "stockid", + "itemid", + "name", + 'timestamp', + 'stock', + 'stock.text1', + 'stock.userid' + ]); + $report = new Report($Strings->get("Stock", false)); + $report->setHeader([ + $Strings->get("stockid", false), + $Strings->get("itemid", false), + $Strings->get("name", false), + $Strings->get("date", false), + $Strings->get("amount", false), + $Strings->get("description", false), + $Strings->get("changed by", false) + ]); + for ($i = 0; $i < count($stock); $i++) { + $user = ""; + if (!is_null($stock[$i]["userid"])) { + $u = new User($stock[$i]["userid"]); + $user = $u->getName() . " (" . $u->getUsername() . ')'; + } + $report->addDataRow([ + $stock[$i]["stockid"], + $stock[$i]["itemid"], + $stock[$i]["name"], + $stock[$i]["timestamp"], + $stock[$i]["stock"], + $stock[$i]["text1"], + $user + ]); + } + return $report; +} + function getReport($type): Report { switch ($type) { case "item": @@ -165,6 +208,9 @@ function getReport($type): Report { case "itemstock": return getItemReport(["AND" => ["qty[<]want", "want[>]" => 0]]); break; + case "stock": + return getStockReport(); + break; default: return new Report("error", ["ERROR"], ["Invalid report type."]); } @@ -173,4 +219,4 @@ function getReport($type): Report { function generateReport($type, $format) { $report = getReport($type); $report->output($format); -} \ No newline at end of file +} diff --git a/pages/edititem.php b/pages/edititem.php index 1b1d8ee..1d2f054 100644 --- a/pages/edititem.php +++ b/pages/edititem.php @@ -165,7 +165,7 @@ if (!empty($VARS['id'])) {