action(function ($database) { global $VARS, $binstack, $Strings, $error, $oktx; if (empty($VARS['items'])) { $error = $Strings->get("no items", false); return false; } $items = $VARS['items']; $payments = $VARS['payments']; $customer = $VARS['customer']; $register = $VARS['register']; $discountpercent = $VARS['discountpercent']; $cashid = null; $editing = false; if (isset($VARS['txid']) && $database->has('transactions', ['txid' => $VARS['txid']])) { $editing = true; $txid = $VARS['txid']; $cashid = $database->get('transactions', 'cashid', ['txid' => $txid]); if (!$database->has('cash_drawer', ['AND' => ['cashid' => $cashid, 'close' => null]])) { $error = $Strings->get("cash already closed", false); return false; } // Nuke the payments to make room for their replacements // Delete payments $oldpayments = $database->select('payments', ['payid', 'amount', 'type', 'certid'], ['txid' => $txid]); foreach ($oldpayments as $p) { // Reset gift card balances if (!is_null($p['certid'])) { $database->update('certificates', ['amount[+]' => $p['amount']], ['certid' => $p['certid']]); } $database->delete('payments', ['payid' => $p['payid']]); } } if ($customer != "" && !$database->has('customers', ['customerid' => $customer])) { $error = $Strings->get("invalid customer", false); return false; } if ($register != "" && !$database->has('registers', ['registerid' => $register])) { $error = $Strings->get("invalid register", false); return false; } if ($register != "" && !$database->has('cash_drawer', ['AND' => ['registerid' => $register, 'close' => null]])) { $error = $Strings->get("cash not open", false); return false; } if ($register != "" && $editing === false) { $cashid = $database->get('cash_drawer', 'cashid', ['AND' => ['registerid' => $register, 'close' => null]]); } $totalcharge = 0.00; $totalpaid = 0.00; $change = 0.0; foreach ($items as $i) { $totalcharge += $i['each'] * $i['qty']; if (!$binstack->has('items', ['itemid' => $i['id']])) { $error = $Strings->get("invalid item", false); return false; } } foreach ($payments as $p) { if (!$database->has('payment_types', ['typename' => $p['type']])) { $error = $Strings->get("invalid payment type", false); return false; } $totalpaid += $p['amount']; if ($p['type'] == "giftcard") { if (!$database->has('certificates', ['AND' => ['amount[>=]' => $p['amount'], 'deleted[!]' => 1, 'certcode' => $p['code']]])) { $error = $Strings->get("invalid giftcard", false); return 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) { $error = $Strings->get("insufficient payment", false); return false; } if ($editing === true) { $database->update('transactions', [ 'txdate' => date('Y-m-d H:i:s'), 'customerid' => ($customer != "" ? $customer : null), 'type' => 1, 'cashier' => $_SESSION['uid'], 'cashid' => $cashid, 'discountpercent' => $discountpercent ], [ 'txid' => $txid ]); } else { $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(); } $olditems = $database->select('lines', ['itemid (id)', 'qty', 'lineid'], ['txid' => $txid]); 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 ]); } if ($totalcharge < $totalpaid) { $change = $totalpaid - $totalcharge; $database->insert('payments', [ 'amount' => $change * -1.0, 'data' => '', 'type' => 1, 'txid' => $txid, 'certid' => null ]); } foreach ($olditems as $i) { $database->delete('lines', ['lineid' => $i['lineid']]); $binstack->update('items', [ 'qty[+]' => $i['qty'] ], [ 'itemid' => $i['id'] ]); } $oktx = $txid; return true; }); if (!is_null($error)) { exit(json_encode(["status" => "ERROR", "message" => $error])); } else { exit(json_encode(["status" => "OK", "txid" => $oktx])); } break; case "finish_return": header("Content-Type: application/json"); $error = null; $oktx = null; $database->action(function ($database) { global $VARS, $binstack, $Strings, $error, $oktx; $items = $VARS['items']; $payments = $VARS['payments']; $customer = $VARS['customer']; $register = $VARS['register']; $cashid = null; if ($customer != "" && !$database->has('customers', ['customerid' => $customer])) { $error = $Strings->get("invalid customer", false); return false; } if ($register != "" && !$database->has('registers', ['registerid' => $register])) { $error = $Strings->get("invalid register", false); return false; } if ($register != "" && !$database->has('cash_drawer', ['AND' => ['registerid' => $register, 'close' => null]])) { $error = $Strings->get("cash not open", false); return false; } if ($register != "") { $cashid = $database->get('cash_drawer', 'cashid', ['AND' => ['registerid' => $register, 'close' => null]]); } $totaldue = 0.00; $totalrefund = 0.00; foreach ($items as $i) { $totaldue += $i['each'] * $i['qty']; if (!$binstack->has('items', ['itemid' => $i['id']])) { $error = $Strings->get("invalid item", false); return false; } } foreach ($payments as $p) { if (!$database->has('payment_types', ['typename' => $p['type']])) { $error = $Strings->get("invalid payment type", false); return false; } $totalrefund += $p['amount']; if ($p['type'] == "giftcard") { if (!$database->has('certificates', ['AND' => ['amount[>=]' => $p['amount'], 'deleted[!]' => 1, 'certcode' => $p['code']]])) { $error = $Strings->get("invalid giftcard", false); return false; } } } $database->insert('transactions', [ 'txdate' => date('Y-m-d H:i:s'), 'customerid' => ($customer != "" ? $customer : null), 'type' => 2, 'cashier' => $_SESSION['uid'], 'cashid' => $cashid, 'discountpercent' => 0.0 ]); $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'] * -1.0 ]); } foreach ($payments as $p) { $certid = null; if ($p['type'] == "giftcard") { $certid = $database->get('certificates', 'certid', ['certcode' => $p['code']]); $database->update('certificates', ['amount[+]' => $p['amount']], ['certid' => $certid]); } $type = $database->get('payment_types', 'typeid', ['typename' => $p['type']]); $database->insert('payments', [ 'amount' => $p['amount'] * -1.0, 'data' => '', 'type' => $type, 'txid' => $txid, 'certid' => $certid ]); } $oktx = $txid; return true; }); if (!is_null($error)) { exit(json_encode(["status" => "ERROR", "message" => $error])); } else { exit(json_encode(["status" => "OK", "txid" => $oktx])); } break; case "delete_transaction": header("Content-Type: application/json"); $error = null; if (isset($VARS['txid']) && $database->has('transactions', ['txid' => $VARS['txid']])) { $txid = $VARS['txid']; $cashid = $database->get('transactions', 'cashid', ['txid' => $txid]); if (!$database->has('cash_drawer', ['AND' => ['cashid' => $cashid, 'close' => null]])) { $error = $Strings->get("cash already closed", false); } $database->action(function ($database) { global $VARS, $binstack, $error, $txid; // Delete payments $payments = $database->select('payments', ['payid', 'amount', 'type', 'certid'], ['txid' => $txid]); foreach ($payments as $p) { // Reset gift card balances if (!is_null($p['certid'])) { $database->update('certificates', ['amount[+]' => $p['amount']], ['certid' => $p['certid']]); } $database->delete('payments', ['payid' => $p['payid']]); } // Delete items/lines $items = $database->select('lines', ['itemid (id)', 'qty', 'lineid'], ['txid' => $txid]); foreach ($items as $i) { $database->delete('lines', ['lineid' => $i['lineid']]); $binstack->update('items', [ 'qty[+]' => $i['qty'] ], [ 'itemid' => $i['id'] ]); } // Delete transaction $database->delete('transactions', ['txid' => $txid, 'LIMIT' => 1]); }); } else { $error = $Strings->get("invalid parameters", false); } if (!is_null($error)) { exit(json_encode(["status" => "ERROR", "message" => $error])); } else { exit(json_encode(["status" => "OK"])); } break; case "getreceipt": $format = "html"; $width = 48; if (isset($VARS['width']) && preg_match("/[0-9]+/", $VARS['width']) && (int) $VARS['width'] > 0) { $width = (int) $VARS['width']; } if (isset($VARS['format'])) { $format = $VARS['format']; } if (!$database->has('transactions', ['txid' => $VARS['txid']])) { header("Content-Type: application/json"); exit(json_encode(["status" => "ERROR", "txid" => null])); } $receipt = GenerateReceipt::getReceipt(GenerateReceipt::RECEIPT_TYPE_TRANSACTION, $VARS['txid']); exit(GenerateReceipt::outputReceipt($receipt, $format, $width, "Tx. #" . $VARS['txid'])); break; case "transactionsearch": header("Content-Type: application/json"); $where = []; if (!empty($VARS['q'])) { $where["AND"]["OR"] = [ "txid" => $VARS['q'], "name[~]" => $VARS['q'], "email[~]" => $VARS['q'], "phone[~]" => $VARS['q'] ]; } $start = date('Y-m-d H:i:s', 946684800); // Jan 1 2000 $end = date('Y-m-d H:i:s'); if (!empty($VARS['start']) && strtotime($VARS['start']) !== FALSE) { $start = date('Y-m-d H:i:s', strtotime($VARS['start'])); } if (!empty($VARS['end']) && strtotime($VARS['end']) !== FALSE) { $end = date('Y-m-d H:i:s', strtotime($VARS['end'])); } $where["AND"]['txdate[>=]'] = $start; $where["AND"]['txdate[<=]'] = $end; $where["LIMIT"] = 50; $transactions = $database->select('transactions', [ '[>]customers' => 'customerid', '[>]cash_drawer' => 'cashid', '[>]registers' => ['cash_drawer.registerid' => 'registerid'], ], [ 'txid', 'txdate', 'type', 'cashier (cashierid)', 'transactions.cashid', 'cash_drawer.registerid', 'registers.registername', 'cash_drawer.open', 'cash_drawer.close', 'customerid', 'customer' => [ 'name', 'email', 'phone', 'address' ]], $where); for ($i = 0; $i < count($transactions); $i++) { if (is_null($transactions[$i]['close']) && !is_null($transactions[$i]['open'])) { $transactions[$i]['editable'] = true; } else { $transactions[$i]['editable'] = false; } if (!is_null($transactions[$i]['cashierid'])) { $cashier = new User($transactions[$i]['cashierid']); $transactions[$i]['cashier'] = [ "name" => $cashier->getName(), "username" => $cashier->getUsername() ]; } } $transactions = (count($transactions) > 0 ? $transactions : false); exit(json_encode(["status" => "OK", "transactions" => $transactions])); case "itemsearch": header("Content-Type: application/json"); if (!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); if (!empty($VARS['customer']) && $database->has('customers', ['customerid' => $VARS['customer']])) { for ($n = 0; $n < count($items); $n++) { $i = $items[$n]; if ($database->has('customer_pricing', ['AND' => ['itemid' => $i['id'], 'customerid' => $VARS['customer']]])) { $items[$n]['price'] = $database->get('customer_pricing', 'price', ['AND' => ['itemid' => $i['id'], 'customerid' => $VARS['customer']]]); } } } $items = (count($items) > 0 ? $items : false); exit(json_encode(["status" => "OK", "items" => $items])); case "getgriditems": header("Content-Type: application/json"); $items = $binstack->select('items', [ 'itemid (id)', 'name', 'price', 'code1', 'code2' ], [ 'AND' => ['price[!]' => null, 'price[!]' => 0] ]); if (!empty($VARS['customer']) && $database->has('customers', ['customerid' => $VARS['customer']])) { for ($n = 0; $n < count($items); $n++) { $i = $items[$n]; if ($database->has('customer_pricing', ['AND' => ['itemid' => $i['id'], 'customerid' => $VARS['customer']]])) { $items[$n]['price'] = $database->get('customer_pricing', 'price', ['AND' => ['itemid' => $i['id'], 'customerid' => $VARS['customer']]]); } } } for ($n = 0; $n < count($items); $n++) { if ($items[$n]['code1'] != "") { $items[$n]['code'] = $items[$n]["code1"]; } else if ($items[$n]['code1'] == "" && $items[$n]['code1'] != "") { $items[$n]['code'] = $items[$n]["code2"]; } else if (code == "") { $items[$n]['code'] = "---"; } } $items = (count($items) > 0 ? $items : false); exit(json_encode(["status" => "OK", "items" => $items])); case "customersearch": header("Content-Type: application/json"); if (!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 "editcustomer": $insert = true; if (empty($VARS['id'])) { $insert = true; } else { if ($database->has('customers', ['customerid' => $VARS['id']])) { $insert = false; } else { returnToSender("invalid_customerid"); } } if (empty($VARS['name'])) { returnToSender('invalid_parameters'); } $data = [ 'name' => $VARS['name'], 'email' => $VARS['email'], 'phone' => $VARS['phone'], 'address' => $VARS['address'], 'notes' => $VARS['notes'] ]; $customerid = null; if ($insert) { $database->insert('customers', $data); $customerid = $database->id(); } else { $database->update('customers', $data, ['customerid' => $VARS['id']]); $customerid = $VARS['id']; } if (!is_null($customerid)) { $custprices = $VARS['pricing']; $newcustprices = []; $oldcustprices = $database->select('customer_pricing', ['itemid (item)', 'price'], ['customerid' => $customerid]); foreach ($custprices as $cp) { if (!$binstack->has('items', ['itemid' => $cp['item']])) { continue; } if (!is_numeric($cp['price'])) { continue; } $newcustprices[] = $cp; $oldcustprices = array_filter($oldcustprices, function ($var) { if ($cp['item'] == $var['item']) { return false; } return true; }); } foreach ($oldcustprices as $cp) { $database->delete('customer_pricing', ['AND' => ['itemid' => $cp['item'], 'customerid' => $customerid]]); } foreach ($newcustprices as $cp) { if ($database->has('customer_pricing', ['AND' => ['itemid' => $cp['item'], 'customerid' => $customerid]])) { $database->update('customer_pricing', ['price' => $cp['price']], ['AND' => ['itemid' => $cp['item'], 'customerid' => $customerid]]); } else { $database->insert('customer_pricing', ['price' => $cp['price'], 'itemid' => $cp['item'], 'customerid' => $customerid]); } } } returnToSender("customer_saved"); case "set_register": $regid = $VARS['register']; if (!$database->has('registers', ['registerid' => $regid])) { returnToSender("invalid_parameters"); } if (!$database->has('cash_drawer', ['AND' => ['registerid' => $regid, 'close' => null]])) { returnToSender("cash_not_open"); } $cashid = $database->get('cash_drawer', 'cashid', ['AND' => ['registerid' => $regid, 'close' => null]]); $_SESSION['register'] = (int) $regid; returnToSender("register_set"); break; case "opencash": $regid = $VARS['register']; $start = $VARS['startamount']; if (!$database->has('registers', ['registerid' => $regid])) { returnToSender("invalid_parameters"); } if ($database->has('cash_drawer', ['AND' => ['registerid' => $regid, 'close' => null]])) { returnToSender("cash_already_open"); } if (!is_numeric($start) || (float) $start < 0) { $start = 0.0; } $database->insert('cash_drawer', [ 'registerid' => $regid, 'open' => date('Y-m-d H:i:s'), 'close' => null, 'start_amount' => $start, 'end_amount' => null ]); returnToSender("cash_opened"); break; case "closecash": $regid = $VARS['register']; if (!$database->has('registers', ['registerid' => $regid])) { returnToSender("invalid_parameters"); } if (!$database->has('cash_drawer', ['AND' => ['registerid' => $regid, 'close' => null]])) { returnToSender("cash_not_open"); } $cash = $database->get('cash_drawer', ['cashid', 'start_amount'], ['AND' => ['registerid' => $regid, 'close' => null]]); $balance = (float) $cash['start_amount']; $rows = $database->select("payments", [ "[>]transactions" => ['txid' => 'txid'] ], 'amount', [ 'AND' => [ 'transactions.cashid' => $cash['cashid'], 'payments.type' => 1 ] ]); foreach ($rows as $row) { $balance += $row; } $database->update('cash_drawer', [ 'close' => date('Y-m-d H:i:s'), 'end_amount' => $balance ], [ 'cashid' => $cash['cashid'] ]); returnToSender("cash_closed"); break; case "editregister": $insert = true; if (empty($VARS['id'])) { $insert = true; } else { if ($database->has('registers', ['registerid' => $VARS['id']])) { $insert = false; } else { returnToSender("invalid_parameters"); } } if (empty($VARS['name'])) { returnToSender('invalid_parameters'); } if ($database->has('registers', ['AND' => ['registerid[!]' => $VARS['id'], 'registername' => $VARS['name']]])) { returnToSender("register_name_taken"); } $data = [ 'registername' => $VARS['name'] ]; if ($insert) { $database->insert('registers', $data); } else { $database->update('registers', $data, ['registerid' => $VARS['id']]); } returnToSender("register_saved"); case "xreport": $format = "html"; $width = 64; if (isset($VARS['width']) && preg_match("/[0-9]+/", $VARS['width']) && (int) $VARS['width'] > 0) { $width = (int) $VARS['width']; } if (isset($VARS['format'])) { $format = $VARS['format']; } if (!$database->has('cash_drawer', ['AND' => ['registerid' => $VARS['register'], 'open[!]' => null, 'close' => null]])) { header("Content-Type: application/json"); exit(json_encode(["status" => "ERROR"])); } $receipt = GenerateReceipt::getReceipt(GenerateReceipt::RECEIPT_TYPE_X, $VARS['register']); exit(GenerateReceipt::outputReceipt($receipt, $format, $width, "X Report")); break; case "zreport": $format = "html"; $width = 64; if (isset($VARS['width']) && preg_match("/[0-9]+/", $VARS['width']) && (int) $VARS['width'] > 0) { $width = (int) $VARS['width']; } if (isset($VARS['format'])) { $format = $VARS['format']; } if (!$database->has('cash_drawer', ['AND' => ['open[!]' => null, 'close[!]' => null, 'cashid' => $VARS['cash']]])) { header("Content-Type: application/json"); exit(json_encode(["status" => "ERROR"])); } $receipt = GenerateReceipt::getReceipt(GenerateReceipt::RECEIPT_TYPE_Z, $VARS['cash']); exit(GenerateReceipt::outputReceipt($receipt, $format, $width, "Z Report")); break; case "editcertificate": $insert = true; $code = $VARS['code']; $amount = $VARS['balance']; if (empty($VARS['id'])) { $insert = true; } else { if ($database->has('certificates', ['certid' => $VARS['id']])) { $insert = false; } else { returnToSender("invalid_parameters"); } } if ($insert && (empty($code) || $database->has('certificates', ['certcode' => $code]))) { do { $code = random_int(100000000000, 999999999999); } while ($database->has('certificates', ['certcode' => $code])); } if (!is_numeric($amount)) { returnToSender("invalid_parameters"); } if ($insert) { $database->insert('certificates', [ 'certcode' => $code, 'amount' => $amount, 'start_amount' => $amount, 'issued' => date('Y-m-d H:i:s'), 'deleted' => 0]); returnToSender("card_x_added", $code); } else { $database->update('certificates', [ 'certcode' => $code, 'amount' => $amount ], [ 'certid' => $VARS['id'] ]); returnToSender("card_x_saved", $code); } break; case "session_keepalive": header("Content-Type: application/json"); exit(json_encode(["status" => "OK"])); case "signout": session_destroy(); header('Location: index.php?logout=1'); die("Logged out."); }