Example #1
0
 public function testDatabase()
 {
     $db = Database::tDataConnect();
     $this->assertInstanceOf('\\COREPOS\\pos\\lib\\SQLManager', $db);
     $this->assertEquals(CoreLocal::get('tDatabase'), $db->default_db);
     $db = Database::pDataConnect();
     $this->assertInstanceOf('\\COREPOS\\pos\\lib\\SQLManager', $db);
     $this->assertEquals(CoreLocal::get('pDatabase'), $db->default_db);
     $this->assertEquals(1, Database::gettransno(-1));
     // not a real emp_no
     $db = Database::tDataConnect();
     $matches = Database::localMatchingColumns($db, 'localtrans', 'localtemptrans');
     $this->assertInternalType('string', $matches);
     $this->assertRegExp('/(.+)/', $matches);
     $globals = array('CashierNo' => 9999, 'cashier' => 'TRAINING', 'LoggedIn' => 0, 'TransNo' => 1, 'TTLFlag' => 0, 'FntlFlag' => 0, 'TaxExempt' => 0);
     Database::setglobalvalues($globals);
     $this->assertEquals(9999, CoreLocal::get('CashierNo'));
     $this->assertEquals('TRAINING', CoreLocal::get('cashier'));
     $this->assertEquals(0, CoreLocal::get('LoggedIn'));
     $this->assertEquals(1, CoreLocal::get('transno'));
     $this->assertEquals(0, CoreLocal::get('ttlflag'));
     $this->assertEquals(0, CoreLocal::get('fntlflag'));
     $this->assertEquals(0, CoreLocal::get('TaxExempt'));
     Database::loadglobalvalues();
     // reload session from db. shouldn't change.
     $this->assertEquals(9999, CoreLocal::get('CashierNo'));
     $this->assertEquals('TRAINING', CoreLocal::get('cashier'));
     $this->assertEquals(0, CoreLocal::get('LoggedIn'));
     $this->assertEquals(1, CoreLocal::get('transno'));
     $this->assertEquals(0, CoreLocal::get('ttlflag'));
     $this->assertEquals(0, CoreLocal::get('fntlflag'));
     $this->assertEquals(0, CoreLocal::get('TaxExempt'));
     Database::setglobalvalue('TTLFlag', 1);
     Database::loadglobalvalues();
     $this->assertEquals(1, CoreLocal::get('ttlflag'));
     Database::setglobalflags(0);
     Database::loadglobalvalues();
     $this->assertEquals(0, CoreLocal::get('ttlflag'));
     $this->assertEquals(0, CoreLocal::get('fntlflag'));
     if (!class_exists('lttLib')) {
         include dirname(__FILE__) . '/lttLib.php';
     }
     lttLib::clear();
     $record = lttLib::genericRecord();
     $record['upc'] = '0000000000000';
     $record['description'] = uniqid('TEST-');
     TransRecord::addRecord($record);
     SuspendLib::suspendorder();
     $db = Database::mDataConnect();
     $query = "\n            SELECT *\n            FROM suspended\n            WHERE upc='{$record['upc']}'\n                AND description='{$record['description']}'\n                AND datetime >= " . $db->curdate();
     $result = $db->query($query);
     $this->assertNotEquals(false, $result, 'Could not query suspended record');
     $this->assertEquals(1, $db->num_rows($result), 'Could not find suspended record');
     $row = $db->fetch_row($result);
     $this->assertInternalType('array', $row, 'Invalid suspended record');
     foreach ($record as $column => $value) {
         $this->assertArrayHasKey($column, $row, 'Suspended missing ' . $column);
         $this->assertEquals($value, $row[$column], 'Suspended mismatch on column ' . $column);
     }
 }
Example #2
0
 function upcscanned($entered)
 {
     $my_url = MiscLib::base_url();
     $ret = $this->default_json();
     /* force cashiers to enter a comment on refunds */
     if (CoreLocal::get("refund") == 1 && CoreLocal::get("refundComment") == "") {
         $ret['udpmsg'] = 'twoPairs';
         if (CoreLocal::get("SecurityRefund") > 20) {
             $ret['main_frame'] = $my_url . "gui-modules/adminlogin.php?class=RefundAdminLogin";
         } else {
             $ret['main_frame'] = $my_url . 'gui-modules/refundComment.php';
         }
         CoreLocal::set("refundComment", CoreLocal::get("strEntered"));
         return $ret;
     }
     if (CoreLocal::get('itemPD') > 0 && CoreLocal::get('SecurityLineItemDiscount') == 30 && CoreLocal::get('msgrepeat') == 0) {
         $ret['main_frame'] = $my_url . "gui-modules/adminlogin.php?class=LineItemDiscountAdminLogin";
         return $ret;
     }
     /**
       11Sep14 Andy
       Disabled until keypress double form submission is
       fixed on paycard confirmation screen. Depending on
       sequence can case flag to be raised, cleared, and
       re-raised leading to spurrious error notifications
     */
     if (false && CoreLocal::get('paycardTendered')) {
         if (CoreLocal::get('msgrepeat') == 0 || CoreLocal::get('lastRepeat') != 'paycardAlreadyApplied') {
             CoreLocal::set('boxMsg', 'Card already tendered<br />
                                         Confirm adding more items');
             CoreLocal::set('lastRepeat', 'paycardAlreadyApplied');
             $ret['main_frame'] = $my_url . 'gui-modules/boxMsg2.php';
             return $ret;
         } else {
             if (CoreLocal::get('lastRepeat') == 'paycardAlreadyApplied') {
                 CoreLocal::set('lastRepeat', '');
                 CoreLocal::set('paycardTendered', false);
             }
         }
     }
     $upc = $this->sanitizeUPC($entered);
     $quantity = CoreLocal::get("quantity");
     if (CoreLocal::get("quantity") == 0 && CoreLocal::get("multiple") == 0) {
         $quantity = 1;
     }
     list($scaleStickerItem, $scalepriceUPC, $scalepricEAN) = $this->rewriteScaleSticker($upc);
     $db = Database::pDataConnect();
     $table = $db->table_definition('products');
     $query = "SELECT inUse,upc,description,normal_price,scale,deposit,\n            qttyEnforced,department,local,cost,tax,foodstamp,discount,\n            discounttype,specialpricemethod,special_price,groupprice,\n            pricemethod,quantity,specialgroupprice,specialquantity,\n            mixmatchcode,idEnforced,tareweight,scaleprice";
     // New column 16Apr14
     if (isset($table['line_item_discountable'])) {
         $query .= ', line_item_discountable';
     } else {
         $query .= ', 1 AS line_item_discountable';
     }
     // New column 16Apr14
     if (isset($table['formatted_name'])) {
         $query .= ', formatted_name';
     } else {
         $query .= ', \'\' AS formatted_name';
     }
     // New column 25Nov14
     if (isset($table['special_limit'])) {
         $query .= ', special_limit';
     } else {
         $query .= ', 0 AS special_limit';
     }
     $query .= " FROM products WHERE upc = '" . $upc . "'";
     $result = $db->query($query);
     $num_rows = $db->num_rows($result);
     /* check for special upcs that aren't really products */
     if ($num_rows == 0) {
         $objs = CoreLocal::get("SpecialUpcClasses");
         foreach ($objs as $class_name) {
             $instance = new $class_name();
             if ($instance->isSpecial($upc)) {
                 return $instance->handle($upc, $ret);
             }
         }
         // no match; not a product, not special
         if ($db->table_exists('IgnoredBarcodes')) {
             // lookup UPC in tabe of ignored barcodes
             // this just suppresses any error message from
             // coming back
             $query = 'SELECT upc FROM IgnoredBarcodes WHERE upc=\'' . $upc . "'";
             $result = $db->query($query);
             if ($result && $db->num_rows($result)) {
                 return $this->default_json();
             }
         }
         $handler = CoreLocal::get('ItemNotFound');
         if ($handler === '' || !class_exists($handler)) {
             $handler = 'ItemNotFound';
         }
         $obj = new $handler();
         $ret = $obj->handle($upc, $ret);
         return $ret;
     }
     /* product exists
           BEGIN error checking round #1
        */
     $row = $db->fetch_array($result);
     /**
       If formatted_name is present, copy it directly over
       products.description. This way nothing further down
       the process has to worry about the distinction between
       two potential naming fields.
     */
     if ($row['formatted_name'] != '') {
         $row['description'] = $row['formatted_name'];
     }
     /* Implementation of inUse flag
      *   if the flag is not set, display a warning dialog noting this
      *   and allowing the sale to be confirmed or canceled
      */
     if ($row["inUse"] == 0) {
         TransRecord::addLogRecord(array('upc' => $row['upc'], 'description' => $row['description'], 'department' => $row['department'], 'charflag' => 'IU'));
     }
     /**
       Apply special department handlers
       based on item's department
     */
     $deptmods = CoreLocal::get('SpecialDeptMap');
     if (!is_array($deptmods) && $db->table_exists('SpecialDeptMap')) {
         $model = new \COREPOS\pos\lib\models\op\SpecialDeptMapModel($db);
         $deptmods = $model->buildMap();
         CoreLocal::set('SpecialDeptMap', $deptmods);
     }
     if (is_array($deptmods) && isset($deptmods[$row['department']])) {
         foreach ($deptmods[$row['department']] as $mod) {
             $obj = new $mod();
             $ret = $obj->handle($row['department'], $row['normal_price'], $ret);
             if ($ret['main_frame']) {
                 return $ret;
             }
         }
     }
     /**
       Detect if a by-weight item has the same weight as the last by-weight
       item. This can indicate a stuck scale.
       The giant if determines whether the item is scalable, that we
       know the weight, and that we know the previous weight (lastWeight)
     
       Pre-weighed items (upc starts with 002) are ignored because they're not
       weighed here. Scalable items that cost one cent are ignored as a special
       case; they're normally entered by keying a quantity multiplier
     */
     if ($num_rows > 0 && $row['scale'] == 1 && CoreLocal::get("lastWeight") > 0 && CoreLocal::get("weight") > 0 && abs(CoreLocal::get("weight") - CoreLocal::get("lastWeight")) < 0.0005 && !$scaleStickerItem && abs($row['normal_price']) > 0.01) {
         if (CoreLocal::get('msgrepeat') == 0) {
             CoreLocal::set("strEntered", $row["upc"]);
             CoreLocal::set("boxMsg", "<b>Same weight as last item</b>");
             CoreLocal::set('boxMsgButtons', array('Confirm Weight [enter]' => '$(\'#reginput\').val(\'\');submitWrapper();', 'Cancel [clear]' => '$(\'#reginput\').val(\'CL\');submitWrapper();'));
             $ret['main_frame'] = $my_url . "gui-modules/boxMsg2.php?quiet=1";
             return $ret;
         }
     }
     if ($row["idEnforced"] > 0) {
         $restrictQ = "SELECT upc,dept_ID FROM dateRestrict WHERE\n                ( upc='{$row['upc']}' AND\n                  ( " . $db->datediff($db->now(), 'restrict_date') . "=0 OR\n                    " . $db->dayofweek($db->now()) . "=restrict_dow\n                  ) AND\n                  ( (restrict_start IS NULL AND restrict_end IS NULL) OR\n                    " . $db->curtime() . " BETWEEN restrict_start AND restrict_end\n                  )\n                 ) OR \n                ( dept_ID='{$row['department']}' AND\n                  ( " . $db->datediff($db->now(), 'restrict_date') . "=0 OR\n                    " . $db->dayofweek($db->now()) . "=restrict_dow\n                  ) AND\n                  ( (restrict_start IS NULL AND restrict_end IS NULL) OR\n                    " . $db->curtime() . " BETWEEN restrict_start AND restrict_end\n                  )\n                )";
         $restrictR = $db->query($restrictQ);
         if ($db->num_rows($restrictR) > 0) {
             $ret['output'] = DisplayLib::boxMsg(_('product cannot be sold right now'), _('Date Restriction'), false, DisplayLib::standardClearButton());
             return $ret;
         }
         list($bad_age, $ret) = PrehLib::ageCheck($row['idEnforced'], $ret);
         if ($bad_age === true) {
             return $ret;
         }
     }
     /**
       Apply automatic tare weight
     */
     if ($row['tareweight'] > 0) {
         $peek = PrehLib::peekItem();
         if (strstr($peek, "** Tare Weight") === False) {
             TransRecord::addTare($row['tareweight'] * 100);
         }
     } elseif ($row['scale'] != 0 && !CoreLocal::get("tare") && Plugin::isEnabled('PromptForTare') && !CoreLocal::get("tarezero")) {
         $ret['main_frame'] = $my_url . 'plugins/PromptForTare/TarePromptInputPage.php?class=UPC&item=' . $entered;
         return $ret;
     } else {
         CoreLocal::set('tarezero', False);
     }
     /* sanity check - ridiculous price 
           (can break db column if it doesn't fit
        */
     if (strlen($row["normal_price"]) > 8) {
         $ret['output'] = DisplayLib::boxMsg($upc . '<br />' . _("Claims to be more than \$100,000"), _('Invalid Item'), false, DisplayLib::standardClearButton());
         return $ret;
     }
     $scale = $row["scale"] == 0 ? 0 : 1;
     $qttyEnforced = $row["qttyEnforced"];
     /* use scaleprice bit column to indicate 
        whether values should be interpretted as 
        UPC or EAN */
     $scaleprice = $row['scaleprice'] == 0 ? $scalepriceUPC : $scalepriceEAN;
     /* need a weight with this item
           retry the UPC in a few milliseconds and see
        */
     if ($scale != 0 && CoreLocal::get("weight") == 0 && $qttyEnforced == 0 && CoreLocal::get("quantity") == 0 && !$scaleStickerItem) {
         CoreLocal::set("SNR", CoreLocal::get('strEntered'));
         $ret['output'] = DisplayLib::boxMsg(_("please put item on scale"), 'Weighed Item', true, DisplayLib::standardClearButton());
         return $ret;
     }
     /* quantity required for this item. Send to
        entry page if one wasn't provided */
     if ($qttyEnforced == 1 && CoreLocal::get("multiple") == 0 && (CoreLocal::get("msgrepeat" == 0) || CoreLocal::get('qttyvalid') == 0)) {
         $ret['main_frame'] = $my_url . 'gui-modules/QuantityEntryPage.php' . '?entered-item=' . CoreLocal::get('strEntered') . '&qty-mode=' . $scale;
         return $ret;
     }
     /* got a scale weight, make sure the tare
        is valid */
     if ($scale != 0 && !$scaleStickerItem) {
         $quantity = CoreLocal::get("weight") - CoreLocal::get("tare");
         if (CoreLocal::get("quantity") != 0) {
             $quantity = CoreLocal::get("quantity") - CoreLocal::get("tare");
         }
         if ($quantity <= 0) {
             $ret['output'] = DisplayLib::boxMsg(_("item weight must be greater than tare weight"), _('Invalid Weight'), false, DisplayLib::standardClearButton());
             return $ret;
         }
         CoreLocal::set("tare", 0);
     }
     /* non-scale items need integer quantities */
     if ($row["scale"] == 0 && (int) CoreLocal::get("quantity") != CoreLocal::get("quantity")) {
         $ret['output'] = DisplayLib::boxMsg(_("fractional quantity cannot be accepted for this item"), _('Invalid Quantity'), false, DisplayLib::standardClearButton());
         return $ret;
     }
     /* wedge I assume
           I don't like this being hard-coded, but since these UPCs
           are entries in products they can't go in a SpecialUPC
           object (unless SpecialUPC checks take place on every
           scan, but that's more overhead than I want on such a common
           operation
        */
     if ($upc == "0000000008010" && CoreLocal::get("msgrepeat") == 0) {
         CoreLocal::set("boxMsg", "<b>" . $total . " gift certificate</b><br />\n                " . _("insert document"));
         CoreLocal::set('boxMsgButtons', array('Endorse [enter]' => '$(\'#reginput\').val(\'\');submitWrapper();', 'Cancel [clear]' => '$(\'#reginput\').val(\'CL\');submitWrapper();'));
         $ret["main_frame"] = $my_url . "gui-modules/boxMsg2.php?endorse=giftcert&endorseAmt=" . $total;
         return $ret;
     }
     /* wedge I assume
           see 0000000008010 above
        */
     if ($upc == "0000000008011" && CoreLocal::get("msgrepeat") == 0) {
         CoreLocal::set("boxMsg", "<b>" . $total . " class registration</b><br />\n                " . _("insert form"));
         CoreLocal::set('boxMsgButtons', array('Endorse [enter]' => '$(\'#reginput\').val(\'\');submitWrapper();', 'Cancel [clear]' => '$(\'#reginput\').val(\'CL\');submitWrapper();'));
         $ret["main_frame"] = $my_url . "gui-modules/boxMsg2.php?endorse=classreg&endorseAmt=" . $total;
         return $ret;
     }
     /*
        END error checking round #1
     */
     // wfc uses deposit field to link another upc
     if (isset($row["deposit"]) && $row["deposit"] > 0) {
         $dupc = (int) $row["deposit"];
         $this->addDeposit($dupc);
     }
     $upc = $row["upc"];
     $row['numflag'] = isset($row["local"]) ? $row["local"] : 0;
     $row['description'] = str_replace("'", "", $row['description']);
     list($tax, $foodstamp, $discountable) = PrehLib::applyToggles($row['tax'], $row['foodstamp'], $row['discount']);
     $row['tax'] = $tax;
     $row['foodstamp'] = $foodstamp;
     $row['discount'] = $discountable;
     /**
       Enforce per-transaction sale limits
     */
     if ($row['special_limit'] > 0) {
         $appliedQ = "\n                SELECT SUM(quantity) AS saleQty\n                FROM " . CoreLocal::get('tDatabase') . $db->sep() . "localtemptrans\n                WHERE discounttype <> 0\n                    AND (\n                        upc='{$row['upc']}'\n                        OR (mixMatch='{$row['mixmatchcode']}' AND mixMatch<>''\n                            AND mixMatch<>'0' AND mixMatch IS NOT NULL)\n                    )";
         $appliedR = $db->query($appliedQ);
         if ($appliedR && $db->num_rows($appliedR)) {
             $appliedW = $db->fetch_row($appliedR);
             if ($appliedW['saleQty'] + $quantity > $row['special_limit']) {
                 $row['discounttype'] = 0;
                 $row['special_price'] = 0;
                 $row['specialpricemethod'] = 0;
                 $row['specialquantity'] = 0;
                 $row['specialgroupprice'] = 0;
             }
         }
     }
     /*
         BEGIN: figure out discounts by type
     */
     /* get discount object 
     
                CORE reserves values 0 through 63 in 
                DiscountType::$MAP for default options.
     
                Additional discounts provided by plugins
                can use values 64 through 127. Because
                the DiscountTypeClasses array is zero-indexed,
                subtract 64 as an offset  
             */
     $discounttype = MiscLib::nullwrap($row["discounttype"]);
     $DiscountObject = null;
     $DTClasses = CoreLocal::get("DiscountTypeClasses");
     if ($row['discounttype'] < 64 && isset(DiscountType::$MAP[$row['discounttype']])) {
         $class = DiscountType::$MAP[$row['discounttype']];
         $DiscountObject = new $class();
     } else {
         if ($row['discounttype'] >= 64 && isset($DTClasses[$row['discounttype'] - 64])) {
             $class = $DTClasses[$row['discounttype'] - 64];
             $DiscountObject = new $class();
         } else {
             // If the requested discounttype isn't available,
             // fallback to normal pricing. Debatable whether
             // this should be a hard error.
             $DiscountObject = new NormalPricing();
         }
     }
     /* add in sticker price and calculate a quantity
                if the item is stickered, scaled, and on sale. 
     
                otherwise, if the item is sticked, scaled, and
                not on sale but has a non-zero price attempt
                to calculate a quantity. this makes the quantity
                field more consistent for reporting purposes.
                however, if the calculated quantity somehow
                introduces a rounding error fall back to the
                sticker's price. for non-sale items, the price
                the customer pays needs to match the sticker
                price exactly.
     
                items that are not scaled do not need a fractional
                quantity and items that do not have a normal_price
                assigned cannot calculate a proper quantity.
             */
     if ($scaleStickerItem) {
         if ($DiscountObject->isSale() && $scale == 1 && $row['normal_price'] != 0) {
             $quantity = MiscLib::truncate2($scaleprice / $row["normal_price"]);
         } else {
             if ($scale == 1 && $row['normal_price'] != 0) {
                 $quantity = MiscLib::truncate2($scaleprice / $row["normal_price"]);
                 if (round($scaleprice, 2) != round($quantity * $row['normal_price'], 2)) {
                     $quantity = 1.0;
                     $row['normal_price'] = $scaleprice;
                 }
             } else {
                 $row['normal_price'] = $scaleprice;
             }
         }
     }
     /*
         END: figure out discounts by type
     */
     /* get price method object  & add item
             
                CORE reserves values 0 through 99 in 
                PriceMethod::$MAP for default methods.
     
                Additional methods provided by plugins
                can use values 100 and up. Because
                the PriceMethodClasses array is zero-indexed,
                subtract 100 as an offset  
             */
     $pricemethod = MiscLib::nullwrap($row["pricemethod"]);
     if ($DiscountObject->isSale()) {
         $pricemethod = MiscLib::nullwrap($row["specialpricemethod"]);
     }
     $PMClasses = CoreLocal::get("PriceMethodClasses");
     $PriceMethodObject = null;
     $row['trans_subtype'] = $this->status;
     if ($pricemethod < 100 && isset(PriceMethod::$MAP[$pricemethod])) {
         $class = PriceMethod::$MAP[$pricemethod];
         $PriceMethodObject = new $class();
     } else {
         if ($pricemethod >= 100 && isset($PMClasses[$pricemethod - 100])) {
             $class = $PMClasses[$pricemethod - 100];
             $PriceMethodObject = new $class();
         } else {
             $PriceMethodObject = new BasicPM();
         }
     }
     // prefetch: otherwise object members
     // pass out of scope in addItem()
     $prefetch = $DiscountObject->priceInfo($row, $quantity);
     $added = $PriceMethodObject->addItem($row, $quantity, $DiscountObject);
     if (!$added) {
         $ret['output'] = DisplayLib::boxMsg($PriceMethodObject->errorInfo(), '', false, DisplayLib::standardClearButton());
         return $ret;
     }
     /* add discount notifications lines, if applicable */
     $DiscountObject->addDiscountLine();
     // cleanup, reset flags and beep
     if ($quantity != 0) {
         CoreLocal::set("msgrepeat", 0);
         CoreLocal::set("qttyvalid", 0);
         $ret['udpmsg'] = 'goodBeep';
     }
     /* reset various flags and variables */
     if (CoreLocal::get("tare") != 0) {
         CoreLocal::set("tare", 0);
     }
     CoreLocal::set("ttlflag", 0);
     CoreLocal::set("fntlflag", 0);
     CoreLocal::set("quantity", 0);
     CoreLocal::set("itemPD", 0);
     Database::setglobalflags(0);
     /* output item list, update totals footer */
     $ret['redraw_footer'] = True;
     $ret['output'] = DisplayLib::lastpage();
     if ($prefetch['unitPrice'] == 0 && $discounttype == 0) {
         $ret['main_frame'] = $my_url . 'gui-modules/priceOverride.php';
     }
     return $ret;
 }