Emart shadowQRcode

- Posted in Uncategorized by

Для повышения продаж в "дохлый" обеденный час, когда торговля замирает, южнокорейский ритейлер придумал дать людям необычный уличный QR-код, который работает только с двенадцати до часу. За месяц эксперимента продажи в обеденное время выросли на 25%, а число новых зарегистрированных пользователей - на 58% по сравнению с предыдущим месяцем!

Смотрите видео:

Emart - крупная сеть магазинов, расположенных по всей Южной Корее. Однако у неё есть слабое место: в обеденное время торговля очень сильно сокращается. Поэтому для решения задачи по поднятию продаж в этот "мертвый час" с 12 до 13 дня Emart решили дать людям уникальный опыт и впечатления, доступные только в это время!

Они установили на улицах объёмные конструкции, которые днём превращаются солнцем в QR-коды :) Их можно сфотографировать и перейти на закодированные в этих кодах страницы магазина со специальным предложением "Sunny Sale" и купоном на $12 скидку. Товар доставляется прямо на дом или в офис, поэтому многие получают удовольствие от такого необычного "индиана-джонсовского" развлечения и заказывают какие-то товары с доставкой -- ящик пива, пиццу и т.п.

"Sunny Sale" конструкции были установлены в 13 местах в Сеуле в феврале 2012 года, впоследствии их число увеличили до 36. В результате было продано 12000 SunnySale-купонов, а число новых пользователей Emart выросло на 58% по сравнению с предыдущим месяцем. Продажи в обеденный час выросли на 25%. И если посмотрите видео - увидите, сколько удовольствия люди получают от самого процесса! А такие эмоции - отличная реклама и запоминаются надолго.

via Google+

Обновлён русский перевод для Opencart 1.5.3

- Posted in Opencart by

Обновлён перевод для Opencart 1.5.3.

Перевод для 1.5.2 приведён в соответствие с релизом Opencart 1.5.2.1 (убрано лишнее, что попало в эту ветку в промежутке от 1521, 1522, 153). 1.5.3 выделена в отдельную ветку.

Opencart: как переименовать способ доставки во что-то более подходящее?

- Posted in Uncategorized by
Как переименовать способ доставки во что-то более подходящее, например "Курьерская доставка по Москве и Московской области"? Где эти файлы лежат и как именно это исправить?

В папке catalog/language/russian/ находятся языковые файлы, которые используются на "витрине" магазина,

в admin/language/russian/ - тексты для админ-части.

Будем менять тексты включенного способа доставки "Фиксированная стоимость доставки". Поэтому нас интересуют файлы .../shipping/flat.php в обеих указанных местах. То есть:

  • catalog/language/russian/shipping/flat.php
  • admin/language/russian/shipping/flat.php

Когда откроете их - увидите строки, которые видны на экране, там уже интуитивно понятно, что на что менять.

В админке тоже поменяйте соответственно, чтобы названия способов доставки совпадали и не было путаницы при их настройке и включении-выключении.

ВНИМАНИЕ: на витрине после изменения этих строк корзину надо очистить от товаров и повторить процесс покупки - иначе эти изменения не будут сразу видны.

Улучшение сортировки на витрине Opencart

- Posted in Uncategorized by

Поступил вопрос:

можно ли сделать по умолчанию сортировку товаров на витрине, одновременно по двум параметрам: по цене и названию. Тоесть сортируются по цене, а те у кого цены одинаковые уже по имени. Я сделал по цене, но я заметил что иногда Опенкарт меняет местами товары с одинаковыми параметрами сортировки, например если менять кол-во выводимого товара на одну страницу, ощущение что их тусует в произвольном порядке.

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

Diff приведён ниже, здесь же приведу описание обычными словами, т.к изменение всего одно и небольшое:

Как внести изменения вручную

1. Открываем файл catalog/model/catalog/product.php

2. Ищем функцию function getProducts

3. Пролистываем почти до конца в поисках кода запроса с ORDER BY и следующего за ним LIMIT. Соответствующий участок кода выглядит так:

$sort_data = array(
                'pd.name',
                'p.model',
                'p.quantity',
                'p.price',
                'rating',
                'p.sort_order',
                'p.date_added'
            );
            if (isset($data['sort']) && in_array($data['sort'], $sort_data)) {
                if ($data['sort'] == 'pd.name' || $data['sort'] == 'p.model') {
                    $sql .= " ORDER BY LCASE(" . $data['sort'] . ")";
                } else {
                    $sql .= " ORDER BY " . $data['sort'];
                }
            } else {
                $sql .= " ORDER BY p.sort_order";
            }
            if (isset($data['order']) && ($data['order'] == 'DESC')) {
                $sql .= " DESC";
            } else {
                $sql .= " ASC";
            }
            if (isset($data['start']) || isset($data['limit'])) {
                if ($data['start'] < 0) {
                    $data['start'] = 0;
                }
                if ($data['limit'] < 1) {
                    $data['limit'] = 20;
                }
                $sql .= " LIMIT " . (int)$data['start'] . "," . (int)$data['limit'];
            }

4. Мы сделаем две вещи: для начала изменим способ сортировки товаров на витрине по умолчанию на сортировку по названию:

} else {
                $sql .= " ORDER BY p.sort_order";
            }

меняем на

} else {
                $sql .= " ORDER BY pd.name";
            }

5. И теперь добавляем вторичную сортировку по названию:

if (isset($data['order']) && ($data['order'] == 'DESC')) {
                $sql .= " DESC";
            } else {
                $sql .= " ASC";
            }
            // БУДЕМ ВСТАВЛЯТЬ КОД СЮДА
            // БУДЕМ ВСТАВЛЯТЬ КОД СЮДА
            // БУДЕМ ВСТАВЛЯТЬ КОД СЮДА
            // БУДЕМ ВСТАВЛЯТЬ КОД СЮДА
            if (isset($data['start']) || isset($data['limit'])) {

Всталяем в указанное место такой блок кода:

// Additional sort order by product name
            if(isset($data['sort']) && $data['sort'] != 'pd.name') {
                $sql .= ", pd.name ASC";
            }

Вот и всё.

То же самое в виде Diff-файла

commit c12debb0a37ade03e773535ed361a4b96bd54c91
Author: Ruslan Brest <rb@labtodo.com>
Date:   Mon May 21 10:26:39 2012 +0300
    [+] Better catalog sort order: additional sorting by product name
    
    Добавлена вторичная сортировка по имени товара для более аккуратного вывода.
    Без этого товары при выборе других сортировок, например по количеству, могут сортироваться
    в случайном порядке. То же самое при изменении количества товаров на странице: один и тот же
    товар может оказаться и на первой, и на второй странице. Сейчас товары сортируются по выбранному
    признаку, а затем по имени.
    
    [!] CHANGED: По умолчанию -- сортировка по имени, а не по sort_order
diff --git a/upload/catalog/model/catalog/product.php b/upload/catalog/model/catalog/product.php
index 8b15e26..c3dec4f 100644
--- a/upload/catalog/model/catalog/product.php
+++ b/upload/catalog/model/catalog/product.php
@@ -162,7 +162,7 @@ class ModelCatalogProduct extends Model {
                    $sql .= " ORDER BY " . $data['sort'];
                }
            } else {
-               $sql .= " ORDER BY p.sort_order";
+               $sql .= " ORDER BY pd.name";
            }
 
            if (isset($data['order']) && ($data['order'] == 'DESC')) {
@@ -171,6 +171,11 @@ class ModelCatalogProduct extends Model {
                $sql .= " ASC";
            }
 
+           // Additional sort order by product name
+           if(isset($data['sort']) && $data['sort'] != 'pd.name') {
+               $sql .= ", pd.name ASC";
+           }
+
            if (isset($data['start']) || isset($data['limit'])) {
                if ($data['start'] < 0) {
                    $data['start'] = 0;

2012-05: webdev links

- Posted in Webdev by

Живые блоги с публикациями о CodeIgniter:

Руководство по оформлению HTML/CSS кода от Google. Руководство вообще-то странное: местами непоследовательное и противоречащее самому себе, а местами и вовсе вредное, на мой взгляд. Видимо, сказываются особенности высоконагруженных проектов. В общем, я бы не стал слепо придерживаться всех изложенных там рекомендаций, но просмотреть список и причины появления таких правил -- полезно.

Обсудить

Как реализовать страхование товаров в Опенкарт для некоторых покупателей?

- Posted in Uncategorized by
при оформлении заказа необходимо предложить покупателю возможность застраховать свою посылку(товар), так как есть и те кто не желает доплачивать эту сумму, то нужна функция с возможностью выбора. То есть при оформлении заказа, если покупатель согласен, то эта сумма плюсуется к общей сумме, если нет, то сумма остаётся без изменений.

Заведите купон на -10% (например) и пусть в корзине применяет - вместо скидки цена будет увеличена на указанную сумму. В купонах может использоваться как процент, так и фиксированная сумма.

Отрицательная величина скидки может применяться и, как можно убедиться, приводит к увеличению суммы при оформлении.

Про бег на длинные дистанции (физиология)

- Posted in Uncategorized by

Расскажу своими словами про впечатления и личный опыт. И физиологию -- на самом примитивном уровне, поскольку не являюсь специалистом в медицине, биологии. Кому нужны более квалифицированные источники -- лучше начать со списка в конце, а саму статью вообще не читать.

Почему я всё же пишу и рефлексирую? Да потому, что бегаю. Регулярно, не теряя интереса и не перенапрягаясь. И без вреда для здоровья, а с ощутимой пользой. На мой взгляд, этого достаточно для того, чтобы делиться своим опытом и собственными интерпретациями происходящего. Простым языком и "на пальцах".

В общем-то всё просто. Из основных работающих систем у нас тут мышцы, связки, лёгкие, кровеносная система и сердце: это то, что развивается до нормального состояния у современных "детей асфальта" при регулярных длительных беговых нагрузках. И высокая скорость не имеет особенного значения. Критерии достаточности при аэробных нагрузках - возможность поговорить на бегу, не сбивая дыхания, бег с закрытым ртом (дыхание носом), ну или считая шаги - 3x3 (3 шага вдох, 3 выдох). Плохая новость - чтобы при беге в таком ритме начал расщепляться жир, надо бегать больше часа.

Мышцы и связки -- то, что наиболее заметно и "на поверхности". Отчётливо ощущается, как это тренируется, развивается и привыкает к нагрузкам при регулярных занятиях. Особенно быстро это сказывается на связках: перестают болеть колени, сухожилия на пятках и т.п. Естественно, если не гнаться за скоростью и не "рвать себе жилы". При появлении болезненных ощущений - переход на ходьбу. А по возможности желательно и сменить поверхность: с асфальта и бетона на грунтовку или даже песок. Поэтому бег трусцой на длинные дистанции (5-10-15 км) хорош для оздоровления. Особенно поначалу. Подготовить организм и голову (психологически: многим бег в течение часа-двух кажется чем-то сверхъестественным).

У меня год регулярного бега (2010) на сравнительно длинные дистанции (8-12 км) ощутимо снизил ЧСС в спокойном состоянии: вместо 70-80 ударов в минуту -- теперь 50-60 (от силы 70). Так что влияние на сердце, сосуды и капилляры -- вот оно, заметно на глаз.

Лёгкие при этом учатся эффективнее усваивать кислород - увеличивается объём, глубина вдоха, кол-во альвеол и мелких капилляров. Организм учится эффективнее быстро насыщать кровь кислородом. Сердце становится больше и мощнее - чтобы успевать больше крови прокачивать во время повышенных нагрузок. Сосуды становятся крепче и шире. Капилляры - тоньше и разветвлённее, чтобы эффективнее доставлять кислород всем органам и выводить разную ерунду наружу. Лишнее тепло в том числе. Побочные эффекты - спортсмены здоровее, краснее и теплее. Ну и чище изнутри - всё лишнее у них обычно наружу выводится и из сосудов, и из тканей. Пульс в спокойном состоянии снижается, и довольно заметно, - поэтому не стоит слушать обывателей и паникёров, которые с ходу заявляют о брадикардии, не задав предварительно вопрос: "А ты случайно не спортсмен(-ка)?".

Первым при нагрузках расходуется гликоген (легко расщепляемые сахара). Их основные запасы находятся в печени и мышцах. В печени процентное содержание наиболее высокое среди всех органов, но поскольку мышц гораздо больше - больше всего гликогена оказывается там. Восполняются углеводами. Белки тоже расщепляются и расходуются, но позже. Поэтому (а) люди с мясом оказываются гораздо более выносливыми и (б) мышцам достается в первую очередь. Всё это активно расщепляется и дает энергию первые 40-50 минут бега трусцой. Примерно, разумеется: в организме ничего так резко не переключается, но мы сейчас не о точности, а об общем представлении. И только потом неохотно вступают в дело жировые залежи. Поэтому те, кто просто бегает, и делает это меньше 1-1.5 часов - теряют по большей части свою воду, а не жир (до 2-3 кило за тренировку - легко). Которую затем восполняют. Вес поэтому еле колеблется, жир остается на месте - это многим знакомо, потому и бросают, что "толку никакого".

Хорошая новость - можно терять жир быстрее, если знать, как всё работает. В общем, из основ - есть 3-5 главных фактора, влияющих на выносливость и происходящие процессы, и такое же кол-во разновидностей тренировок. Аэробные нагрузки (когда кислорода достаточно поступает в мышцы), анаэробные (избыток углекислого газа), максимально потребление кислорода (способность усваивать его) и что-то там ещё. Ещё есть длина и частота шагов. Но это всё надо знать, понимать, помнить и применять, если интересует результат (бегать далеко и быстро). Если интересует гораздо меньше - достаточно понять основы, пару раз понять, что именно и в каких примерно пропорциях и дозах надо, и делать в своё удовольствие. Если заглянуть в ответ и начинать с конца - лучше варьировать разные типы нагрузок (которых всего 3-5), чтобы всем внутренностям досталось и все росли. Подробности лучше в книгах уточнить. Для быстрого сжигания жиров некоторые рекомендуют интервальный бег - смесь ускорений и бега на полную катушку 100-800 метров и таких же интервалов спокойной трусцы для восстановления. Мне сложно подтвердить или опровергнуть: на моих запасах жира это не заметишь. Мне интервальный бег наиболее заметен при работе над скоростью.

По своему опыту скажу, что лучше всего за бегом на длинные дистанции (которые отменно выматывают, хоть и приятны) не забывать о растягивании (йога например) и силовых нагрузках (гантельки, отжимания - руки, плечи, грудь). Иначе однобоко получается и в итоге ничего особенно хорошего: через 1-2 года занятий только бегом, отчетливо становится заметен недостаток этих усилий на поддержание "остальной формы". Гантельки весьма и весьма нужны - мясо в верхней части тела при беге совсем не лишнее, как оказалось.

Легкоусвояемая книга - Пит Фитзингер, Скотт Дуглас. Бег по шоссе для серьёзных бегунов. Дистанции от 5 км до марафона.

См. также:

Как модифицируются способы оплаты для использования с QCPM.1513

- Posted in Uncategorized by
QCPM из-за необходимости изменения способов оплаты делает невозможным одновременное использование стандартной формы и QCPM: либо одна, либо другая. Поэтому обязательно храните резервные копии изменяемых стандартных файлов (точный путь указан в документации - README или INSTALL файлах).

Суть изменений проста: в TPL файлах способов оплаты содержится самая последняя кнопка подтверждения заказа, на нажатие которой обычно вешается обработчик (javascript-функция). Эта функция делает 2 вещи: окончательно подтверждает заказ (после этого он формируется и становится виден в админке) и делает что-то требуемое этому способу оплаты (переадресация на внешний сайт плат. системы или другие действия).

В стандартной форме заказа все проверки производятся до действия этой кнопки.

В QCPM - нет, поскольку всё на одной странице и эта кнопка может быть нажата сразу после загрузки, первой. Поэтому нам требуется вмешаться в стандартный процесс:

  1. Сначала произвести валидацию формы заказа;
  2. Затем сделать то, что предполагалось сделать модулем по нажатию на кнопку "Подтвердить заказ" (передать управление стандартной функции модуля оплаты)

Изменяемые файлы модулей оплаты находятся в catalog/view/theme/default/template/payment/

Теперь на примерах: alertpay.tpl

<form action="<?php echo $action; ?>" method="post">
   <input type="hidden" name="ap_merchant" value="<?php echo $ap_merchant; ?>" />
   <input type="hidden" name="ap_amount" value="<?php echo $ap_amount; ?>" />
   <input type="hidden" name="ap_currency" value="<?php echo $ap_currency; ?>" />
   <input type="hidden" name="ap_purchasetype" value="<?php echo $ap_purchasetype; ?>" />
   <input type="hidden" name="ap_itemname" value="<?php echo $ap_itemname; ?>" />
   <input type="hidden" name="ap_itemcode" value="<?php echo $ap_itemcode; ?>" />
   <input type="hidden" name="ap_returnurl" value="<?php echo $ap_returnurl; ?>" />
   <input type="hidden" name="ap_cancelurl" value="<?php echo $ap_cancelurl; ?>" />
   <div class="buttons">
     <div class="right">
       <input type="submit" value="<?php echo $button_confirm; ?>" class="button" />
     </div>
   </div>
 </form>

Что мы видим? Есть форма, по нажатию кнопки происходит отсылка данных этой формы.

Вмешиваемся в этот процесс:

1. по нажатию на кнопку делаем валидацию формы заказа (поменяли кнопку на свою):

<div class="buttons">
     <div class="right"><a onclick="validate_generate();" class="button"><span><?php echo $button_confirm; ?></span></a></div>
   </div>

2. Обеспечиваем работу того, что было предусмотрено модулем. QCPM вызывает функцию payment_confirm(), поэтому надо просто переместить предусмотренную модулем функцию по нажатию на кнопку, в функцию с таким названием:

<script type="text/javascript"><!--
 function payment_confirm()
 {
         $('#payment').submit();
 }
 //--></script>

Этот механизм переделки стандартный для всех модулей: переносим стандартную реакцию на кнопку подтверждения заказа в ф-цию payment_confirm(), а на её место ставим вызов валидации формы заказа.

Другой пример: cheque.tpl

Была кнопка

<div class="buttons">
   <div class="right">
     <input type="button" value="<?php echo $button_confirm; ?>" id="button-confirm" class="button" />
   </div>
 </div>
 <script type="text/javascript"><!--
 $('#button-confirm').bind('click', function() {
         $.ajax({ 
                 type: 'GET',
                 url: 'index.php?route=payment/cheque/confirm',
                 success: function() {
                         location = '<?php echo $continue; ?>';
                 } 
         });
 });
 //--></script>

По ID кнопки (id="button-confirm") вызывается AJAX-обработчик. Который что-то там делает.

Мы переносим этот обработчик в функцию payment_confirm(), а нажатие на кнопку заменяем на валидацию формы:

<script type="text/javascript"><!--
 $('#button-confirm').bind('click', function() {
         validate_generate();
 });
 function payment_confirm()
 {
         $.ajax({
                 type: 'GET',
                 url: 'index.php?route=payment/cheque/confirm',
                 success: function() {
                         location = '<?php echo $continue; ?>';
                 }
         });
 }
 //--></script>

Перенесли стандартный обработчик в payment_confirm и вызвали validate_generate. Те же два действия.

Все остальные модули точно так же устроены и модифицируются аналогично. Они все похожи и используют буквално 2-3 очень похожих схемы работы.

Проще всего увидеть изменения, сравнив стандартный файл и модифицированный. Программы, которые это умеют делать: TotalCommander, WinMerge.org (Windows), Meld (Linux), про MacOS не знаю.

В них всё наглядно видно.

При использовании QCPM желательно все файлы модуля держать только в теме Default и не дублировать в используемую тему - файлы будут подхватываться оттуда. Иначе сложней обслуживать и обновлять.

Quickcheckout: как сделать необязательным поле email?

- Posted in Uncategorized by

Как скрыть "Адрес доставки: Адрес (продолжение):"

открыть файл catalog/view/theme/default/template/checkout/quickcheckout.tpl

найти там:

<tr>
                        <td><?php echo $entry_address_2; ?></td>
                        <td><input type="text" name="address_2" value="<?php echo $address_2; ?>" class="large-field"/></td>
                </tr>

и первую строку (<tr>) изменить на:

<tr style="display:none;">

Как сделать ввод почтового ящика необязательным

в этом же файле найдите чуть выше строку

<td><span class="required">*</span> <?php echo $entry_email; ?></td>

из неё надо убрать звёздочку:

<td><?php echo $entry_email; ?></td>

Дальше надо найти и открыть файл catalog/controller/checkout/quickcheckout_address.php

найти там строки (#30-33)

$l = utf8_strlen($this->request->post['email']);
                                if (($l < 1) || ($l > 96) || !preg_match('/^[^\@]+@.*\.[a-z]{2,6}$/i', $this->request->post['email'])) {
                                        $json['error']['email'] = $this->language->get('error_email');
                                }

и закомментировать их. На этом изменения в модуле закончены, их должно быть достаточно.

После этого надо исправить в движке Опенкарт часть, которая отсылает письма

Надо открыть файл catalog/model/checkout/order.php

найти там строку:

// Admin Alert Mail

(у меня это строка #438)

Над ней будет блок, который выглядит так:

$mail = new Mail();
                                $mail->protocol = $this->config->get('config_mail_protocol');
                                $mail->parameter = $this->config->get('config_mail_parameter');
                                $mail->hostname = $this->config->get('config_smtp_host');
                                $mail->username = $this->config->get('config_smtp_username');
                                $mail->password = $this->config->get('config_smtp_password');
                                $mail->port = $this->config->get('config_smtp_port');
                                $mail->timeout = $this->config->get('config_smtp_timeout');
                                $mail->setTo($order_info['email']);
                                $mail->setFrom($this->config->get('config_email'));
                                $mail->setSender($order_info['store_name']);
                                $mail->setSubject($subject);
                                $mail->setHtml($html);
                                $mail->setText(html_entity_decode($text, ENT_QUOTES, 'UTF-8'));
                                $mail->addAttachment(DIR_IMAGE . $this->config->get('config_logo'), md5(basename($this->config->get('config
_logo'))));
                                $mail->send();

Его надо заменить на такой:

if( !empty($order_info['email']) )
                        {
                                $mail = new Mail();
                                $mail->protocol = $this->config->get('config_mail_protocol');
                                $mail->parameter = $this->config->get('config_mail_parameter');
                                $mail->hostname = $this->config->get('config_smtp_host');
                                $mail->username = $this->config->get('config_smtp_username');
                                $mail->password = $this->config->get('config_smtp_password');
                                $mail->port = $this->config->get('config_smtp_port');
                                $mail->timeout = $this->config->get('config_smtp_timeout');
                                $mail->setTo($order_info['email']);
                                $mail->setFrom($this->config->get('config_email'));
                                $mail->setSender($order_info['store_name']);
                                $mail->setSubject($subject);
                                $mail->setHtml($html);
                                $mail->setText(html_entity_decode($text, ENT_QUOTES, 'UTF-8'));
                                $mail->addAttachment(DIR_IMAGE . $this->config->get('config_logo'), md5(basename($this->config->get('config
_logo'))));
                                $mail->send();
                        }

то есть сверху добавится условие, снизу - закрывающая скобка.

После этого всё должно работать без обязательного заполнения покупателем поля Email.

См. также: