суббота, 28 ноября 2015 г.

Если вы ошиблись в коде (PHP)

Правлю модуль доставки, вроде бы все ОК. перегружаю магазин - получаю вот такое:

     Deploying store [step 1 of 13], please wait...

Cleaning up the cache... [0.00sec, 1.0MB (53kB)]
Building classes tree... [0.81sec, 9.1MB (8.1MB)]
Run the "Doctrine_Plugin_Cache" plugin... [0.00sec, 9.2MB (131kB)]
Run the "Doctrine_Plugin_DocBlock_FakeEntities" plugin...



ERROR: "Includes\ErrorHandler::FATAL_ERROR" (code 2)
Class 'XLite\Model\OrderAbstract' not found


и виснет.

Причем то, что подсвечено розовым - отображается далеко не всегда, а только после удаления папки VAR (хотя виснет всегда).

а все было просто - в PHP коде забыл одну скобку...

четверг, 19 ноября 2015 г.

Немного о CSS

Помещенный в папку skins/YouSkin/default/en/css/ файл style.css оказывает влияние на стили сразу, не требуя перезагрузки x-cart

НО!!! @media запросы у меня в нем так и не заработали...

понедельник, 19 октября 2015 г.

Начиная с 5.2.6 - могут быть проблемы с вашими шаблонами.

Совершенно не думал об этом до сегодняшнего дня, пока не понадобилось настроить рассылку почты через яндексовый SMTP сервер.

и в логах до кучи нашел вот такую каку:

XLite [warning] Warning: call_user_func_array() expects parameter 1 to be a valid callback, class 'XLite\Module\DAG\MaayaSkin\Main' does not have a method 'getLayoutTypes' in /home/almaayar/data/www/maaya.ru/var/run/classes/XLite/Model/Module.php on line 426

Вроде бы варнинг, но разобраться то хочется. ответ есть по ссылке : Errors with custom skin

То есть  все (все? но ошибка вроде бы ушла) что нужно сделать - это в Main скина дописать


    public static function getLayoutTypes()
    {
        return \XLite\Core\Layout::getInstance()->getLayoutTypes();
    }

ну и чудесно.

второй варнинг "does not have a method getLayoutColors" лечится ровно так же - пишем в main.php код


    const COLOR_SCHEME_STANDARD = 'Standard';
    public static function getLayoutColors()
    {
        return array(
            static::COLOR_SCHEME_STANDARD => \XLite\Core\Translation::lbl('Pmall'),
        );
    }

Кстати о почте - в настройках почты ЧЕТЫРЕ ящика, а слать почту можно только с одного, иначе сервер сделает вашему письму rejected. Кто сразу догадается, с какого?

четверг, 4 июня 2015 г.

Как изменить шаблон чужого МОДУЛЯ

Это важно, потом сам забуду

в X-CART есть такой замечательный модуль Add2CartPopup и вот его шаблон то мне и понадобилось поправить.

Напрямую - нельзя, при его следующем обновлении моим изменениям придет капут, да и отслеживать их все - глупо и лениво.

Простое дублирование item.message.tpl (мне его надо было поправить) в своем модуле приводит к тому, что и ожидалось - перекрытия нет, это message отображается два раза, старое и новое.

А вот если разместить item.message.tpl по пути \skins\ваш_скин\default\en\modules\XC\Add2CartPopup\parts то все перекроется просто расчудесно!

Интересно, а что будет, если я когда нибудь забуду о этой зависимости и снесу Add2CartPopup? Скорее всего ничего страшного, лень проверять заранее...

Перешел на 5.2.5

В общем то все прошло быстро и не больно, только мой скин теперь не отключается, можно только зайти в... "его" настройки, и сменить его. ну и ладно.

Зачем только все это менять, только люди привыкли...

вторник, 28 апреля 2015 г.

И еще раз о Яндекс Метрике.

В общем, не заработала она у меня - модуль активен, ,код в страницу вставляется верно, а не видит его Метрика, хоть треснись об него...

Путем экспериментов выяснилось что нужно разделить JS скрипт метрики и сам счетчик, убрав у счетчика тег <nosctipt>

Как результат - я вообще удалил этот модуль, а все, что нужно для метрики - дописал в powered_by.tpl, причем сначала идет код счетчика, потом - вывод копирайта, и уже потом - JS скрипт Метрики.


А копирайт оказывается удалять было нельзя... йо... вернул, поставил сылку на x-cart.com - так правильно. :)

О скидках в X-Cart

Надо было давно написать... уже сам забыл все :(

В общем модуль скидок в X-Cart конечно есть, но он есть в платной версии (полбеды) плюс мне нужны были очень специфические скидки, которые все равно было им не реализовать...

Спасибо Антону, ПОМОГ, /// за прошедшее время на офф сайте появилось много документации http://kb.x-cart.com/display/XDD/Changing+store+logic - прошу любить и жаловать :)

В моем случае код (вдруг кому пригодится) такой:

 1. Определяем, имеет ли покупатель право на 10% скидку, для этого у него не должно быть невыкупленных заказов и должны быть выкупленные:

    protected function has10Discount()
    {
        $discountState = false;

        $profile = \XLite\Core\Auth::getInstance()->getProfile();
        if ($profile) {
            $cnd = new \XLite\Core\CommonCell();
            $cnd->profile = $profile;
            foreach (\XLite\Core\Database::getRepo('XLite\Model\Order')->search($cnd) as $order) {
                $orderState = $order->getPaymentStatusCode();
        if ($orderState == \XLite\Model\Order\Status\Payment::STATUS_REFUNDED)
            { $discountState = false; break; }
                if ($orderState == \XLite\Model\Order\Status\Payment::STATUS_PAID)
            $discountState = true;
            }
        }

        return $discountState;
    }



2. Вычисляем скидку в зависимости от количества товаров:

    public function calculate()
    {

    $quantity = 0;
    $surcharge = 0;
    $pastaPrice = 700;
    $pastaOptDiscount = 300;

        foreach ($this->getOrderItems() as $item)
        {
                if (floor($item->getItemPrice()) == $pastaPrice)
                    $quantity += $item->getAmount();
        }

    if ($quantity < 3) $surcharge = 0;
    if ($quantity > 2 && $quantity < 6) $surcharge = $pastaPrice;
    if ($quantity == 6) $surcharge = 2*$pastaPrice;
    if ($quantity > 6)
    {
          if (round($quantity/6,0) != round($quantity/6,1)) $surcharge = 2*$pastaPrice;
          else $surcharge = $quantity*$pastaOptDiscount;
    }

    if ($this->has10Discount())
            $surcharge += floor(($this->getOrder()->getSubtotal() - $surcharge)/ 10);

        if ($surcharge <= 0) {
            $surcharge = null;
        }  else {
            $surcharge = $this->addOrderSurcharge($this->code, $surcharge * -1);
        }

        return $surcharge;
    }


Куда и как положить этот код - смотрите на офф сайте по ссылке http://kb.x-cart.com/display/XDD/Creating+global+discount, я же отмечу, что скидка считается не на товар, а общая, то есть при покупке трех товаров по 700 рублей сумма будет 2100 и сумма скидки - 700. Если это повторный заказ - то скидка будет уже 840...

Обидно, что за прошедшее время сам забыл, как активировать этот код (если это вообще надо)... Ну понадобится - вспомню, а впредь буду писать в блог сразу, он же именно для этого...

понедельник, 27 апреля 2015 г.

И снова о Powered by X-Cart

Строка "Powered by X-Cart" (на самом деле целиком оно звучит вот так: Powered by X-Cart eCommerce solution - первое что все почему то хотят удалить. Оставлю в покое совесть удаляющего, ибо не возбраняется разработчиками X-Cart, а значит можно.

Раньше (до 5.2) это удалялось или стилями (чаще всего) или в модуле (visible = false, по моему..), или в шаблоне (реже всего, потому что чайнеку пришлось бы разобраться с ними, что само по себе задача)

Лично я "очищался" в моем модуле, отвечающем за коррекцию отображения скина, вот так:

<?php
namespace XLite\Module\DAG\MySkin\View;
                                         
class PoweredBy extends \XLite\View\PoweredBy implements \XLite\Base\IDecorator
{
    protected function getPhrase()
    {
        return '';
    }
}


Апдейт до 5.2 ОК, перегружаю магазин, ОП! "Powered by X-Cart" появилось.

Да, смотрю шаблон (/skins/default/en/powered_by.tpl) - вот оно, прям жостко зашито:

<div class="powered-by">
...
  <p class="powered-by-label">Powered by X-Cart {getMessage():h}</p>
</div>


а getMessage():h = getPhrase() из кода выше.

Убрать всё это можно, воспользовавшись инструкцией на офф сайте: http://kb.x-cart.com/pages/viewpage.action?pageId=6389847 

 Insert the following CSS code into the field below:

.powered-by .powered-by-label {
    display: none;
}


Мне эта затея не нравится, поэтому копируем powered_by.tpl из дефаултного шаблона в наш, убираем из него строку <p class="powered-by-label">,  за ненадобностью (теперь) убираем из модуля файл с getPhrase(), пересобираем магазин...

Все работает. и теперь чего бы не добавляли разработчики в футер - буду надеяться, что у меня это не скоро отобразится.

Обновление X-Cart до 5.2.3

Хоть и затянулось внедрение моего магазина, а обновлять его надо. И вот сегодня проделал оное  с версии 5.1.15 - на 5.2.3

Обновление установилось приятно беспроблемно, единственное - пришлось отключить модуль Яндекс Метрики, которое еще перед обновлением заругалось... уже не помню как, ну мне проще его установить заново, благо реально то сайт еще не работал...

Еще выяснилось, что мои модули - отключены.И включаться не хотят. Ну тут все просто - после обновления движка меняем в их Main.php
    
     public static function getMajorVersion()
    {
        return '5.2';
    }

и в базе для этих модулей апдейтим:

UPDATE `xc5_modules` SET `majorVersion` = '5.2' WHERE `xc5_modules`.`moduleID` = ваш ID модуля

Включаю модули, один включается, а модуль скидок ругается:

Unknown column type "uinteger" requested. Any Doctrine type that you use has to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database introspection then you might have forgot to register all database types for a Doctrine Type. Use AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement Type#getMappedDatabaseTypes(). If the type name is empty you might have a problem with the cache or forgot some mapping information...

Забавно. Это у меня в модуле использовалась (точнее создавалась, до реального использования руки не дошли) моя таблица, и одно из полей имело тип uinteger. Больше такой тип не поддерживается? Меняю тип

    /**
     * Unique ID
     *
     * @var integer
     *
     * @Id
     * @GeneratedValue (strategy="AUTO")
     * @Column         (type="integer")
     */



Все ОК, модуль включается.


Все обновление заняло минут 10... Имхо, это по божески!
Иду в Маркетплейс, нахожу Yandex Metrika (надо же ее вернуть после обновления) - йопт! а оно уже $4.99 стоит! йопт... не, я конечно понимаю, что все это дело хозяйское, но.... Хочу дам, хочу отберу обратно - это не дело.
Посмотрел код модуля Yandex Metrika, который был бесплатен в 5.1 - там на 90% стандартный main.php плюс тоже стандартный JS код метрики. Поскольку мне продавать модуль не надо (а значит не нужен и ввод - сохранение ID счетчика) написание своего модуля "по мотивам" заняло минут 15 :)

суббота, 28 февраля 2015 г.

Где в X-CART 5 смотреть ошибки?

Все просто: /VAR/LOGS

Причем логи ведутся по человечески, все наглядно и достаточно для отладки. Причем наверно не стоит забывать их чистить...  Судя по моему другому сайду они могут разбухать до сотен мегабайт в год, а зачем оно надо... бекапить все это сто раз (например)

Эх, еще бы в случае критических ошибок сделать так, чтобы мне на телефон отправлялась СМС... это так, заметка на будущее )))

понедельник, 16 февраля 2015 г.

Как перестать бояться REST API и полюбить ее

Прежде всего читаем официальную доку: http://kb.x-cart.com/display/XDD/REST+API+documentation вполне возможно, что информация там уже куда более новая и дополненная, ну а это... это я пишу для себя.

1. Прежде всего важно, что можно получать не только productuser and order entities, но и другие, ровно точно так же, например, передав &_path=address/22 вернет нам адрес с его ID=22.

2. REST API X-Cart (в 5.1.10 - точно) может отдавать данные не только JSON, но и XML, для этого нам нужно просто WebRequest.Accept присвоить значение 'application/xml', но какого то практического смысла я в этом не нашел (хотя с XML работал много и привык).

3. Мы можем выбирать данные с ЛЮБЫМИ условиями, для этого нам надо уметь их передать... это делается так:

http://<MY-XCART-PATH>/admin.php?target=RESTAPI&_key=rest-api-key&_path=product&_cnd[orderNumber]=3

Если нам нужно передать больше параметров, то передаем их как массив...

...&_cnd[orderNumber][0]=3&_cnd[orderNumber][1]=8 

Какие же выборки мы можем делать?


Идем в папку xcart\classes\XLite\Model\Repo - вот какие классы мы тут видим, те мы и можем выбирать, по крайней мере для меня это кажется логичным.

Далее, какие условия для фильтрации данных нам доступны? Смотрим код интересующего нас класса - видим блок

    /**
     * Allowable search params
     */



Это оно. Например для Currency доступно два параметра 

    const SEARCH_ORDER_BY = 'orderBy';
    const SEARCH_LIMIT    = 'limit';


а гдето ниже по коду мы найдем два соответствующих метода... да, есть такие! например

protected function prepareCndOrderBy(\Doctrine\ORM\QueryBuilder $queryBuilder, array $value, $countOnly)
    {
        if (!$countOnly) {
            list($sort, $order) = $this->getSortOrderValue($value);
            $queryBuilder->addOrderBy($sort, $order);
        }
    }


интереснее конечно выборки например для ордеров


    protected function prepareCndOrderMore(\Doctrine\ORM\QueryBuilder $queryBuilder, $value)
    {
        if (!empty($value)) {
            $queryBuilder->andWhere('o.order_id > :order
More')
                ->setParameter('order
More', $value);
        }
    }
а вызывать мы его будем как 

........&_path=order&_cnd[orderMore]=value

важно только не забыть (я поначалу забыл) прописать 

    const ORDER_MORE = 'orderMore';

    protected function getHandlingSearchParams()
    {
        $params = parent::getHandlingSearchParams();
        $params[] = self::ORDER_MORE;
        return $params;
    }

 

разумеется все это кладем в 

abstract class Order extends \XLite\Model\Repo\Order implements \XLite\Base\IDecorator
{
.....


в соответствующий файл (в нашем случае - Order.php) в папку /Model/Repo вашего модуля.

среда, 28 января 2015 г.

Где X-Cart хранит настройки (например логин и пароль - доступ к БД)

Все очень просто - /etc/config.php

Постоянно забываю, лучше запишу тут.

пятница, 9 января 2015 г.

Как отключать некоторые виды оплаты в зависимости от способа доставки

Весьма обыденная ситуация: вам нужно запретить "наложенный платеж" при доставке "транспортной компанией" (собственно, варианты могут быть любые, например ваш курьер не принимает карты, не важно - принцип один).

Как правило, в крупных, давно и правильно настроенных ИМ (типа Озона) это делается путем последовательного отображения пользователю страниц выбора адреса - вариантов доставки - вариантов оплаты. В X-Cart 5 же (и это ЛУЧШЕ - потому что наглядно, потому что все видно сразу) все это собрано на одной странице, а значит (де)активация способов оплаты должна происходить динамически, в зависимости от выбранного пользователем способа доставки.

Мой вопрос на офф форуме, его обсуждение и ссылку на статью в документации по X-Cart можно посмотреть по ссылке.

В моем случае есть два способа доставки:
= Почта (ID=1) и Транспортная Компания (ID=2)
и два способа оплаты:
=  Яндекс Касса (ID=67) и Наложенный Платеж (ID=73).
ID методов доставки и оплаты можно увидев, зайдя в админке в их настройку - ID отобразится в урле соответствующей страницы, например так: site.ru/admin.php?target=payment_method&method_id=67.

Мне нужно запретить Наложенный Платеж в случае, если доставка будет осуществляться транспортной компанией. Для этого в вашем модуле, который отвечает за бизнес логику вашего ИМ, создаем файл XLite\Module\DAG\SpecialOffer\Model\Order.php (на имя конкретно моего модуля внимания не обращаем, так вышло, что я начал со скидок, а теперь уже просто лень его переименовывать)
<?
// vim: set ts=4 sw=4 sts=4 et:

namespace XLite\Module\DAG\SpecialOffer\Model;

/**
 * Class represents an order
 */
abstract class Order extends \XLite\Model\Order implements \XLite\Base\IDecorator
{
    public function getPaymentMethods()
    {
        if (0 < $this->getOpenTotal())
    {

            $list = \XLite\Core\Database::getRepo('XLite\Model\Payment\Method')
                ->findAllActive();

            foreach ($list as $i => $method)
        {
                if (!$method->isEnabled() || !$method->getProcessor()->isApplicable($this, $method)
                    || $this->getShippingId() == 2 && $method->getMethodId() == 73)
        {
                    unset($list[$i]);
                }
            }

        }
    else
    {
            $list = array();
        }

        return $list;
    }
}
Красным цветом выделены ID методов доставки и оплаты, комбинация которых недопустима. Перезагружаем магазин, убеждаемся что работает. Честно говоря, я не ожидал, что будет работать так здорово!




Много пустого места вокруг иконок с изображением товара

Или сами иконки слишком мелкие, реально 180 х 180 пикселов — ну куда это годится для современных мониторов?

Код простой, в файле XLite\Module\DAG\MaayaSkin\View\ItemsList\Product\Customer\ACustomer.php вашего модуля-скина пишем следующее:

 <?php

namespace XLite\Module\DAG\MaayaSkin\View\ItemsList\Product\Customer;

abstract class ACustomer extends \XLite\View\ItemsList\Product\Customer\ACustomer implements \XLite\Base\IDecorator
{

    public static function getIconSizes()

    {
        return array(
            static::WIDGET_TYPE_SIDEBAR . '.' . static::DISPLAY_MODE_STHUMB => array(180, 180),
            static::WIDGET_TYPE_SIDEBAR . '.' . static::DISPLAY_MODE_BTHUMB => array(180, 180),
            static::WIDGET_TYPE_CENTER . '.' . static::DISPLAY_MODE_GRID => array(220, 220)
            static::WIDGET_TYPE_CENTER . '.' . static::DISPLAY_MODE_LIST => array(220, 220),
            'other' => array(110, 110),
        );
    }
}
Уверен, что где прописать размеры иконок продуктов - понятно :) где какие иконки - понятно не сразу, но разобраться не сложно.

Сложнее другое. Эти иконки общие и используются и в других модулях, дизайн которых портится после изменения размена иконок. Так, например "рекомендуемые товары" в модуле Add2CartPopup начинают наползать друг на друга. И что делать?

Нужно найти ВСЕ такие проблемные модули и декорировать их классы, указав, что виджет должен оперировать "старым" размером изображений товаров. Для Add2CartPopup я в своем модуле-скине добавил файл XLite\Module\DAG\MaayaSkin\View\Products.php со следующим кодом (использование именно этого метода класса Products спорно, но почему бы и нет?):

<?php
// vim: set ts=4 sw=4 sts=4 et:

namespace XLite\Module\DAG\MaayaSkin\View;

class Products extends \XLite\Module\XC\Add2CartPopup\View\Products implements \XLite\Base\IDecorator
{

    protected function getMaxCount()
    {
        $this->widgetParams[self::PARAM_ICON_MAX_WIDTH]->setValue(180);
        $this->widgetParams[self::PARAM_ICON_MAX_HEIGHT]->setValue(180);

        return static::PARAM_MAX_PRODUCT_COUNT;
    }

}
Перезагружаем магазин и видим, что все ОК.

Я подозреваю, что явно указывая вместо self нужный класс виджета (в нашем случае это \XLite\Module\XC\Add2CartPopup\View\Products)- можно менять его параметры из другого виджета, не затрагивая кода самого класса.

обсуждение этой темы на офф форуме X-Cart доступно по ссылке