An easy point of sale system with automatic inventory tracking. https://netsyms.biz/apps/nickelbox/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pos.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. <?php
  2. /*
  3. * This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. */
  7. $register = [
  8. "name" => lang("no cash", false),
  9. "id" => ""
  10. ];
  11. $registeropen = false;
  12. if (isset($_SESSION['register'])) {
  13. $registeropen = $database->has("registers", ['[>]cash_drawer' => ['registerid' => 'registerid']], ['AND' => ['open[!]' => null, 'close' => null, 'registers.registerid' => $_SESSION['register']]]);
  14. }
  15. if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
  16. require_once __DIR__ . "/../lib/chooseregister.php";
  17. } else {
  18. $register = $database->get('registers', ['registerid (id)', 'registername (name)'], ['registerid' => $_SESSION['register']]);
  19. $showgridbydefault = $binstack->count('items', ['AND' => ['price[!]' => null, 'price[!]' => 0]]) <= GRID_BY_DEFAULT_MAX_ITEMS;
  20. $items = [];
  21. $payments = [];
  22. $editing = false;
  23. $returning = false;
  24. if (isset($VARS['txid']) && $database->has('transactions', ['txid' => $VARS['txid']])) {
  25. $tx = $database->get('transactions', ['[>]customers' => 'customerid'], ['txid', 'discountpercent', 'transactions.customerid', 'customers.name (customername)', 'cashid', 'type'], ['txid' => $VARS['txid']]);
  26. if ($tx['type'] != 1) {
  27. header('Location: app.php?page=pos&msg=return_transaction_no_edit');
  28. die();
  29. }
  30. $items = $database->select('lines', ['lineid', 'amount', 'name', 'itemid', 'qty'], ['txid' => $tx['txid']]);
  31. if ($database->has('cash_drawer', ['AND' => ['cashid' => $tx['cashid'], 'open[!]' => null, 'close' => null]])) {
  32. $editing = true;
  33. $payments = $database->select('payments', ['[>]certificates' => 'certid', '[>]payment_types' => ['type' => 'typeid']], ['payments.amount', 'typename', 'icon', 'text', 'certcode'], ['txid' => $tx['txid']]);
  34. echo "<input type=\"hidden\" id=\"txid\" value=\"$tx[txid]\">";
  35. } else {
  36. $returning = true;
  37. echo "<input type=\"hidden\" id=\"return\" value=\"1\">";
  38. }
  39. }
  40. ?>
  41. <div class="modal fade" tabindex="-1" role="dialog" id="receiptmodal">
  42. <div class="modal-dialog" role="document">
  43. <div class="modal-content">
  44. <div class="modal-header">
  45. <h5 class="modal-title"><i class="fas fa-receipt"></i> <?php lang("receipt"); ?></h5>
  46. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  47. <span aria-hidden="true">&times;</span>
  48. </button>
  49. </div>
  50. <div class="modal-body">
  51. <div class="display-4 text-center" id="receiptchangediv"><?php lang("change"); ?>: $<span id="receiptchange">0.00</span></div>
  52. <iframe class="w-100 shadow-lg" id="receiptframe"></iframe>
  53. </div>
  54. <div class="modal-footer">
  55. <button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("close"); ?></button>
  56. <button type="button" class="btn btn-primary" id="receiptprintbtn"><i class="fas fa-print"></i> <?php lang("print"); ?></button>
  57. </div>
  58. </div>
  59. </div>
  60. </div>
  61. <div class="modal fade" tabindex="-1" role="dialog" id="customermodal">
  62. <div class="modal-dialog modal-lg" role="document">
  63. <div class="modal-content">
  64. <div class="modal-header">
  65. <h5 class="modal-title"><i class="fas fa-user"></i> <?php lang("customer"); ?></h5>
  66. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  67. <span aria-hidden="true">&times;</span>
  68. </button>
  69. </div>
  70. <div class="modal-body">
  71. <div class="input-group">
  72. <input type="text" class="form-control" id="customersearch" placeholder="<?php lang("customer search"); ?>" />
  73. <div class="input-group-append">
  74. <button class="btn btn-link" type="button" id="customersearchbtn"><i class="fas fa-search"></i></button>
  75. </div>
  76. </div>
  77. <div class="list-group mt-2" id="customerselection">
  78. </div>
  79. </div>
  80. <div class="modal-footer">
  81. <button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("close"); ?></button>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. <div class="modal fade" tabindex="-1" role="dialog" id="managermodal">
  87. <div class="modal-dialog modal-lg" role="document">
  88. <div class="modal-content">
  89. <div class="modal-header">
  90. <h5 class="modal-title"><i class="fas fa-cog"></i> <?php lang("register management"); ?></h5>
  91. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  92. <span aria-hidden="true">&times;</span>
  93. </button>
  94. </div>
  95. <div class="modal-body">
  96. <div class="row">
  97. <div class="col-12 col-md-6">
  98. <div class="input-group">
  99. <input type="text" class="form-control" id="transactionsearch" placeholder="<?php lang("transaction search"); ?>" />
  100. <div class="input-group-append">
  101. <span class="btn btn-link open-number-pad-btn">
  102. <i class="fas fa-keyboard"></i>
  103. </span>
  104. <span class="btn btn-link" id="txsearch-datetime-btn">
  105. <span class="fa-layers fa-fw">
  106. <i class="fas fa-calendar" data-fa-transform="shrink-3 up-4 left-4"></i>
  107. <i class="fas fa-clock" data-fa-transform="shrink-3 down-4 right-4"></i>
  108. </span>
  109. </span>
  110. <button class="btn btn-link" type="button" id="transactionsearchbtn"><i class="fas fa-search"></i></button>
  111. </div>
  112. </div>
  113. <div class="row d-none" id="txsearch-datetimefilters">
  114. <div class="input-group col-md-6">
  115. <div class="input-group-prepend">
  116. <span class="input-group-text"><i class="fas fa-play"></i></span>
  117. </div>
  118. <input type="text" class="form-control" id="tx-startdate" data-toggle="datetimepicker" data-target="#tx-startdate" />
  119. </div>
  120. <div class="input-group col-md-6">
  121. <div class="input-group-prepend">
  122. <span class="input-group-text"><i class="fas fa-stop"></i></span>
  123. </div>
  124. <input type="text" class="form-control" id="tx-enddate" data-toggle="datetimepicker" data-target="#tx-enddate" />
  125. </div>
  126. </div>
  127. <div class="list-group mt-2" id="transactionselection">
  128. </div>
  129. </div>
  130. <div class="col-12 col-md-6">
  131. <div class="d-flex justify-content-between flex-wrap">
  132. <button type="button" class="btn btn-default" id="opendrawerbtn"><i class="fas fa-lock-open"></i> <?php lang("open drawer"); ?></button>
  133. <button type="button" class="btn btn-primary ml-auto" id="xprintbtn"><i class="fas fa-print"></i> <?php lang("print"); ?></button>
  134. </div>
  135. <iframe class="w-100 shadow-lg" id="xframe" src="action.php?action=xreport&format=html&register=<?php echo $register['id']; ?>"></iframe>
  136. </div>
  137. </div>
  138. </div>
  139. <div class="modal-footer">
  140. <button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("close"); ?></button>
  141. </div>
  142. </div>
  143. </div>
  144. </div>
  145. <div class="row">
  146. <div class="col-12 col-md-6 order-1 order-md-0">
  147. <div class="card d-flex">
  148. <div class="card-header p-1">
  149. <div class="input-group">
  150. <div class="input-group-prepend">
  151. <span class="btn btn-default" id="gridviewbtn" title="<?php lang("grid view"); ?>"><i class="fas fa-th fa-fw"></i></span>
  152. </div>
  153. <?php
  154. if (isset($_SESSION['mobile'])) {
  155. ?>
  156. <div class="input-group-prepend">
  157. <span class="btn btn-default" onclick="scancode('#barcode'); mobilecode = true;">
  158. <i class="fas fa-barcode fa-fw"></i>
  159. </span>
  160. </div>
  161. <?php } ?>
  162. <input type="text" class="form-control" id="barcode" placeholder="<?php lang("barcode or search"); ?>" />
  163. <div class="input-group-append">
  164. <button class="btn btn-link" type="button" id="barcodebtn"><i class="fas fa-search"></i></button>
  165. </div>
  166. </div>
  167. </div>
  168. <div class="d-none justify-content-around flex-wrap" id="gridview">
  169. </div>
  170. <div>
  171. <div class="list-group list-group-flush" id="pos-lines-box">
  172. <?php
  173. foreach ($items as $i) {
  174. $linetotal = $i['amount'] * $i['qty'];
  175. $amount = $i['amount'];
  176. // Include percentage discount for returns
  177. if ($returning) {
  178. $linetotal *= 1.0 - ($tx['discountpercent'] / 100.0);
  179. $amount *= 1.0 - ($tx['discountpercent'] / 100);
  180. }
  181. ?>
  182. <div class="list-group-item" data-itemid="<?php echo $i['itemid']; ?>">
  183. <div class="d-flex w-100 justify-content-between mb-2">
  184. <h5 class="item-name"><?php echo $i['name']; ?></h5>
  185. <h5>
  186. <span class="badge badge-light">
  187. $<span class="line-total"><?php echo number_format($linetotal, 2); ?></span>
  188. </span>
  189. </h5>
  190. </div>
  191. <div class="d-inline-flex">
  192. <div class="input-group qty-control">
  193. <div class="input-group-prepend">
  194. <span class="input-group-text pr-1"><b>$</b></span>
  195. </div>
  196. <input type="money" class="form-control item-price" value="<?php echo number_format($amount, 2); ?>"/>
  197. <div class="input-group-append">
  198. <span class="btn btn-outline-primary open-number-pad-btn">
  199. <i class="fas fa-keyboard"></i>
  200. </span>
  201. </div>
  202. <div class="input-group-prepend">
  203. <span class="input-group-text px-2"><i class="fas fa-times"></i></span>
  204. <button class="btn btn-red qty-minus" type="button"><i class="fas <?php
  205. if ($i['qty'] > 1) {
  206. echo "fa-minus";
  207. } else {
  208. echo "fa-trash";
  209. }
  210. ?>"></i></button>
  211. </div>
  212. <input type="number" class="form-control item-qty px-2" value="<?php echo $i['qty']; ?>" />
  213. <div class="input-group-append">
  214. <button class="btn btn-light-green qty-plus" type="button"><i class="fas fa-plus"></i></button>
  215. </div>
  216. </div>
  217. </div>
  218. </div>
  219. <?php
  220. }
  221. ?>
  222. </div>
  223. </div>
  224. </div>
  225. </div>
  226. <div class="col-12 col-md-6 order-0 order-md-1">
  227. <div class="card mb-3 mb-md-0">
  228. <div class="w-100 position-absolute d-flex align-items-start px-3 pt-2">
  229. <a href="#" class="mr-auto text-body" id="openmanagement" data-toggle="tooltip" title="<?php lang("manage register") ?>"><i class="fas fa-cog"></i> <?php lang("manage"); ?></a>
  230. <a href="app.php?page=pos&switch" class="ml-auto text-body" id="register" data-id="<?php echo $register['id']; ?>" data-toggle="tooltip" title="<?php lang("change register") ?>"><i class="fas fa-exchange-alt"></i> <?php echo $register['name']; ?></a>
  231. </div>
  232. <div class="display-4 p-1 p-md-3 text-center">$<span id="grand-total">0.00</span></div>
  233. <div class="card-body d-flex justify-content-center flex-wrap py-0 my-0">
  234. <div class="btn m-1" id="addcustomerbtn">
  235. <i class="fas fa-user-circle"></i>
  236. <span id="customerbtnlabel"><?php
  237. if ($editing && isset($tx['customername'])) {
  238. echo $tx['customername'];
  239. }
  240. ?></span>
  241. <span class="sr-only"><?php lang("customer"); ?></span>
  242. </div>
  243. <?php
  244. if (!$returning) {
  245. ?>
  246. <div class="btn m-1" id="discountpercentbtn" data-percent="<?php
  247. if ($editing && isset($tx['discountpercent']) && $tx['discountpercent'] != 0) {
  248. echo (float) $tx['discountpercent'];
  249. } else {
  250. echo "0";
  251. }
  252. ?>">
  253. <span id="discountpercentbtnlabel"><?php
  254. if ($editing && isset($tx['discountpercent']) && $tx['discountpercent'] != 0) {
  255. echo (float) $tx['discountpercent'];
  256. }
  257. ?></span>
  258. <i class="fas fa-percent"></i>
  259. <span class="sr-only"><?php lang("transaction discount"); ?></span>
  260. </div>
  261. <?php
  262. }
  263. ?>
  264. <div class="btn m-1" id="deletetxbtn">
  265. <i class="fas fa-trash"></i>
  266. <span class="sr-only"><?php lang("delete transaction"); ?></span>
  267. </div>
  268. </div>
  269. <div class="card-body d-md-none">
  270. <span class="btn btn-green btn-lg btn-block" id="paymentbtn"><i class="fas fa-money-bill-wave"></i> <?php
  271. if ($returning) {
  272. lang("enter refund");
  273. } else {
  274. lang("enter payment");
  275. }
  276. ?></span>
  277. </div>
  278. <div id="paymentui" class="d-none d-md-block">
  279. <div class="card-body">
  280. <div class="d-flex justify-content-around flex-wrap">
  281. <?php
  282. $payment_methods = $database->select('payment_types', ['typeid (id)', 'typename (name)', 'icon', 'text']);
  283. $hideforreturns = ['check', 'free'];
  284. foreach ($payment_methods as $data) {
  285. if ($returning && in_array($data['name'], $hideforreturns)) {
  286. continue;
  287. }
  288. ?>
  289. <div class="card p-2 text-center m-1 payment-method-button" data-payment-method="<?php echo $data['name']; ?>" data-icon="<?php echo $data['icon']; ?>" data-text="<?php lang($data['text']); ?>">
  290. <i class="<?php echo $data['icon']; ?> fa-3x fa-fw"></i>
  291. <?php lang($data['text']); ?>
  292. </div>
  293. <?php
  294. }
  295. ?>
  296. </div>
  297. </div>
  298. <hr />
  299. <div class="row px-2 mb-3 text-center">
  300. <?php
  301. if ($returning) {
  302. ?>
  303. <div class="col-12 col-sm-4">
  304. <?php lang("refund"); ?> $<span id="paid-amount">0.00</span>
  305. </div>
  306. <div class="col-12 col-sm-4">
  307. <?php lang("owed"); ?> $<span id="owed-amount">0.00</span>
  308. </div>
  309. <div class="col-12 col-sm-4">
  310. <?php lang("change"); ?> $<span id="change-amount">0.00</span>
  311. </div>
  312. <?php
  313. } else {
  314. ?>
  315. <div class="col-12 col-sm-4">
  316. <?php lang("paid"); ?> $<span id="paid-amount">0.00</span>
  317. </div>
  318. <div class="col-12 col-sm-4">
  319. <?php lang("owed"); ?> $<span id="owed-amount">0.00</span>
  320. </div>
  321. <div class="col-12 col-sm-4">
  322. <?php lang("change"); ?> $<span id="change-amount">0.00</span>
  323. </div>
  324. <?php
  325. }
  326. ?>
  327. </div>
  328. <div class="list-group list-group-flush" id="payment-lines">
  329. <?php
  330. if ($editing) {
  331. foreach ($payments as $p) {
  332. if ($p['amount'] <= 0) {
  333. continue;
  334. }
  335. ?>
  336. <div class="list-group-item">
  337. <div class="input-group">
  338. <div class="input-group-prepend">
  339. <span class="input-group-text">
  340. <i class="<?php echo $p['icon']; ?> fa-fw mr-1"></i>
  341. <?php lang($p['text']); ?>
  342. </span>
  343. </div>
  344. <div class="input-group-prepend">
  345. <span class="input-group-text">
  346. $
  347. </span>
  348. </div>
  349. <input class="form-control payment-entry" type="money" data-type="<?php echo $p['typename']; ?>" value="<?php echo number_format($p['amount'], 2); ?>" />
  350. <div class="input-group-prepend input-group-append">
  351. <span class="btn btn-outline-success open-money-pad-btn">
  352. <i class="fas fa-calculator"></i>
  353. </span>
  354. </div>
  355. <?php
  356. if ($p['typename'] == 'giftcard') {
  357. ?>
  358. <div class="input-group-prepend input-group-append">
  359. <span class="input-group-text">
  360. #
  361. </span>
  362. </div>
  363. <input class="form-control giftcard-number" type="number" value="<?php echo $p['certcode']; ?>" />
  364. <div class="input-group-prepend input-group-append">
  365. <span class="btn btn-outline-primary open-number-pad-btn" data-gc="1">
  366. <i class="fas fa-keyboard"></i>
  367. </span>
  368. </div>
  369. <?php
  370. }
  371. ?>
  372. <div class="input-group-append">
  373. <span class="btn btn-outline-danger remove-payment-btn">
  374. <i class="fas fa-trash"></i>
  375. </span>
  376. </div>
  377. </div>
  378. </div>
  379. <?php
  380. }
  381. }
  382. ?>
  383. <!-- Payments go here -->
  384. </div>
  385. <div class="card-body">
  386. <span class="btn btn-green btn-lg btn-block" id="finishbtn"><i class="fas fa-receipt"></i> <?php
  387. if ($editing) {
  388. lang("update");
  389. } else if ($returning) {
  390. lang("return");
  391. } else {
  392. lang("finish");
  393. }
  394. ?></span>
  395. </div>
  396. </div>
  397. </div>
  398. </div>
  399. </div>
  400. <script nonce="<?php echo $SECURE_NONCE; ?>">
  401. var showgridbydefault = <?php echo $showgridbydefault === true && $editing !== true && $returning !== true ? "true" : "false" ?>;
  402. </script>
  403. <?php
  404. }
  405. ?>