pos/is4c-nf/lib/PrehLib.php
<?php
/*******************************************************************************
Copyright 2001, 2004 Wedge Community Co-op
This file is part of IT CORE.
IT CORE is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
IT CORE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
in the file license.txt along with IT CORE; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*********************************************************************************/
namespace COREPOS\pos\lib;
use COREPOS\pos\lib\Database;
use COREPOS\pos\lib\DiscountModule;
use COREPOS\pos\lib\DisplayLib;
use COREPOS\pos\lib\MiscLib;
use COREPOS\pos\lib\TransRecord;
use COREPOS\pos\lib\TotalActions\TotalAction;
use \CoreLocal;
use \DateTime;
use \DateInterval;
use \Exception;
/**
@class PrehLib
A horrible, horrible catch-all clutter of functions
*/
class PrehLib
{
static private function getTenderMods($right)
{
$ret = array('COREPOS\\pos\\lib\\Tenders\\TenderModule');
/**
Get a tender-specific module if
one has been configured
*/
$map = CoreLocal::get("TenderMap");
$dbc = Database::pDataConnect();
/**
Fetch module mapping from the database
if the schema supports it
16Mar2015
*/
if (CoreLocal::get('NoCompat') == 1) {
$tenderModel = new \COREPOS\pos\lib\models\op\TendersModel($dbc);
$map = $tenderModel->getMap();
} else {
$tenderTable = $dbc->tableDefinition('tenders');
if (isset($tenderTable['TenderModule'])) {
$tenderModel = new \COREPOS\pos\lib\models\op\TendersModel($dbc);
$map = $tenderModel->getMap();
}
}
if (is_array($map) && isset($map[$right])) {
$class = $map[$right];
if ($class != 'COREPOS\\pos\\lib\\Tenders\\TenderModule') {
$ret[] = $class;
}
}
return $ret;
}
/**
Add a tender to the transaction
@right tender amount in cents (100 = $1)
@strl tender code from tenders table
@return An array see Parser::default_json()
for format explanation.
This function will automatically end a transaction
if the amount due becomes <= zero.
*/
static public function tender($right, $strl)
{
$ret = array('main_frame'=>false,
'redraw_footer'=>false,
'target'=>'.baseHeight',
'output'=>"");
$strl = MiscLib::centStrToDouble($strl);
if (CoreLocal::get('RepeatAgain')) {
// the default tender prompt utilizes boxMsg2.php to
// repeat the previous input, plus amount, on confirmation
// the tender's preReqCheck methods will need to pretend
// this is the first input rather than a repeat
CoreLocal::set('msgrepeat', 0);
CoreLocal::set('RepeatAgain', false);
}
$tenderMods = self::getTenderMods($right);
$tenderObject = null;
foreach ($tenderMods as $class) {
if (!class_exists($class)) { // try namespaced version
$class = 'COREPOS\\pos\\lib\\Tenders\\' . $class;
}
if (!class_exists($class)) {
$ret['output'] = DisplayLib::boxMsg(
_('tender is misconfigured'),
_('Notify Administrator'),
false,
DisplayLib::standardClearButton()
);
return $ret;
}
$tenderObject = new $class($right, $strl);
/**
Do tender-specific error checking and prereqs
*/
$error = $tenderObject->ErrorCheck();
if ($error !== true) {
$ret['output'] = $error;
return $ret;
}
$prereq = $tenderObject->PreReqCheck();
if ($prereq !== true) {
$ret['main_frame'] = $prereq;
return $ret;
}
}
// add the tender record
$tenderObject->Add();
Database::getsubtotals();
// see if transaction has ended
if (CoreLocal::get("amtdue") <= 0.005 && $tenderObject->endsTransaction()) {
$ret = self::tenderEndsTransaction($tenderObject, $ret);
} else {
$ret = self::tenderContinuesTransaction($ret);
}
$ret['redraw_footer'] = true;
return $ret;
}
private static function tenderEndsTransaction($tenderObject, $ret)
{
CoreLocal::set("change",-1 * CoreLocal::get("amtdue"));
CoreLocal::set('strEntered', '');
CoreLocal::set('msgrepeat', 0);
$cashReturn = CoreLocal::get("change");
TransRecord::addchange($cashReturn, $tenderObject->ChangeType(), $tenderObject->ChangeMsg());
CoreLocal::set("End",1);
$ret['receipt'] = 'full';
$ret['output'] = DisplayLib::printReceiptFooter();
TransRecord::finalizeTransaction();
return $ret;
}
private static function tenderContinuesTransaction($ret)
{
CoreLocal::set("change",0);
CoreLocal::set("fntlflag",0);
Database::setglobalvalue("FntlFlag", 0);
$chk = self::ttl();
if ($chk === true) {
$ret['output'] = DisplayLib::lastpage();
} else {
$ret['main_frame'] = $chk;
}
return $ret;
}
static private function addRemoveDiscountViews()
{
$dbc = Database::tDataConnect();
if (CoreLocal::get("isMember") == 1 || CoreLocal::get("memberID") == CoreLocal::get("visitingMem")) {
$cols = Database::localMatchingColumns($dbc,"localtemptrans","memdiscountadd");
$dbc->query("INSERT INTO localtemptrans ({$cols}) SELECT {$cols} FROM memdiscountadd");
} else {
$cols = Database::localMatchingColumns($dbc,"localtemptrans","memdiscountremove");
$dbc->query("INSERT INTO localtemptrans ({$cols}) SELECT {$cols} FROM memdiscountremove");
}
if (CoreLocal::get("isStaff") != 0) {
$cols = Database::localMatchingColumns($dbc,"localtemptrans","staffdiscountadd");
$dbc->query("INSERT INTO localtemptrans ({$cols}) SELECT {$cols} FROM staffdiscountadd");
} else {
$cols = Database::localMatchingColumns($dbc,"localtemptrans","staffdiscountremove");
$dbc->query("INSERT INTO localtemptrans ({$cols}) SELECT {$cols} FROM staffdiscountremove");
}
}
static private function runTotalActions()
{
$ttlHooks = CoreLocal::get('TotalActions');
if (is_array($ttlHooks)) {
foreach($ttlHooks as $ttlClass) {
if ("$ttlClass" == "") {
continue;
}
$mod = TotalAction::factory($ttlClass);
$result = $mod->apply();
if ($result !== true && is_string($result)) {
return $result; // redirect URL
}
}
}
return true;
}
/**
Total the transaction
@return
True - total successfully
String - URL
If ttl() returns a string, go to that URL for
more information on the error or to resolve the
problem.
The most common error, by far, is no
member number in which case the return value
is the member-entry page.
*/
static public function ttl()
{
$ret = true;
if (CoreLocal::get("memberID") == "0") {
return MiscLib::baseURL()."gui-modules/memlist.php";
}
self::addRemoveDiscountViews();
CoreLocal::set("ttlflag",1);
Database::setglobalvalue("TTLFlag", 1);
// if total is called before records have been added to the transaction,
// Database::getsubtotals will zero out the discount
$savePD = CoreLocal::get('percentDiscount');
// Refresh totals after staff and member discounts.
Database::getsubtotals();
$ttlHooks = self::runTotalActions();
if ($ttlHooks !== true) {
// follow redirect
$ret = $ttlHooks;
}
// Refresh totals after total actions
Database::getsubtotals();
CoreLocal::set('percentDiscount', $savePD);
if (CoreLocal::get("percentDiscount") > 0) {
if (CoreLocal::get('member_subtotal') === 0 || CoreLocal::get('member_subtotal') === '0') {
// 5May14 Andy
// Why is this different trans_type & voided from
// the other Subtotal record generated farther down?
TransRecord::addRecord(array(
'description' => 'Subtotal',
'trans_type' => '0',
'trans_status' => 'D',
'unitPrice' => MiscLib::truncate2(CoreLocal::get('transDiscount') + CoreLocal::get('subtotal')),
'voided' => 7,
));
}
TransRecord::discountnotify(CoreLocal::get("percentDiscount"));
TransRecord::addRecord(array(
'description' => CoreLocal::get('percentDiscount') . '% Discount',
'trans_type' => 'C',
'trans_status' => 'D',
'unitPrice' => MiscLib::truncate2(-1 * CoreLocal::get('transDiscount')),
'voided' => 5,
));
}
$amtDue = str_replace(",", "", CoreLocal::get("amtdue"));
$memline = "";
if(CoreLocal::get("memberID") != CoreLocal::get("defaultNonMem")) {
$memline = " #" . CoreLocal::get("memberID");
}
// temporary fix Andy 13Feb13
// my cashiers don't like the behavior; not configurable yet
if (CoreLocal::get("store") == "wfc") $memline="";
TransRecord::addRecord(array(
'description' => 'Subtotal '
. MiscLib::truncate2(CoreLocal::get('subtotal'))
. ', Tax'
. MiscLib::truncate2(CoreLocal::get('taxTotal'))
. $memline,
'trans_type' => 'C',
'trans_status' => 'D',
'unitPrice' => $amtDue,
'voided' => 3,
));
if (CoreLocal::get("fntlflag") == 1) {
TransRecord::addRecord(array(
'description' => 'Foodstamps Eligible',
'trans_type' => '0',
'trans_status' => 'D',
'unitPrice' => MiscLib::truncate2(CoreLocal::get('fsEligible')),
'voided' => 7,
));
}
if (CoreLocal::get('lotterySpin') === false) {
if (function_exists('random_int')) { // php 7+
CoreLocal::set('lotterySpin', (random_int(0, PHP_INT_MAX-1)/PHP_INT_MAX));
} else {
CoreLocal::set('lotterySpin', lcg_value());
}
}
return $ret;
}
/**
Total the transaction, which the cashier thinks may be eligible for the
Ontario Meal Tax Rebate.
@return
True - total successfully
String - URL
If ttl() returns a string, go to that URL for
more information on the error or to resolve the
problem.
The most common error, by far, is no
member number in which case the return value
is the member-entry page.
The Ontario Meal Tax Rebate refunds the provincial part of the
Harmonized Sales Tax if the total of the transaction is not more
than a certain amount.
If the transaction qualifies,
change the tax status for each item at the higher rate to the lower rate.
Display a message that a change was made.
Otherwise display a message about that.
Total the transaction as usual.
*/
static public function omtr_ttl()
{
// Must have gotten member number before totaling.
if (CoreLocal::get("memberID") == "0") {
return MiscLib::baseURL()."gui-modules/memlist.php";
}
self::addRemoveDiscountViews();
CoreLocal::set("ttlflag",1);
Database::setglobalvalue("TTLFlag", 1);
// Refresh totals after staff and member discounts.
Database::getsubtotals();
// Is the before-tax total within range?
if (CoreLocal::get("runningTotal") <= 4.00 ) {
$totalBefore = CoreLocal::get("amtdue");
$ret = Database::changeLttTaxCode("HST","GST");
if ( $ret !== True ) {
TransRecord::addcomment("$ret");
} else {
Database::getsubtotals();
$saved = ($totalBefore - CoreLocal::get("amtdue"));
$comment = sprintf("OMTR OK. You saved: $%.2f", $saved);
TransRecord::addcomment("$comment");
}
}
else {
TransRecord::addcomment("Does NOT qualify for OMTR");
}
/* If member can do Store Charge, warn on certain conditions.
* Important preliminary is to refresh totals.
*/
\COREPOS\pos\lib\MemberLib::chargeOk();
if (CoreLocal::get("balance") < CoreLocal::get("memChargeTotal") && CoreLocal::get("memChargeTotal") > 0){
if (CoreLocal::get('msgrepeat') == 0){
CoreLocal::set("boxMsg",sprintf("<b>A/R Imbalance</b><br />
Total AR payments $%.2f exceeds AR balance %.2f<br />",
CoreLocal::get("memChargeTotal"),
CoreLocal::get("balance")));
CoreLocal::set('boxMsgButtons', array(
'Confirm [enter]' => '$(\'#reginput\').val(\'\');submitWrapper();',
'Cancel [clear]' => '$(\'#reginput\').val(\'CL\');submitWrapper();',
));
CoreLocal::set("strEntered","TL");
return MiscLib::baseURL()."gui-modules/boxMsg2.php?quiet=1";
}
}
// Display discount.
if (CoreLocal::get("percentDiscount") > 0) {
TransRecord::addRecord(array(
'description' => CoreLocal::get('percentDiscount') . '% Discount',
'trans_type' => 'C',
'trans_status' => 'D',
'unitPrice' => MiscLib::truncate2(-1 * CoreLocal::get('transDiscount')),
'voided' => 5,
));
}
$amtDue = str_replace(",", "", CoreLocal::get("amtdue"));
// Compose the member ID string for the description.
$memline = "";
if(CoreLocal::get("memberID") != CoreLocal::get("defaultNonMem")) {
$memline = " #" . CoreLocal::get("memberID");
}
// Put out the Subtotal line.
$peek = self::peekItem();
if (True || substr($peek,0,9) != "Subtotal "){
TransRecord::addRecord(array(
'description' => 'Subtotal '
. MiscLib::truncate2(CoreLocal::get('subtotal'))
. ', Tax'
. MiscLib::truncate2(CoreLocal::get('taxTotal'))
. $memline,
'trans_type' => 'C',
'trans_status' => 'D',
'unitPrice' => $amtDue,
'voided' => 3,
));
}
if (CoreLocal::get("fntlflag") == 1) {
TransRecord::addRecord(array(
'description' => 'Foodstamps Eligible',
'trans_type' => '0',
'trans_status' => 'D',
'unitPrice' => MiscLib::truncate2(CoreLocal::get('fsEligible')),
'voided' => 7,
));
}
return true;
// omtr_ttl
}
/**
See what the last item in the transaction is currently
@param $fullRecord [boolean] return full database record.
Default is false. Just returns description.
@return localtemptrans.description for the last item
or localtemptrans record for the last item
If no record exists, returns false
*/
static public function peekItem($fullRecord=false, $transID=false)
{
$dbc = Database::tDataConnect();
$query = "SELECT * FROM localtemptrans ";
if ($transID) {
$query .= ' WHERE trans_id=' . ((int)$transID);
}
$query .= " ORDER BY trans_id DESC";
$res = $dbc->query($query);
$row = $dbc->fetchRow($res);
if ($fullRecord) {
return $row ? $row : false;
}
return isset($row['description']) ? $row['description'] : false;
}
/**
Add foodstamp elgibile total record
*/
static public function fsEligible()
{
Database::getsubtotals();
CoreLocal::set("fntlflag",1);
Database::setglobalvalue("FntlFlag", 1);
if (CoreLocal::get("ttlflag") != 1) {
return self::ttl();
}
TransRecord::addRecord(array(
'description' => 'Foodstamps Eligible',
'trans_type' => '0',
'trans_status' => 'D',
'unitPrice' => MiscLib::truncate2(CoreLocal::get('fsEligible')),
'voided' => 7,
));
return true;
}
/**
Add a percent discount notification
@param $strl discount percentage
@param $json keyed array
@return An array see Parser::default_json()
@deprecated
Use discountnotify() instead. This just adds
hard-coded percentages and PLUs that likely
aren't applicable anywhere but the Wedge.
*/
static public function percentDiscount($strl,$json=array())
{
if ($strl == 10.01) {
$strl = 10;
}
if (!is_numeric($strl) || $strl > 100 || $strl < 0) {
$json['output'] = DisplayLib::boxMsg(
_("discount invalid"),
'',
false,
DisplayLib::standardClearButton()
);
} else {
$dbc = Database::tDataConnect();
if ($strl != 0) {
TransRecord::discountnotify($strl);
}
$dbc->query("update localtemptrans set percentDiscount = ".$strl);
CoreLocal::set('percentDiscount', $strl);
DiscountModule::transReset();
$chk = self::ttl();
if ($chk !== true) {
$json['main_frame'] = $chk;
}
$json['output'] = DisplayLib::lastpage();
}
return $json;
}
/**
Enforce age-based restrictions
@param $requiredAge [int] age in years
@param $ret [array] Parser-formatted return value
@return [array]
0 - boolean age-related approval required
1 - array Parser-formatted return value
*/
public static function ageCheck($requiredAge, $ret)
{
$myUrl = MiscLib::baseURL();
if (CoreLocal::get("cashierAge") < 18 && CoreLocal::get("cashierAgeOverride") != 1){
$ret['main_frame'] = $myUrl."gui-modules/adminlogin.php?class=COREPOS-pos-lib-adminlogin-AgeApproveAdminLogin";
return array(true, $ret);
}
$badEntry = 1;
if (CoreLocal::get("memAge")=="" || CoreLocal::get('memAge') == date('mdY') || CoreLocal::get('memAge') == date('Ymd')) {
$badEntry = 0;
CoreLocal::set("memAge",date('Ymd'));
if (CoreLocal::get('AgeMode') == 'mdY') {
CoreLocal::set("memAge",date('mdY'));
}
}
try {
$birthDateYMD = CoreLocal::get('memAge');
if (CoreLocal::get('AgeMode') == 'mdY') {
$birthDateYMD = substr(CoreLocal::get('memAge'), -4) . substr(CoreLocal::get('memAge'), 0, 4);
}
$ofAgeOnDay = new DateTime($birthDateYMD);
$ofAgeOnDay->add(new DateInterval("P{$requiredAge}Y"));
} catch (Exception $ex) {
$ofAgeOnDay = new DateTime(date('Y-m-d', strtotime('tomorrow')));
}
$today = new DateTime(date('Y-m-d'));
if ($ofAgeOnDay > $today && CoreLocal::get('memAge') !== '1') {
$ret['udpmsg'] = 'twoPairs';
$ret['main_frame'] = $myUrl.'gui-modules/requestInfo.php?class=COREPOS-pos-parser-parse-UPC&badEntry=' . $badEntry;
return array(true, $ret);
}
return array(false, $ret);
}
public static function applyToggles($tax, $foodstamp, $discount)
{
if (CoreLocal::get("toggletax") != 0) {
$tax = ($tax==0) ? 1 : 0;
CoreLocal::set("toggletax",0);
}
if (CoreLocal::get("togglefoodstamp") != 0){
CoreLocal::set("togglefoodstamp",0);
$foodstamp = ($foodstamp==0) ? 1 : 0;
}
if (CoreLocal::get("toggleDiscountable") == 1) {
CoreLocal::set("toggleDiscountable",0);
$discount = ($discount == 0) ? 1 : 0;
}
return array($tax, $foodstamp, $discount);
}
}