|
|
|
<?php
|
|
|
|
|
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make things happen when buttons are pressed and forms submitted.
|
|
|
|
*/
|
|
|
|
require_once __DIR__ . "/required.php";
|
|
|
|
require_once __DIR__ . "/lib/userinfo.php";
|
|
|
|
|
|
|
|
if ($VARS['action'] !== "signout") {
|
|
|
|
dieifnotloggedin();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirects back to the page ID in $_POST/$_GET['source'] with the given message ID.
|
|
|
|
* The message will be displayed by the app.
|
|
|
|
* @param string $msg message ID (see lang/messages.php)
|
|
|
|
* @param string $arg If set, replaces "{arg}" in the message string when displayed to the user.
|
|
|
|
*/
|
|
|
|
function returnToSender($msg, $arg = "") {
|
|
|
|
global $VARS;
|
|
|
|
if ($arg == "") {
|
|
|
|
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=" . $msg);
|
|
|
|
} else {
|
|
|
|
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=$arg");
|
|
|
|
}
|
|
|
|
die();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($VARS['action']) {
|
|
|
|
case "finish_transaction":
|
|
|
|
header("Content-Type: application/json");
|
|
|
|
$items = $VARS['items'];
|
|
|
|
$payments = $VARS['payments'];
|
|
|
|
$customer = $VARS['customer'];
|
|
|
|
$register = $VARS['register'];
|
|
|
|
$discountpercent = $VARS['discountpercent'];
|
|
|
|
|
|
|
|
if ($customer != "" && !$database->has('customers', ['customerid' => $customer])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("invalid customer", false)]));
|
|
|
|
// exit(json_encode(["status" => "ERROR", "message" => lang("", false)]));
|
|
|
|
}
|
|
|
|
if ($register != "" && !$database->has('registers', ['registerid' => $register])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("invalid register", false)]));
|
|
|
|
}
|
|
|
|
if ($register != "" && !$database->has('cash_drawer', ['AND' => ['registerid' => $register, 'close' => null]])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("cash not open", false)]));
|
|
|
|
}
|
|
|
|
|
|
|
|
$totalcharge = 0.00;
|
|
|
|
$totalpaid = 0.00;
|
|
|
|
foreach ($items as $i) {
|
|
|
|
$totalcharge += $i['each'] * $i['qty'];
|
|
|
|
if (!$binstack->has('items', ['itemid' => $i['id']])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("invalid item", false)]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($payments as $p) {
|
|
|
|
if (!$database->has('payment_types', ['typename' => $p['type']])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("invalid payment type", false)]));
|
|
|
|
}
|
|
|
|
$totalpaid += $p['amount'];
|
|
|
|
if ($p['type'] == "giftcard") {
|
|
|
|
if (!$database->has('certificates', ['AND' => ['amount[>=]' => $p['amount'], 'deleted[!]' => 1, 'certcode' => $p['code']]])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("invalid giftcard", false)]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_numeric($discountpercent) && $discountpercent > 0 && $discountpercent < 100) {
|
|
|
|
$discountpercent = $discountpercent * 1.0;
|
|
|
|
$totalcharge *= 1.0 - ($discountpercent / 100.0);
|
|
|
|
} else {
|
|
|
|
$discountpercent = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($totalcharge > $totalpaid) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "message" => lang("insufficient payment", false)]));
|
|
|
|
}
|
|
|
|
|
|
|
|
$cashid = null;
|
|
|
|
if ($register != "") {
|
|
|
|
$cashid = $database->get('cash_drawer', 'cashid', ['AND' => ['registerid' => $register, 'close' => null]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
$database->insert('transactions', [
|
|
|
|
'txdate' => date('Y-m-d H:i:s'),
|
|
|
|
'customerid' => ($customer != "" ? $customer : null),
|
|
|
|
'type' => 1,
|
|
|
|
'cashier' => $_SESSION['uid'],
|
|
|
|
'cashid' => $cashid,
|
|
|
|
'discountpercent' => $discountpercent
|
|
|
|
]);
|
|
|
|
$txid = $database->id();
|
|
|
|
|
|
|
|
foreach ($items as $i) {
|
|
|
|
$item = $binstack->get('items', ['name', 'qty'], ['itemid' => $i['id']]);
|
|
|
|
|
|
|
|
$database->insert('lines', [
|
|
|
|
'txid' => $txid,
|
|
|
|
'amount' => $i['each'],
|
|
|
|
'name' => $item['name'],
|
|
|
|
'itemid' => $i['id'],
|
|
|
|
'qty' => $i['qty']
|
|
|
|
]);
|
|
|
|
$binstack->update('items', [
|
|
|
|
'qty[-]' => $i['qty']
|
|
|
|
], [
|
|
|
|
'itemid' => $i['id']
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($payments as $p) {
|
|
|
|
$certid = null;
|
|
|
|
if ($p['type'] == "giftcard") {
|
|
|
|
$certid = $database->get('certificates', 'certid', ['certcode' => $p['code']]);
|
|
|
|
}
|
|
|
|
$type = $database->get('payment_types', 'typeid', ['typename' => $p['type']]);
|
|
|
|
$database->insert('payments', [
|
|
|
|
'amount' => $p['amount'],
|
|
|
|
'data' => '',
|
|
|
|
'type' => $type,
|
|
|
|
'txid' => $txid,
|
|
|
|
'certid' => $certid
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(json_encode(["status" => "OK", "txid" => $txid]));
|
|
|
|
|
|
|
|
break;
|
|
|
|
case "getreceipt":
|
|
|
|
header("Content-Type: text/html");
|
|
|
|
if (!$database->has('transactions', ['txid' => $VARS['txid']])) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "txid" => null]));
|
|
|
|
}
|
|
|
|
|
|
|
|
$tx = $database->get('transactions', ['txid', 'txdate', 'customerid', 'type', 'cashier', 'discountpercent'], ['txid' => $VARS['txid']]);
|
|
|
|
|
|
|
|
$txid = $tx['txid'];
|
|
|
|
$datetime = date(DATETIME_FORMAT, strtotime($tx['txdate']));
|
|
|
|
$type = $tx['type'];
|
|
|
|
$cashier = getUserByID($tx['cashier'])['name'];
|
|
|
|
$customerid = $tx['customerid'];
|
|
|
|
$customerline = "";
|
|
|
|
if (!is_null($customerid) && !empty($customerid)) {
|
|
|
|
$customerline = "<br />Customer: " . $database->get('customers', 'name', ['customerid' => $customerid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
$itemhtml = "";
|
|
|
|
$items = $database->select('lines', ['amount', 'name', 'itemid', 'qty'], ['txid' => $txid]);
|
|
|
|
$subtotal = 0.0;
|
|
|
|
$paid = 0.0;
|
|
|
|
foreach ($items as $i) {
|
|
|
|
$itemhtml .= "\n";
|
|
|
|
$itemhtml .= '<div class="flexrow">';
|
|
|
|
$itemhtml .= '<div>' . $i['name'] . '</div>';
|
|
|
|
$itemhtml .= '<div>$' . number_format($i['amount'], 2) . '</div>';
|
|
|
|
$itemhtml .= '<div>x' . (float) $i['qty'] . '</div>';
|
|
|
|
$itemhtml .= '<div>$' . number_format($i['qty'] * $i['amount'] * 1.0, 2) . '</div>';
|
|
|
|
$itemhtml .= '</div>';
|
|
|
|
$subtotal += $i['qty'] * $i['amount'] * 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$total = $subtotal * (1.0 - ((float) $tx['discountpercent'] / 100));
|
|
|
|
|
|
|
|
$paymenthtml = "";
|
|
|
|
$payments = $database->select('payments', [
|
|
|
|
'[>]payment_types' => ['type' => 'typeid']
|
|
|
|
], [
|
|
|
|
'amount', 'type', 'typename', 'text'
|
|
|
|
], [
|
|
|
|
'txid' => $txid
|
|
|
|
]);
|
|
|
|
foreach ($payments as $p) {
|
|
|
|
$paymenthtml .= "\n";
|
|
|
|
$paymenthtml .= '<div class="flexrow">';
|
|
|
|
$paymenthtml .= '<div>' . lang($p['text'], false) . '</div>';
|
|
|
|
$paymenthtml .= '<div>$' . number_format($p['amount'] * 1.0, 2) . '</div>';
|
|
|
|
$paymenthtml .= '</div>';
|
|
|
|
$paid += $p['amount'] * 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$change = $paid - $total;
|
|
|
|
if ($change <= 0) {
|
|
|
|
$change = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$subtotalstr = number_format($subtotal, 2);
|
|
|
|
$paidstr = number_format($paid, 2);
|
|
|
|
$changestr = number_format($change, 2);
|
|
|
|
$totalstr = $subtotalstr;
|
|
|
|
$discountstr = "";
|
|
|
|
if ($tx['discountpercent'] > 0) {
|
|
|
|
$discountstr = '<div class="flexrow"><span>Discount: </span><span>' . (float) $tx['discountpercent'] . '% off</span></div>';
|
|
|
|
$totalstr = number_format($total, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
$html = <<<END
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
<title>Tx #$txid</title>
|
|
|
|
<style nonce="$SECURE_NONCE">
|
|
|
|
.flexrow {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
margin: 0;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<hr />
|
|
|
|
Date: $datetime<br />
|
|
|
|
Tx. ID: $txid<br />
|
|
|
|
Cashier: $cashier
|
|
|
|
$customerline
|
|
|
|
<hr />
|
|
|
|
<div id="items">
|
|
|
|
$itemhtml
|
|
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
<div class="flexrow"><span>Subtotal: </span><span>$$subtotalstr</span></div>
|
|
|
|
$discountstr
|
|
|
|
<b class="flexrow"><span>Total: </span><span>$$totalstr</span></b>
|
|
|
|
<hr />
|
|
|
|
<div id="payments">
|
|
|
|
$paymenthtml
|
|
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
<b class="flexrow"><span>Paid: </span><span>$$paidstr</span></b>
|
|
|
|
<b class="flexrow"><span>Change: </span><span>$$changestr</span></b>
|
|
|
|
END;
|
|
|
|
exit($html);
|
|
|
|
break;
|
|
|
|
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 "customersearch":
|
|
|
|
header("Content-Type: application/json");
|
|
|
|
if (!is_empty($VARS['q'])) {
|
|
|
|
$where["AND"]["OR"] = [
|
|
|
|
"customerid" => $VARS['q'],
|
|
|
|
"name[~]" => $VARS['q'],
|
|
|
|
"email[~]" => $VARS['q'],
|
|
|
|
"phone[~]" => $VARS['q']
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
exit(json_encode(["status" => "ERROR", "customers" => false]));
|
|
|
|
}
|
|
|
|
|
|
|
|
$where["LIMIT"] = 10;
|
|
|
|
|
|
|
|
$customers = $database->select('customers', [
|
|
|
|
'customerid (id)',
|
|
|
|
'name',
|
|
|
|
'email',
|
|
|
|
'phone',
|
|
|
|
'address',
|
|
|
|
'notes'
|
|
|
|
], $where);
|
|
|
|
$customers = (count($customers) > 0 ? $customers : false);
|
|
|
|
exit(json_encode(["status" => "OK", "customers" => $customers]));
|
|
|
|
case "giftcard_lookup":
|
|
|
|
header("Content-Type: application/json");
|
|
|
|
$code = $VARS['code'];
|
|
|
|
if (empty($code)) {
|
|
|
|
exit(json_encode(["status" => "ERROR", "cards" => []]));
|
|
|
|
}
|
|
|
|
$cards = $database->select('certificates', ['certid (id)', 'certcode (code)', 'amount (balance)', 'start_amount (amount)'], ['certcode' => $code]);
|
|
|
|
exit(json_encode(["status" => "OK", "cards" => $cards]));
|
|
|
|
break;
|
|
|
|
case "session_keepalive":
|
|
|
|
header("Content-Type: application/json");
|
|
|
|
exit(json_encode(["status" => "OK"]));
|
|
|
|
case "signout":
|
|
|
|
session_destroy();
|
|
|
|
header('Location: index.php');
|
|
|
|
die("Logged out.");
|
|
|
|
}
|