среда, 4 декабря 2013 г.

Себе на память - редактирование пользовательского свойства заказа в событии перед изменением заказа (Битрикс)

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

public static function OnBeforeOrderUpdateHandler($ID, $arFields) {
global $USER;
$arGroups = $USER->GetUserGroupArray();
if (in_array(6,$arGroups) || in_array(1,$arGroups)){
$db_vals = CSaleOrderPropsValue::GetList(array("SORT" => "ASC"), array("ORDER_ID" => $ID, "CODE"=>"F_MENEDGER"));  
$order_props=array();
if ($arVals = $db_vals->Fetch()){

$order_props=$arVals;

if (!$order_props['VALUE'])
{
if ($arProp = CSaleOrderProps::GetByID(10)) {
CSaleOrderPropsValue::Add(array(
'ORDER_ID' => $ID,
'ORDER_PROPS_ID' => $arProp['ID'],
'NAME' => $arProp['NAME'],
'CODE' => $arProp['CODE'],
'VALUE' => $USER->GetID(),
));
}
}
}
}

понедельник, 16 сентября 2013 г.

Зло таилось в засаде (о разработке продающего интернет-магазина часть вторая)

В своей предыдущей статье «Сапоги сапожника – взгляд с другой стороны» я писала о том, как разработала и запустила свой собственный интернет-магазин на базе 1С Битрикс.  Магазин кукол Barbie Mattel. В той статье я описала сложности и проблемы, с которыми  я столкнулась на этапе разработки и запуска магазина, однако и после запуска мне еще не раз пришлось столкнуться с затаившимся злом. Зло было повержено, хотя борьба с ним еще продолжается.

Запустив магазин, я наивно думала, что если я могу в любой момент дозаказать товар у производителя, можно не отмечать в каталоге, какой товар есть в наличии на моем складе, а какого – нет. Из-за этого товары, которые уже были на моем складе – распродавались медленно, мне приходилось пускать в оборот все новые и новые средства, когда клиенты делали заказ на кукол, которых не было у меня в наличии. Опять же клиенты не были довольны сроками доставки.

Mattel выпустили новую коллекцию, я закупила, склад расширился. Склад не распродавался с должной скоростью. Я расстраивалась из-за того, что не видела прибыли.
Тогда я поняла, что дальше так продолжаться не может, и что я должна уже распродать склад – вынуть эти деньги, а потом уже снова вложить их в оборот. Однако вести в Битрикс количественный учет товара я не хотела – по опыту своих клиентов, я поняла, что это очень хлопотное дело, и отмечать себе наличие в блокноте мне на данном этапе удобнее.

Я добавила к карточке товара в Битрикс свойство «Наличие», которое может принимать 3 возможных значения:
- под заказ (по умолчанию);
- ожидается (те товары, которые уже едут ко мне на склад и скоро приедут)
- в наличии

Вне зависимости от того, в наличии товар или под заказ, я даю возможность покупателю положить его в корзину, однако в информации о доставке указываю, что товары «под заказ» придется ждать дольше. В каталоге товаров я сделала, чтобы для товаров, которые есть в наличии отображалась кнопка «купить», а для товаров, которые ожидаются или под заказ – отображалась кнопка «заказать». Функционал кнопок, естественно, одинаков.
Свойство «Наличие» я вынесла в верхнюю часть фильтра, чтобы покупатели могли отбирать товары по этому признаку.



Это дало свои плоды – склад стал уходить лучше, а срок доставки товаров «под заказ» перестал вызывать возмущение клиентов. Я вздохнула с облегчением.

Следующая засада ждала меня там, где я совсем не ожидала. Как ни странно, но оплата квитанцией «сбербанка» оказалась самым популярным способом оплаты моего магазина. Специфика товара такова, что его часто бабушки и дедушки внучкам покупают, и на предложение оплатить заказ кредитной картой или электронными деньгами многие из них удивленно спрашивают: «Чего?». 

Квитанции выписывались – но половина квитанций не оплачивалась.
Я набралась наглости и, будучи человеком достаточно коммуникабельным, обзвонила несколько клиентов, которые не оплатили свой заказ по квитанции с просьбой посоветовать мне, как улучшить сервис магазина и рассказать мне о том, почему они отказались от заказа. Многие из них ответили мне, что они очень хотели моих кукол, но в сбербанке не приняли мою квитанцию.

Я удивилась. Магазин у меня открыт на то же ИП, с которого я занимаюсь разработкой интернет-сайтов (я просто добавила себе вид деятельности) и ранее никто из моих клиентов-физиков не жаловался на мои квитанции. Я прозвонила одному из своих клиентов-физиков и спросила, как он оплачивает мои квитанции. Оказалось, что таки да – их можно оплатить в любом коммерческом банке, но сбер их не принимает. На самом деле эта информация – ни для кого не тайна и широко известна. Но я не знала об этом: для того, чтобы твои квитанции принимал Сбербанк, нужно подписывать с ним специальный договор. 
Но почему, почему в Битриксе этот способ оплаты называется «Квитанция сбербанка», если по-умолчанию квитанцию любого-любого ИП или ООО можно оплатить практически где угодно, но только не в Сбербанке?

Договор со Сбербанком я собираюсь заключать, пока же переименовала способ оплаты. 

Что еще интересного я открыла для себя? В одной из следующих статей я напишу о своем опыте добавления интернет-магазина в Яндекс.Маркет  (сейчас прохожу проверку) – интересный опыт, должна я сказать, познавательный.

А еще, с удивлением и стыдом я обнаружила, что мои познания о Барби – чрезвычайно бедны. Клиенты порой рассказывают мне такие удивительные факты о Барби и ее подругах, что в пору записывать и выпускать сборник легенд. В целом это приносит мне колоссальное удовольствие.

воскресенье, 18 августа 2013 г.

Сапоги сапожника – взгляд с другой стороны (О разработке продающего интернет-магазина)

Кажется, я уже 300 лет ничего не писала в блог. Не писала, потому что последние 2 месяца была очень и очень занята. У меня было несколько интересных и масштабных проектов, некоторые из них еще не закончены, но я у меня появилось время, и я хочу поделиться. Благо – того, чем делиться – накопилось.

Я, как всегда, начну издалека и с предыстории: мне скоро 28, и я стала задумываться о старости. Разработка web-сайтов, своя команда разработчиков – это все очень и очень хорошо. Я довольна своей командой, довольна своими заказчиками, довольна своей прибылью и зарплатой, которую сама же себе и плачу, НО это все есть только пока я сама лично впахиваю, и если я устану, заболею, впаду в творческий кризис – все это разрушится, если не в одну секунду, то в течение года – точно. Поэтому,  заработав некоторый излишек денег и найдя соучредителя, я решила заняться параллельно и другим бизнесом – торговлей. Торговлей через интернет-магазин.  Почему? Наверное, мне очень понравились эти сапоги моего производства на чужих ногах, и я захотела сшить себе такие же.

Продажей какого товара я теперь занимаюсь – я расскажу в конце статьи (а то вступление и так затянуто) а сейчас я хочу рассказать о том, как перевернулось мое сознание и мой взгляд на разработку интернет-магазинов вообще и интернет-магазинов на 1С Битрикс – в частности.
Вопрос с выбором движка, естественно, даже не стоял.  Я сообщила советчикам, советовавшим мне взять бесплатный движок, что скупой платит дважды, и купила для своего магазинчика 1С Битрикс Бизнес (со временем у меня будет соц. сеть для покупателей). 
Приступая к разработке магазина, я думала: «мои клиенты ничего не понимают в интернет-магазинах, парятся фигней, изобретают велосипеды, страдают перфекционизмом – затягивают запуск проектов. В то время, когда можно просто взять готовое стандартное решение магазина от 1С Битрикс, натянуть на него красивый оригинальный дизайн, наполнить товаром – и все: ловись рыбка – большая и маленькая». На деле же, когда заказчиком магазина оказалась я сама, "фигней" пришлось пострадать и мне.

Я показала дизайнеру, с которым я работаю (Галине Сканцевой), демку магазина Битрикс, объяснила, что функционал будет вот точно таким же, рассказала, какой стиль я хочу видеть в дизайне. А надо сказать, Галина очень точно умеет воспроизвести любой стиль. И стиль «кич 90х годов», в котором я видела дизайн сайта, она воспроизвела с изумительной точностью. 
То, что мы сразу решили рисовать шкурку для Битрикс – было хорошим ходом, и это, действительно, сильно ускорило разработку (наверное, поэтому я уже и запустилась, и уже продаю). Но подводных камней после этого было еще миллион, и таки полноценный магазин на Битрикс – не развернешь за 4 часа.

С какими же сложностями я столкнулась и какие открытия для себя совершила? 

1) Расчет стоимости доставки. Клиенты, для которых я делала интернет-магазины ранее – все, как один, - из Москвы. Их основной способ доставки – курьером, а основной способ оплаты – наличными курьеру. Все остальные способы доставки и оплаты считались второстепенными, о региональных покупателях заботились только самые крупные из моих заказчиков, да и то – не в полной мере, как я теперь понимаю. Я же живу не в default-city, но продавать свой товар была намерена по всей России. Благо он востребован, известен и скудно представлен в регионах. 

Естественно, я была в курсе, что в своем стандарте Битрикс рассчитывает доставку только из Москвы в другие города. Я поставила себе модуль eDost из маркетплейса, настроила расчет доставки из Армавира и считала, что проблема решена. Каково же было мое удивление, когда в один из первых дней работы магазина, я обнаружила у себя в почте письмо: «Юлия, пожалуйста, посчитайте, сколько будет стоить для нас доставка заказа в город Зареченск Пензенской области». Я ответила, что сейчас посчитаю, а сама думаю: «Вот ведь пользователи – как они умудряются так делать, что у них не срабатывает встроенный в заказ механизм расчета?» Захожу на сайт, пробую оформлять заказ в Зареченск. В местоположениях он у меня есть – подсказка вываливается. Но сервис eDost не знает такого города (а ведь это город, а не село, но кому же интересна жизнь в Замкадье?). В итоге я рассчитала покупателю стоимость доставки вручную, отредактировала их заказ и вручную же отписала им письмо. А в форме оформления заказа добавила пункт «Почта России» 400р «Если автоматический расчет доставки не рассчитал сумму, выберете этот пункт» 

2) Оплата заказа. Я долго выбирала систему приема платежей, и остановила свой выбор на Робокассе, ее и подключила. Хотя сейчас подумываю сменить ее на ДеньгиОнлайн. Но не суть. Обе системы – удобные сами по себе, вот только многие пользователи не знают, что такое Робокасса – для них это пустой звук. Люди хотят видеть в форме оформления заказа знакомые слова: WebMoney, Яндекс.Деньги, QIWI, Mastercard, VISA. Допилкой формы оформления заказа с тем, чтобы пользователь мог выбирать из знакомых слов я только еще собираюсь заняться – пока же отвечаю на письма пользователей и рассказываю, что Робокасса – это система, через которую можно оплатить заказ любым из вышеперечисленных способов.

3) Форма обратной связи. Стоило мне написать рядом с формой обратной связи свое имя, как сообщений стало сыпаться на порядок больше. Люди хотят обращаться к живому человеку, а не писать в пустоту. Реально, я и не думала даже. Советую всем владельцам и менеджерам магазина подписывать в контактах имя менеджера, который по этим контактам доступен.


4) Яндекс.Директ. Это не относится к разработке, но я и о нем хочу рассказать. Сразу после запуска магазина, я решила рекламировать его через Яндекс.Директ. До этого я никогда никому не организовывала рекламные компании в Директе, но решила не нанимать Директолога, а разобраться самой – так как, кроме меня никто туда душу не вложит.  И вот я сидела дня три и кидала деньги в эту топку, эксперементируя с разными вариантами настроек и текстов объявлений. Я выкинула в мусор за 3 дня, наверное, долларов 200, но мне таки удалось добиться показа именно на тех рекламных площадках, на которых я хотела крутиться, и конверсия не заставила себя ждать. Тфу-тфу-тфу, чтоб не сглазить.

Что именно я делала? Я поняла, что крутить объявление над выдачей яндекса – мне не по карману на первых порах – деньги улетают мгновенно – кликов и заходов масса – конверсии 0. Опять же 80% кликов делали Москвичи. Но у них в городе есть мой товар  и без меня, а конкурировать сроком доставки с московскими магазинами – я не могу. И первым делом я сняла галочку, чтобы объявления крутились во всех регионах кроме Москвы и Московской области. Затем я нашла среди рекламных площадок закрытый форум, где тусуются любители моего товара (там уже 6 лет не регистрируют новых пользователей вообще – перенаселенность, хотела вступить в это сообщество, но мне отказали – придется делать свое) Я посмотрела, какие из моих объявлений попали туда (по каким узким ключам) и выставила им максимальную ставку на клик, и теперь мои объявления там крутятся самыми первыми. (По крайней мере, в тех разделах, что доступны на чтение без регистрации) У заходов от туда – конверсия до 50%. Это улет. Заинтригованы, что за товар? Нет, это не то, о чем вы подумали.

5) То, что деньги в Директе улетают, пусть и давая мне неплохую конверсию, меня угнетало. Я решила, что  мне нужно собирать с моих случайных посетителей емайлы. Но как? Стандартная форма подписки Битрикс? Я подумала, что она слишком сложна для моей target group. Я написала свой компонент, который выводит на главной странице сайта всплывающее окошко, предлагающее получить секретный код на скидку прямо на email.  Если пользователь закрыл окно – я показываю ему его снова через сутки. Если подписался – больше не показываю (Печеньки ставлю). Сделала, чтобы пользователь просто вводил емайл, нажимал на кнопку и все – никаких настроек и подтверждений. У них будет возможность отписаться, когда я буду долбать их mailchimp-ом. Офигенно много людей оставили свой емайл в обмен на 7% секретный многоразовый купон (о котором нельзя никому рассказывать – он же секретный). Секретность дала свои плоды. Заказы с секретным кодом оформляли не только те люди, которым я этот код рассылала. Письма с кодом я рассылаю в конце каждого дня всем подписавшимся за день через уже упомянутый mailchimp. 

6) Чего еще мне не хватило в стандарте Битрикс? Не хватило компонента для создания Landing pages. В одной из ранее написанных статей, я писала о своем опыте создания лендингов на стандартных компонентах новостей. Но то же для клиента, а для себя мне тот вариант показался бедненьким. Написала полноценный комплексный компонент – гибрид новостей, обсуждения и каталога. Пока не прикрутила к магазину – допиливаю.

7) Импорт товаров. Интеграция 1С Предприятия и 1С Битрикс проработана в стандарте достаточно здорово (да и нестандартных решений у моей команды хватает). Да вот только я не веду учет товаров в 1С – зачем? Да и база товаров, хоть в 1С, хоть на сайте – должна от куда-то появиться. Про то, как я писала импорт в Битрикс из YML – я уже писала. Для моего проекта этот загрузчик мне пригодился. Но потом я поняла, что это мелковато. Разработали командными усилиями парсер, который периодически обходит сайт моего поставщика, и тянет инфу непосредственно от туда. Сам добавляет новые товары, сам меняет цены на уже добавленные, сам скрывает те, которых нет в наличии.

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

Встречайте: http://barbie-world.net/

Да, я торгую куклами. Куклами Barbie Mattel. Почему куклами, спросите вы? Открою вам страшную тайну. Я коллекционирую Барби. У меня не было куклы Барби в детстве. Настоящие Барби в то время стоили больше, чем зарабатывали в месяц мои родители. У меня была только китайская подделка. Когда мне было лет 12, я поклялась себе, что когда я выросту и начну хорошо зарабатывать, я куплю себе сама всех тех кукол, которых мне не купили в детстве. Сейчас я обладательница достаточно редкой коллекции кукол конца 80х – начала 90х – я скупала их у других коллекционеров.  

Удовлетворив свой аппетит куклами из моего детства, я решила, что можно начать покупать и современных кукол. Но меня ждало разочарование.  В магазинах моего города нет Barbie Mattel.  Встречаются иногда 1-2 бюджетных варианта с GG-молдом. Попробовала заказать на ozone. Заказывала Ракель и Никки Модных Штучек. Молд Ракель тогда еще был в новинку. То, что мне пришло – воняло формальдегидом. Я не могла поверить в это. Крупный магазин. Вонючие куклы. Но Barbie Mattel не могут вонять. Оригинальные Барби либо не пахнут ничем, либо слегка пахнут карамелью. Если у волос Барби карамельная отдушка, то производитель сообщает об этом – дабы куклу не купили аллергику (сейчас я уже знаю об этом). Тогда же я решила, что Барби испортились со временем, что они уже не те. Но однажды мой отчим отправился в командировку в Германию, и я попросила его, чтобы он привез мне от туда Барби Модную Штучку. Хотела проверить, будет ли вонять формальдегидом она. Как и следовало ожидать, привезенная мне из Германии кукла, отличалась от купленных мною в России, как небо от земли. 

Я поняла, что я не встречала в своем регионе  настоящей Барби, произведенной после 98 года. И тогда я подумала: «Тот, кто наладит сюда поставки оригинальных Barbie Mattel - озолотится». Я нашла проверенных поставщиков Barbie Mattel и организовала интернет-магазин. В моих планах сделать на Битрикс социальную сеть любителей Барби, аналогичную англоязычному  barbiecollector.com  Но я рада уже сейчас тому, что могу снабжать детей и взрослых настоящими Барби, чтобы они могли познать это чудо и это счастье вне зависимости от того, живут ли они в Москве или в Зареченске.

понедельник, 20 мая 2013 г.

Всегда актуальная, гибкая и настраиваемая sitemap.xml для Битрикс

К сожалению, мое предыдущее решение sitemap для Битрикс оказалось нежизнеспособным. Но не бывает худа без добра: я получила очень много откликов как в сообществе битрикс разработчиков, так и на емайл. Удивительно, скольким людям интересна эта тема. Многие присланные мне идеи помогли мне по-новому взглянуть на проблему генерации sitemap для Битрикс. Я поняла, что большинству пользователей нужна просто sitemap, генерируемая по расписанию, но не привязанная к модулю поиска.

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

Я подумала и написала новое решение, позволяющее администраторам сайта настроить генерацию sitemap быстро, просто и опционально. В маркетплейсе оно появится на днях, когда  мне отрисуют для него иконку, а я пока его анонсирую (и соберу новые пожелания).
На этот раз я решила добавить генератор sitemap в админку как новый тип экспорта.

Можно настроить, какие инфоблоки выгружать в сайтмеп и с каким приоритетом индексирования (я имею в виду пути к элементам и разделам инфоблока, взятые из его настроек).

Можно настроить, какие статичные каталоги и страницы (на основе меню сайта) выгружать в сайтмеп и с каким приоритетом.

Нажимаем «Сформировать» - и sitemap формируется.



Можно добавить профили экспорта sitemap таким же образом, как для стандартных видов экспорта.




А профиль в свою очередь можно привязать к крону.


И конечно, проверим в валидаторе, что получилось:

Да, Яндекс ее полюбит.

понедельник, 13 мая 2013 г.

Интеграция 1С Предприятия и 1С Битрикс: про то, как мы добавили возможность передачи архива по FTP


Работая над одним из проектов, я столкнулась с тем, что когда архив с данными от 1С больше 1,5Г - обмен проходит нестабильно - рвался именно на этапе передачи файла (иногда рвался, а иногда - нет). Возможно, это общая проблема, а возможно – на конкретном сервере, а сервер там солидный. Заказчик же хотел иметь возможность делать полную выгрузку каталога, полная выгрузка представляла собой архив, объемом около 6Г.

Как известно, на третьем шаге стандартного протокола обмена 1С Предприятие по частям посылает Битриксу архив с файлами обмена в виде POST (я рассматриваю наш частный случай – объемный архив).

Я предложила 1С Программисту переписать механизм импорта каталога товаров на его и на моей стороне так, чтобы на 3м шаге обмена 1С Предприятие выкладывало архив на сервер по фтп и просто сообщало сайту об этом. Посовещавшись, решили не заменять способ передачи архива от 1С на сайт, а сделать возможность передавать его как стандартно через http в виде POST, так и посредством ftp. Наш видоизмененный протокол обмена между 1С Предприятием и 1С Битрикс выглядит следующим образом:

Шаг 1 и 2 – такие же, как в стандартном протоколе обмена
Шаг 3: 

Вариант 1:
http://<сайт>/bitrix/admin/1c_exchange.php?type=catalog&mode=file&filename=<имя файла>&method=http
1С посылает запрос и загружает на сервер файлы обмена в формате CommerceML 2, посылая содержимое файла или его части в виде POST. 

Вариант 2:
1C заливает сайт по ftp, а затем посылает сайту запрос:
http://<сайт>/bitrix/admin/1c_exchange.php?type=catalog&mode=file&filename=<имя файла>&method=ftp
(тут нужно учесть возможность того, что если 1С не успеет залить архив на сервер за время жизни сессии, то Битрикс уже может разовтаризовать 1С, и тогда перед этим запросом нужно снова авторизоваться, но у нас за время жизни сессии все по ftp залиться успевало, и не стали пока эту возможность учитывать)

В случае успешной записи файла 1С-Битрикс выдает "success".

Шаг 4 – такой же, как в стандартном протоколе.

Таким образом, мы видоизменили только 3й шаг протокола обмена – остальные шаги остались стандартными.

Для того, чтобы реализовать эти изменения на стороне сайта, я сделала в ранее уже кастомизированном компоненте импорта (но конкретно на этом участке его код совпадал со стандартным компонентом) следующее:

В блоке 

if(($_GET["mode"] == "file") && $ABS_FILE_NAME) {}

Сразу после коммента //And save it the file

Заменила условие 

if($DATA_LEN > 0) 
на 
if($DATA_LEN > 0 || $_GET["method"]='ftp')
И все – после этого компонент успешно принимал архив как загруженный кусочками в POST, так и залитый целиком на ftp (при условии, что 1С присылала соответствующий маркер). Возможно, что при этом не все возможные ошибки вылавливаются, но тогда в любом случае возникает ошибка при попытке скрипта распаковать архив, поэтому углубляться не стала.

На тестирование же опять ушло много-много часов. Я уже привыкла, что при кастомизации интеграции основное время нужно закладывать не на написание кода, а на тестирование. Дополнили лог обмена на стороне сайта фиксацией времени прихода запросов от 1С – это помогло следить за синхронностью. Важно, чтобы 1С выжидала, пока весь файл загрузится на ftp, прежде чем посылать сайту очередной запрос.

В итоге проблема заливки выгрузки объемом более 1,5Г была решена.

вторник, 7 мая 2013 г.

Рекурсивный алгоритм обхода дерева (General tree) (в контексте разделов инфоблока Битрикс)

Во время моей учебы в университете СИАОД казался мне скучнейшим предметом. Мы лежали на партах и не знали, куда себя девать, считая секунды до конца. Спустя годы я понимаю, что должна быть благодарна нашему преподавателю, который не позволял нам пропускать свои заунылые лекции. Потому что, занимаясь оптимизацией web-проектов на битрикс, я сплошь и рядом вижу ситуации, когда незнание такого простого алгоритма, как обход естественного дерева, вынуждает разработчиков делать лишние запросы к базе данных.

Дерево в своем общем виде - это направленный граф, каждый узел которого может содержать неограниченное число потомков. Разделы инфоблока в Битрикс с их неограниченной вложенностью по факту представляют собой дерево.

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

Сначала я выбираю все активные разделы из инфоблока

    $map=array();
    if(isset($arParams["IBLOCK_MAP_ID"]))
    {   
        $arFilter = Array('IBLOCK_ID'=>$arParams["IBLOCK_MAP_ID"], 'GLOBAL_ACTIVE'=>'Y');
        $db_list = CIBlockSection::GetList(Array("left_margin"=>"asc"), $arFilter, true, array("UF_ID_IB"));
       
        while($ar_result = $db_list->GetNext())
        {
            $map[''.$ar_result['ID']]=$ar_result;
         }
   
    }

Многие думают, что функция CIBlockSection::GetTreeList или CIBlockSection::GetList с сортировкой "left_margin"=>"asc" сразу возвращает нам дерево. Но она возвращает не дерево, а один из вариантов его обхода. Хорошо, если этот обход совпадает с тем путем, по которому вам нужно обойти дерево, но часто нужно обойти его в другом порядке. В массиве же $map каждый потомок содержит ссылку на предка, но предок не содержит ссылок на потомков.

В массиве $map сейчас потомки идут сразу за своими предками - то есть прямой ссылки нет - она определяется порядком следования элементов в массиве. Да, можно обойти дерево как угодно и в этом его представлении - примеров в исходниках Битрикс - масса. Но это, как я уже говорила - громоздкие алгоритмы. Если нам нужно, к примеру, вывести ветку, начиная с определенного узла, нам придется перебирать весь массив в цикле, пока мы не дойдем до этого узла (а чаще делают дополнительный аякс запрос по мере необходимости - в лучшем случае, а в худшем - запросы в цикле фигачат). А я предпочитаю не делать даже аякс запросов, если можно их не делать. Поэтому я преобразую дерево к другому очень удобному для работы виду - гроздьями.

$map_sec=array();
    foreach($map as $key=>$val){
   
        if($val['IBLOCK_SECTION_ID']>0){
            $parent_id=$map[$val['IBLOCK_SECTION_ID']]['ID'];
            $map_sec[''.$parent_id]['CODE']=$map[$parent_id]['CODE'];
            $map_sec[''.$parent_id]['NAME']=$map[$parent_id]['NAME'];
            $map_sec[''.$parent_id]['UF_ID_IB']=$map[$parent_id]['UF_ID_IB'];
            $map_sec[''.$parent_id]['CHILDS'][]=array("ID"=>$val['ID'],"CODE"=>$val['CODE'],"NAME"=>$val['NAME'],"UF_ID_IB"=>$val['UF_ID_IB']);
           
        }

    }
   
 $arResult['MAP']=$map_sec;

В массиве  $arResult['MAP'] у нас дерево в виде гроздей - каждый предок содержит ссылки на своих потомков. Вот к нему мы уже можем применять классические алгоритмы работы с деревьями в их неизменном виде.

Функция рекурсивного обхода такого дерева будет выглядеть следующим образом:

function outTree($category_arr,$parent_id) {
       
        if (isset($category_arr[$parent_id])) {
           
                foreach ($category_arr[$parent_id]['CHILDS'] as $value) { //Обходим
                    echo "<div style=\"margin-left:" . ($value['DEPTH_LEVEL']  * 25) . "px;\">" . $value["NAME"] . "</div>";
                  
                    if (count($category_arr[$value["ID"]]['CHILDS'])>0){
                        //Рекурсивно вызываем эту же функцию, но с новым $parent_id
                        outTree($category_arr,$value["ID"]);
                    }
                    else{
                         //Выводим листик
                     }
                 
                }
           
           }
       
    }


Теперь для того, чтобы вывести ветку дерева, начиная с любого узла $id_uzla достаточно написать:
outTree($arResult['MAP'],$id_uzla);
то есть мы можем выводить ветки дерева в любом порядке, в каком они нужны нам в шаблоне, например, чтобы вывести иерархию каталога на вложенных друг в друга закладочках, не обращаясь при этом к базе данных каждый раз при переключении закладки.

В практическом плане. Данный метод (с несколькими модификациями, естественно) я использовала для представления иерархии каталога товаров в виде вот таких вложенных закладочек. И вложенность там ничем не ограничена (только фантазией дизайнера).



Обращение к базе - только единожды (одна функция CIBlockSection::GetList) - и все построено на результатах ее работы. Работающий пример тоже покажу - ближе к запуску проекта, для которого писала это.

среда, 1 мая 2013 г.

Дикие ягоды и подводные камни... (опыт интеграции 1С Битрикс и 1С ПРедприятия)

"Хочу все, как на дикой ягоде (как на wildberries.ru)" - над таким незамысловатым заданием заказчика нам недавно довелось поработать (нам - это в данном случае мне и моей помощнице Юлии Ханусяк). Уточнив, что именно заказчик хочет "как на дикой ягоде", я поняла, что он хочет такую же организацию каталога и карточки товаров, как там и чтобы каталог наполнялся на стороне 1С и уже не требовал дозаполнения на стороне сайта. Несколько разработчиков до меня благоразумно отказались от этой задачи, я же изначально не увидела в ней всех подводных камней. Мы определились, какие задачи заказчик хочет видеть решенными до запуска магазина, а какие мы оставим на будущее, и я приступила к реализации.

Тут следует упомянуть об особенностях организации каталога на wildberries.ru.
Каждый товар (предмет одежды) имеет торговые предложения, обладающие характеристиками цвета и размера. И к каждому цвету привязано свое фото, и если пользователь в карточке товара кликает по определенному цвету,  то картинка этого цвета становится на место большой картинки. 

Как известно, подготовить структуру инфоблоков для такой задачи на стороне сайта - не составляет большого труда. Об организации каталога с торговыми предложениями хорошо написано в учебных курсах для разработчиков, и я не буду пересказывать их. Привязать картинки к торговым предложениям в Битрикс тоже вполне возможно.

На стороне 1С (у клиента стандартная 1С УТ 11.0) - тоже, казалось бы, нет проблемы - включаем учет по характеристикам. Создаем характеристики "цвет" и "размер" для нужных номенклатурных групп, правильно заводим документы прихода на склад и документы установки цен, создаем профиль обмена с фильтром по наличию на складе и делаем выгрузку.

И вот тут и всплыл большой и жирный подводный камень: на стороне 1С Предприятия в стандартной конфигурации УТ нет никакой возможности указать, к какому цвету относится та или другая прикрепленная к номенклатурной позиции картинка. 

1С программиста у заказчика не было, и заказчик искренне полагал, что, взявшись делать интеграцию, я должна делать ее с обеих сторон. "Что же, Юля, в следующий раз ты будешь тщательнее продумывать текст договора" - сказала я себе и вспомнила о замечательном 1С Франчайзи, интеграторе и внедренце, с которым я имею честь быть знакомой, а именно об Анатолии Полякове (Skype: tolpolyakov). К нему я и обратилась за консультацией по указанной проблеме. Анатолий проконсультировал меня и, кстати, отказался от денег, попросив взамен дружбу и аналогичные дружеские консультации по Битриксу, если они понадобятся. Анатолий, я не забываю добро и помню о своих обещаниях. Но я отвлеклась от темы. 

Анатолий предложил мне следующий способ решения задачи привязки конкретной картинки к конкретному цвету: на стороне 1С отказаться от учета по характеристики "цвет", вести учет только по характеристике "размер", на стороне 1С считать, что разные цвета одного товара - это разные товары, но иметь некое объединяющее поле и на стороне сайта, кастомизируя импорт, сливать несколько товаров, различающихся цветом, в один товар с торговыми предложениями, обладающие цветом и размером. 

Этот способ мне не очень понравился... и не только из-за его трудоемкости. Мне нравится сама идея ведения учета товара в 1С Предприятии по характеристикам, и отказ от полноценного использования такого учета ради реализации маленькой фичи показался мне неадекватно большой потерей, хотя заказчик был готов на нее пойти. 

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

Мы решили использовать в 1С стандартное поле "Описание файла" для хранения нужных нам признаков картинки - цвета и того, является ли она "мордочкой" или "спинкой". 

Договорились, что в описании файла заказчик будет писать, к примеру, "красный№1" если это главная картинка красного цвета, "красный" - если это картинка красного цвета, но не главная, "красный№0" - если это картинка красного цвета, к-я должна быть использована как картинка по-умолчанию, если для какого-то цвета нет картинки.

При импорте в файле обмена описания файлов картинок выгружаются 1С в составе реквизитов товара.

О том, как их можно обработать на стороне сайта - я тоже умолчу в данном посте - я уже опубликовала несколько постов с описанием того, как я кастомизирую импорт. А то, что у нас получилось - можно увидеть на сайте luckyfamily.ru 

Конечно, пройдет еще не мало итераций разработки, пока этот проект приблизится к своему идеалу - "диким ягодам". Но каталог успешно наполняется на стороне 1С, регулярно импортируется на сайт и мы знаем, какая фотка - какого цвета, на какой мордочка, а на какой спинка.

понедельник, 22 апреля 2013 г.

Импорт скидок номенклатуры из 1С Предприятия в 1С Битрикс

Начну с цитаты

...если документом "Установка скидок номенклатуры" определены скидки на товары в зависимости от их количества в заказе, то данная информация поступит на сайт и скидка будет применяться и при продаже в Интернет-магазине.
Кроме этого, модуль на стороне 1С выгружает в CommerceML и другие типы скидок, но их автоматический импорт в 1С-Битрикс предлагается реализовать разработчикам интернет-магазина, поскольку их применение очень сильно зависит от особенностей ценообразования для каждого конкретного случая.

Цитата от сюда: https://1c.1c-bitrix.ru/ecommerce/v2.php

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

Данную задачу мы (я и мой помощник Вадим Соловьев) решали для того же самого проекта, для которого я незадолго до этого подогнала импорт под готовую структуру инфоблоков (см. статью Как подогнать стандартный импорт из 1С под готовое решение из маркетплейс Битрикс). В этом проекте у нас уже был унаследован класс CIBlockCMLImport, был кастомизирован компонент catalog.import.1c для работы именно с этим классом.

class CIBlockCMLCustomImport extends CIBlockCMLImport{}

и была перегружена функция ImportElement

Этот же перегруженный метод  ImportElement мы и дополнили функционалом для импорта из 1С безусловных скидок на товар.

Информация о скидках приходила к нам из 1С Предприятия в следующем виде:
<СкидкиНаценки>
     <СкидкаНаценка>
      <Условие>Количество одного товара в документе превысило</Условие>
      <Процент>20</Процент>
      <Валюта>руб</Валюта>
      <Получатель>8a21ac94-2ac6-11e0-8e8a-00265abc1d64</Получатель>
     </СкидкаНаценка>
...
</СкидкиНаценки>
И торговые предложения, у которых в секции  СкидкиНаценки присутствовала скидка указанного вида нам нужно было интерпретировать как торговые предложения, на которые действует безусловная процентная скидка.

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

Итак, в нашем перегруженном методе ImportElement мы дописали следующее:

В самом начале метода мы смотрим, есть ли для указанного элемента безусловная процентная скидка. Если есть, пишем ее в переменную $discount_perc. Если нет - эта переменная остается равной 0.

$discount_perc = 0.0;
if(isset($arXMLElement[GetMessage("IBLOCK_XML2_DISCOUNTS")]))
{
    foreach($arXMLElement[GetMessage("IBLOCK_XML2_DISCOUNTS")] as $key=>$discount)
    {
        if(
            isset($discount[GetMessage("IBLOCK_XML2_DISCOUNT_CONDITION")])
            && $discount[GetMessage("IBLOCK_XML2_DISCOUNT_CONDITION")]===GetMessage("IBLOCK_XML2_DISCOUNT_COND_VOLUME")
        )
        {
            $discount_perc = $this->ToFloat($discount[GetMessage("IBLOCK_XML2_DISCOUNT_COND_PERCENT")]);
        }
    }
}
Далее, ниже секции, в которой в стандартном методе ImportElement определяются скидки на товары в зависимости от  их количества в заказе, мы вставили код, создающи/обновляющий/удаляющий наши безусловные процентные скидки. В этом коде мы пошли на небольшую хитрость и ставим каждой новой скидке ID равный ID элемента, для которого данная скидка была создана.

$discount_id = intval($arElement["ID"]);
$arDiscount = array();
$arDiscount = CCatalogDiscount::GetByID($discount_id);
if ($discount_perc == 0.0)
{
    if($arDiscount) CCatalogDiscount::Delete($discount_id);
}
else
{
    if($arDiscount)
    {
        if($arDiscount["VALUE"] != $discount_perc)
        {
            $arDiscount["VALUE"] = $discount_perc;
            CCatalogDiscount::Update($discount_id, $arDiscount);
        }
    }
    else
    {
       
        if ($discount_perc>0){
        $arLogic = array (
            "CLASS_ID" => "CondGroup",
            "DATA" => array (
                "All" => "OR",
                "True" => "True",
                ),
            "CHILDREN" => array (
                "0" => array (
                    "CLASS_ID" => "CondIBElement",
                    "DATA" => array (
                        "logic" => "Equal",
                        "value" => $discount_id,
                        ),
                ),
            ),
        );

        $arDiscount = array (
            "ID" => $discount_id,
            "SITE_ID" => "s1",
            "ACTIVE" => "Y",
            "NAME" => strval($discount_id),
            "MAX_USES" => 0,
            "COUNT_USES" => 0,
            "COUPON" => "",
            "SORT" => 100,
            "MAX_DISCOUNT" => 0.0000,
            "VALUE_TYPE" => "P",
            "VALUE" => $discount_perc,
            "CURRENCY" => "RUB",
            "MIN_ORDER_SUM" => 0.0000,
            "NOTES" => "",
            "RENEWAL" => "N",
            "ACTIVE_FROM" => "",
            "ACTIVE_TO" => "",
            "PRIORITY" => 1,
            "LAST_DISCOUNT" => "Y",
            "CONDITIONS" => serialize($arLogic),
        );
       
        CCatalogDiscount::Add($arDiscount);
       
        }
    }
}
И безусловные скидки на товары были успешно импортированы из 1С Предприятия в Битрикс.
Решение не претендует на идеальность, но оно имеет место быть, успешно внедрено и не перегружает достаточно стандартную виртуальную хостинговую площадку.

суббота, 20 апреля 2013 г.

Как увеличить продажи интернет-магазина на 1С Битрикс

Примерно 80% моих заказчиков занимаются розничными продажами через интернет-магазин, и проблема низкой конверсии неизменно волнует их. (конверсия - отношение покупателей к посетителям, выраженное в процентах)  Чего только ни делают люди, чтобы увеличить продажи: покупают все больше и больше ссылок, покупают рекламу, заказывают продающие статьи и т.д., и т.п. А конверсия часто - не растет.

Одна из причин по которой конверсии нет - в том, что SEO-шники привлекли на сайт не целевых посетителей - выбрали не то семантическое ядро. О выборе семантического ядра я не буду писать в этой статье - это больше по части моей SEO-шницы Ерыгиной Ирины. Я же хочу рассказать о том, как выжать максимум из того, что имеешь. А именно о тех замечательных возможностях, которые дает нам Битрикс и про которые многие забывают.

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

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

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

- В инфоблоке добавляем множественное свойство  "Товары", тип для свойства указываем "Привязка к элементам инфоблока", в качестве объекта привязки выбираем инфоблок товаров. Выводим контент-менеждеру это поле в форму добавления статьи.


Теперь добавляя SEO-статью, контент-менеджер сможет выбрать, какие товары мы будем показывать-предлагать пользователю на странице с этой статьей. Зачем это нужно? Поисковик SEO-статью съест, и она долго-долго еще будет появляться в его выдаче по тому запросу, под который была оптимизирована. Страница с этой статьей - будет для пользователя, нашедшего ее через поисковик точкой входа на сайт. И когда он зайдет и прочитает статью, его уже нельзя отпускать - поэтому в конце статьи стоит показать ему несколько товаров из каталога магазина которые соответствуют теме статьи. Статья - формирует желание. А в конце ее будут выводиться товары - конкретное предложение. Ассортимент магазина может меняться, и, изменяя привязанные товары, можно распределять трафик посетителей, зашедших через поисковик.

Можно так же добавить в инфоблок еще одно свойство типа "Файл" для добавления верхнего баннера, к-й маркетологи любят размещать в верхней части лендинга. Если поле для ввода баннера добавили, то логично добавить и поле для ссылки - перехода по клику на этот баннер.

После этого в настройках компонента новостей нужно включить вывод данных свойств. В свойстве "Товары" у нас будут содержаться только ID товаров, поэтому нужно кастомизировать компонент новостей либо на уровне самого компонента, либо на уровне файла result_modifier.php шаблона. (Я предпочитаю на уровне компонента). А затем кастомизировать шаблон детальной страницы новости, чтобы внизу (а можно вверху и внизу) выводились картиночки товаров со ссылками на их детальные страницы (или даже с кнопками купить). 

К примеру, на сайте моих клиентов http://sandalshoes.ru мы с ними сделали вот так:
На верх лендинга контент-менеджер может добавлять веселенький баннер
А внизу - сразу под статьей - выводятся привязанные к этой статье товары:

Согласитесь - это более эффективное использование SEO-статьи - превращение ее из механизма поискового продвижения в механизм маркетинга.

среда, 10 апреля 2013 г.

О лишних запросах

Совершенство достигается не тогда, когда уже нечего прибавить, но когда уже ничего нельзя отнять
Антуан де Сент-Экзюпери

Оптимизация процесса интеграции 1С Предприятия и 1С Битрикс – это неисчерпаемая тема.  Чего только не увидишь порой в обработчиках событий добавления и обновления элемента инфоблока.
Больше всего меня убивает, когда я вижу, что разработчики тянут из базы то, что у них и так уже есть. Пример из жизни (нет, серьезно – это реальный пример):

AddEventHandler("iblock", "OnAfterIBlockElementAdd", "BXMUpdateElement_FIELDS");
function BXMUpdateElement_FIELDS(&$arFields)
{
   
     $ibe = new CIBlockElement();
    
    $dbr = $ibe->GetList(array(), array('ID'=>$arFields['ID']) );
    while($oe = $dbr->GetNextElement())
    {
        $arP = $oe->GetProperty('CML2_TRAITS');
   
     
}


       
}

А то, что нужно – уже передано в обработчик в $arFields['PROPERTY_VALUES'][92]
Зачем так неэкономно разбрасываться запросами к базе? Одна строка – и 20 000 лишних обращений к базе при импорте 20 000 элементов.

О событиях, которые возникают при импорте каталога из 1С в Битрикс

В контексте интеграции 1С Предприятия и Битрикс не утихают споры по поводу того, какие события при этом отрабатывают, а какие не отрабатывают. А спорить, в общем-то, не о чем – благо, Битрикс поставляется нам в исходных кодах.
Класс импорта CIBlockCMLImport, который я так люблю наследовать, описан в файле 
\bitrix\modules\iblock\classes\general\cml2
Открыв этот файл, мы ясно видим, что элементы инфоблока товаров, как и элементы инфоблока предложений добавляются функцией CIBlockElement::Add
, а изменяются функцией CIBlockElement::Update

Исходные коды этих функций мы можем посмотреть в файлах (в одном описан класс CAllIBlockElement в другом – его наследник CIBlockElement)
 \bitrix\modules\iblock\classes\general\iblockelement.php
\bitrix\modules\iblock\classes\mysql\iblockelement.php

Что же мы видим там?
В функции Add имеется вот такая конструкция – перед самым сбросом управляемого кеша:

$events = GetModuleEvents("iblock", "OnAfterIBlockElementAdd");
while ($arEvent = $events->Fetch())
ExecuteModuleEventEx($arEvent, array(&$arFields));

То есть вызывается и отрабатывает событие OnAfterIBlockElementAdd Причем каждый из описанных обработчиков этого события. Как мы можем видеть, вызов этого события происходит всегда – безусловно, то есть каждый раз при отработке функции CIBlockElement::Add и не важно, вызвана ли она в классе импорта или где-то еще.

Кроме этого в этой же функции вызывается другое событие

if(!isset($arFields["WF_PARENT_ELEMENT_ID"]) && $arIBlock["FIELDS"]["LOG_ELEMENT_ADD"]["IS_REQUIRED"] == "Y")
{
    $USER_ID = is_object($USER)? intval($USER->GetID()) : 0;
    $db_events = GetModuleEvents("main", "OnBeforeEventLog");
    $arEvent = $db_events->Fetch();

}
Но не каждый раз, а по условию. Это не что иное, как событие при записи в журнал событий. И оно выполняется, если в настройках инфоблока указано журналировать добавление элемента.

А вот вызова события OnBeforeIBlockElementAdd я в функции Add не нашла (оно там есть, но вызвано не напрямую). Нет его и в классе импорта CIBlockCMLImport 

Посмотрим теперь, что у нас имеется в функции  CIBlockElement::Update

$events = GetModuleEvents("iblock", "OnAfterIBlockElementUpdate");
while ($arEvent = $events->Fetch())
ExecuteModuleEventEx($arEvent, array(&$arFields));

Вызов события OnAfterIBlockElementUpdate – имеется
Вызова события OnBeforeIBlockElementUpdate – не имеется

Зато вызовы событий OnBeforeIBlockElementUpdate и OnBeforeIBlockElementAdd присутствует в методе класса CAllIBlockElement CheckFields, который в свою очередь вызывается и в Add, и в Update Но функция CheckFields вызывается в них уже по условию.

Из вышесказанного я делаю следующие выводы: безусловно, при импорте каталога из 1С Предприятия в Битрикс отрабатывают обработчики событий OnAfterIBlockElementAdd и OnAfterIBlockElementUpdate, они отрабатывают уже после вставки/обновления элемента и, в принципе, могут быть использованы для модификации данных в инфоблоке, но ценой дополнительных запросов к базе. А дополнительные запросы к базе при импорте – это порой убийство импорта.

События OnBeforeIBlockElementUpdate и OnBeforeIBlockElementAdd так же могут отрабатывать при импорте при определенных условиях, однако уже по тому, каким образом они вставлены в исходный код битрикс, видно, что они задуманы не для модификации данных перед вставкой/обновлением, а для какой-то необычной кастомной проверки, возможно, какого-то поля необычного формата.

Поэтому, как я уже писала в одной из предыдущих статей, мне однозначно не нравится кастомизация импорта посредством использования этих событий – я для этих целей предпочитаю кастомизировать компонент импорта и наследовать класс CIBlockCMLImport, добавив наследнику требуемый функционал. Еще лучшим решением было бы конечно, написание, композитного класса, но это очень трудоемко. (Кстати, как видно из исходных кодов, разработчики Битрикс не гнушаются использовать наследование, значит и нам – сам Бог велел).

О событиях, возникающих при добавлении/обновлении раздела инфоблока, а так же при обновлении/добавлении цены товара я писать здесь не буду – нужно вклиниться в них – открываем исходники и смотрим, где они и как.

О событии
foreach(GetModuleEvents("catalog", "OnSuccessCatalogImport1C", true) as $arEvent)
ExecuteModuleEventEx($arEvent);
которое Битриксоиды вставили прямо на последний шаг работы своего стандартного компонента импорта из 1С, наверное, знают все. Но если вы используете его для кастомизации импорта … это плохо.

Единственное, что можно безболезненно повесить на это событие – это, к примеру, отправку письма администратору сайта о том, что импорт успешно завершен или что-то иное в этом роде. Но, к примеру, обходить в обработчике этого события весь многотысячный каталог и что-то там перераспределять – пересчитывать….  я думаю, все уже поняли, каково мое мнение на этот счет, так что я воздержусь от нелитературных выражений.

Я думаю, к интеграции 1С Предприятия и Битрикс нельзя подходить по принципу «если работает, то ничего не нужно менять». Нужно оптимизировать, оптимизировать и еще раз оптимизировать.

четверг, 28 марта 2013 г.

Как подогнать стандартный импорт из 1С под готовое решение из маркетплейс Битрикс

Недавно передо мной встала задача сделать, чтобы при импорте из 1С Предприятия в Битрикс, свойства товаров аккуратненько вставали туда, куда предполагается в одном из готовых решений маркетплейс. Решение - не мое. В маркетплейсе его уже нет, и партнера-разработчика тоже нет. Но заказчику это решение очень нравилось, и он хотел использовать именно его.

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

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

Пришла к выводу, что в данном случае стоит прибегнуть к наследованию и полиморфизму.

Что я сделала. Сначала, как обычно, скопировала компонент импорта в свое пространство имен, завела другой файл импорта, чтобы он обращался уже к моему компоненту, а не к стандартному. После этого объявила класс наследник для класса CIBlockCMLImport

Вот так:

class CIBlockCMLCustomImport extends CIBlockCMLImport
{

}

И подключила файл с наследником к моему компоненту.
Наследование я организовала ради того, чтобы перегрузить метод

ImportProperties

В php 5 перегрузку делать очень просто – нужно в теле наследника написать перегружаемую функцию с тем же самым именем, что и в классе-предке. И тогда для объектов класса-наследника будет выполняться именно она. А для объектов предка – его старая функция. Это и есть полиморфизм.


Метод ImportProperties непосредственно формирует структуру и состав свойств инфоблока для импорта.

Я нашла там место, где формируется

$arProperty

и вместо вот этой строки

if($arP["VALUE"] == GetMessage("IBLOCK_XML2_TYPE_LIST"))
$arProperty["PROPERTY_TYPE"] = "L";

написала свой код, проверяющий создан ли инфоблок для хранения значений этого свойства, и если не создан – я его создаю. (проверяю по XML_ID). А так же сделала полю тип E вместо L.

Кстати, чтобы создалось свойство типа «привязка к элементу инфоблока», нужно еще указать для создаваемого свойства LINK_IBLOCK_ID – ID инфоблока, к которому привязывать.

$arProperty["PROPERTY_TYPE"] = "E";
$arProperty["LINK_IBLOCK_ID"] = $найденный_выше_код_инфоблока;

А потом ниже по коду, там где в стандартном методе заполнялся список списочного поля, я вместо этого заполняю инфоблоки.

Но это еще не все – потом я еще начала искать то место, где непосредственно записывалось для данного элемента значение данного поля и поняла, что нужно делать перегрузку и для метода ImportElement. Я перегрузила этот метод и в теле уже своего перегруженного метода после обработки свойств типа L дописала свою обработку для свойства типа E.

После того, как я перегрузила эти функции, я прошла по файлу components.php своего кастамизированного компонента и заменила там все конструкторы системного класса CIBlockCMLImport на конструкторы своего - CIBlockCMLCustomImport.

Потом уже после того, как скрипт импорта сформировал мне в новом инфоблоке нужную структуру, я открыла старый инфоблок и новый – и перенесла в старый инфоблок все параметры свойств, их внешние коды, внешний код самого каталога.

Потом я сделала то же самое со старыми инфоблоками брендов, материалов, подошв и т.д., к-е были ранее в решении. Новые инфоблоки поудаляла. И все – импорт пошел куда надо.

Кстати, на форуме частенько появляются вопросы из разряда «Почему 1С не хочет грузить в существующий инфоблок, а создает рядом такой же другой?» Потому что он не такой же. Забывают люди, что в настройках инфоблока на закладке свойств есть еще и кнопочка такая, кликнув по которой можно редактировать дополнительные параметры свойства, и большинство таких вопросов – от банальной невнимательности.

На словах – все просто, а отлаживать этот кастомизированный импорт было тяжковато.

Работу обмена эмулировала скрипом bx_1c_import.php Прописала в нем путь к своему файлу импорта и свой компонент импорта. В коде своих функций выводила отладочную информацию в лог-файл.

Нет ничего невозможного.

вторник, 26 марта 2013 г.

Как добавить в магазин на Битрикс возможность импорта из нового формата данных

Помогая своим близким друзьям делать интернет магазин, я столкнулась с задачей импорта данных в Битрикс из формата YML (из формата файлов, который обычно применяется для экспорта в Яндекс.Маркет). За деньги, наверное, не согласилась бы писать такой импорт. А по дружбе – святое дело, и я стала копать. И очень интересные вещи накопала, которые, как ни странно, слабо освещены в документации 1С Битрикс.

А вещи элементарные на самом деле. Оказывается, чтобы добавить возможность собственного импорта в магазин на Битрикс, достаточно добавить в директорию

bitrix/php_interface/include/catalog_import

Два файла

имя_нового _импорта_setup.php – с настройками импорта
имя_нового _импорта _run.php – собственно импорт

И у них дожжен быть именно такой формат имен, как я указала выше – иначе не получится.

После того, как эти 2 файла добавлены, система САМА добавляет новый вид импорта в админку на страницу импорта товаров в магазин (/bitrix/admin/cat_import_setup.php) – пометила на рисунке зеленой стрелочке.

А вот оранжевой стрелочкой я пометила результат своего эксперимента с  функцией CCatalogImport::Add  Страница с документацией по этой функции пока практически пуста, но читая код  ядра, я поняла, что она применяется для добавления нового профиля для того или иного вида импорта, позволяет добавить его в меню или посадить на крон.

На данный момент мой модуль импорта данных из YML практически готов, и я допиливаю его для публикации в Маркетплейс.

вторник, 19 февраля 2013 г.

Как развернуть несколько сайтов в локали в Битрикс Веб-окружении

Казалось бы, избитая тема, но меня часто спрашивают, как сделать это, поэтому решила написать.
Ищем файл default.conf
у меня он лежит в
C:\Bitrix\apache2\conf\sites
у других может лежать в другом месте, в зависимости от версии Битрикс Веб-окружения и от того, куда она установлена - суть от этого не меняется.

В нем по-умолчанию прописан только один виртуальный хост

#Bitrix Env replace()
Listen 6448
<VirtualHost _default_:6448>
DocumentRoot "C:/Bitrix/www"
ErrorLog "logs/default-error.log"
CustomLog "logs/default-access.log" common
</VirtualHost>
#/Bitrix Env replace()

Но можно прописать сколько угодно дополнительных, например так:

#Bitrix Env replace()
Listen 6448
<VirtualHost _default_:6448>
DocumentRoot "C:/Bitrix/www"
ErrorLog "logs/default-error.log"
CustomLog "logs/default-access.log" common
</VirtualHost>
Listen 6449
<VirtualHost _default_:6449>
DocumentRoot "
C:/папка, в которой будут лежать файлы другого сайта/"
ErrorLog "logs/default-error.log"
CustomLog "logs/default-access.log" common
</VirtualHost>
#/Bitrix Env replace()

Записали, сохранили, перезапустили Веб-окружение, и теперь по адресу http://localhost:6448 будет открываться один сайт, а по адресу http://localhost:6449 - другой

Git обновить текущую ветку из master

Если разработка велась в ветке, и за время этой разработки ветка master сильно изменилась, лучше не вливать ветку сразу в мастер, а сначала влить мастер в ветку – чтобы предварительно протестировать.
Для этого нужно переключиться на ту ветку, в которую мы будем вливать мастер, а затем выполнить команду
git pull origin master
Cкорее всего, после этого возникнут конфликты – гит скажет об этом.
Чтобы просмотреть все файлы, в которых произошли конфликты, нужно выполнить команду
git status
После разрешения конфликтов, нужно сделать коммит, а потом снова выполнить
git pull origin master
Таким же образом можно периодически вливать в ветку новые изменения из мастера, если нужно, чтобы ветка не сильно «отошла» от основной версии проекта.

суббота, 2 февраля 2013 г.

Кастомизация экспорта заказов в 1С Предприятие без модификаций ядра 1С Битрикс

Продолжая тему нестандартной интеграции 1С Битрикс и 1С Предприятия, хочу рассказать о том, как я кастомизирую экспорт заказов. Это тоже довольно востребованная и распространённая задача, и, что интересно, практически всегда заказчиков не устраивает экспорт «из коробки», и они просят пусть какие-то мелочи, но переделать.

Например, недавно я столкнулась с такой задачей. Оптовые покупатели интернет-магазина (на 1С Битрикс Бизнес 11й версии ядра) обладают дополнительными свойствами: номерами договоров и датами заключения этих договоров. При оформлении заказа они выбирают один из своих договоров, на основании которого будет происходить сделка. Номер договора и его дата – должны передаваться в 1С предприятие при экспорте заказов.

Формированием xml-файла заказов, который передается в 1С Предприятие, занимается функция из ядра Битрикс ExportOrders2Xml, которая описана в файле bitrix/modules/sale/general/export.php

Конечно, соблазн исправить пару строк в ней прямо там на месте – велик. Это займет 5 минут. НО ядро битрикс станет модифицированным и каждый раз, обновляя ядро, нужно будет помнить об этой модификации и вносить ее снова. Каждый раз. А если проект перейдет на сопровождение другой студии или разработчику (как частенько бывает), и заказчик забудет о том, что ядро его проекта модифицировано, в один прекрасный день это может обернуться серьезными проблемами. Поэтому, я думаю, что лучше сразу потратить 30 минут вместо 5, но кастомизировать экспорт так, чтобы ядро не было затронуто, и чтобы об этой модификации не нужно было помнить.

Как я делаю? Я копирую компонент sale.export.1c  из папки /bitrix/components/bitrix/ в свое пространство имен /bitrix/components/bedrosova/, затем в папке компонента создаю еще 1 файлик /bitrix/components/bedrosova/sale.export.1c/functions.php

В этот файлик я копирую полное описание функции ExportOrders2Xml из файла bitrix/modules/sale/general/export.php  и незамысловато переименовываю ее в ExportOrders2Xml2 (а здесь нужно отметить, что исходная функция ExportOrders2Xml – это статический метод класса, и именно поэтому описываемый мною способ применим. Если бы этот метод не был статическим, то пришлось бы либо копировать весь класс, содержащий его, либо заводить класс – наследующий его. Наследование – это, конечно, спорный механизм, но не в сравнении с модификацией исходного класса) и затем уже в своей скопированной функции вношу все необходимые мне модификации.

После этого я подключаю файл со своей функцией
 /bitrix/components/bedrosova/sale.export.1c/functions.php
 в своем компоненте /bitrix/components/bedrosova/sale.export.1c/components.php 

Затем заменю вызов статического метода класса
CSaleExport::ExportOrders2Xml на вызов свой функции ExportOrders2Xml2

Все, остается только не забыть заменить в файле обмена вызов стандартного компонента sale.export.1c   на вызов кастомизированного.

Кастомизация экспорта заказов описанным методом не слетает даже при переходе с 11й версии ядра на 12ю, в которой стандартный экспорт организован с использованием класса CIBlockCMLExport. О его кастомизации без модификации ядра я напишу как-нибудь в другой раз.