fannie/batches/newbatch/EditBatchPage.php
<?php
/*******************************************************************************
Copyright 2009,2010 Whole Foods Co-op
This file is part of CORE-POS.
CORE-POS 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.
CORE-POS 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
*********************************************************************************/
use COREPOS\Fannie\API\lib\PriceLib;
include(dirname(__FILE__) . '/../../config.php');
if (!class_exists('FannieAPI')) {
include_once(__DIR__ . '/../../classlib2.0/FannieAPI.php');
}
if (!function_exists('checkLogin')) {
include_once(__DIR__ . '/../../auth/login.php');
}
class EditBatchPage extends FannieRESTfulPage
{
protected $must_authenticate = true;
protected $auth_classes = array('batches','batches_audited');
protected $title = 'Sales Batches Tool';
protected $header = 'Sales Batches Tool';
protected $enable_linea = true;
protected $debug_routing = false;
public $description = '[Sales Batches] is the primary tool for creating, editing, and managing
sale and price change batches.';
private $audited = 1;
private $con = null;
public function preprocess()
{
// maintain user logins longer
refreshSession();
if (validateUserQuiet('batches')) {
$this->audited = 0;
}
// autoclear old data from clipboard table on intial page load
$clipboard = $this->connection->tableDefinition('batchCutPaste');
if (isset($clipboard['tdate'])) {
$this->connection->query('DELETE FROM batchCutPaste WHERE tdate < ' . $this->connection->curdate());
}
$this->addRoute(
'get<id><paste>',
'post<id><addUPC>',
'post<id><addLC>',
'post<id><upc><price>',
'post<id><autotag>',
'post<id><force>',
'post<id><forceoneitem>',
'post<id><unsale>',
'post<id><limit>',
'post<id><upc><uid><cut>',
'post<id><upc><price><qty>',
'delete<id><upc>',
'post<id><upc><swap>',
'post<id><qualifiers><discount>',
'post<id><trim>',
'post<id><storeID>',
'post<noteID><batchNotes>',
'post<partialID>', 'post<editBatch>',
'post<editDate>',
'post<editVendorID>'
);
return parent::preprocess();
}
protected function post_editVendorID_handler()
{
$vendorID = FormLib::get('editVendorID');
$batchID = FormLib::get('batchID');
$model = new BatchesModel($this->connection);
$model->batchID($batchID);
$model->vendorID($vendorID);
$model->save();
return true;
}
protected function post_editDate_handler()
{
$id = FormLib::get('id');
$start = FormLib::get('startDate');
$end = FormLib::get('endDate');
$action = FormLib::get('action');
$model = new BatchesModel($this->connection);
$model->batchID($id);
switch ($action) {
case 'start':
$model->startDate($start);
break;
case 'end':
$model->endDate($end);
break;
}
$model->save();
echo 'Saved';
$this->runCallbacks($id);
return false;
}
protected function post_editBatch_handler()
{
$id = FormLib::get('id');
$name = FormLib::get('name');
$model = new BatchesModel($this->connection);
$model->batchID($id);
$model->batchName($name);
$model->save();
echo 'Saved';
$this->runCallbacks($id);
return false;
}
protected function post_noteID_batchNotes_handler()
{
$model = new BatchesModel($this->connection);
$model->batchID($this->noteID);
$model->notes($this->batchNotes);
$model->save();
echo 'Saved';
return false;
}
protected function post_partialID_handler()
{
$partial = new PartialBatchesModel($this->connection);
$partial->batchID($this->partialID);
foreach ($partial->find() as $obj) {
$partial = $obj;
break;
}
$partial->startTime(FormLib::get('pStart', null));
$partial->endTime(FormLib::get('pEnd', null));
$partial->overwriteSales(FormLib::get('pOver', 0));
$partial->repetition(FormLib::get('pRepeat'));
$partial->save();
echo 'Saved';
return false;
}
protected function get_id_paste_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$uid = getUID($this->current_user);
$uid = ltrim($uid,'0');
$bu = new BatchUpdateModel($dbc);
$prep = $dbc->prepare("
SELECT listID,b.upc
FROM batchList AS l
INNER JOIN batchCutPaste as b ON b.upc=l.upc AND b.batchID=l.batchID
WHERE b.uid=?"
);
$res = $dbc->execute($prep,array($uid));
$upP = $dbc->prepare('UPDATE batchList SET batchID=? WHERE listID=?');
$count = 0;
while ($row = $dbc->fetchRow($res)) {
$dbc->execute($upP,array($this->id,$row['listID']));
$count++;
$bu->reset();
$bu->batchID($this->id);
$bu->upc($row['upc']);
$bu->logUpdate($bu::UPDATE_ADDED);
}
$delP = $dbc->prepare("DELETE FROM batchCutPaste WHERE uid=?");
$dbc->execute($delP,$uid);
$this->addOnloadCommand("showBootstrapAlert('#inputarea', 'success', 'Pasted $count items');\n");
$this->runCallbacks($this->id);
return true;
}
private function checkAllOverlap($id)
{
$dbc = $this->connection;
$batch = new BatchesModel($dbc);
$batch->batchID($id);
$batch->load();
if ($batch->discountType() <= 0) {
return false;
}
$batchP = $dbc->prepare("SELECT batchID FROM batches
WHERE batchID <> ?
AND discounttype > 0
AND endDate >= ?");
$batchIDs = $dbc->getAllValues($batchP, array($id, date('Y-m-d')));
list($inStr, $args) = $dbc->safeInClause($batchIDs);
$overlapP = $dbc->prepare('
SELECT b.batchName,
b.startDate,
b.endDate,
b.batchID,
l.upc
FROM batchList AS l
INNER JOIN batches AS b ON l.batchID=b.batchID
WHERE l.batchID IN (' . $inStr . ')
AND b.endDate >= ?
AND b.startDate <= ?
AND b.discounttype > 0
AND b.endDate >= ' . $dbc->curdate()
);
$stamp = strtotime($batch->startDate());
if ($stamp === false) {
return false;
}
$args[] = $stamp ? date('Y-m-d', $stamp) : '1900-01-01';
$stamp = strtotime($batch->endDate());
$args[] = $stamp ? date('Y-m-d', $stamp) : '1900-01-01';
$overlapR = $dbc->execute($overlapP, $args);
$ret = array();
while ($row = $dbc->fetchRow($overlapR)) {
$ret[$row['upc']] = $row;
}
return $ret;
}
private function checkOverlap($id, $upc)
{
$dbc = $this->connection;
$batch = new BatchesModel($dbc);
$batch->batchID($id);
$batch->load();
if ($batch->discountType() <= 0) {
return false;
}
$overlapP = $dbc->prepare('
SELECT b.batchName,
b.startDate,
b.endDate,
b.batchID
FROM batchList AS l
INNER JOIN batches AS b ON l.batchID=b.batchID
WHERE l.batchID <> ?
AND l.upc = ?
AND ? <= b.endDate
AND ? >= b.startDate
AND b.discounttype > 0
AND b.endDate >= ' . $dbc->curdate()
);
$args = array(
$id,
$upc,
);
$stamp = strtotime($batch->startDate());
if ($stamp === false) {
return false;
}
$args[] = $stamp ? date('Y-m-d', $stamp) : '1900-01-01';
$stamp = strtotime($batch->endDate());
$args[] = $stamp ? date('Y-m-d', $stamp) : '1900-01-01';
$overlapR = $dbc->execute($overlapP, $args);
if ($dbc->numRows($overlapR) > 0) {
return $dbc->fetchRow($overlapR);
}
return false;
}
protected function post_id_addUPC_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$upc = trim($this->addUPC);
$upc = BarcodeLib::padUPC($upc);
$json = array(
'error' => 0,
'msg' => '',
'content' => '',
'field' => '#addItemUPC',
);
/**
Nothing submitted; don't do anything
*/
if ($upc === '') {
echo $this->debugJSON($json);
return false;
}
$overlap = $this->checkOverlap($this->id, $upc);
if ($this->config->get('STORE_MODE') != 'HQ' && $overlap !== false) {
$error = 'Item already in concurrent batch: '
. '<a style="color:blue;" href="EditBatchPage.php?id=' . $overlap['batchID'] . '">'
. $overlap['batchName'] . '</a> ('
. date('Y-m-d', strtotime($overlap['startDate'])) . ' - '
. date('Y-m-d', strtotime($overlap['endDate'])) . ')'
. '<br />'
. 'Either remove item from conflicting batch or change
dates so the batches do not overlap.';
$json['error'] = 1;
$json['msg'] = $error;
} else {
$product = new ProductsModel($dbc);
$product->upc($upc);
if (!$product->load()) {
$json['error'] = 1;
$json['msg'] = 'Item not found: ' . $upc;
} else {
$json['content'] = $this->addItemPriceInput($upc, $product->description(), $product->normal_price());
$json['field'] = '#add-item-price';
}
}
echo $this->debugJSON($json);
return false;
}
protected function post_id_addLC_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$json = array(
'error' => 0,
'msg' => '',
'content' => '',
'field' => '#addItemLC',
);
/**
Nothing submitted; don't do anything
*/
if ($this->addLC === '') {
echo $this->debugJSON($json);
return false;
}
$infoP = $dbc->prepare('
SELECT l.likeCodeDesc,
p.normal_price
FROM likeCodes AS l
INNER JOIN upcLike AS u ON l.likeCode=u.likeCode
' . DTrans::joinProducts('u', 'p', 'INNER') . '
WHERE l.likeCode=?
ORDER BY p.normal_price
');
$infoW = $dbc->getRow($infoP, array($this->addLC));
if ($infoW === false) {
$json['error'] = 1;
$json['msg'] = 'Like code #' . $this->addLC . ' not found';
} else {
$json['content'] = $this->addItemPriceInput('LC' . $this->addLC, $infoW['likeCodeDesc'], $infoW['normal_price']);
$json['field'] = '#add-item-price';
}
echo $this->debugJSON($json);
return false;
}
protected function post_id_upc_price_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$price = trim($this->price);
$json = array(
'input' => $this->addItemUPCInput(),
'added' => 0,
);
if ($price != '') {
$model = new BatchListModel($dbc);
$model->upc($this->upc);
$model->batchID($this->id);
$model->salePrice($price);
$model->groupSalePrice($price);
$model->quantity(0);
$model->pricemethod(0);
$saved = $model->save();
if ($saved == true) {
$bu = new BatchUpdateModel($dbc);
$bu->batchID($this->id);
$bu->upc($this->upc);
$bu->logUpdate($bu::UPDATE_ADDED);
}
if (FormLib::get('audited') == '1') {
\COREPOS\Fannie\API\lib\AuditLib::batchNotification(
$this->id,
$this->upc,
\COREPOS\Fannie\API\lib\AuditLib::BATCH_ADD);
}
$json['added'] = 1;
$json['display'] = $this->showBatchDisplay($this->id);
$this->runCallbacks($this->id);
}
echo $this->debugJSON($json);
return false;
}
protected function post_id_autotag_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$bid = $this->id;
$delQ = $dbc->prepare("DELETE FROM batchBarcodes where batchID=?");
$dbc->execute($delQ,array($bid));
$selQ = "
SELECT l.upc,
l.salePrice,
l.batchID
from batchList as l
" . DTrans::joinProducts('l', 'p', 'INNER') . "
WHERE l.batchID=? ";
$args = array($bid);
if ($this->config->get('STORE_MODE') == 'HQ') {
$selQ .= " AND p.store_id=? ";
$args[] = $this->config->get('STORE_ID');
}
$selQ .= " ORDER BY l.upc";
$selP = $dbc->prepare($selQ);
$selR = $dbc->execute($selP, $args);
$upc = "";
$insP = $dbc->prepare("INSERT INTO batchBarcodes
(upc,description,normal_price,brand,sku,size,units,vendor,batchID)
VALUES (?,?,?,?,?,?,?,?,?)");
$tag_count = 0;
$source = $this->config->get('TAG_DATA_SOURCE');
if (empty($source) || !class_exists($source)) {
$source = 'COREPOS\\Fannie\\API\\item\\TagDataSource';
}
$tagSource = new $source();
while ($selW = $dbc->fetchRow($selR)) {
if ($upc != $selW['upc']){
$tag = $tagSource->getTagData($dbc, $selW['upc'], $selW['salePrice']);
$dbc->execute($insP,array(
$tag['upc'], $tag['description'],
$tag['normal_price'], $tag['brand'],
$tag['sku'], $tag['size'],
$tag['units'], $tag['vendor'],
$selW['batchID']
));
$tag_count++;
}
$upc = $selW['upc'];
}
$json = array('tags' => $tag_count);
echo $this->debugJSON($json);
return false;
}
protected function post_id_force_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$model = new BatchesModel($dbc);
$model->forceStartBatch($this->id);
$json = array('error'=>0, 'msg'=>'Batch #' . $this->id . ' has been applied');
echo $this->debugJSON($json);
return false;
}
protected function post_id_forceoneitem_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$upc = FormLib::get('upc');
$batchID = FormLib::get('id');
$model = new BatchesModel($dbc);
$model->forceStartBatch($batchID, $upc);
$json = array('error'=>0, 'msg'=>'Batch #' . $batchID . ' upc #' . $upc . ' has been applied');
echo $this->debugJSON($json);
return false;
}
protected function post_id_unsale_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$model = new BatchesModel($dbc);
$model->forceStopBatch($this->id);
$json = array('error'=>0, 'msg'=> 'Batch items taken off sale');
echo $this->debugJSON($json);
return false;
}
protected function post_id_limit_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$batches = new BatchesModel($dbc);
$batches->batchID($this->id);
$batches->transLimit($this->limit);
$batches->save();
return false;
}
protected function post_id_upc_uid_cut_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$delP = $dbc->prepare('
DELETE
FROM batchCutPaste
WHERE upc=?
AND batchID=?
AND uid=?
');
$args = array($this->upc, $this->id, $this->uid);
$dbc->execute($delP, $args);
if ($this->cut) {
$insP = $dbc->prepare('
INSERT INTO batchCutPaste
(upc, batchID, uid, tdate)
VALUES
(?, ?, ?, ' . $dbc->now() . ')
');
$dbc->execute($insP, $args);
}
$bu = new BatchUpdateModel($dbc);
$bu->upc($this->upc);
$bu->batchID($this->id);
$bu->logUpdate($bu::UPDATE_REMOVED);
return false;
}
protected function post_id_upc_price_qty_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->get('OP_DB'));
$json = array('error'=>0, 'msg'=>'');
if (!is_numeric($this->qty) || $this->qty < 1 || $this->qty != round($this->qty)) {
$json['error'] = 1;
$json['msg'] = 'Invalid quantity "' . $this->qty . '"; using quantity one';
$this->qty = 1;
}
$pmethod = ($this->qty >= 2) ? 2 : 0;
if ($this->qty == 2) {
$pmethod = $this->config->get('BOGO_MODE', 2);
}
$model = new BatchListModel($dbc);
$model->upc($this->upc);
$model->batchID($this->id);
$model->salePrice($this->price);
$model->groupSalePrice($this->price);
$model->quantity($this->qty);
// quantity functions as a per-transaction limit
// when pricemethod=0
if ($this->qty <= 1) {
$this->qty = 1;
$model->quantity(0);
}
$model->pricemethod($pmethod);
$saved = $model->save();
$bu = new BatchUpdateModel($dbc);
$bu->upc($this->upc);
$bu->batchID($this->id);
$bu->specialPrice($this->price);
$bu->quantity($this->qty);
if ($this->qty <= 1) {
$this->qty = 1;
$bu->quantity(0);
}
$bu->logUpdate($bu::UPDATE_PRICE_EDIT);
$json['price'] = sprintf('%.2f', $this->price);
$json['qty'] = (int)$this->qty;
$upQ = $dbc->prepare("update batchBarcodes set normal_price=? where upc=? and batchID=?");
$upR = $dbc->execute($upQ,array($this->price,$this->upc,$this->id));
if (FormLib::get('audited') == '1') {
\COREPOS\Fannie\API\lib\AuditLib::batchNotification(
$this->id,
$this->upc,
\COREPOS\Fannie\API\lib\AuditLib::BATCH_EDIT,
(substr($this->upc,0,2)=='LC' ? true : false));
}
$this->runCallbacks($this->id);
echo $this->debugJSON($json);
return false;
}
private function unsaleItem($upc, $json)
{
if (substr($upc,0,2) != 'LC') {
// take the item off sale if this batch is currently on sale
if ($this->unsaleUPC($upc) === false) {
$json['error'] = 1;
$json['msg'] = 'Error taking item ' . $upc . ' off sale';
}
COREPOS\Fannie\API\data\ItemSync::sync($upc);
} else {
$likecode = substr($upc,2);
if ($this->unsaleLikeCode($likecode) === false) {
$json['error'] = 1;
$json['msg'] = 'Error taking like code ' . $likecode . ' off sale';
}
}
return $json;
}
private function unsaleUPC($upc)
{
// take the item off sale if this batch is currently on sale
$product = new ProductsModel($this->connection);
$product->upc($upc);
$ret = true;
foreach ($product->find('store_id') as $obj) {
$obj->discountType(0);
$obj->special_price(0);
$obj->start_date('1900-01-01');
$obj->end_date('1900-01-01');
$ret = $obj->save();
}
return $ret ? true : false;
}
private function unsaleLikeCode($likecode)
{
$upcLike = new UpcLikeModel($this->connection);
$upcLike->likeCode($likecode);
$ret = true;
foreach ($upcLike->find() as $u) {
$ret = $this->unsaleUPC($u->upc());
}
return $ret ? true : false;
}
private function repriceItem($upc, $data, $json, $useStores=false)
{
if (substr($upc,0,2) != 'LC') {
// take the item off sale if this batch is currently on sale
if ($this->repriceUPC($upc,$data,$useStores) === false) {
$json['error'] = 1;
$json['msg'] = 'Error repricing item ' . $upc;
}
COREPOS\Fannie\API\data\ItemSync::sync($upc);
} else {
$likecode = substr($upc,2);
if ($this->repriceLikeCode($likecode,$data,$useStores) === false) {
$json['error'] = 1;
$json['msg'] = 'Error taking like code ' . $likecode . ' repricing like code';
}
}
return $json;
}
private function repriceLikeCode($likecode, $data, $useStores=false)
{
$upcLike = new UpcLikeModel($this->connection);
$upcLike->likeCode($likecode);
$ret = true;
foreach ($upcLike->find() as $u) {
$ret = $this->repriceUPC($u->upc(),$data,$useStores);
}
return $ret ? true : false;
}
private function repriceUPC($upc, $data, $useStores=false)
{
// set price of item to match current sale batch
$product = new ProductsModel($this->connection);
$product->upc($upc);
$ret = true;
foreach ($product->find('store_id') as $obj) {
if ($useStores && !isset($data[$obj->store_id()])) {
$data[$obj->store_id()] = array(
'discountType'=>0,
'salePrice'=>0,
'startDate'=>'1900-01-01',
'endDate'=>'1900-01-01',
);
}
$cur = $useStores ? $data[$obj->store_id()] : $data;
$obj->discountType(isset($cur['discountType']) ? $cur['discountType'] : 0);
$obj->special_price($cur['salePrice']);
$obj->start_date($cur['startDate']);
$obj->end_date($cur['endDate']);
$ret = $obj->save();
}
return $ret ? true : false;
}
protected function delete_id_upc_handler()
{
global $FANNIE_OP_DB;
$dbc = FannieDB::get($FANNIE_OP_DB);
$id = $this->id;
$upc = $this->upc;
$bu = new BatchUpdateModel($dbc);
$bu->upc($upc);
$bu->batchID($id);
$bu->logUpdate($bu::UPDATE_REMOVED);
$delQ = $dbc->prepare("delete from batchList where batchID=? and upc=?");
$delR = $dbc->execute($delQ,array($id,$upc));
if ($delR === false) {
if ($json['error']) {
$json['msg'] .= '<br />Error deleting item ' . $upc . ' from batch';
} else {
$json['error'] = 1;
$json['msg'] = 'Error deleting item ' . $upc . ' from batch';
}
}
$delQ = $dbc->prepare("delete from batchBarcodes where upc=? and batchID=?");
$delR = $dbc->execute($delQ,array($upc,$id));
if (FormLib::get_form_value('audited') == '1') {
\COREPOS\Fannie\API\lib\AuditLib::batchNotification(
$id,
$upc,
\COREPOS\Fannie\API\lib\AuditLib::BATCH_DELETE,
(substr($upc,0,2)=='LC' ? true : false));
}
$json = array('error'=>0, 'msg'=>'Item ' . $upc . ' removed from batch');
$currentP = $dbc->prepare('SELECT batchID FROM batches WHERE ? BETWEEN startDate AND endDate AND batchID=?');
$current = $dbc->getValue($currentP, array(date('Y-m-d 00:00:00'), $this->id));
if ($current) {
$effective = PriceLib::effectiveSalePrice($dbc, $this->config, $upc);
if (!isset($effective[$upc])) { // Item is not on sale
$this->unsaleItem($upc, $json);
} else { // Item is on sale [at some stores, possibly]
$useStores = $this->config->get('STORE_MODE') == 'HQ' ? true : false;
$json = $this->repriceItem($upc, $effective[$upc], $json, $useStores);
}
/*
$currentSalesA = array($upc, date('Y-m-d 00:00:00'));
$currentSalesP = $dbc->prepare("
SELECT b.batchID, bl.upc, bl.salePrice, b.discountType, b.startDate, b.endDate
FROM batches AS b
LEFT JOIN batchList AS bl ON b.batchID=bl.batchID
WHERE b.discountType > 0
AND bl.upc = ?
AND ? BETWEEN startDate AND endDate;
");
$currentSalesR = $dbc->execute($currentSalesP,$currentSalesA);
$curSale = array(
'batchID' => $row['batchID'],
'salePrice' => 0,
'discountType' => 1,
'startDate' => '',
'endDate' => '',
);
while ($row = $dbc->fetchRow($currentSalesR)) {
if ($row['batchID'] != $id) {
if ($row['salePrice'] < $curSale['salePrice'] || $curSale['salePrice'] == 0) {
$curSale['salePrice'] = $row['salePrice'];
$curSale['batchID'] = $row['batchID'];
$cursale['discountType'] = $row['discountType'];
$cursale['startDate'] = $row['startDate'];
$cursale['endDate'] = $row['endDate'];
}
}
}
if ($curSale['batchID'] == $id) {
$json = $this->unsaleItem($upc, $json);
} elseif ($curSale['batchID'] != $id && $curSale['salePrice'] != 0) {
$json = $this->repriceItem($upc, $curSale, $json);
}
*/
}
$this->runCallbacks($this->id);
echo $this->debugJSON($json);
return false;
}
protected function post_id_upc_swap_handler()
{
global $FANNIE_OP_DB;
$dbc = FannieDB::get($FANNIE_OP_DB);
$q = $dbc->prepare("UPDATE batchList SET salePrice = -1*salePrice
WHERE batchID=? AND upc=?");
$r = $dbc->execute($q,array($this->id,$this->upc));
$json = array('error' => 0);
if ($r === false) {
$json['error'] = 'Error moving item';
}
echo $this->debugJSON($json);
return false;
}
protected function post_id_qualifiers_discount_handler()
{
global $FANNIE_OP_DB;
$dbc = FannieDB::get($FANNIE_OP_DB);
$batch = new BatchesModel($dbc);
$batch->batchID($this->id);
if (FormLib::get('member') == '1') {
$batch->discountType(2);
} else {
$batch->discountType(1);
}
$save1 = $batch->save();
$pmethod = 4;
if (FormLib::get('split') == '1') {
$pmethod = 3;
}
$upQ2 = $dbc->prepare("UPDATE batchList SET
quantity=?,pricemethod=?,
salePrice=?, groupSalePrice=? WHERE batchID=?
AND salePrice >= 0");
$upQ3 = $dbc->prepare("UPDATE batchList SET
quantity=?,pricemethod=?,
salePrice=?, groupSalePrice=? WHERE batchID=?
AND salePrice < 0");
$save2 = $dbc->execute($upQ2, array($this->qualifiers+1,$pmethod,$this->discount,$this->discount,$this->id));
$save3 = $dbc->execute($upQ3,array($this->qualifiers+1,$pmethod,-1*$this->discount,$this->discount,$this->id));
$json['error'] = 0;
if (!$save1 || !$save2 || !$save3) {
$json['error'] = 'Error saving paired sale settings';
}
echo $this->debugJSON($json);
return false;
}
protected function post_id_trim_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->OP_DB);
$ret = array('error'=>0, 'display'=>'');
$query = '
SELECT b.upc
FROM batchList AS b
' . DTrans::joinProducts('b', 'p', 'INNER') . '
WHERE b.batchID=?
AND b.salePrice=p.normal_price
GROUP BY b.upc';
$prep = $dbc->prepare($query);
$res = $dbc->execute($prep, array($this->id));
$delP = $dbc->prepare('
DELETE FROM batchList
WHERE batchID=?
AND upc=?');
$dbc->startTransaction();
$bu = new BatchUpdateModel($dbc);
while ($w = $dbc->fetchRow($res)) {
$dbc->execute($delP, array($this->id, $w['upc']));
$bu->reset();
$bu->batchID($this->id);
$bu->upc($w['upc']);
$bu->logUpdate($bu::UPDATE_REMOVED);
}
$dbc->commitTransaction();
$query = "SELECT upc, salePrice FROM batchList WHERE batchID=? AND upc like 'LC%'";
$prep = $dbc->prepare($query);
$res = $dbc->execute($prep, array($this->id));
$priceP = $dbc->prepare("SELECT MIN(normal_price), MAX(normal_price)
FROM upcLike AS u INNER JOIN products AS p ON p.upc=u.upc
WHERE u.likeCode=?");
$dbc->startTransaction();
while ($row = $dbc->fetchRow($res)) {
$price = $row['salePrice'];
$minMax = $dbc->getRow($priceP, array(substr($row['upc'], 2)));
if (abs($price - $minMax[0]) < 0.005 && abs($price - $minMax[1]) < 0.005) {
$dbc->execute($delP, array($this->id, $row['upc']));
}
}
$dbc->commitTransaction();
$ret['display'] = $this->showBatchDisplay($this->id);
echo $this->debugJSON($ret);
$this->runCallbacks($this->id);
return false;
}
public function post_id_storeID_handler()
{
$dbc = $this->connection;
$dbc->selectDB($this->config->OP_DB);
$ret = array('error'=>0, 'display'=>'');
$bu = new BatchUpdateModel($dbc);
$map = new StoreBatchMapModel($dbc);
$map->storeID($this->storeID);
$map->batchID($this->id);
if ($map->load()) {
$deleted = $map->delete();
if (!$deleted) {
$ret['error'] = 'Error removing store mapping';
} else {
$bu->batchID($this->id);
$bu->logUpdate($bu::UPDATE_MAP);
}
} else {
$saved = $map->save();
if (!$saved) {
$ret['error'] = 'Error saving store mapping';
} else {
$bu->batchID($this->id);
$bu->logUpdate($bu::UPDATE_MAP);
}
}
echo $this->debugJSON($ret);
$this->runCallbacks($this->id);
return false;
}
private function runCallbacks($batchID)
{
$cbs = $this->config->get('BATCH_CALLBACKS');
$this->logger->debug("Attempting batch callbacks");
foreach ($cbs as $cb) {
$obj = new $cb();
$obj->run($batchID);
$this->logger->debug("Running $cb for batch $batchID");
}
}
private function addItemUPCInput($newtags=false)
{
$dbc = $this->connection;
$dbc->selectDB($this->config->OP_DB);
$model = new LikeCodesModel($dbc);
$lcOpts = $model->toOptions(-1);
return <<<HTML
<form class="form-inline" onsubmit="batchEdit.advanceToPrice(); return false;" id="add-item-form">
<span class="add-by-upc-fields">
<label class=\"control-label\">UPC</label>
<input type=text maxlength=13 name="addUPC" id=addItemUPC
class="form-control" />
</span>
<span class="add-by-lc-fields collapse">
<label class="control-label">Like code</label>
<input type=text id=addItemLC name="addLC" size=4 value=1 class="form-control" disabled />
<select id=lcselect onchange="\$('#addItemLC').val(this.value);" class="form-control chosen-select" disabled>
{$lcOpts}
</select>
</span>
<button type=submit value=Add class="btn btn-default">Add</button>
<input type=checkbox id=addItemLikeCode onchange="batchEdit.toggleUpcLcInput();" />
<label for="addItemLikeCode" class="control-label">Likecode</label>
</form>
HTML;
}
private function addItemPriceInput($upc, $description, $price)
{
return <<<HTML
<form onsubmit="batchEdit.addItemPrice('{$upc}'); return false;" id="add-price-form" class="form-inline">
<label>ID</label>: {$upc}
<label>Description</label>: {$description}
<label>Normal price</label>: {$price}
<label>Sale price</label>
<input class="form-control" type=text id=add-item-price name=price size=5 />
<button type=submit value=Add class="btn btn-default">Add</button>
</form>
HTML;
}
protected function showBatchDisplay($id, $order='natural')
{
global $FANNIE_SERVER_DBMS,$FANNIE_URL;
$dbc = $this->connection;
$uid = getUID($this->current_user);
$uid = ltrim($uid,'0');
$authorized = false;
if (FannieAuth::validateUserQuiet('admin')) {
$authorized = true;
}
$orderby = '';
switch($order) {
case 'upc_a':
$orderby = 'ORDER BY b.upc ASC';
break;
case 'upc_d':
$orderby = 'ORDER BY b.upc DESC';
break;
case 'desc_a':
$orderby = 'ORDER BY description ASC';
break;
case 'desc_d':
$orderby = 'ORDER BY description DESC';
break;
case 'price_a':
$orderby = 'ORDER BY p.normal_price ASC';
break;
case 'price_d':
$orderby = 'ORDER BY p.normal_price DESC';
break;
case 'sale_a':
$orderby = 'ORDER BY b.salePrice ASC';
break;
case 'sale_d':
$orderby = 'ORDER BY b.salePrice DESC';
break;
case 'loc_a':
$orderby = 'ORDER BY locationName';
break;
case 'loc_d':
$orderby = 'ORDER BY locationName';
break;
case 'brand_a':
$orderby = 'ORDER BY p.brand ASC';
break;
case 'brand_d':
$orderby = 'ORDER BY p.brand DESC';
break;
case 'natural':
default:
$orderby = 'ORDER BY b.listID DESC';
break;
}
$model = new BatchesModel($dbc);
$model->batchID($id);
$model->load();
$name = $model->batchName();
$type = $model->batchType();
$dtype = $model->discountType();
$start = strtotime($model->startDate());
$end = strtotime($model->endDate()) + (60*60*24);
$typeModel = new BatchTypeModel($dbc);
$typeModel->batchTypeID($type);
$typeModel->load();
if ($typeModel->editorUI() == 2) {
return $this->showPairedBatchDisplay($id,$name);
}
$noprices = $typeModel->editorUI() == 4 ? 'collapse' : '';
$normalPriceHeader = ($type != 17) ? 'Normal Price' : 'Normal Cost';
$limit = $model->transLimit();
$hasLimit = $limit > 0 ? true : false;
$saleHeader = "Sale Price";
if ($dtype == 3) {
$saleHeader = "$ Discount";
} elseif ($dtype == 4) {
$saleHeader = "% Discount";
} elseif ($dtype == 0) {
$saleHeader = "New price";
} elseif ($dtype == -1) {
$saleHeader = "New Cost";
}
$vidExistsP = $dbc->prepare("SHOW COLUMNS FROM `batches` LIKE 'vendorID'");
$vidExists = $dbc->getRow($vidExistsP);
$vendorID = null;
if ($vidExists != 0) {
$vidP = $dbc->prepare("SELECT vendorID FROM batches WHERE batchID=?");
$vendorID = $dbc->getValue($vidP, $id);
}
$fetchArgs = array();
$store_location = COREPOS\Fannie\API\lib\Store::getIdByIp();
// logically string "LC" followed by like code number
$joinColumn = $dbc->concat("'LC'", $dbc->convert('l.likeCode', 'CHAR'), '');
$fetchQ = "
SELECT b.upc,
CASE
WHEN l.likeCode IS NULL THEN p.description
ELSE l.likeCodeDesc
END AS description,
p.normal_price,
p.cost,
b.salePrice,
CASE WHEN c.upc IS NULL then 0 ELSE 1 END as isCut,
b.quantity,
b.pricemethod,
p.brand,
NULL AS locationName,
r.maxPrice,
r.priceRuleID,
r.priceRuleTypeID
FROM batchList AS b
" . DTrans::joinProducts('b') . "
LEFT JOIN likeCodes AS l ON b.upc = {$joinColumn}
LEFT JOIN batchCutPaste AS c ON b.upc=c.upc AND b.batchID=c.batchID
LEFT JOIN FloorSectionsListView as f on b.upc=f.upc and f.storeID=?
LEFT JOIN PriceRules AS r ON p.price_rule_id=r.priceRuleID
WHERE b.batchID = ?
$orderby";
$fetchArgs[] = $store_location;
$fetchArgs[] = $id;
if ($dbc->tableExists('FloorSectionsListView')) {
$fetchQ = str_replace('NULL AS locationName', 'f.sections AS locationName', $fetchQ);
}
/*elseif ($dbc->tableExists('FloorSections')) {
$fetchQ = str_replace('NULL AS locationName', 's.name AS locationName', $fetchQ);
}*/
$fetchP = $dbc->prepare($fetchQ);
$fetchR = $dbc->execute($fetchP, $fetchArgs);
$overlapP = $dbc->prepare('
SELECT b.batchID,
b.batchName
FROM batchList as l
INNER JOIN batches AS b ON b.batchID=l.batchID
WHERE l.upc=?
AND l.batchID <> ?
AND b.discounttype > 0
AND (
(b.startDate BETWEEN ? AND ?)
OR
(b.endDate BETWEEN ? AND ?)
)
AND b.endDate <= ' . $dbc->curdate()
);
$overlap_args = array($model->startDate(), $model->endDate(), $model->startDate(), $model->endDate());
$allOverlap = $this->checkAllOverlap($id);
$cpCount = $dbc->prepare("SELECT count(*) FROM batchCutPaste WHERE uid=?");
$res = $dbc->execute($cpCount,array($uid));
$row = $dbc->fetch_row($res);
$cpCount = $row[0];
$this->addOnloadCommand("$('.be-editable-date').datepicker();");
//$this->addOnloadCommand("$('#batchName').removeAttribute('tabIndex');");
$vendorDatalist = "";
$vendorsP = $dbc->prepare("SELECT vendorID, vendorName FROM vendors");
$vendorsR = $dbc->execute($vendorsP);
while ($vendorsRow = $dbc->fetchRow($vendorsR)) {
$vid = $vendorsRow['vendorID'];
$vendorName = $vendorsRow['vendorName'];
$vendorDatalist .= "<option value=\"$vid\">$vendorName</option>";
}
$vendorID_HTML = '';
if ($type == 17) {
$vendorID_HTML = "
<datalist id=\"vendorList\"$vendorDatalist>$vendorDatalist</datalist>
<b>Vendor ID</b>:
<input list=\"vendorList\" type=\"text\" name=\"vendorID\" id=\"vendorID\" class=\"form-control \"
placeholder=\"vendor ID\" style=\"display: inline-block; width: 110px\" autocomplete=\"off\"
onchange=\"batchEdit.setVendorID(); return false;\" value=\"$vendorID\"/>";
}
$ret = "<span class=\"newBatchBlack\"><b>Batch name</b>: <input type=\"text\" class=\"be-editable form-control wide\"
value=\"$name\" name=\"batchName\" id=\"batchName\" onchange=\"batchEdit.renameBatch('$name'); return false;\" /></span> | ";
$startYMD = date('Y-m-d', strtotime($model->startDate()));
$endYMD = date('Y-m-d', strtotime($model->endDate()));
$readonly = (strpos($name, 'Co-op Deals') !== false) ? ' readonly ' : '';
$beEditableDate = (strpos($name, 'Co-op Deals') !== false) ? ' ' : ' be-editable-date ';
$ret .= "<input type=\"hidden\" id=\"batchStartDate\" value=\"$startYMD\"/>";
$ret .= "<input type=\"hidden\" id=\"batchEndDate\" value=\"$endYMD\"/>";
$ret .= "<input type=\"hidden\" id=\"batchType\" value=\"$type\"/>";
$ret .= '<b>Sale Dates</b>: <input type="text" class="be-editable ' . $beEditableDate . ' form-control"
onchange="batchEdit.editBatchDate(\''.$startYMD.'\', \'start\'); return false;"
name="startDate" id="startDate" value="'
. date('Y-m-d', strtotime($model->startDate()))
. '"' . $readonly . '/> - <input type="text" class="be-editable ' . $beEditableDate . ' form-control"
onchange="batchEdit.editBatchDate(\''.$endYMD.'\', \'end\'); return false;"
name="startDate" id="endDate" value="'
. date('Y-m-d', strtotime($model->endDate()))
. '"' . $readonly . '/> ' . $vendorID_HTML . ' | <a href="batchReport.php?batchID=' . $id . '">Report</a><br />';
if ($this->config->get('STORE_MODE') === 'HQ') {
$stores = new StoresModel($dbc);
$stores->hasOwnItems(1);
$mapP = $dbc->prepare('SELECT storeID FROM StoreBatchMap WHERE storeID=? AND batchID=?');
foreach ($stores->find('storeID') as $s) {
$mapR = $dbc->execute($mapP, array($s->storeID(), $id));
$checked = ($mapR && $dbc->numRows($mapR)) ? 'checked' : '';
$disabled = $typeModel->allowSingleStore() ? '' : 'disabled';
$ret .= sprintf('<label>
<input type="checkbox" onchange="batchEdit.toggleStore(%d, %d);" %s %s />
%s
</label> | ',
$s->storeID(), $id,
$disabled, $checked, $s->description());
}
$ret .= '<br />';
}
if ($model->discountType() == 0) {
$ret .= '<div class="alert alert-danger">This is a price change batch</div>';
}
$ret .= '<span class="hidden-print">';
$ret .= "<a href=\"BatchListPage.php\">Back to batch list</a> | ";
$ret .= sprintf('<input type="hidden" id="batch-discount-type" value="%d" />', $model->discountType());
/**
Price change batches probably want the upcoming retail
rather than the current retail. Current sales will want
the current sale price; future sales will want the future
sale price. Past sales probably won't print signs under
normal circumstances.
*/
$future_mode = false;
if ($model->discountType() == 0) {
$future_mode = true;
} elseif (strtotime($model->startDate()) >= strtotime(mktime(0,0,0,date('n'),date('j'),date('Y')))) {
$future_mode = true;
}
$ret .= sprintf('<input type="hidden" id="batch-future-mode" value="%d" />', $future_mode ? 1 : 0);
$ret .= "<a href=\"../../admin/labels/SignFromSearch.php?batch=$id\">Print Sale Signs</a> | ";
$ret .= "<a href=\"BatchSignStylesPage.php?id=$id\">Sign Pricing</a> | ";
$ret .= "<a href=\"{$FANNIE_URL}admin/labels/BatchShelfTags.php?batchID%5B%5D=$id\">Print Shelf Tags</a> | ";
$ret .= "<a href=\"\" onclick=\"batchEdit.generateTags($id); return false;\">Auto-tag</a> | ";
if ($cpCount > 0) {
$ret .= "<a href=\"EditBatchPage.php?id=$id&paste=1\">Paste Items ($cpCount)</a> | ";
}
if ($dtype == 0 || (time() >= $start && time() <= $end)) {
$ret .= "<a href=\"\" class=\"{$noprices}\" onclick=\"batchEdit.forceNow($id); return false;\">Force batch</a> | ";
}
if ($dtype != 0) {
$ret .= "<a href=\"\" class=\"{$noprices}\" onclick=\"batchEdit.unsaleNow($id); return false;\">Stop Sale</a> | ";
}
$ret .= "<a href=\"\" onclick=\"batchEdit.cutAll($id,$uid); return false;\">Cut All</a> ";
if ($dtype <= 0) {
$ret .= " | <a href=\"\" class=\"{$noprices}\" onclick=\"batchEdit.trimPcBatch($id); return false;\">Trim Unchanged</a> ";
} else {
$ret .= " | <span id=\"edit-limit-link\"><a href=\"\"
onclick=\"batchEdit.editTransLimit(); return false;\">" . ($hasLimit ? 'Edit' : 'Add' ) . " Limit</a></span>";
$ret .= "<span id=\"save-limit-link\" class=\"collapse\"><a href=\"\" onclick=\"batchEdit.saveTransLimit($id); return false;\">Save Limit</a></span>";
$ret .= " <span class=\"form-group form-inline\" id=\"currentLimit\" style=\"color:#000;\">{$limit}</span>";
}
$ret .= " | <a data-toggle='modal' data-target='#myModal'>Batch History</a>";
$ret .= '</span>';
if ($authorized === true && $type == 4) {
$ret .= " | <a href='' onclick='batchEdit.logBatch($id); return false;'>
<span class='btn-info'>Admin</span>: Stage Price Change</a>";
}
/**
Insert extra fields to manage partial day batch
*/
if ($typeModel->editorUI() == 3) {
$partialP = $dbc->prepare('SELECT * FROM PartialBatches WHERE batchID=?');
$partial = $dbc->getRow($partialP, array($id));
$ret .= '<table class="table small table-bordered">';
$ret .= '<tr><th>Start Time</th><th>End Time</th><th>Override</th><th>Frequency</th></tr>';
$ret .= sprintf('<tr><td><input type="text" class="form-control small partialBatch"
onchange="batchEdit.updatePartial(%d);"
name="pStart" placeholder="HH:MM" value="%s" /></td>', $id, $partial['startTime']);
$ret .= sprintf('<td><input type="text" class="form-control small partialBatch"
onchange="batchEdit.updatePartial(%d);"
name="pEnd" placeholder="HH:MM" value="%s" /></td>', $id, $partial['endTime']);
$ret .= sprintf('<td><input type="checkbox" class="partialBatch" name="pOver" %s value="1"
onchange="batchEdit.updatePartial(%d);" /></td>',
($partial['overwriteSales'] ? 'checked' : ''), $id);
$ret .= '<td><select name="pRepeat" class="form-control small partialBatch"
onchange="batchEdit.updatePartial(' . $id . ');">';
foreach (array('daily', 'weekdays', 'short weekends', 'long weekends', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday') as $r) {
$ret .= sprintf('<option %s value="%s">%s</option>',
($r == $partial['repetition'] ? 'selected' : ''), $r, ucwords($r));
}
$ret .= '</select></td></tr></table>';
}
$ret .= "<br />";
$ret .= "<table id=yeoldetable class=\"table\">";
$ret .= "<tr>";
if ($orderby != "ORDER BY b.upc ASC") {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=upc_a\">UPC</a></th>";
} else {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=upc_d\">UPC</a></th>";
}
if ($orderby != "ORDER BY p.brand ASC") {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=brand_a\">Brand</a></th>";
} else {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=brand_d\">Brand</a></th>";
}
if ($orderby != "ORDER BY description ASC") {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=desc_a\">Description</a></th>";
} else {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=desc_d\">Description</a></th>";
}
if ($orderby != "ORDER BY p.normal_price DESC") {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=price_d\">$normalPriceHeader</a></th>";
} else {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=price_a\">Normal Price</a></th>";
}
if ($orderby != "ORDER BY b.salePrice DESC") {
$ret .= "<th class=\"{$noprices}\"><a href=\"EditBatchPage.php?id=$id&sort=sale_d\">$saleHeader</a></th>";
} else {
$ret .= "<th class=\"{$noprices}\"><a href=\"EditBatchPage.php?id=$id&sort=sale_a\">$saleHeader</a></th>";
}
$ret .= "<th class=\"hidden-print\" colspan=\"3\"> </th>";
if ($orderby != 'ORDER BY locationName') {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=loc_a\">Location</a></th>";
} else {
$ret .= "<th><a href=\"EditBatchPage.php?id=$id&sort=loc_d\">Location</a></th>";
}
$ret .= "</tr>";
$colors = array('#ffffff','#ffffcc');
$cur = 0;
$upcFields = '';
$upcs = '';
$allLCs = true;
$products = new ProductsModel($dbc);
while ($fetchW = $dbc->fetchRow($fetchR)) {
$products->reset();
$products->upc($fetchW['upc']);
if ($this->config->get('STORE_MODE') == 'HQ') {
$storeLocation = COREPOS\Fannie\API\lib\Store::getIdByIp();
$products->store_id($storeLocation);
}
$products->load();
$tr_style = ($products->inUse() === '0') ? "style=\"color: black; background-color: lightgrey;\"" : "";
$cur = ($cur + 1) % 2;
$ret .= "<tr>";
$fetchW['upc'] = rtrim($fetchW['upc']);
if (substr($fetchW['upc'],0,2) == "LC") {
$likecode = rtrim(substr($fetchW['upc'],2));
$ret .= "<td bgcolor=$colors[$cur]>" . $fetchW['upc'];
$ret .= " <a href=\"\" onclick=\"\$('.lc-item-{$likecode}').toggle(); return false;\">[+]</a>";
$ret .= "</td>";
} else {
$allLCs = false;
$upcFields .= sprintf('<input type="hidden" name="u[]" value="%s" />', $fetchW['upc']);
$upcs .= $fetchW['upc'] . "\n";
$conflict = '';
if ($dtype > 0) {
//$overlapR = $dbc->execute($overlapP, array_merge(array($fetchW['upc'], $id), $overlap_args));
//if ($overlapR && $dbc->numRows($overlapR)) {
if (isset($allOverlap[$fetchW['upc']])) {
//$overlap = $dbc->fetchRow($overlapR);
$overlap = $allOverlap[$fetchW['upc']];
$conflict = sprintf('<a href="EditBatchPage.php?id=%d" target="_batch%d"
title="!!Conflicts with batch %s" class="btn btn-xs btn-danger">
<span class="fas fa-exclamation-circle">
</span></a>',
$overlap['batchID'], $overlap['batchID'],
$overlap['batchName']);
}
}
if ($fetchW['priceRuleID'] != NULL && $fetchW['maxPrice'] > 0) {
$mp = $fetchW['maxPrice'];
$sp = $fetchW['salePrice'];
if ($sp < $mp && $fetchW['priceRuleTypeID'] == 10) {
$conflict .= '<a href="#" class="btn btn-warning btn-xs"
title="Sale price falls below MAP restriction. Minimum Price: $'.$mp.'">
<span class="fas fa-exclamation-circle"></span></span>';
}
}
$ret .= "<td bgcolor=$colors[$cur]><a href=\"{$FANNIE_URL}item/ItemEditorPage.php?searchupc={$fetchW['upc']}\"
target=\"_new{$fetchW['upc']}\">{$fetchW['upc']}</a>{$conflict}</td>";
}
$ret .= "<td bgcolor=$colors[$cur]><span $tr_style>{$fetchW['brand']}</span></td>";
$ret .= "<td bgcolor=$colors[$cur]><span $tr_style>{$fetchW['description']}</span></td>";
if ($type != 17) {
$ret .= "<td bgcolor=$colors[$cur] class=\"price\">{$fetchW['normal_price']}</td>";
} else {
$ret .= "<td bgcolor=$colors[$cur] class=\"price\">{$fetchW['cost']}</td>";
}
$qtystr = ($fetchW['pricemethod']>0 && is_numeric($fetchW['quantity']) && $fetchW['quantity'] > 0) ? $fetchW['quantity'] . " for " : "";
$qty = is_numeric($fetchW['quantity']) && $fetchW['quantity'] > 0 ? $fetchW['quantity'] : 1;
$ret .= "<td bgcolor=$colors[$cur] class=\"{$noprices} saleprice\">";
$ret .= '<span id="editable-text-' . $fetchW['upc'] . '">';
$ret .= '<span class="editable-' . $fetchW['upc'] . ($qty == 1 ? ' collapse ' : '') . '"'
. ' id="item-qty-' . $fetchW['upc'] . '" data-name="qty">'
. $qty . ' for </span>';
$ret .= "<span class=\"editable-{$fetchW['upc']}\"
id=\"sale-price-{$fetchW['upc']}\" data-name=\"price\">"
. sprintf("%.2f</span>",$fetchW['salePrice']);
$ret .= '</span>';
$ret .= '<div class="form-group form-inline collapse" id="editable-fields-' . $fetchW['upc'] . '">';
$ret .= '<div class="input-group">';
$ret .= sprintf('<input type="text" class="form-control" name="qty" value="%d" />', $qty);
$ret .= '<span class="input-group-addon">for</span>';
$ret .= '<span class="input-group-addon">$</span>';
$ret .= sprintf('<input text="text" class="form-control" name="price" value="%.2f" />', $fetchW['salePrice']);
$ret .= '</div></div></td>';
$ret .= "<td class=\"hidden-print\" bgcolor=$colors[$cur] id=editLink{$fetchW['upc']}>
<a href=\"\" class=\"edit {$noprices}\" onclick=\"batchEdit.editUpcPrice('{$fetchW['upc']}'); return false;\">
" . \COREPOS\Fannie\API\lib\FannieUI::editIcon() . "</a>
<a href=\"\" class=\"save collapse\" onclick=\"batchEdit.saveUpcPrice('{$fetchW['upc']}'); return false;\">
" . \COREPOS\Fannie\API\lib\FannieUI::saveIcon() . "</a>
</td>";
$ret .= "<td class=\"hidden-print\" bgcolor=$colors[$cur]><a href=\"\"
onclick=\"batchEdit.deleteUPC.call(this, $id, '{$fetchW['upc']}'); return false;\">"
. \COREPOS\Fannie\API\lib\FannieUI::deleteIcon() . "</a>
</td>";
if ($fetchW['isCut'] == 1) {
$ret .= "<td class=\"hidden-print\" bgcolor=$colors[$cur] id=cpLink{$fetchW['upc']}>
<a href=\"\" class=\"unCutLink\" id=\"unCut{$fetchW['upc']}\" onclick=\"batchEdit.cutItem('{$fetchW['upc']}',$id,$uid, 0); return false;\">Undo</a>
<a href=\"\" class=\"cutLink collapse\" id=\"doCut{$fetchW['upc']}\" onclick=\"batchEdit.cutItem('{$fetchW['upc']}',$id,$uid, 1); return false;\">Cut</a>
</td>";
} else {
$ret .= "<td class=\"hidden-print\" bgcolor=$colors[$cur] id=cpLink{$fetchW['upc']}>
<a href=\"\" class=\"unCutLink collapse\" id=\"unCut{$fetchW['upc']}\" onclick=\"batchEdit.cutItem('{$fetchW['upc']}',$id,$uid,0); return false;\">Undo</a>
<a href=\"\" class=\"cutLink\" id=\"doCut{$fetchW['upc']}\" onclick=\"batchEdit.cutItem('{$fetchW['upc']}',$id,$uid,1); return false;\">Cut</a>
</td>";
}
$loc = 'n/a';
if (!empty($fetchW['locationName'])) {
$loc = $fetchW['locationName'];
}
$ret .= "<td bgcolor=$colors[$cur]>".$loc.'</td>';
$ret .= '<input type="hidden" class="batch-hidden-upc" value="' . $fetchW['upc'] . '" />';
$ret .= "</tr>";
if (substr($fetchW['upc'], 0, 2) == "LC") {
$likecode = rtrim(substr($fetchW['upc'],2));
$ret .= self::likeToTable($dbc, $likecode, $fetchW['salePrice']);
}
}
$ret .= "</table>";
$ret .= "<input type=hidden id=currentBatchID value=$id />";
$ret .= '<label>Notes</label><textarea name="batchNotes" id="batchNotes" class="form-control" rows="4"
onchange="batchEdit.saveNotes(' . $id . ');" onkeyup="batchEdit.noteTyped(' . $id . ');">'
. $model->notes() . '</textarea>';
if ($dbc->numRows($fetchR) > 0) {
$ret .= '<p>
<a href="BatchImportExportPage.php?id=' . $id . '">Export as JSON</a>
| <a href="BatchExportExcel.php?id=' . $id . '">Export as Excel</a>
| <a href="" onclick="$(\'#previousPromos\').submit(); return false;">Previous Promos</a>
| <a href="" onclick="$(\'#searchForm\').submit(); return false;">Search These</a>
<form method="post" id="previousPromos" action="../../reports/from-search/PreviousPromos/PreviousPromosReport.php">
' . $upcFields . '</form>
<form method="post" id="searchForm" action="../../item/AdvancedItemSearch.php">
<input type="hidden" name="extern" value="1" />
<input type="hidden" name="upcs" value="' . $upcs . '" />
</p>';
}
if ($allLCs && $model->discountType() == 0) {
$ret = str_replace('SignFromSearch.php?batch', 'LikeCodeBatchSigns.php?id', $ret);
}
return $ret;
}
private static $like_stmt = null;
private static $like_args = null;
private static function likeToTable($dbc, $likecode, $salePrice)
{
// singleton prepared statement
if (self::$like_stmt === null) {
$likeQ = "
SELECT p.upc,
p.description,
p.normal_price
FROM products AS p
INNER JOIN upcLike AS u ON p.upc=u.upc
WHERE 1=1 ";
self::$like_args = array();
if (FannieConfig::config('STORE_MODE') == 'HQ') {
$likeQ .= " AND p.store_id=? ";
self::$like_args[] = FannieConfig::config('STORE_ID');
}
$likeQ .= "
AND u.likeCode=?
ORDER BY p.upc DESC";
self::$like_stmt = $dbc->prepare($likeQ);
}
$likeR = $dbc->execute(self::$like_stmt, array_merge(self::$like_args, array($likecode)));
$ret = '';
$FANNIE_URL = FannieConfig::config('URL');
while ($likeW = $dbc->fetch_row($likeR)) {
$ret .= '<tr class="collapse lc-item-' . $likecode . '">';
$ret .= "<td><a href=\"{$FANNIE_URL}item/ItemEditorPage.php?searchupc={$likeW['upc']}\"
target=_new{$likeW['upc']}>{$likeW['upc']}</a></td>";
$ret .= "<td>{$likeW['description']}</td>";
$ret .= "<td>{$likeW['normal_price']}</td>";
$ret .= "<td>{$salePrice}</td>";
$ret .= "<td> </td>";
$ret .= "<td> </td>";
$ret .= '</tr>';
}
return $ret;
}
private function pairedTableBody($dbc, $result, $down=true)
{
$colors = array('#ffffff','#ffffcc');
$cur = 0;
$FANNIE_URL = $this->config->get('URL');
$ret = '';
while ($fetchW = $dbc->fetchRow($result)) {
$cur = ($cur + 1) % 2;
$ret .= "<tr>";
$fetchW[0] = rtrim($fetchW[0]);
if (substr($fetchW[0],0,2) == "LC") {
$likecode = rtrim(substr($fetchW[0],2));
$ret .= "<td bgcolor=$colors[$cur]>" . $fetchW['upc'];
$ret .= " <a href=\"\" onclick=\"\$('.lc-item-{$likecode}').toggle(); return false;\">[+]</a>";
$ret .= "</td>";
} else {
$ret .= "<td bgcolor=$colors[$cur]><a href={$FANNIE_URL}item/ItemEditorPage.php?searchupc=$fetchW[0] target=_new$fetchW[0]>$fetchW[0]</a></td>";
}
$ret .= "<td bgcolor=$colors[$cur]>$fetchW[1]</td>";
$showDown = $down ? '' : 'collapse';
$showUp = $down ? 'collapse' : '';
$ret .= "<td bgcolor=$colors[$cur]>
<a href=\"\" class=\"down-arrow {$showDown}\" onclick=\"batchEdit.swapQualifierToDiscount(this, '$fetchW[0]'); return false;\">
<img src=\"{$FANNIE_URL}src/img/buttons/arrow_down.gif\" alt=\"Make Discount Item\" /></a>
<a href=\"\" class=\"up-arrow {$showUp}\" onclick=\"batchEdit.swapDiscountToQualifier(this, '$fetchW[0]'); return false;\">
<img src=\"{$FANNIE_URL}src/img/buttons/arrow_up.gif\" alt=\"Make Qualifying Item\" />
</a>
</td>";
$ret .= "<td bgcolor=$colors[$cur]><a href=\"\" onclick=\"batchEdit.deleteUPC.call(this, {$fetchW['batchID']}, '$fetchW[0]'); return false;\">"
. \COREPOS\Fannie\API\lib\FannieUI::deleteIcon() . '</a></td>';
$ret .= "</tr>";
if (substr($fetchW['upc'], 0, 2) == "LC") {
$likecode = rtrim(substr($fetchW['upc'],2));
$ret .= self::likeToTable($dbc, $likecode, $fetchW['salePrice']);
}
}
return $ret;
}
protected function showPairedBatchDisplay($id, $name)
{
global $FANNIE_SERVER_DBMS;
$dbc = $this->connection;
$uid = getUID($this->current_user);
$uid = ltrim($uid,'0');
$ret = "";
$ret .= sprintf('<input type="hidden" id="currentBatchID" value="%d" />',$id);
$ret .= "<b>Batch name</b>: <input type=\"text\" class=\"editable\" value=\"$name\"
name=\"batchName\" /><br />";
$ret .= "<a href=\"BatchListPage.php\">Back to batch list</a> | ";
$ret .= "<a href=\"\" onclick=\"batchEdit.forceNow($id); return false;\">Force batch</a>";
$ret .= " | No limit";
$ret .= " <span id=\"currentLimit\" style=\"color:#000;\"></span>";
$q = $dbc->prepare("SELECT b.discounttype,salePrice,
CASE WHEN l.pricemethod IS NULL THEN 4 ELSE l.pricemethod END as pricemethod,
CASE WHEN l.quantity IS NULL THEN 1 ELSE l.quantity END as quantity
FROM batches AS b LEFT JOIN batchList AS l
ON b.batchID=l.batchID WHERE b.batchID=? ORDER BY l.pricemethod");
$r = $dbc->execute($q,array($id));
$w = $dbc->fetch_row($r);
if (!empty($w['salePrice'])){
$ret .= "<div class=\"well\">Add all items before fiddling with these settings
or they'll tend to go haywire</div>";
$ret .= '<div id="paired-fields">
<div class="form-group form-inline">';
$ret .= '<label>Member only sale
<input type="checkbox" name="member" value="1" '
.($w['discounttype']==2?'checked':'').' />
</label>';
$ret .= ' | ';
$ret .= '<label>Split discount
<input type="checkbox" name="split" value="1" '
.($w['pricemethod']==4?'':'checked').' />
</label>';
$ret .= '</div>';
$ret .= '<div class="form-group form-inline">';
$ret .= '<label>Qualifiers Required</label> ';
$ret .= sprintf('<input type="number" class="form-control" value="%d"
name="qualifiers" />',
$w['quantity']-1);
$ret .= ' <label>Discount</label> ';
$ret .= sprintf('<div class="input-group">
<span class="input-group-addon">$</span>
<input type="text" class="form-control" value="%.2f"
name="discount" /></div>',
(empty($w['salePrice'])?'':abs($w['salePrice'])));
$ret .= sprintf(' <button type="submit" class="btn btn-default"
onclick="batchEdit.savePairedPricing(%d); return false;">Update Pricing</button>',$id);
$ret .= '</div>';
$ret .= '</div>'; // end #paired-fields
} else {
$ret .= "<div class=\"alert alert-warning\">Add items first</div>";
}
// logically string "LC" followed by like code number
$joinColumn = $dbc->concat("'LC'", $dbc->convert('l.likeCode', 'CHAR'), '');
$fetchQ = $dbc->prepare("
SELECT b.upc,
case when l.likeCode is null then p.description else l.likeCodeDesc end as description,
p.normal_price,
b.salePrice,
b.batchID
FROM batchList AS b
" . DTrans::joinProducts('b') . "
LEFT JOIN likeCodes as l on b.upc = {$joinColumn}
WHERE b.batchID = ?
AND b.salePrice >= 0");
$fetchR = $dbc->execute($fetchQ,array($id));
$ret .= '<table class="table" id="qualifier-table">';
$ret .= '<tr><th colspan="4">Qualifying Item(s)</th></tr>';
$ret .= $this->pairedTableBody($dbc, $fetchR);
$ret .= "</table>";
$fetchQ = $dbc->prepare("
SELECT b.upc,
case when l.likeCode is null then p.description else l.likeCodeDesc end as description,
p.normal_price,
b.salePrice,
b.batchID
FROM batchList AS b
" . DTrans::joinProducts('b') . "
LEFT JOIN likeCodes as l on b.upc = {$joinColumn}
WHERE b.batchID = ?
AND b.salePrice < 0");
$fetchR = $dbc->execute($fetchQ,array($id));
$ret .= '<table class="table" id="discount-table">';
$ret .= '<tr><th colspan="4">Discount Item(s)</th></tr>';
$ret .= $this->pairedTableBody($dbc, $fetchR, false);
$ret .= "</table>";
return $ret;
}
public function get_id_paste_view()
{
return $this->get_id_view();
}
public function batch_history($bid)
{
include('../batchhistory/BatchHistoryPage.php');
$modal = '';
$modal .= '
<style>
.vertical-alignment-helper {
display:table;
height: 100%;
width: 100%;
pointer-events:none; /* This makes sure that we can still click outside of the modal to close it */
}
.vertical-align-center {
/* To center vertically */
display: table-cell;
vertical-align: middle;
pointer-events:none;
}
.modal-content {
/* Bootstrap sets the size of the modal in the modal-dialog class, we need to inherit it */
width:inherit;
height:inherit;
/* To center horizontally */
margin: 0 auto;
pointer-events: all;
}
</style>
';
$modal .= '
<!-- Modal -->
<div id="myModal" class="modal" role="dialog">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center">
<!-- Modal content-->
<div class="modal-content" style="height: 85vh; width: 85vw;">
<div style="max-height: 85vh; overflow-y:auto;">
';
$bhp = new BatchHistoryPage;
$modal .= $bhp->getBatchHistory($bid);
$modal .='
</div>
</div>
</div>
</div>
</div>
';
return $modal;
}
public function get_id_view()
{
$this->addScript($this->config->get('URL') . 'src/javascript/chosen/chosen.jquery.min.js');
$this->addCssFile($this->config->get('URL') . 'src/javascript/chosen/bootstrap-chosen.css');
$this->addScript('edit.js?20180524');
$this->addCssFile('index.css');
$this->addOnloadCommand('$(\'#addItemUPC\').focus()');
$this->addOnloadCommand("enableLinea('#addItemUPC');\n");
$cmd = <<<JAVASCRIPT
function resizeInput()
{
$('.be-editable').each(function(){
var elm = $(this);
var newWidth = (parseInt(elm.val().length, 10) + 1) * 10;
newWidth = newWidth.toString() + "px";
elm.css('width', newWidth);
});
$('.be-editable').on('keyup', function(){
var elm = $(this);
var newWidth = (parseInt(elm.val().length, 10) + 1) * 10;
newWidth = newWidth.toString() + "px";
elm.css('width', newWidth);
});
}
resizeInput();
JAVASCRIPT;
$this->addOnloadCommand($cmd);
$url = $this->config->get('URL');
$sort = FormLib::get('sort', 'natural');
$inputForm = $this->addItemUPCInput();
$test = 'test';
$batchList = $this->showBatchDisplay($this->id, $sort);
$linea = $this->enable_linea ? '<script type="text/javascript">' . $this->lineaJS() . '</script>' : '';
$history = $this->batch_history($this->id);
return <<<HTML
<div id="inputarea" class="hidden-print">
{$inputForm}
</div>
<div class="progress collapse" id="progress-bar">
<div class="progress-bar progress-bar-striped active"
role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"
style="width: 100%" title="Working">
<span class="sr-only">Working</span>
</div>
</div>
<div id="displayarea">
{$batchList}
</div>
<input type=hidden id=uid value="{$this->current_user}" />
<input type=hidden id=isAudited value="{$this->audited}" />
<input type="hidden" id="batchID" value="{$this->id}" />
<input type=hidden id=buttonimgpath value="{$url}src/img/buttons/" />
{$linea}
{$history}
HTML;
}
// intentionally blank so Linea device javascript
// isn't appended to AJAX responses
public function postFlight()
{
}
public function helpContent()
{
return '<p>Add one or more items to the batch. Enter a UPC by default or check
the likecode option to add items by likecode. Next enter a price. Note that if
you enter an incorrect UPC (or likecode) you can simply press enter with the price
field blank to skip that item and enter another UPC (or likecode)</p>
<p><em>Force Batch</em> will apply the batch prices immediately and push those
changes to the lanes. Forcing a batch will ignore start and end dates.</p>
<p><em>Stop Sale</em> will take items off sale immediately. However,
depending on start and end dates the batch may be reapplied on the next automated
batch update. Change the dates or delete the batch after stopping the sale
if needed.</p>
<p><em>Auto-tag</em> creates shelf tags for the batch using the batch price
and vendor catalog data. This is primarily used with price change batches.
<em>Print shelf tags</em> of course shows the actual tags.</p>
<p><em>Add Limit</em> creates a per-transaction limit on each item in the
batch. Setting a limit of one for example means the sale price only applies
once per transaction. Additional identical items ring up at regular price. This limit
applies to each item individually rather than all items in the batch
collectively. These limits cannot be used for volume sale price (i.e., 2-for-$1).</p>
<p><em>Cut</em> and <em>Paste</em> can move items items from one batch to
another. This feature requires user authentication so that each user has their
own clipboard and don\'t interfere with each other.</p>
<p><em>Items highlighted</em> in grey are not in-use in POS for the store the
batch is being viewed from.</p>
';
}
public function unitTest($phpunit)
{
$this->id = 1;
$this->paste = 1;
$phpunit->assertEquals(true, $this->get_id_paste_handler());
$phpunit->assertNotEquals(0, strlen($this->get_id_paste_view()));
ob_start();
$phpunit->assertEquals(false, $this->post_id_trim_handler());
ob_get_clean();
$this->upc = '0000000004011';
ob_start();
$phpunit->assertEquals(false, $this->post_id_upc_swap_handler());
ob_get_clean();
ob_start();
$phpunit->assertEquals(false, $this->delete_id_upc_handler());
ob_get_clean();
$this->limit = 1;
$phpunit->assertEquals(false, $this->post_id_limit_handler());
ob_start();
$phpunit->assertEquals(false, $this->post_id_unsale_handler());
ob_get_clean();
ob_start();
$phpunit->assertEquals(false, $this->post_id_force_handler());
ob_get_clean();
ob_start();
$phpunit->assertEquals(false, $this->post_id_autotag_handler());
ob_get_clean();
$this->addUPC = $this->upc;
ob_start();
$phpunit->assertEquals(false, $this->post_id_addUPC_handler());
ob_get_clean();
$this->addLC = 1;
ob_start();
$phpunit->assertEquals(false, $this->post_id_addLC_handler());
ob_get_clean();
$phpunit->assertEquals(true, $this->get_id_paste_handler());
}
}
FannieDispatch::conditionalExec();