CORE-POS/IS4C

View on GitHub
fannie/item/modules/ProdUserModule.php

Summary

Maintainability
D
3 days
Test Coverage
F
46%
<?php
/*******************************************************************************

    Copyright 2014 Whole Foods Co-op, Duluth, MN

    This file is part of CORE-POS.

    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

*********************************************************************************/

if (!class_exists('FannieAPI')) {
    include_once(dirname(__FILE__).'/../../classlib2.0/FannieAPI.php');
}

class ProdUserModule extends \COREPOS\Fannie\API\item\ItemModule 
{

    public function showEditForm($upc, $display_mode=1, $expand_mode=1)
    {
        $FANNIE_URL = FannieConfig::config('URL');
        $upc = BarcodeLib::padUPC($upc);

        $ret = '<div id="ProdUserFieldset" class="panel panel-default">';
        $ret .=  "<div class=\"panel-heading\">
                <a href=\"\" onclick=\"\$('#ProdUserFieldsetContent').toggle();return false;\">
                Sign/Web Info</a>
                </div>";
        $css = ($expand_mode == 1) ? '' : ' collapse';
        $ret .= '<div id="ProdUserFieldsetContent" class="panel-body' . $css . '">';

        $dbc = $this->db();
        $model = new ProductUserModel($dbc);
        $s_def = $dbc->tableDefinition('SignProperties');
        if (isset($s_def['signCount'])) {
            $signPropertiesModel = new SignPropertiesModel($dbc);
        };
        $model->upc($upc);
        $model->load();

        $sections = new FloorSectionsModel($dbc);
        $sectionP = $dbc->prepare("SELECT s.description AS store,
                f.floorSectionID,
                f.name,
                CASE WHEN m.upc IS NULL THEN 0 ELSE 1 END AS matched,
                b.subSection AS sub
            FROM FloorSections AS f
                LEFT JOIN Stores AS s ON f.storeId=s.storeID
                LEFT JOIN FloorSectionProductMap AS m ON f.floorSectionID=m.floorSectionID AND m.upc=?
                LEFT JOIN FloorSubSections AS b ON f.floorSectionID=b.floorSectionID AND b.upc=m.upc
            ORDER BY s.description, f.name");
        $sectionR = $dbc->execute($sectionP, array($upc));
        $sections = array();
        $marked = array();
        while ($row = $dbc->fetchRow($sectionR)) {
            $sections[$row['floorSectionID']] = array('label'=> $row['store'] . ' ' . $row['name'], 'matches' => $row['matched'], 'sub'=>$row['sub']);
            if ($row['matched']) {
                $marked[] = $row['floorSectionID'];
            }
        }

        $originP = $dbc->prepare('SELECT current_origin_id FROM products WHERE upc=?');
        $originID = $dbc->getValue($originP, array($upc));

        /**
         * Migrating to SignProperties if available
         * Create a table of per-store sign properties using the values
         * from productUser if the newer SignProperties table is not
         * populated yet.
         *
         * Check for <td> at the end to ensure a table was built and
         * if not fall back to the old behavior
         */
        $haveProperties = $dbc->tableExists('SignProperties');
        if ($haveProperties) {
            $propP = $dbc->prepare("SELECT storeID, narrow, signCount FROM SignProperties WHERE upc=?");
            $propR = $dbc->execute($propP, array($upc));
            $props = array();
            while ($row = $dbc->fetchRow($propR)) {
                $props[$row['storeID']] = $row;
            }
            $propsTable = '<table class="table">';
            $propsTable .= '<tr><th>Store</th><th>Sign Count</th><th>Narrow Tag</th></tr>';
            $storeR = $dbc->execute("SELECT storeID, description FROM Stores WHERE hasOwnItems=1");
            while ($row = $dbc->fetchRow($storeR)) {
                $propSet = array('signCount' => $model->signCount(), 'narrow' => $model->narrow());
                if (isset($props[$row['storeID']])) {
                    $propSet = $props[$row['storeID']];
                }
                $propsTable .= sprintf('<tr>
                    <td>%s<input type="hidden" name="lf_store[]" value="%d" /></td>
                    <td><input type="text" class="form-control" name="sign-count[]" value="%d" /></td>
                    <td><input type="checkbox" name="narrowTag[]" value="%d" %s /></td>
                    </tr>',
                    $row['description'],
                    $row['storeID'],
                    $propSet['signCount'],
                    $row['storeID'],
                    $propSet['narrow'] ? 'checked' : '');
            }
            $propsTable .= '</table>';
            if (strpos($propsTable, '<td>') === false) {
                $haveProperties = false;
            }
        }

        $ret .= '<div class="col-sm-6">';
        $ret .= '<div class="row form-group">'
                . '<label class="col-sm-1">Brand</label> '
                . '<div class="col-sm-8">'
                . '<input type="text" class="form-control" id="lf_brand" name="lf_brand" value="' . $model->brand() . '" 
                    placeholder="Optional longer brand" />'
                . '</div>'
                . '<div class="col-sm-3">'
                . ' <a href="" onclick="createSign(); return false;">Make Sign</a>'
                . '</div>'
                . '</div>';
        $ret .= '<div class="row form-group">'
                . '<label class="col-sm-1">Desc.</label> '
                . '<div class="col-sm-8">'
                . '<textarea class="form-control" rows="2" id="lf_desc" name="lf_desc"
                    placeholder="Optional longer description; values from above are used if this brand and description are blank." >'
                . $model->description()
                . '</textarea>'
                . '</div>'
                . '</div>';

        if ($haveProperties) {
            $ret .= $propsTable;
        } else {
            $ret .= '<div class="row form-group">'
                    . '<label class="small col-sm-1">Sign Ct.</label> '
                    . '<div class="col-sm-4">'
                    . '<input type="number" class="form-control price-field"
                        name="sign-count" value="' . $model->signCount() . '" />'
                    . '</div>'
                    . '<div class="col-sm-3">'
                    . '<label>Narrow Tag <input type="checkbox" value="1" name="narrowTag" ' . ($model->narrow() ? 'checked' : '') . ' /></label>'
                    . '</div>'
                    . '</div>';
        }

        // ensure there's always an extra <select> for new entries
        $marked[] = -1;
        for ($i=0; $i<count($marked); $i++) {
            $ret .= '<div class="row form-group">
                        <label title="Location on the floor" class="col-sm-1">Loc.</label>
                        <div class="col-sm-6">
                            <select name="floorID[]" class="form-control">
                            <option value="0">n/a</option>';
            $sub = '';
            foreach ($sections as $id => $arr) {
                $ret .= sprintf('<option %s value="%d">%s</option>',
                        isset($marked[$i]) && $marked[$i] == $id ? 'selected' : '',
                        $id, $arr['label']
                );
                if (isset($marked[$i]) && $marked[$i] == $id && $arr['sub']) {
                    $sub = $arr['sub'];
                }
            }
            $ret .= '</select>
                    </div>
                    <div class="col-sm-2">
                        <input type="text" class="form-control input-sm" name="floorSub[]" value="' . $sub . '" />
                    </div>
                    <div class="col-sm-3 text-left">
                        ' . ($i == 0 ? '<a href="mapping/FloorSectionsPage.php" target="_blank">Add more</a>' : '') . '
                    </div>
                    </div>';
        }
        foreach ($marked as $m) {
            if ($m > 0) {
                $ret .= '<input type="hidden" name="currentFloor[]" value="' . $m . '" />';
        }
        }

        $otherOriginBlock = '<div class=row>
                <div class=col-sm-1 />
                <div class=col-sm-8>
            <select name=otherOrigin[] class=form-control><option value=0>n/a</option>';

        $ret .= '<div class="row form-group">'
                . '<label class="col-sm-1"><a href="' . $FANNIE_URL . 'item/origins/OriginEditor.php">Origin</a></label>'
                . '<div class="col-sm-8">'
                . ' <select name="origin" class="form-control">'
                . '<option value="0">n/a</option>';
        $origins = new OriginsModel($dbc);
        $origins->local(0);
        foreach ($origins->find('name') as $o) {
            $ret .= sprintf('<option %s value="%d">%s</option>',
                        $originID == $o->originID() ? 'selected' : '',
                        $o->originID(), $o->name());
            $otherOriginBlock .= sprintf('<option value=%d>%s</option>',
                                            $o->originID(), $o->name());
        }
        $ret .= '</select></div>';
        $otherOriginBlock .= '</div></div>';
        $otherOriginBlock = str_replace("\n", "", $otherOriginBlock);
        $ret .= '<div class="col-sm-3 text-left">';
        $ret .= '&nbsp;&nbsp;&nbsp;&nbsp;<a href="" 
                onclick="$(\'#originsBeforeMe\').before(\'' . $otherOriginBlock . '\'); return false;">Add more</a>';
        $ret .= '</div></div>';

        $mapP = 'SELECT originID FROM ProductOriginsMap WHERE upc=? AND originID <> ?';
        $mapR = $dbc->execute($mapP, array($upc, $originID));
        while ($mapW = $dbc->fetch_row($mapR)) {
            $ret .= '<div class="row form-group">'
                . '<label class="col-sm-1"><a href="' . $FANNIE_URL . 'item/origins/OriginEditor.php">Origin</a></label>'
                . '<div class="col-sm-8">';
            $ret .= '<select name="otherOrigin[]" class="form-control"><option value="0">n/a</option>';
            foreach ($origins->find('name') as $o) {
                $ret .= sprintf('<option %s value="%d">%s</option>',
                            $mapW['originID'] == $o->originID() ? 'selected' : '',
                            $o->originID(), $o->name());
            }
            $ret .= '</select></div></div>';
        }
        $ret .= '<div id="originsBeforeMe"></div>';
        $ret .= '<label>Item Photo</label>';
        $ret .= $this->imageInput($model->photo(), 'item');
        $ret .= '</div>';

        $ret .= '<div class="col-sm-6">';
        $ret .= '<div class="form-group"><label>Ad Text</label></div>';
        $ret .= '<div class="form-group">
                <textarea name="lf_text" class="form-control"
                    rows="8" cols="45">' 
                    . str_replace('<br />', "\n", $model->long_text()) 
                    . '</textarea></div>';

        $ret .= '<label>Nutrition Facts</label>';
        $ret .= $this->imageInput($model->nutritionFacts(), 'nf');
        $ret .= '</div>';
        $ret .= '</div>';
        $ret .= '</div>';

        return $ret;
    }

    private function imageInput($filename, $type='item')
    {
        $url = $this->imageUrl($type);
        $file = dirname(__FILE__) . '/../' . $url . $filename;
        $ret = '';
        if (file_exists($file) && is_file($file)) {
            $ret .= sprintf('<div><img src="%s" alt="image" width="200" /></div>', $url . $filename);
        } else {
            $ret .= '<div class="alert alert-info">Currently no image</div>';
        }
        $ret .= '<br /><label>Upload new image</label>
            <input type="file" name="image_' . $type . '" class="form-control" 
                accept="image/png" />';

        return $ret;
    }

    private function imageUrl($type)
    {
        switch (strtolower($type)) {
            case 'item':
                return 'images/done/';
            case 'nf':
                return 'images/nutrition-facts/';
            default:
                return false;
        }
    }

    /**
     * Update location data for the item (FloorSectionProductMap)
     * @param $dbc [SQLManager] database connection
     * @param $upc [string] UPC
     * @param $floorIDs [array] submitted floor section IDs
     * @param $oldFloorIDs [array] current floor section IDs assigned to the item
     *
     * This method compares new and old values instead of just DELETING the current entries
     * and re-populating with the submitted values. This is mostly to avoid churning
     * through identity column values on every save.
     */
    private function saveLocation($dbc, $upc, $floorIDs, $oldFloorIDs, $floorSubs)
    {
        $insP = $dbc->prepare('INSERT INTO FloorSectionProductMap (floorSectionID, upc) VALUES (?, ?)');
        $upP = $dbc->prepare('UPDATE FloorSectionProductMap SET floorSectionID=? WHERE floorSectionID=? AND upc=?');
        $delP = $dbc->prepare('DELETE FROM FloorSectionProductMap WHERE floorSectionID=? AND upc=?');
        $clearSubP = $dbc->prepare("DELETE FROM FloorSubSections WHERE upc=?");
        $dbc->execute($clearSubP, array($upc));
        $addSubP = $dbc->prepare("INSERT INTO FloorSubSections (upc, floorSectionID, subSection) VALUES (?, ?, ?)");
        for ($i=0; $i<count($floorIDs); $i++) {
            $newID = $floorIDs[$i];
            if ($newID == 0 && isset($oldFloorIDs[$i]))  {
                $dbc->execute($delP, array($oldFloorIDs[$i], $upc));
            } elseif (isset($oldFloorIDs[$i]) && $newID != $oldFloorIDs[$i]) {
                $dbc->execute($upP, array($newID, $oldFloorIDs[$i], $upc));
            } elseif ($newID != 0 && (!isset($oldFloorIDs[$i]) || $newID != $oldFloorIDs[$i])) {
                $dbc->execute($insP, array($newID, $upc));
            }
            if ($newID && trim($floorSubs[$i])) {
                $dbc->execute($addSubP, array($upc, $newID, strtolower($floorSubs[$i])));
            }
        }
    }

    function SaveFormData($upc)
    {
        $upc = BarcodeLib::padUPC($upc);
        $brand = FormLib::get('lf_brand');
        $desc = FormLib::get('lf_desc');
        $origin = FormLib::get('origin', 0);
        $floorIDs = FormLib::get('floorID', array());
        $floorSubs = FormLib::get('floorSub', array());
        $oldfloorIDs = FormLib::get('currentFloor', array());
        $narrowTag = FormLib::get('narrowTag');
        $text = FormLib::get('lf_text');
        $text = str_replace("\r", '', $text);
        $text = str_replace("\n", '<br />', $text);
        // strip non-ASCII (word copy/paste artifacts)
        $text = preg_replace("/[^\x01-\x7F]/","", $text); 
        $signCount = FormLib::get('sign-count');

        $dbc = $this->db();

        /*
         * Means SignProperties data was sent
         * Save those records & also populate variables
         * so that productUser will get values to store.
         * By convention, productUser will track values
         * for store #1.
         */
        if (is_array($signCount)) {
            $narrowTag = FormLib::get('narrowTag', array()); // ensure array if none checked
            $narrow = in_array(1, $narrowTag) ? 1 : 0; // for use in productUser
            $signs = $signCount[0]; // for use in productUser
            $props = new SignPropertiesModel($dbc);
            $stores = FormLib::get('lf_store');
            for ($i = 0; $i< count($stores); $i++) {
                $props->storeID($stores[$i]);
                $props->upc($upc);
                $props->signCount($signCount[$i] > 0 ? $signCount[$i] : 1);
                $props->narrow(in_array($stores[$i], $narrowTag) ? 1 : 0);
                $props->save();
            }
        } else {
            $signs = $signCount;
            $narrow = $narrowTag ? 1 : 0;
        }

        if ($signs < 1) {
            $signs = 1;
        }

        $this->saveLocation($dbc, $upc, $floorIDs, $oldfloorIDs, $floorSubs);

        $model = new ProductUserModel($dbc);
        $model->upc($upc);
        $model->brand($brand);
        $model->description($desc);
        $model->long_text($text);
        $model->signCount($signs);
        $model->narrow($narrow);

        $multiOrigin = FormLib::get('otherOrigin', array());
        $originMap = array();
        if ($origin != 0) {
            $originMap[] = $origin;
        }
        foreach ($multiOrigin as $originID) {
            if ($originID != 0) {
                $originMap[] = $originID;
            }
        }
        
        $mapP = $dbc->prepare('DELETE FROM ProductOriginsMap WHERE upc=?');
        $addP = $dbc->prepare('INSERT INTO ProductOriginsMap
                                (originID, upc, active)
                                VALUES (?, ?, 1)');

        $lcP = $dbc->prepare('SELECT u.upc
                            FROM upcLike AS u
                                ' . DTrans::joinProducts('u', 'p', 'INNER') . '
                            WHERE u.likeCode IN (
                                SELECT l.likeCode
                                FROM upcLike AS l
                                WHERE l.upc = ?
                            )');
        $lcR = $dbc->execute($lcP, array($upc));
        $items = array($upc);
        while ($w = $dbc->fetch_row($lcR)) {
            if ($w['upc'] == $upc) {
                continue;
            }
            $items[] = $w['upc'];
        }

        $prodP = $dbc->prepare('UPDATE products SET current_origin_id=? WHERE upc=?');
        foreach ($items as $item) {
            // not adding to prodUpdate here is intentional
            $dbc->execute($prodP, array($origin, $item));

            $dbc->execute($mapP, array($item));
            foreach ($originMap as $originID) {
                $dbc->execute($addP, array($originID, $item));
            }
        }

        $model = $this->savePhotos($model);
        
        return $model->save();
    }

    private function savePhotos($model)
    {
        if (($file=$this->savePhoto($model->upc(), 'item')) !== false) {
            $model->photo($file);
        }
        if ($this->savePhoto($model->upc(), 'nf')) {
            $model->nutritionFacts(ltrim($model->upc(), '0') . '.png');
        }

        return $model;
    }

    private function savePhoto($upc, $type)
    {
        if ($this->validPhoto($type)) {
            $infile = $_FILES['image_' . $type]['tmp_name'];
            $pinfo = pathinfo($_FILES['image_'.$type]['name']);
            $outfile = dirname(__FILE__) . '/../' . $this->imageUrl($type) . ltrim($upc, 0) . '.' . $pinfo['extension'];
            if (file_exists($outfile)) {
                unlink($outfile);
            }
            $res = move_uploaded_file($infile, $outfile);

            return $res ? basename($outfile) : false;
        }

        return false;
    }

    private function validPhoto($type)
    {
        if (!isset($_FILES['image_' . $type])) {
            return false;
        }

        if ($_FILES['image_' . $type]['error'] !== UPLOAD_ERR_OK) {
            return false;
        }

        /*
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        if ($finfo->file($_FILES['image_' . $type]['tmp_name']) !== 'image/png') {
            return false;
        }
         */

        return true;
    }

    public function getFormJavascript($upc)
    {
        $FANNIE_URL = FannieConfig::config('URL');
        ob_start();
        ?>
        function createSign()
        {
           var form = $('<form />',
                            { action: '<?php echo $FANNIE_URL; ?>admin/labels/SignFromSearch.php',
                              method: 'post',
                              id: 'newSignForm' }
            );
           form.append($('<input />',
                        { type: 'hidden', name: 'u[]', value: '<?php echo $upc; ?>' }));

           $('body').append(form);
           $('#newSignForm').submit();
        }
        <?php
        return ob_get_clean();

    }

    public function summaryRows($upc)
    {
        $FANNIE_URL = FannieConfig::config('URL');
        $form = sprintf('<form id="newSignForm" method="post" action="%sadmin/labels/SignFromSearch.php">
                        <input type="hidden" name="u[]" value="%s" />
                        </form>', $FANNIE_URL, $upc);
        $ret = '<td>' . $form . '<a href="" onclick="$(\'#newSignForm\').submit();return false;">Create Sign</a></td>';

        return array($ret);
    }
}