diff --git a/composer.json b/composer.json
index fb37c32..9a3473c 100644
--- a/composer.json
+++ b/composer.json
@@ -4,7 +4,9 @@
"type": "project",
"require": {
"catfan/medoo": "^1.5",
- "guzzlehttp/guzzle": "^6.2"
+ "guzzlehttp/guzzle": "^6.2",
+ "league/csv": "^9.1",
+ "lapinator/ods-php-generator": "^0.0.3"
},
"license": "MPL-2.0",
"authors": [
diff --git a/composer.lock b/composer.lock
index f6f5538..590de35 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "577921e9d14ff39571692f88476151ee",
- "content-hash": "1c8b61c5d506ae016285b99b20040cf0",
+ "hash": "0e5db12408080dd084cad072b8cfd599",
+ "content-hash": "348006dfc1d25121fcc3b4cb32bc3369",
"packages": [
{
"name": "catfan/medoo",
@@ -247,6 +247,116 @@
],
"time": "2017-03-20 17:10:46"
},
+ {
+ "name": "lapinator/ods-php-generator",
+ "version": "v0.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Lapinator/odsPhpGenerator.git",
+ "reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Lapinator/odsPhpGenerator/zipball/575314c003c2ec3032813bedcc1d27032b7b7ab2",
+ "reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Laurent VUIBERT",
+ "email": "lapinator@gmx.fr",
+ "homepage": "http://lapinator.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "Open Document Spreadsheet (.ods) generator ",
+ "homepage": "https://odsphpgenerator.lapinator.net/",
+ "keywords": [
+ "LibreOffice",
+ "ods"
+ ],
+ "time": "2016-04-14 21:51:27"
+ },
+ {
+ "name": "league/csv",
+ "version": "9.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/csv.git",
+ "reference": "9c8ad06fb5d747c149875beb6133566c00eaa481"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/csv/zipball/9c8ad06fb5d747c149875beb6133566c00eaa481",
+ "reference": "9c8ad06fb5d747c149875beb6133566c00eaa481",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=7.0.10"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "friendsofphp/php-cs-fixer": "^2.0",
+ "phpstan/phpstan": "^0.9.2",
+ "phpstan/phpstan-phpunit": "^0.9.4",
+ "phpstan/phpstan-strict-rules": "^0.9.0",
+ "phpunit/phpunit": "^6.0"
+ },
+ "suggest": {
+ "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Csv\\": "src"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://github.com/nyamsprod/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Csv data manipulation made easy in PHP",
+ "homepage": "http://csv.thephpleague.com",
+ "keywords": [
+ "csv",
+ "export",
+ "filter",
+ "import",
+ "read",
+ "write"
+ ],
+ "time": "2018-05-01 18:32:48"
+ },
{
"name": "psr/http-message",
"version": "1.0.1",
diff --git a/database.mwb b/database.mwb
index 0dad56f..ce72d53 100644
Binary files a/database.mwb and b/database.mwb differ
diff --git a/lang/en_us.php b/lang/en_us.php
index f06e796..19f44ba 100644
--- a/lang/en_us.php
+++ b/lang/en_us.php
@@ -89,4 +89,19 @@ define("STRINGS", [
"cash closed" => "Cash closed.",
"register set" => "Register set.",
"change register" => "Change register",
-]);
\ No newline at end of file
+ "reports" => "Reports",
+ "report type" => "Report Type",
+ "format" => "Format",
+ "filter" => "Filter",
+ "generate report" => "Generate Report",
+ "cashflow" => "Cash Flow",
+ "z report" => "Z Report",
+ "csv file" => "CSV text file",
+ "ods file" => "ODS spreadsheet",
+ "html file" => "HTML web page",
+ "register" => "Register",
+ "all" => "All",
+ "date range" => "Date Range",
+ "start" => "Start",
+ "end" => "End"
+]);
diff --git a/lib/reports.php b/lib/reports.php
new file mode 100644
index 0000000..7fce0e0
--- /dev/null
+++ b/lib/reports.php
@@ -0,0 +1,297 @@
+has('report_access_codes', ["AND" => ['code' => $VARS['code'], 'expires[>]' => $date]])) {
+ dieifnotloggedin();
+ $requester = $_SESSION['uid'];
+ } else {
+ $requester = $database->get('report_access_codes', 'uid', ['code' => $VARS['code']]);
+ }
+} else {
+ dieifnotloggedin();
+ $requester = $_SESSION['uid'];
+}
+
+if (account_has_permission($_SESSION['username'], "ADMIN")) {
+ $allowed_users = true;
+} else {
+ if (account_has_permission($_SESSION['username'], "QWIKCLOCK_MANAGE")) {
+ $allowed_users = getManagedUIDs($requester);
+ }
+
+ if (account_has_permission($_SESSION['username'], "QWIKCLOCK_EDITSELF")) {
+ $allowed_users[] = $_SESSION['uid'];
+ }
+}
+
+// Delete old DB entries
+$database->delete('report_access_codes', ['expires[<=]' => $date]);
+
+if (LOADED) {
+ $user = null;
+ if (isset($VARS['type']) && isset($VARS['format'])) {
+ generateReport($VARS['type'], $VARS['format'], $VARS['register'], $VARS['startdate'], $VARS['enddate']);
+ die();
+ } else {
+ lang("invalid parameters");
+ die();
+ }
+}
+
+function getCashFlowReport($register = null, $start = null, $end = null) {
+ global $database;
+ $where = [];
+
+ if (!is_null($register) && $database->has('registers', ['registerid' => $register])) {
+ $where["registers.registerid"] = $register;
+ }
+
+ if ((bool) strtotime($start) == TRUE) {
+ $where["OR #open"] = [
+ "open[>=]" => date("Y-m-d", strtotime($start)),
+ "close[>=]" => date("Y-m-d", strtotime($start))
+ ];
+ }
+ if ((bool) strtotime($end) == TRUE) {
+ // Make the date be the end of the day, not the start
+ $where["close[<=]"] = date("Y-m-d", strtotime($end)) . " 23:59:59";
+ }
+
+ if (count($where) > 1) {
+ $where = ["AND" => $where];
+ }
+ $cash = $database->select(
+ "cash_drawer", [
+ '[>]registers' => ['cash_drawer.registerid' => 'registerid'],
+ '[>]transactions' => ['cash_drawer.cashid' => 'cashid'],
+ '[>]payments' => ['transactions.txid' => 'txid'],
+ '[>]payment_types' => ['payments.type' => 'typeid']
+ ], [
+ "cash_drawer.cashid",
+ "registers.registername",
+ "cash_drawer.registerid",
+ "open",
+ "close",
+ "payments.type",
+ "payments.amount",
+ "payment_types.typename"
+ ], $where
+ );
+ $header = [lang("register", false), lang("open", false), lang("close", false), lang("cash", false), lang("card", false), lang("check", false), lang("crypto", false), lang("gift card", false), lang("free", false)];
+ $out = [$header];
+
+ $registers = [];
+
+ foreach ($cash as $c) {
+ $registers[$c['registerid']]['name'] = $c['registername'];
+ $registers[$c['registerid']]['id'] = $c['registerid'];
+ $registers[$c['registerid']]['open'] = date(DATETIME_FORMAT, strtotime($c['open']));
+ if (is_null($c['close'])) {
+ $registers[$c['registerid']]['close'] = date(DATETIME_FORMAT);
+ } else {
+ $registers[$c['registerid']]['close'] = date(DATETIME_FORMAT, strtotime($c['close']));
+ }
+ if (!isset($registers[$c['registerid']][$c['typename']])) {
+ $registers[$c['registerid']][$c['typename']] = 0.0;
+ }
+ $registers[$c['registerid']][$c['typename']] += $c['amount'];
+ }
+
+ foreach ($registers as $r) {
+ $types = $database->select('payment_types', 'typename');
+ foreach ($types as $t) {
+ if (!isset($r[$t])) {
+ $r[$t] = 0.0;
+ }
+ }
+ $out[] = [
+ $r['name'],
+ $r['open'],
+ $r['close'],
+ $r['cash'] . "",
+ $r['card'] . "",
+ $r['check'] . "",
+ $r['crypto'] . "",
+ $r['giftcard'] . "",
+ $r['free'] . ""
+ ];
+ }
+
+ return $out;
+}
+
+function getReportData($type, $register = null, $start = null, $end = null) {
+ switch ($type) {
+ case "cashflow":
+ return getCashFlowReport($register, $start, $end);
+ default:
+ return [["error"]];
+ }
+}
+
+function dataToCSV($data, $name = "report", $register = null, $start = null, $end = null) {
+ $csv = Writer::createFromString('');
+ $usernotice = "";
+ $usertitle = "";
+ $datetitle = "";
+ if ($start != null && (bool) strtotime($start)) {
+ $datenotice = lang2("report filtered to start date", ["date" => date(DATE_FORMAT, strtotime($start))], false);
+ $datetitle = "_" . date(DATE_FORMAT, strtotime($start));
+ $csv->insertOne([$datenotice]);
+ }
+ if ($end != null && (bool) strtotime($end)) {
+ $datenotice = lang2("report filtered to end date", ["date" => date(DATE_FORMAT, strtotime($end))], false);
+ $datetitle .= ($datetitle == "" ? "_" : "-") . date(DATE_FORMAT, strtotime($end));
+ $csv->insertOne([$datenotice]);
+ }
+ $csv->insertAll($data);
+ header('Content-type: text/csv');
+ header('Content-Disposition: attachment; filename="' . $name . $usertitle . $datetitle . "_" . date("Y-m-d_Hi") . ".csv" . '"');
+ echo $csv;
+ die();
+}
+
+function dataToODS($data, $name = "report", $register = null, $start = null, $end = null) {
+ $ods = new ods();
+ $styleColumn = new odsStyleTableColumn();
+ $styleColumn->setUseOptimalColumnWidth(true);
+ $headerstyle = new odsStyleTableCell();
+ $headerstyle->setFontWeight("bold");
+ $table = new odsTable($name);
+
+ for ($i = 0; $i < count($data[0]); $i++) {
+ $table->addTableColumn(new odsTableColumn($styleColumn));
+ }
+
+ $usernotice = "";
+ $usertitle = "";
+ $datetitle = "";
+ if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) {
+ $usernotice = lang2("report filtered to user", ["name" => $user['name'], "username" => $user['username']], false);
+ $usertitle = "_" . $user['username'];
+ $row = new odsTableRow();
+ $row->addCell(new odsTableCellString($usernotice));
+ $table->addRow($row);
+ }
+ if ($start != null && (bool) strtotime($start)) {
+ $datenotice = lang2("report filtered to start date", ["date" => date(DATE_FORMAT, strtotime($start))], false);
+ $datetitle = "_" . date(DATE_FORMAT, strtotime($start));
+ $row = new odsTableRow();
+ $row->addCell(new odsTableCellString($datenotice));
+ $table->addRow($row);
+ }
+ if ($end != null && (bool) strtotime($end)) {
+ $datenotice = lang2("report filtered to end date", ["date" => date(DATE_FORMAT, strtotime($end))], false);
+ $datetitle .= ($datetitle == "" ? "_" : "-") . date(DATE_FORMAT, strtotime($end));
+ $row = new odsTableRow();
+ $row->addCell(new odsTableCellString($datenotice));
+ $table->addRow($row);
+ }
+
+ $rowid = 0;
+ foreach ($data as $datarow) {
+ $row = new odsTableRow();
+ foreach ($datarow as $cell) {
+ if ($rowid == 0) {
+ $row->addCell(new odsTableCellString($cell, $headerstyle));
+ } else {
+ $row->addCell(new odsTableCellString($cell));
+ }
+ }
+ $table->addRow($row);
+ $rowid++;
+ }
+ $ods->addTable($table);
+ $ods->downloadOdsFile($name . $usertitle . $datetitle . "_" . date("Y-m-d_Hi") . ".ods");
+}
+
+function dataToHTML($data, $name = "report", $register = null, $start = null, $end = null) {
+ global $SECURE_NONCE;
+ // HTML exporter doesn't like null values
+ for ($i = 0; $i < count($data); $i++) {
+ for ($j = 0; $j < count($data[$i]); $j++) {
+ if (is_null($data[$i][$j])) {
+ $data[$i][$j] = '';
+ }
+ }
+ }
+ $datenotice = "";
+ $datetitle = "";
+ if ($start != null && (bool) strtotime($start)) {
+ $datenotice = "" . lang2("report filtered to start date", ["date" => date(DATE_FORMAT, strtotime($start))], false) . "
";
+ $datetitle = "_" . date(DATE_FORMAT, strtotime($start));
+ }
+ if ($end != null && (bool) strtotime($end)) {
+ $datenotice .= "" . lang2("report filtered to end date", ["date" => date(DATE_FORMAT, strtotime($end))], false) . "
";
+ $datetitle .= ($datetitle == "" ? "_" : "-") . date(DATE_FORMAT, strtotime($end));
+ }
+ header('Content-type: text/html');
+ $converter = new HTMLConverter();
+ $out = "\n"
+ . "\n"
+ . "\n"
+ . "