pos/is4c-nf/lib/ReceiptLib.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\Bitmap;
use COREPOS\pos\lib\Database;
use COREPOS\pos\lib\MiscLib;
use COREPOS\pos\lib\PrintHandlers\PrintHandler;
use COREPOS\pos\lib\PrintHandlers\ESCNetRawHandler;
use COREPOS\pos\lib\ReceiptBuilding\Messages\StoreChargeMessage;
use \CoreLocal;
/**
@class ReceiptLib
Receipt functions
*/
class ReceiptLib
{
static private $PRINT;
static private $EMAIL = 'COREPOS\\pos\\lib\\PrintHandlers\EmailPrintHandler';
static private $HTML = 'COREPOS\\pos\\lib\\PrintHandlers\HtmlEmailPrintHandler';
// --------------------------------------------------------------
static public function build_time($timestamp) {
return date('m/d/y h:i A', $timestamp);
}
// --------------------------------------------------------------
static public function centerString($text) {
return self::center($text, 59);
}
// --------------------------------------------------------------
static public function writeLine($text)
{
if (CoreLocal::get("print") != 0) {
$printerPort = CoreLocal::get('printerPort');
if (substr($printerPort, 0, 6) == "tcp://") {
$net = new ESCNetRawHandler();
$net->setTarget(substr($printerPort, 6));
$net->writeLine($text);
} else {
/* check fails on LTP1: in PHP4
suppress open errors and check result
instead
*/
$fptr = fopen(CoreLocal::get("printerPort"), "w");
fwrite($fptr, $text);
fclose($fptr);
}
}
}
static public function center($text, $linewidth) {
$blank = str_repeat(" ", 59);
$text = trim($text);
$lead = (int) (($linewidth - strlen($text)) / 2);
$newline = substr($blank, 0, $lead).$text;
return $newline;
}
// -------------------------------------------------------------
// -------------------------------------------------------------
static public function printReceiptHeader($dateTimeStamp, $ref)
{
self::initDriver();
$receipt = self::$PRINT->TextStyle(True);
$imgCache = CoreLocal::get('ImageCache');
if (!is_array($imgCache)) $imgCache = array();
for ($i=1; $i <= CoreLocal::get("receiptHeaderCount"); $i++){
/**
If the receipt header line includes non-printable characters,
send it to the receipt printer exactly as-is.
If the receipt header line is "nv" and number(s), print the
corresponding image # from the printer's nonvolatile RAM.
If the receipt header line is a .bmp file (and it exists),
print it on the receipt. Otherwise just print the line of
text centered.
*/
$headerLine = CoreLocal::get("receiptHeader".$i);
$graphicsPath = __DIR__ . '/../graphics';
if (!ctype_print($headerLine)) {
$receipt .= self::$PRINT->rawEscCommand($headerLine) . "\n";
} elseif (preg_match('/nv(\d+),(\d+)/i', $headerLine, $match)) {
$receipt .= self::$PRINT->renderBitmapFromRam(array((int)$match[1], (int)$match[2]));
} elseif (preg_match('/nv(\d{1,3})/i', $headerLine, $match)) {
$receipt .= self::$PRINT->renderBitmapFromRam((int)$match[1]);
} elseif (substr($headerLine,-4) == ".bmp" && file_exists($graphicsPath.'/'.$headerLine)){
// save image bytes in cache so they're not recalculated
// on every receipt
$imgFile = $graphicsPath.'/'.$headerLine;
if (isset($imgCache[basename($imgFile)]) && !empty($imgCache[basename($imgFile)])
&& get_class(self::$PRINT)!=self::$EMAIL
&& get_class(self::$PRINT)!=self::$HTML
){
$receipt .= $imgCache[basename($imgFile)]."\n";
} else {
$img = self::$PRINT->RenderBitmapFromFile($imgFile);
$receipt .= $img."\n";
$imgCache[basename($imgFile)] = $img;
CoreLocal::set('ImageCache',$imgCache);
$receipt .= "\n";
}
} else {
/** put first header line in larger font **/
if ($i == 1) {
$receipt .= self::$PRINT->TextStyle(true, false, true);
$receipt .= self::$PRINT->centerString(CoreLocal::get("receiptHeader$i"));
$receipt .= self::$PRINT->TextStyle(true);
} else {
$receipt .= self::$PRINT->centerString(CoreLocal::get("receiptHeader$i"), false);
}
$receipt .= "\n";
}
}
$receipt .= "\n\n";
$time = self::build_time($dateTimeStamp);
$time = str_replace(" "," ",$time);
list($emp, $reg, $trans) = self::parseRef($ref);
$ref = $emp . '-' . $reg . '-' . $trans;
$spaces = 55 - strlen($time) - strlen($ref);
$receipt .= $time.str_repeat(' ',$spaces).$ref."\n";
return $receipt;
}
/**
Get a signature slip for use with a charge account
@param $dateTimeStamp [string] representing date and time
@param $ref [string] transaction identifer
@param $program [string, optional] identifier for different
types of charge accounts that require different text
@return [string] receipt text
*/
static public function printChargeFooterStore($dateTimeStamp, $ref, $program="charge")
{
$chgName = \COREPOS\pos\lib\MemberLib::getChgName(); // added by apbw 2/14/05 SCR
$date = self::build_time($dateTimeStamp);
/* Where should the label values come from, be entered?
20Mar15 Eric Lee. Andy's comment was about Coop Cred which
is now implemented as he describes.
24Apr14 Andy
Implementing these as ReceiptMessage subclasses might work
better. Plugins could provide their own ReceiptMessage subclass
with the proper labels (or config settings for the labels)
*/
$labels = array();
$labels['charge'] = array(
_("CUSTOMER CHARGE ACCOUNT\n"),
_("Charge Amount:"),
_("I AGREE TO PAY THE ABOVE AMOUNT\n"),
_("TO MY CHARGE ACCOUNT\n"),
);
$labels['debit'] = array(
_("CUSTOMER DEBIT ACCOUNT\n"),
_("Debit Amount:"),
_("I ACKNOWLEDGE THE ABOVE DEBIT\n"),
_("TO MY DEBIT ACCOUNT\n"),
);
/* Could append labels from other modules
foreach (CoreLocal::get('plugins') as $plugin)
if (isset($plugin['printChargeFooterCustLabels'])) {
$labels[]=$plugin['printChargeFooterCustLabels']
}
*/
$receipt = "\n\n\n\n\n\n\n"
.chr(27).chr(105)
.chr(27).chr(33).chr(5) // apbw 3/18/05
."\n".self::centerString(CoreLocal::get("chargeSlip2"))."\n"
.self::centerString("................................................")."\n"
.self::centerString(CoreLocal::get("chargeSlip1"))."\n\n"
. $labels["$program"][0]
._("Name: ").trim($chgName)."\n" // changed by apbw 2/14/05 SCR
._("Member Number: ").trim(CoreLocal::get("memberID"))."\n"
._("Date: ").$date."\n"
._("REFERENCE #: ").$ref."\n"
.$labels["$program"][1] . " $".number_format(-1 * CoreLocal::get("chargeTotal"), 2)."\n"
. $labels["$program"][2]
. $labels["$program"][3]
._("Purchaser Sign Below\n\n\n")
."X____________________________________________\n"
.CoreLocal::get("fname")." ".CoreLocal::get("lname")."\n\n"
.self::centerString(".................................................")."\n\n";
return self::chargeBalance($receipt, $program, $ref);
}
static public function printCabCoupon($dateTimeStamp, $ref)
{
$receipt = "\n";
$receipt .= self::biggerFont(self::centerBig("WHOLE FOODS COMMUNITY CO-OP"))."\n\n";
$receipt .= self::centerString("(218) 728-0884")."\n";
$receipt .= self::centerString("MEMBER OWNED SINCE 1970")."\n";
$receipt .= self::centerString(self::build_time($dateTimeStamp))."\n";
$receipt .= self::centerString('Effective this date ONLY')."\n";
$parts = explode("-",$ref);
$receipt .= self::centerString("Cashier: $parts[0]")."\n";
$receipt .= self::centerString("Transaction: $ref")."\n";
$receipt .= "\n";
$receipt .= "Your net purchase today of at least $30.00"."\n";
$receipt .= "qualifies you for a WFC CAB COUPON"."\n";
$receipt .= "in the amount of $3.00";
$receipt .= " with\n\n";
$receipt .= "YELLOW CAB OF DULUTH (727-1515)"."\n";
$receipt .= "from WFC toward the destination of\n";
$receipt .= "your choice TODAY"."\n\n";
$receipt .= ""
."This coupon is not transferable.\n"
."One coupon/day/customer.\n"
."Any amount of fare UNDER the value of this coupon\n"
."is the property of the cab company.\n"
."Any amount of fare OVER the value of this coupon\n"
."is your responsibility.\n"
."Tips are NOT covered by this coupon.\n"
."Acceptance of this coupon by the cab driver is\n"
."subject to the terms and conditions noted above.\n";
return $receipt;
}
static public function printGiftReceipt($dateTimeStamp, $ref)
{
$receipt = "\n";
$reciept .= self::printReceiptHeader($dateTimeStamp, $ref);
$receipt .= "\n";
$receipt .= self::biggerFont(self::centerBig("GIFT RECEIPT"))."\n\n";
$receipt .= "\n";
return $receipt;
}
/***** jqh 09/29/05 functions added for new receipt *****/
static public function biggerFont($str) {
$receipt=chr(29).chr(33).chr(17);
$receipt.=$str;
$receipt.=chr(29).chr(33).chr(00);
return $receipt;
}
static public function centerBig($text) {
$blank = str_repeat(" ", 30);
$text = trim($text);
$lead = (int) ((30 - strlen($text)) / 2);
$newline = substr($blank, 0, $lead).$text;
return $newline;
}
/***** jqh end change *****/
/***** CvR 06/28/06 calculate current balance for receipt ****/
static public function chargeBalance($receipt, $program="charge", $transNum='')
{
\COREPOS\pos\lib\MemberLib::chargeOk();
$labels = array();
$labels['charge'] = array(_("Current IOU Balance:") , 1);
$labels['debit'] = array(_("Debit available:"), -1);
if (CoreLocal::get('InvertAR')) {
$labels['charge'][1] = -1;
}
$dbc = Database::tDataConnect();
list($emp, $reg, $trans) = self::parseRef($transNum);
$arDepts = MiscLib::getNumbers(CoreLocal::get('ArDepartments'));
$checkQ = "SELECT trans_id
FROM localtranstoday
WHERE
emp_no=" . ((int)$emp) . "
AND register_no=" . ((int)$reg) . "
AND trans_no=" . ((int)$trans);
if (count($arDepts) == 0) {
$checkQ .= " AND trans_subtype='MI'";
} else {
$checkQ .= " AND (trans_subtype='MI' OR department IN (";
foreach ($arDepts as $arDept) {
$checkQ .= $arDept . ',';
}
$checkQ = substr($checkQ, 0, strlen($checkQ)-1) . '))';
}
$checkR = $dbc->query($checkQ);
$numRows = $dbc->numRows($checkR);
$currActivity = is_numeric(CoreLocal::get("memChargeTotal")) ? CoreLocal::get("memChargeTotal") : 0;
$currBalance = is_numeric(CoreLocal::get("balance")) ? CoreLocal::get("balance") : 0;
$currBalance -= $currActivity;
if (($numRows > 0 || $currBalance != 0) && CoreLocal::get("memberID") != CoreLocal::get('defaultNonMem')) {
$chargeString = $labels["$program"][0] .
" $".sprintf("%.2f",($labels["$program"][1] * $currBalance));
$receipt = $receipt."\n\n".self::biggerFont(self::centerBig($chargeString))."\n";
}
return $receipt;
}
static public function normalFont() {
return chr(27).chr(33).chr(5);
}
static public function boldFont() {
return chr(27).chr(33).chr(9);
}
static private function initDriver()
{
if (!is_object(self::$PRINT)) {
self::$PRINT= PrintHandler::factory(CoreLocal::get('ReceiptDriver'));
}
}
static public function bold()
{
self::initDriver();
return self::$PRINT->TextStyle(true, true);
}
static public function unbold()
{
self::initDriver();
return self::$PRINT->TextStyle(true, false);
}
static private function lookupLocal($ref)
{
$dbc = Database::tDataConnect();
list($empNo, $laneNo, $transNo) = self::parseRef($ref);
$lookup = sprintf("SELECT
SUM(CASE WHEN p.local=1 THEN l.total ELSE 0 END) as localTTL,
SUM(CASE WHEN l.trans_type IN ('I','D') then l.total ELSE 0 END) as itemTTL
FROM localtranstoday AS l LEFT JOIN ".
CoreLocal::get('pDatabase').$dbc->sep()."products AS p
ON l.upc=p.upc
WHERE l.trans_type IN ('I','D')
AND emp_no=%d AND register_no=%d AND trans_no=%d",
$empNo, $laneNo, $transNo);
$lookup = $dbc->query($lookup);
$ret = array('localTTL' => 0, 'itemTTL' => 0);
if ($dbc->numRows($lookup) > 0) {
$ret = $dbc->fetchRow($lookup);
}
return $ret;
}
static public function localTTL($ref)
{
$row = self::lookupLocal($ref);
if ($row['localTTL'] == 0)
return '';
$str = sprintf(_("LOCAL PURCHASES = \$%.2f"), $row['localTTL']);
return $str."\n";
}
static public function graphedLocalTTL($ref)
{
$row = self::lookupLocal($ref);
if ($row['localTTL'] == 0)
return '';
$percent = ((float)$row['localTTL']) / ((float)$row['itemTTL']);
$str = sprintf(_('LOCAL PURCHASES = $%.2f (%.2f%%)'),
$row['localTTL'], 100*$percent);
$str .= "\n";
$str .= self::$PRINT->RenderBitmap(Bitmap::barGraph($percent), 'L');
return $str."\n";
}
static private function getFetch()
{
$FETCH = CoreLocal::get("RBFetchData");
if ($FETCH == 'DefaultReceiptDataFetch') {
$FETCH = 'COREPOS\\pos\\lib\\ReceiptBuilding\\DataFetch\\' . $FETCH;
}
return $FETCH == '' ? 'COREPOS\\pos\\lib\\ReceiptBuilding\\DataFetch\\DefaultReceiptDataFetch' : $FETCH;
}
static private function getFilter()
{
$mod = CoreLocal::get("RBFilter");
if ($mod == 'DefaultReceiptFilter' || $mod == 'InOrderReceiptFilter') {
$mod = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Filter\\' . $mod;
}
return $mod == '' ? 'COREPOS\\pos\\lib\\ReceiptBuilding\\Filter\\DefaultReceiptFilter' : $mod;
}
static private $sorts = array(
'DefaultReceiptSort',
'DiscountFirstReceiptSort',
'DoubleSubtotalReceiptSort',
'GroupSavingsSort',
'InOrderReceiptSort',
);
static private function getSort()
{
$mod = CoreLocal::get("RBSort");
if ($mod != '' && in_array($mod, self::$sorts)) {
$mod = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Sort\\' . $mod;
}
return $mod == '' ? 'COREPOS\\pos\\lib\\ReceiptBuilding\\Sort\\DefaultReceiptSort' : $mod;
}
static private function getTag()
{
$mod = CoreLocal::get("RBTag");
if ($mod == 'DefaultReceiptTag') {
$mod = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Tag\\' . $mod;
}
return $mod == '' ? 'COREPOS\\pos\\lib\\ReceiptBuilding\\Tag\\DefaultReceiptTag' : $mod;
}
static public function receiptFromBuilders($transNum='')
{
$empNo=0;$laneNo=0;$transNo=0;
list($empNo, $laneNo, $transNo) = self::parseRef($transNum);
self::initDriver();
$FETCH = self::getFetch();
$mod = new $FETCH();
$data = array();
$dbc = Database::tDataConnect();
$data = $mod->fetch($dbc,$empNo,$laneNo,$transNo);
// load module configuration
$FILTER = self::getFilter();
$SORT = self::getSort();
$TAG = self::getTag();
$fil = new $FILTER();
$recordset = $fil->filter($dbc, $data);
$sort = new $SORT();
$recordset = $sort->sort($recordset);
$tag = new $TAG();
$recordset = $tag->tag($recordset);
$ret = "";
$width = CoreLocal::get('ReceiptLineWidth');
if (!is_numeric($width) || $width <= 0 || !$width) {
$width = 56;
}
foreach ($recordset as $record) {
$className = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Format\\' . $record['tag'] . 'ReceiptFormat';
if (!class_exists($className)) continue;
$obj = new $className(self::$PRINT, $width);
$line = $obj->format($record);
if ($obj->isBold()){
$ret .= self::$PRINT->TextStyle(True,True);
$ret .= $line;
$ret .= self::$PRINT->TextStyle(True,False);
$ret .= "\n";
} else {
$ret .= $line;
$ret .= "\n";
}
}
return $ret;
}
static public function receiptDetail($reprint=false, $transNum='')
{
// put into its own function to make it easier to follow, and slightly
// modified for wider-spread use of joe's "new" receipt format --- apbw 7/3/2007
if (CoreLocal::get("newReceipt") == 2) {
return self::receiptFromBuilders($transNum);
}
$detail = "";
$empNo=0; $laneNo=0; $transNo=0;
list($empNo, $laneNo, $transNo) = self::parseRef($transNum);
if (CoreLocal::get("newReceipt") == 0 ) {
// if old style has been specifically requested
// for a partial or reprint, use old format
$query = "select linetoprint from rp_receipt
where emp_no=$empNo and register_no=$laneNo
and trans_no=$transNo order by trans_id";
$dbc = Database::tDataConnect();
$result = $dbc->query($query);
$numRows = $dbc->numRows($result);
// loop through the results to generate the items listing.
for ($i = 0; $i < $numRows; $i++) {
$row = $dbc->fetchRow($result);
$detail .= $row[0]."\n";
}
return $detail;
}
// the rest of this is deprecated
$dbc = Database::tDataConnect();
/**
The newReceipt=1 option should not be shown in the configuration
UI if the view doesn't exist, but if the configuration gets
messed up try to do something useful rather than printing
nothing.
*/
if (!$dbc->tableExists('rp_receipt_reorder_unions_g')) {
return self::receiptFromBuilders($transNum);
}
// otherwise use new format
$query = "select linetoprint,sequence,dept_name,ordered, 0 as ".
$dbc->identifierEscape('local')
." from rp_receipt_reorder_unions_g where emp_no=$empNo and "
." register_no=$laneNo and trans_no=$transNo "
." order by ordered,dept_name, "
." case when ordered=4 then '' else upc end, "
.$dbc->identifierEscape('sequence');
$result = $dbc->query($query);
$numRows = $dbc->numRows($result);
// loop through the results to generate the items listing.
$lastDept="";
while ($row = $dbc->fetchRow($result)) {
if ($row[2]!=$lastDept){ // department header
if ($row['2']==''){
$detail .= "\n";
} else{
$detail .= self::$PRINT->TextStyle(True,True);
$detail .= $row[2];
$detail .= self::$PRINT->TextStyle(True,False);
$detail .= "\n";
}
}
/***** jqh 12/14/05 fix tax exempt on receipt *****/
if ($row[1]==2 and CoreLocal::get("TaxExempt")==1){
$detail .= " TAX 0.00\n";
} elseif ($row[1]==1 and CoreLocal::get("TaxExempt")==1){
$queryExempt="select ".$dbc->concat(
"right(".$dbc->concat('space(44)',"'SUBTOTAL'",'').", 44)",
"right(".$dbc->concat('space(8)',$dbc->convert('runningTotal-tenderTotal','char'),'').", 8)",
"space(4)",'')." as linetoprint,
1 as sequence,null as dept_name,3 as ordered,'' as upc
from lttsummary";
$resultExempt = $dbc->query($queryExempt);
$rowExempt = $dbc->fetchRow($resultExempt);
$detail .= $rowExempt[0]."\n";
} else {
if (CoreLocal::get("promoMsg") == 1 && $row[4] == 1 ){
// '*' added to local items 8/15/2007 apbw for eat local challenge
$detail .= '*'.$row[0]."\n";
} else {
if ( strpos($row[0]," TOTAL") ) {
// if it's the grand total line . . .
$detail .= self::$PRINT->TextStyle(True,True);
$detail .= $row[0]."\n";
$detail .= self::$PRINT->TextStyle(True,False);
} else {
$detail .= $row[0]."\n";
}
}
}
/***** jqh end change *****/
$lastDept=$row[2];
} // end for loop
return $detail;
}
static private function processColumn($col1)
{
$c1max = 0;
$col1s = array();
foreach( $col1 as $c1) {
$c1s = trim(str_replace(array(self::boldFont(),self::normalFont()), "", $c1));
$col1s[] = $c1s;
$c1max = max($c1max, strlen($c1s));
}
return array($col1s, $c1max);
}
static public function twoColumns($col1, $col2) {
// init
$max = 56;
$text = "";
// find longest string in each column, ignoring font change strings
list($col1s, $c1max) = self::processColumn($col1);
list($col2s, $c2max) = self::processColumn($col2);
// avoid warnings when calculated length < 0
$safeRepeat = function ($i) {
if ($i <= 0) {
return '';
}
return str_repeat(' ', $i);
};
// space the columns as much as they'll fit
$spacer = $max - $c1max - $c2max;
// scan both columns
for( $x=0; isset($col1[$x]) && isset($col2[$x]); $x++) {
$c1r = trim($col1[$x]); $c1l = strlen($col1s[$x]);
$c2r = trim($col2[$x]); $c2l = strlen($col2s[$x]);
if( ($c1max+$spacer+$c2l) <= $max) {
$text .= $c1r . $safeRepeat(($c1max+$spacer)-$c1l) . $c2r . "\n";
} else {
$text .= $c1r . "\n" . $safeRepeat($c1max+$spacer) . $c2r . "\n";
}
}
// if one column is longer than the other, print the extras
// (only one of these should happen since the loop above runs as long as both columns still have rows)
for( $y=$x; isset($col1[$y]); $y++) {
$text .= trim($col1[$y]) . "\n";
} // col1 extras
for( $y=$x; isset($col2[$y]); $y++) {
$text .= str_repeat(" ", $c1max+$spacer) . trim($col2[$y]) . "\n";
} // col2 extras
return $text;
}
static public function parseRef($ref)
{
$emp=$reg=$trans=0;
if (!strstr($ref, '-') && !strstr($ref, '::')) {
$ref = self::mostRecentReceipt();
}
$separator = strstr($ref, '::') ? '::' : '-';
list($emp, $reg, $trans) = explode($separator, $ref, 3);
return array($emp, $reg, $trans);
}
static private function setupReprint($where)
{
// lookup trans information
$dbc = Database::tDataConnect();
$queryHeader = "
SELECT
MIN(datetime) AS dateTimeStamp,
MAX(card_no) AS memberID,
SUM(CASE WHEN upc='0000000008005' THEN total ELSE 0 END) AS couponTotal,
SUM(CASE WHEN upc='DISCOUNT' THEN total ELSE 0 END) AS transDiscount,
SUM(CASE WHEN trans_subtype IN ('MI','CX') THEN total ELSE 0 END) AS chargeTotal,
SUM(CASE WHEN discounttype=1 THEN discount ELSE 0 END) AS discountTTL,
SUM(CASE WHEN discounttype=2 THEN memDiscount ELSE 0 END) AS memSpecial
FROM localtranstoday
WHERE " . $where . "
AND datetime >= " . $dbc->curdate() . "
GROUP BY register_no,
emp_no,
trans_no";
$header = $dbc->query($queryHeader);
$row = $dbc->fetchRow($header);
$dateTimeStamp = $row["dateTimeStamp"];
$dateTimeStamp = strtotime($dateTimeStamp);
// set session variables from trans information
CoreLocal::set("memberID",$row["memberID"]);
CoreLocal::set("memCouponTLL",$row["couponTotal"]);
CoreLocal::set("transDiscount",$row["transDiscount"]);
CoreLocal::set("chargeTotal",-1*$row["chargeTotal"]);
CoreLocal::set("discounttotal",$row["discountTTL"]);
CoreLocal::set("memSpecial",$row["memSpecial"]);
// lookup member info
$dbc = Database::pDataConnect();
$queryID = "select LastName,FirstName,Type,blueLine from custdata
where CardNo = '".CoreLocal::get("memberID")."' and personNum=1";
$result = $dbc->query($queryID);
$row = $dbc->fetchRow($result);
// set session variables from member info
CoreLocal::set("lname",$row["LastName"]);
CoreLocal::set("fname",$row["FirstName"]);
CoreLocal::set('isMember', ($row['Type']=='PC' ? 1 : 0));
CoreLocal::set("memMsg",$row["blueLine"]);
if (CoreLocal::get("isMember") == 1) {
CoreLocal::set("yousaved",number_format( CoreLocal::get("transDiscount")
+ CoreLocal::get("discounttotal") + CoreLocal::get("memSpecial"), 2));
CoreLocal::set("couldhavesaved",0);
CoreLocal::set("specials",number_format(CoreLocal::get("discounttotal")
+ CoreLocal::get("memSpecial"), 2));
} else {
CoreLocal::set("yousaved",CoreLocal::get("discounttotal"));
CoreLocal::set("couldhavesaved",number_format(CoreLocal::get("memSpecial") == '' ? 0 : CoreLocal::get('memSpecial'), 2));
CoreLocal::set("specials",CoreLocal::get("discounttotal"));
}
return $dateTimeStamp;
}
static private $msgMods = array(
'BarcodeTransIdentifierMessage',
'CCReceiptMessage',
'DeclineReceiptMessage',
'EbtReceiptMessage',
'EquitySoldReceiptMessage',
'GCBalanceReceiptMessage',
'GCReceiptMessage',
'GenericSigSlipMessage',
'ReceiptMessage',
'StoreCreditIssuedReceiptMessage',
'WicReceiptMessage',
);
static private function getTypeMap()
{
$typeMap = array();
foreach(self::messageMods(true) as $class){
if (in_array($class, self::$msgMods)) {
$class = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Messages\\' . $class;
}
if (!class_exists($class)) {
continue;
}
$obj = new $class();
if ($obj->standalone_receipt_type != '') {
$obj->setPrintHandler(self::$PRINT);
$typeMap[$obj->standalone_receipt_type] = $obj;
}
}
return $typeMap;
}
static private function memberFooter($receipt, $ref)
{
$mod = CoreLocal::get('ReceiptThankYou');
if ($mod != '' && substr($mod, 0, 7) !== 'COREPOS' && class_exists('COREPOS\\pos\\lib\\ReceiptBuilding\\ThankYou\\' . $mod)) {
$mod = 'COREPOS\\pos\\lib\\ReceiptBuilding\\ThankYou\\' . $mod;
} elseif ($mod === '' || !class_exists($mod)) {
$mod = 'COREPOS\\pos\\lib\\ReceiptBuilding\\ThankYou\\DefaultReceiptThanks';
}
$obj = new $mod();
$obj->setPrintHandler(self::$PRINT);
$receipt['any'] .= $obj->message($ref);
return $receipt;
}
static private function receiptFooters($receipt, $ref)
{
for ($i = 1; $i <= CoreLocal::get("receiptFooterCount"); $i++){
$receipt['any'] .= self::$PRINT->centerString(CoreLocal::get("receiptFooter$i"));
$receipt['any'] .= "\n";
}
if (CoreLocal::get("store")=="wfc") {
$refundDate = date("m/d/Y",mktime(0,0,0,date("n"),date("j")+30,date("Y")));
$receipt['any'] .= self::$PRINT->centerString("returns accepted with this receipt through ".$refundDate);
$receipt['any'] .= "\n";
}
$chargeProgram = 'charge';
/***** CvR add charge total to receipt bottom ****/
$receipt['any'] = self::chargeBalance($receipt['any'], $chargeProgram, $ref);
/**** CvR end ****/
return $receipt;
}
static private function messageModFooters($receipt, $where, $ref, $reprint, $nth)
{
// check if message mods have data
// and add them to the receipt
$dbc = Database::tDataConnect();
$modQ = "SELECT ";
$selectMods = array();
foreach(self::messageMods($nth) as $class){
if (in_array($class, self::$msgMods)) {
$class = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Messages\\' . $class;
}
if (!class_exists($class)) {
continue;
}
$obj = new $class();
$obj->setPrintHandler(self::$PRINT);
$modQ .= $obj->select_condition().' AS '.$dbc->identifierEscape($class).',';
$selectMods[$class] = $obj;
}
$modQ = rtrim($modQ,',');
if (count($selectMods) > 0){
$modQ .= ' FROM localtranstoday
WHERE ' . $where . '
AND datetime >= ' . $dbc->curdate();
$modR = $dbc->query($modQ);
$row = array();
if ($dbc->numRows($modR) > 0) $row = $dbc->fetchRow($modR);
foreach($selectMods as $class => $obj){
if (!isset($row[$class])) continue;
if ($obj->paper_only)
$receipt['print'] .= $obj->message($row[$class], $ref, $reprint);
else
$receipt['any'] .= $obj->message($row[$class], $ref, $reprint);
}
}
return $receipt;
}
static private function messageMods($nth)
{
$messageMods = CoreLocal::get('ReceiptMessageMods');
if (!is_array($messageMods)) $messageMods = array();
if ($nth) {
$add = CoreLocal::get('NthReceiptMods');
if (is_array($add)) {
foreach ($add as $a) {
$messageMods[] = $a;
}
}
}
return $messageMods;
}
/**
generates a receipt string
@param $arg1 string receipt type
@param $ref string transaction identifier
@param $second boolean indicating it's a second receipt
@param $email generate email-style receipt
@return string receipt content
*/
static public function printReceipt($arg1, $ref, $second=False, $email=False)
{
if($second) $email = False; // store copy always prints
if($arg1 != "full") $email = False;
$reprint = $arg1 == 'reprint' ? true : false;
$dateTimeStamp = time();
list($emp, $reg, $trans) = self::parseRef($ref);
$where = sprintf('emp_no=%d AND register_no=%d AND trans_no=%d',
$emp, $reg, $trans);
if ($reprint) {
$arg1 = 'full';
$email = false;
$second = false;
$dateTimeStamp = self::setupReprint($where);
}
$chargeProgram = 'charge';
self::$PRINT= PrintHandler::factory(CoreLocal::get('ReceiptDriver'));
$receipt = "";
$noreceipt = (CoreLocal::get("receiptToggle")==1 ? 0 : 1);
$ignoreNR = array("ccSlip", "accessSignupSlip", "survey", "wicSlip");
$nthReceipt = false;
if (!$reprint && $arg1 == 'full') {
$nthReceipt = self::nthReceipt();
if ($nthReceipt) {
$ignoreNR[] = 'full';
$email = false;
}
}
// find receipt types, or segments, provided via modules
$typeMap = self::getTypeMap();
if ($noreceipt != 1 || in_array($arg1,$ignoreNR) || $email) {
$receipt = self::printReceiptHeader($dateTimeStamp, $ref);
if ($second) {
$ins = self::$PRINT->centerString(_("( S T O R E C O P Y )"))."\n";
$receipt = substr($receipt,0,3).$ins.substr($receipt,3);
} elseif ($reprint !== false) {
$ins = self::$PRINT->centerString(_("*** R E P R I N T ***"))."\n";
$receipt = substr($receipt,0,3).$ins.substr($receipt,3);
}
if ($arg1 == "full") {
$receipt = array('any'=>'','print'=>'');
if ($email) {
$eph = self::emailReceiptMod();
self::$PRINT= new $eph();
}
$receipt['any'] = self::printReceiptHeader($dateTimeStamp, $ref);
$receipt['any'] .= self::receiptDetail($reprint, $ref);
$receipt['any'] .= self::$PRINT->addRenderingSpacer('end of items');
$savingsMode = CoreLocal::get('ReceiptSavingsMode');
if ($savingsMode != '' && substr($savingsMode, 0, 7) !== 'COREPOS' && class_exists('COREPOS\\pos\\lib\\ReceiptBuilding\\Savings\\' . $savingsMode)) {
$savingsMode = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Savings\\' . $savingsMode;
} elseif ($savingsMode === '' || !class_exists($savingsMode)) {
$savingsMode = 'COREPOS\\pos\\lib\\ReceiptBuilding\\Savings\\DefaultReceiptSavings';
}
$savings = new $savingsMode();
$savings->setPrintHandler(self::$PRINT);
$receipt['any'] .= $savings->savingsMessage($ref);
/**
List local total as defined by settings
Default to $ total if no setting exists
*/
if (CoreLocal::get('ReceiptLocalMode') == 'total' || CoreLocal::get('ReceiptLocalMode') == '') {
$receipt['any'] .= self::localTTL($ref);
} elseif (CoreLocal::get('ReceiptLocalMode') == 'percent') {
$receipt['any'] .= self::graphedLocalTTL($ref);
}
$receipt['any'] .= "\n";
$receipt = self::memberFooter($receipt, $ref);
$receipt = self::receiptFooters($receipt, $ref);
$receipt = self::messageModFooters($receipt, $where, $ref, $reprint, $nthReceipt);
if (CoreLocal::get('memberID') != CoreLocal::get('defaultNonMem')) {
$memMessages = self::memReceiptMessages(CoreLocal::get("memberID"));
$receipt['print'] .= $memMessages['print'];
$receipt['any'] .= $memMessages['any'];
}
CoreLocal::set("equityNoticeAmt",0);
// knit pieces back together if not emailing
if (!$email) $receipt = ''.$receipt['any'].$receipt['print'];
CoreLocal::set("headerprinted",0);
} elseif (isset($typeMap[$arg1])) {
$obj = $typeMap[$arg1];
$receipt = $obj->standalone_receipt($ref, $reprint);
} elseif ($arg1 == "cab") {
$ref = CoreLocal::get("cabReference");
$receipt = self::printCabCoupon($dateTimeStamp, $ref);
CoreLocal::set("cabReference","");
} elseif ($arg1 == "giftReceipt") {
$receipt = self::printGiftReceipt($dateTimeStamp, $ref);
} else {
$receipt = self::simpleReceipt($receipt, $arg1, $where);
}
}
/* --------------------------------------------------------------
print store copy of charge slip regardless of receipt print setting - apbw 2/14/05
---------------------------------------------------------------- */
$tmap = CoreLocal::get('TenderMap');
// skip signature slips if using electronic signature capture (unless it's a reprint)
if ((is_array($tmap) && isset($tmap['MI']) && $tmap['MI'] != 'SignedStoreChargeTender') || $reprint) {
if (CoreLocal::get("chargeTotal") != 0 && ((CoreLocal::get("End") == 1 && !$second) || $reprint)) {
/** PLACEHOLDER: deal with charge stuff via StoreChargeMessage
$msg = new StoreChargeMessage();
$msg->setPrintHandler(self::$PRINT);
*/
if (is_array($receipt)) {
$receipt['print'] .= self::printChargeFooterStore($dateTimeStamp, $ref, $chargeProgram);
// $receipt['print'] .= $msg->standalone_receipt($ref);
} else {
$receipt .= self::printChargeFooterStore($dateTimeStamp, $ref, $chargeProgram);
// $receipt .= $msg->standalone_receipt($ref);
}
}
}
$receipt .= self::transactionBarcode($ref);
$receipt = self::cutReceipt($receipt, $second);
if (!in_array($arg1,$ignoreNR))
CoreLocal::set("receiptToggle",1);
if ($reprint){
CoreLocal::set("memMsg","");
CoreLocal::set("memberID","0");
CoreLocal::set("percentDiscount",0);
CoreLocal::set('isMember', 0);
}
return $receipt;
}
static public function cutReceipt($receipt, $second)
{
if (is_array($receipt)){
if ($second){
// second always prints
$receipt['print'] = $receipt['any'].$receipt['print'];
$receipt['any'] = '';
}
if ($receipt['print'] !== ''){
$receipt['print'] = $receipt['print']."\n\n\n\n\n\n\n";
$receipt['print'] .= chr(27).chr(105);
}
} elseif ($receipt !== ""){
$receipt = $receipt."\n\n\n\n\n\n\n";
$receipt .= chr(27).chr(105);
}
return $receipt;
}
static private function simpleReceipt($receipt, $arg1, $where)
{
/***** jqh 09/29/05 if receipt isn't full, then display receipt in old style *****/
$query="select linetoprint from rp_receipt WHERE " . $where . ' ORDER BY trans_id';
if ($arg1 == 'partial') {
// partial has to use localtemptrans
$query = 'SELECT linetoprint FROM receipt';
}
$dbc = Database::tDataConnect();
$result = $dbc->query($query);
$numRows = $dbc->numRows($result);
// loop through the results to generate the items listing.
for ($i = 0; $i < $numRows; $i++) {
$row = $dbc->fetchRow($result);
$receipt .= $row[0]."\n";
}
/***** jqh end change *****/
$dashes = "\n".self::centerString("----------------------------------------------")."\n";
if ($arg1 == "partial") {
$receipt .= $dashes.self::centerString(_("* P A R T I A L T R A N S A C T I O N *")).$dashes;
} elseif ($arg1 == "cancelled") {
$receipt .= $dashes.self::centerString(_("* T R A N S A C T I O N C A N C E L L E D *")).$dashes;
} elseif ($arg1 == "resume") {
$receipt .= $dashes.self::centerString(_("* T R A N S A C T I O N R E S U M E D *")).$dashes
.self::centerString("A complete receipt will be printed\n")
.self::centerString("at the end of the transaction");
} elseif ($arg1 == "suspended") {
$receipt .= $dashes.self::centerString(_("* T R A N S A C T I O N S U S P E N D E D *")).$dashes
.self::mostRecentReceipt();
}
return $receipt;
}
/**
Get per-member receipt messages
@param $cardNo [int] member number
@return [array] receipt text
Array keys are "any" and "print".
*/
static public function memReceiptMessages($cardNo)
{
$dbc = Database::pDataConnect();
$memQ = 'SELECT msg_text,modifier_module
FROM custReceiptMessage
WHERE card_no=' . ((int)$cardNo) . '
ORDER BY msg_text';
// use newer CustomerNotifications table if present
if (CoreLocal::get('NoCompat') == 1 || $dbc->tableExists('CustomerNotifications')) {
$memQ = '
SELECT message AS msg_text,
modifierModule AS modifier_module
FROM CustomerNotifications
WHERE cardNo=' . ((int)$cardNo) . '
AND type=\'receipt\'
ORDER BY message';
}
$memR = $dbc->query($memQ);
$ret = array('any'=>'', 'print'=>'');
while ($row = $dbc->fetchRow($memR)) {
// EL This bit new for messages from plugins.
$className = $row['modifier_module'];
if (!empty($className) && substr($className, 0, 7) !== 'COREPOS' && class_exists('COREPOS\\pos\\lib\\ReceiptBuilding\\CustMessages\\' . $className)) {
$className = 'COREPOS\\pos\\lib\\ReceiptBuilding\\CustMessages\\' . $className;
}
if (!empty($className) && class_exists($className)) {
$obj = new $className();
$obj->setPrintHandler(self::$PRINT);
$msgText = $obj->message($row['msg_text']);
if (is_array($msgText)) {
if (isset($msgText['any'])) {
$ret['any'] .= $msgText['any'];
}
if (isset($msgText['print'])) {
$ret['print'] .= $msgText['print'];
}
} else {
$ret['any'] .= $msgText;
}
} else {
$ret['any'] .= $row['msg_text']."\n";
}
}
return $ret;
}
/**
get current receipt number
*/
static public function receiptNumber()
{
return CoreLocal::get('CashierNo')
. '-'
. CoreLocal::get('laneno')
. '-'
. CoreLocal::get('transno');
}
/**
Get most recent receipt number
*/
static public function mostRecentReceipt()
{
$dbc = Database::tDataConnect();
$query = "SELECT emp_no, register_no, trans_no
FROM localtranstoday
ORDER BY datetime DESC";
$query = $dbc->addSelectLimit($query, 1);
$result = $dbc->query($query);
if ($dbc->numRows($result) == 0) {
return false;
}
$row = $dbc->fetchRow($result);
return $row['emp_no'] . '-' . $row['register_no'] . '-' . $row['trans_no'];
}
static public function code39($barcode, $forcePaper=false)
{
if (!is_object(self::$PRINT)) {
self::$PRINT= PrintHandler::factory(CoreLocal::get('ReceiptDriver'));
}
$printMod = self::$PRINT;
if ($forcePaper && (get_class(self::$PRINT) == self::$EMAIL || get_class(self::$PRINT) == self::$HTML)) {
$printMod = PrintHandler::factory(CoreLocal::get('ReceiptDriver'));
}
return $printMod->printBarcode(PrintHandler::BARCODE_CODE39, $barcode);
}
static public function code128($barcode, $forcePaper=false)
{
if (!is_object(self::$PRINT)) {
self::$PRINT= PrintHandler::factory(CoreLocal::get('ReceiptDriver'));
}
$printMod = self::$PRINT;
if ($forcePaper && (get_class(self::$PRINT) == self::$EMAIL || get_class(self::$PRINT) == self::$HTML)) {
$printMod = PrintHandler::factory(CoreLocal::get('ReceiptDriver'));
}
return $printMod->printBarcode(PrintHandler::BARCODE_CODE128, $barcode);
}
static private function haveMailer()
{
if (class_exists('PHPMailer')) {
return true;
} else if (class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
return true;
}
return false;
}
static public function emailReceiptMod()
{
if (self::haveMailer() && CoreLocal::get('emailReceiptHtml') != '' && class_exists(CoreLocal::get('emailReceiptHtml'))) {
return self::$HTML;
}
return self::$EMAIL;
}
static private function nthReceipt()
{
if (CoreLocal::get('lotterySpin') !== false && CoreLocal::get('lotterySpin') !== '') {
return CoreLocal::get('lotterySpin') < CoreLocal::get('nthReceipt');
} elseif (CoreLocal::get('nthReceipt') > 0 && CoreLocal::get('nthReceipt') < 1) {
if (function_exists('random_int')) { // php 7+
return (random_int(0, PHP_INT_MAX-1)/PHP_INT_MAX) < CoreLocal::get('nthReceipt');
}
return lcg_value() < CoreLocal::get('nthReceipt');
} elseif (CoreLocal::get('nthReceipt') > 0 && CoreLocal::get('standalone') == 0) {
return (CoreLocal::get('transno') % CoreLocal::get('nthReceipt')) === 0;
}
return false;
}
/**
Create barcode representing transaction
Format is: CPYYMMDDEEEEERRRTTTT
* CP (identifier)
* YY last 2 digits of year (yes, y3k problem...)
* MM month
* DD day
* EEEEE emp_no
* RRR register_no
* TTTT trans_no
*/
static private function transactionBarcode($ref)
{
// {ACPYYMMDDEEEEERRRTTTT
list($e,$r,$t) = self::parseRef($ref);
$txnBarcode = '{A'
. 'CP'
. substr(date('Ymd') , -6)
. str_pad($e, 5, '0', STR_PAD_LEFT)
. str_pad($r, 3, '0', STR_PAD_LEFT)
. str_pad($t, 4, '0', STR_PAD_LEFT);
return self::code128($txnBarcode);
}
}