Parcourir la source

Add BinStack integration, barcode scanning, and item lookup

master
Skylar Ittner il y a 1 an
Parent
révision
40bc592a8d
8 fichiers modifiés avec 250 ajouts et 46 suppressions
  1. 22
    1
      action.php
  2. 1
    0
      pages.php
  3. 0
    1
      pages/home.php
  4. 3
    37
      pages/pos.php
  5. 9
    0
      required.php
  6. 9
    0
      settings.template.php
  7. 91
    0
      static/js/bsalert.js
  8. 115
    7
      static/js/pos.js

+ 22
- 1
action.php Voir le fichier

@@ -7,7 +7,6 @@
/**
* Make things happen when buttons are pressed and forms submitted.
*/

require_once __DIR__ . "/required.php";

if ($VARS['action'] !== "signout") {
@@ -31,6 +30,28 @@ function returnToSender($msg, $arg = "") {
}

switch ($VARS['action']) {
case "itemsearch":
header("Content-Type: application/json");
if (!is_empty($VARS['q'])) {
$where["AND"]["OR"] = [
"name[~]" => $VARS['q'],
"code1[~]" => $VARS['q'],
"code2[~]" => $VARS['q']
];
} else {
exit(json_encode(["status" => "ERROR", "items" => false]));
}

$items = $binstack->select('items', [
'itemid (id)',
'name',
'code1',
'code2',
'cost',
'price'
], $where);
$items = (count($items) > 0 ? $items : false);
exit(json_encode(["status" => "OK", "items" => $items]));
case "signout":
session_destroy();
header('Location: index.php');

+ 1
- 0
pages.php Voir le fichier

@@ -16,6 +16,7 @@ define("PAGES", [
"navbar" => true,
"icon" => "far fa-money-bill-alt",
"scripts" => [
"static/js/bsalert.js",
"static/js/pos.js",
]
],

+ 0
- 1
pages/home.php Voir le fichier

@@ -3,4 +3,3 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
?>
<h1>Hello World</h1>

+ 3
- 37
pages/pos.php Voir le fichier

@@ -15,51 +15,17 @@
<div class="card-header p-1">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-barcode"></i></span>
<span class="input-group-text px-2"><i class="fas fa-barcode"></i></span>
</div>
<input type="text" class="form-control" id="barcode" placeholder="<?php lang("barcode"); ?>" />
<div class="input-group-append">
<button class="btn btn-link" type="button"><i class="fas fa-plus"></i></button>
<button class="btn btn-link" type="button" id="barcodebtn"><i class="fas fa-search"></i></button>
</div>
</div>
</div>
<div>
<div class="list-group" id="pos-lines-box">
<?php
for ($i = 0; $i < 5; $i++) {
?>
<div class="list-group-item">
<div class="d-flex w-100 justify-content-between mb-2">
<h5 class="item-name">
Cool Widget
</h5>
<h5>
<small class="item-code">659321</small>
<span class="badge badge-light">
$<span class="line-total">10.23</span>
</span>
</h5>
</div>
<div class="d-inline-flex">
<div class="input-group input-group-sm qty-control">
<div class="input-group-prepend">
<span class="input-group-text mx-0 px-0"><b>$</b></span>
</div>
<input type="number" class="form-control item-price" value="10.23"/>
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-times"></i></span>
<button class="btn btn-red qty-minus" type="button"><i class="fas fa-trash"></i></button>
</div>
<input type="number" class="form-control item-qty" value="1" />
<div class="input-group-append">
<button class="btn btn-light-green qty-plus" type="button"><i class="fas fa-plus"></i></button>
</div>
</div>
</div>
</div>
<?php
}
?>
<!-- Items go here -->
</div>
</div>
</div>

+ 9
- 0
required.php Voir le fichier

@@ -92,6 +92,7 @@ date_default_timezone_set(TIMEZONE);
use Medoo\Medoo;

$database;
$binstack;
try {
$database = new Medoo([
'database_type' => DB_TYPE,
@@ -101,6 +102,14 @@ try {
'password' => DB_PASS,
'charset' => DB_CHARSET
]);
$binstack = new Medoo([
'database_type' => BINSTACK_DB_TYPE,
'database_name' => BINSTACK_DB_NAME,
'server' => BINSTACK_DB_SERVER,
'username' => BINSTACK_DB_USER,
'password' => BINSTACK_DB_PASS,
'charset' => BINSTACK_DB_CHARSET
]);
} catch (Exception $ex) {
//header('HTTP/1.1 500 Internal Server Error');
sendError("Database error. Try again later. $ex");

+ 9
- 0
settings.template.php Voir le fichier

@@ -17,6 +17,15 @@ define("DB_USER", "nickelbox");
define("DB_PASS", "");
define("DB_CHARSET", "utf8");

// BinStack database connection settings
define("BINSTACK_DB_TYPE", "mysql");
define("BINSTACK_DB_NAME", "inventory");
define("BINSTACK_DB_SERVER", "localhost");
define("BINSTACK_DB_USER", "inventory");
define("BINSTACK_DB_PASS", "");
define("BINSTACK_DB_CHARSET", "utf8");


// Name of the app.
define("SITE_TITLE", "NickelBox");


+ 91
- 0
static/js/bsalert.js Voir le fichier

@@ -0,0 +1,91 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

function bsprompt(title, message, okbtn, cancelbtn, type, callback) {
var html = '<div class="modal fade" id="bsprompt">'
+ ' <div class="modal-dialog">'
+ ' <div class="modal-content">'
+ ' <div class="modal-header">'
+ ' <h5 class="modal-title" id="bsprompt-title"></h5>'
+ ' <button type="button" class="close" data-dismiss="modal" aria-label="Close">'
+ ' <span aria-hidden="true">&times;</span>'
+ ' </button>'
+ ' </div>'
+ ' <div class="modal-body" id="bsprompt-body">'
+ ' <div id="bsprompt-message"></div>'
+ ' <input id="bsprompt-input" class="form-control" />'
+ ' </div>'
+ ' <div class="modal-footer">'
+ ' <button type="button" class="btn btn-secondary" data-dismiss="modal" id="bsprompt-cancel">Cancel</button>'
+ ' <button type="button" class="btn btn-primary" id="bsprompt-ok">OK</button>'
+ ' </div>'
+ ' </div>'
+ ' </div>'
+ '</div>';
$("body").append(html);
$("#bsprompt-title").text(title);
$("#bsprompt-message").text(message);
$("#bsprompt-ok").text(okbtn);
$("#bsprompt-cancel").text(cancelbtn);
$("#bsprompt-input").attr("type", type);
$("#bsprompt-input").on("keypress", function (e) {
if (e.which === 13) {
callback($("#bsprompt-input").val());
$("#bsprompt").modal("hide");
}
});
$("#bsprompt-ok").on("click", function () {
callback($("#bsprompt-input").val());
$("#bsprompt").modal("hide");
});
$("#bsprompt").on("shown.bs.modal", function () {
$("#bsprompt-input").focus();
});
$("#bsprompt").on("hidden.bs.modal", function () {
$("#bsprompt").remove();
});
$("#bsprompt").modal("show");
}

function bschoices(title, message, options, cancelbtn, callback) {
var html = '<div class="modal fade" id="bschoices">'
+ ' <div class="modal-dialog">'
+ ' <div class="modal-content">'
+ ' <div class="modal-header">'
+ ' <h5 class="modal-title" id="bschoices-title"></h5>'
+ ' <button type="button" class="close" data-dismiss="modal" aria-label="Close">'
+ ' <span aria-hidden="true">&times;</span>'
+ ' </button>'
+ ' </div>'
+ ' <div class="modal-body" id="bschoices-body">'
+ ' <div id="bschoices-message" class="mb-2"></div>'
+ ' <div class="list-group" id="bschoices-list"></div>'
+ ' </div>'
+ ' <div class="modal-footer">'
+ ' <button type="button" class="btn btn-secondary" data-dismiss="modal" id="bschoices-cancel">Cancel</button>'
+ ' </div>'
+ ' </div>'
+ ' </div>'
+ '</div>';
$("body").append(html);
$("#bschoices-title").text(title);
$("#bschoices-message").text(message);
$("#bschoices-cancel").text(cancelbtn);
for (var i = 0; i < options.length; i++) {
var text = options[i]["text"];
var val = options[i]["val"];
$("#bschoices-list").append('<div class="list-group-item bschoices-option" data-value="' + val + '">' + text + '</div>');
}
$(".bschoices-option").css("cursor", "pointer");
$(".bschoices-option").on("click", function () {
callback($(this).data("value"));
$("#bschoices").modal("hide");
});
$("#bschoices").on("hidden.bs.modal", function () {
$("#bschoices").remove();
});
$("#bschoices").modal("show");
}

+ 115
- 7
static/js/pos.js Voir le fichier

@@ -9,13 +9,14 @@ function addItem(name, code, price) {
updateQty($(".list-group-item[data-code='" + code + "']").find(".qty-plus"), 1);
return;
}
price = (price * 1.0).toFixed(2);
$("#pos-lines-box").append('<div class="list-group-item" data-code="' + code + '">'
+ '<div class="d-flex w-100 justify-content-between mb-2">'
+ '<h5 class="item-name">'
+ name
+ '</h5>'
+ '<h5>'
+ '<small class="item-code">' + code + '</small>'
+ '<small class="item-code mr-1">' + code + '</small>'
+ '<span class="badge badge-light">'
+ '$<span class="line-total">'
+ price
@@ -24,16 +25,16 @@ function addItem(name, code, price) {
+ '</h5>'
+ '</div>'
+ '<div class="d-inline-flex">'
+ '<div class="input-group input-group-sm qty-control">'
+ '<div class="input-group qty-control">'
+ '<div class="input-group-prepend">'
+ '<span class="input-group-text mx-0 px-0"><b>$</b></span>'
+ '<span class="input-group-text pr-1"><b>$</b></span>'
+ '</div>'
+ '<input type="number" class="form-control item-price" value="' + price + '"/>'
+ '<input type="money" class="form-control item-price" value="' + price + '"/>'
+ '<div class="input-group-prepend">'
+ '<span class="input-group-text"><i class="fas fa-times"></i></span>'
+ '<span class="input-group-text px-2"><i class="fas fa-times"></i></span>'
+ '<button class="btn btn-red qty-minus" type="button"><i class="fas fa-trash"></i></button>'
+ '</div>'
+ '<input type="number" class="form-control item-qty" value="1" />'
+ '<input type="number" class="form-control item-qty px-2" value="1" />'
+ '<div class="input-group-append">'
+ '<button class="btn btn-light-green qty-plus" type="button"><i class="fas fa-plus"></i></button>'
+ '</div>'
@@ -42,7 +43,81 @@ function addItem(name, code, price) {
+ '</div>');
}

function findItem(q) {
function decodeThenAddItem(item) {
var code = item.code1;
console.log(code);
if (code == "" && item["code2"] != "") {
code = item["code2"];
} else if (code == "") {
code = "---";
}
var price = item['price'];
if (price == null || price == "" || price == 0) {
if (!$(".list-group-item[data-code='" + code + "']").length) {
bsprompt("Enter Price",
"No price set. Enter a price for this item:",
"Add Item",
"Cancel",
"number",
function (result) {
addItem(item['name'], code, result);
});
return;
}
}
addItem(item['name'], code, price);
$("#barcode").val("");
}
if (q == "") {
return;
}
$.get("action.php", {
action: "itemsearch",
q: q
}, function (data) {
if (data['items'].length == 1) {
decodeThenAddItem(data['items'][0]);
} else if (data['items'].length > 1) {
var options = [];
for (var i = 0; i < data['items'].length; i++) {
var text = data['items'][i]['name'];
if (data['items'][i]['price'] != null) {
text += " ($" + data['items'][i]['price'] + ")";
}
options.push({"text": text, "val": data['items'][i]['id']});
}
bschoices(
"Multiple Results",
"More than one item match the query. Pick the correct one:",
options,
"Cancel",
function (result) {
for (var i = 0; i < data['items'].length; i++) {
if (data['items'][i]['id'] == result) {
decodeThenAddItem(data['items'][i]);
break;
}
}
}
);
}
}).fail(function () {
alert("Error");
});
}

function removezero() {
$("#pos-lines-box .list-group-item").each(function () {
var qty = $(".item-qty", this).val() * 1.0;
if (qty == 0) {
$(this).remove();
}
});
}

function recalculate() {
removezero();
var total = 0.0;
$("#pos-lines-box .list-group-item").each(function () {
var each = $(".item-price", this).val() * 1.0;
@@ -50,6 +125,7 @@ function recalculate() {
var line = each * qty;
$(".line-total", this).text(line.toFixed(2));
$(".item-price", this).val(each.toFixed(2));
console.log(each.toFixed(2));
total += line;
});
$(".grand-total").text(total.toFixed(2));
@@ -83,4 +159,36 @@ $("#pos-lines-box").on("click", ".qty-plus", function () {

$("#pos-lines-box").on("change", ".item-qty,.item-price", function () {
recalculate();
});
});

$("#pos-lines-box").on("keypress", ".item-qty,.item-price", function (e) {
if (e.which === 13) {
recalculate();
}
});

$("#barcode").on('keypress', function (e) {
if (e.which === 13) {
findItem($("#barcode").val());
}
});

$("#barcodebtn").on("click", function () {
findItem($("#barcode").val());
});

$("body").on("keypress", "input[type=money],input[type=number]", function (e) {
//console.log(String.fromCharCode(e.which) + "|" + e.which);
var c = String.fromCharCode(e.which);
var k = e.which;
if (/[0-9]|[\.]/.test(c)) {
// Numbers and period
} else if (k == 0 || k == 8) {
// Delete, backspace, etc
} else {
e.preventDefault();
return false;
}
});

$("#barcode").focus();

Chargement…
Annuler
Enregistrer