Задача распространенная для бизнеса на территории РФ, где многие перепродают товары из-за рубежа. Получается, что отпускные цены зависят от колебания валют, т.к. продавать надо за рубли. День менеджера может начинаться с актуализации цен в прайсах.
Популярный модуль интернет-магазина Ubercart позволяет настроить валюту общую для всех товаров магазина. В этой валюте цены вводятся в базу и в ней же выводятся для пользователя. А хотелось бы вводить цены в экспортной валюте (своей для каждого случая), а выводить в местной, в той что товар отпускается для потребителя.
Начал поиск готового решения. Народ что то делал для решения этой задачи, но почему то пишут, что пришлось перелопатить кучу родных модулей уберкарта. Это закономерно приводит к невозможности штатного обновления Ubercart, превращая его в отдельно стоящую модификацию модуля. Мне такой вариант не нужен, придется делать что то самому :)
Тезисно сформулируем задачу.
- Загрузка курсов валют с cbr.ru
- Возможность задания цены как в рублях, так и в одной из валют (пусть это будут -EUR и USD).
- Пересчет цен товаров при актуализации курса, где задана цена в валюте.
- (бонус) Вывод блока с курсами валют на сайте.
Надо заметить, что Ubercart в моём случае имеет версию 2.12, а Drupal — 6. Возможно, для Ubercart версии 3 задача вообще не стоит, это надо отдельно смотреть, не знаю так ли это. :) Потому, если у вас сайт в стадии проектирования, и вы решили использовать Ubercart — посмотрите, что можно или нельзя делать посредством 3-й версии. Мне же надо было внести изменения в существующий проект.
Как обычно, все функции пишем в рамках какого то модуля — у нас это будет shra_contribution (складываю всё доп. программирование в Drupal в модуль «Вклад Шра»). Вы, естественно, переименуете функции так как вам нужно, если вдруг захотите использовать.
Загрузка курсов с cbr.ru
Пример, как можно читать курсы с cbr.ru, я уже приводил, сейчас используем это код в программе. Нам понадобятся две функции — одна возвращает полный список курсов, другая — курс только указанной валюты (все курсы идут по отношению к рублю).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/* получить текущий курс указанной валюты */ function _shra_contribution_course($valuteName) { //тут организуем кеш в рамках исполнения PHP кода static $vc = array(); if (!empty($vc[$valuteName])) return $vc[$valuteName]; //запрос все курсы $course = _shra_contribution_courses(); //выберем тот, что надо if (!empty($course['today']['data'][$valuteName])) { $vc[$valuteName] = $course['today']['data'][$valuteName]; return $vc[$valuteName]; } //не удалось получить данные return false; } /* получить данные о курсах */ function _shra_contribution_courses() { $now = time(); $nowStr = date('d.m.Y', $now); $default = array( 'today' => array( 'dtm' => $now, 'data' => ''), 'yesterday' => array( 'dtm' => $now - 84600, 'data' => ''), 'lastquery' => 0 ); $course = variable_get('sc_valuteCrs', $default); //принятие рещения о запросе данных с cbr if ((date('d.m.Y', $course['today']['dtm']) != $nowStr || empty($course['today']['data'])) && $now - $course['lastquery'] > 3600) { //XML запрос данных с cbr.ru $dataxml = file_get_contents('http://www.cbr.ru/scripts/XML_daily.asp?date_req=' . $nowStr); if ($dataxml !== false) { $xml = new SimpleXMLElement($dataxml); $atr = $xml->attributes(); if ($atr['Date'] == $nowStr && !empty($xml->Valute)) { $data = array(); foreach($xml->Valute as $v) { //уделим немного внимания преобразованию типов $data[$v->CharCode . ''] = (string) round(strtr($v->Value,',','.') / $v->Nominal, 6); } $course['yesterday'] = $course['today']; $course['today'] = array('dtm' => $now, 'data' => $data); $course['lastquery'] = $now; variable_set('sc_valuteCrs', $course); //вызов функции, которая обновит цены в базе UberCart, согласно новым курсам _shra_contribution_updatePrices(); } } } return $course; } |
Дополняем поля для типа публикации Product
Чтобы задавать цены в валюте, нужно добавить пару полей в тип публикации product. Одно поле — цена в валюте, второе — тип валюты. У меня в списке будет — USD и EUR. Вы можете расширить список, используя обозначения валют, используемых центробанком.
Я сгруппировал поля с помощью модуля Fieldgroup, это дань эстетике, для работы значения не имеет. Как видно — цена в валюте, это поле для задания числа с плавающей запятой, простое текстовое поле. Поле для выбора типа валют — список (select), где надо будет задать список значений :
После этих манипуляций получим блок значений в форме редакторе публикаций типа product вроде того, что на картинке:
Дальше нам нужно, чтобы при вводе цены в валюте рассчитывалась цена в рублях в штатном поле ubercart. Не буду делать это «на лету», запрограммирую хук drupal, чтобы значение вычислялось при сохранении публикации.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* hook node */ function shra_contribution_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) { if ($op == 'update' || $op == 'presave') { if ($node->type == 'product') { //пересчет из валюты в рубли if (!empty($node->field_valuteprice[0]['value'])) { $vc = _shra_contribution_course($node->field_valute[0]['value']); if ($vc) { $node->sell_price = round(($node->field_valuteprice[0]['value'] + 0) * $vc, 2); if ($op == 'update') drupal_set_message('Цена пересчитана по текущему курсу валюты.'); } else if ($op == 'update') drupal_set_message('Не могу определить текущий курс валюты - ' . $node->field_valute[0]['value']); } } } } |
Обновление цен при смене курса
Также нам понадобится обновление цены при получении данных о новом курсе. Она вызывается каждый раз из _shra_contribution_courses() при обновлении курсов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* обновление цен, заданных в валюте */ function _shra_contribution_updatePrices() { //получим список валют, цены в которых надо обновить $res = db_query(" SELECT DISTINCT field_valute_value FROM content_type_product WHERE field_valuteprice_value > 0 AND NOT field_valute_value IS NULL"); if (!mysql_numrows($res)) return false; while ($r = db_fetch_object($res)) { $course = _shra_contribution_course($r->field_valute_value); //обновляем цены по новому курсу выбранной валюты if ($course) db_query(" UPDATE uc_products, content_type_product SET uc_products.sell_price = ROUND(content_type_product.field_valuteprice_value * $course, 2) WHERE uc_products.vid = content_type_product.vid AND content_type_product.field_valuteprice_value > 0 AND content_type_product.field_valute_value = '$r->field_valute_value'"); } } |
Бонус — блок с курсами валют
Ну и как бонус — запрограммируем блок с выводом курсов валют, раз уж все возможности для реализации такого блока у нас имеются. Я приведу и кусочек каскадных стилей, чтобы вам было от чего отталкиваться при настройке в вашем окружении.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//для самых ленивых напоминаю как настроить блок в модуле :) function shra_contribution_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': $block = array(); ... $block[7]["info"] = t('Блок валют'); return $block; case 'view': switch ($delta) { ... case 7: return _shra_contribution_valutes(); } } } /* блок с валютами будет выведен курс и дельта по сравнению с прошлым (обычно вчерашним) курсом */ function _shra_contribution_valutes() { $course = _shra_contribution_courses(); //вывод данных; я сделал ссылку на cbr.ru, не помню - требуют ли они это в обязательном порядке $v_names = array('USD', 'EUR', ); /* вы можете тут вставить нужные вам валюты */ $content = '<div><a target="_blank" href="http://www.cbr.ru">Курс валют ЦБ</a> на <font>' . date('d.m.y', $course['today']['dtm']) . '</font></div>'; if (!empty($course['today']['data'])) { foreach ($v_names as $code) { if (!empty($course['today']['data'][$code])) { //определение разницы $v = $course['today']['data'][$code]; $last_v = strtr($v, ',', '.'); if (!empty($course['yesterday']['data'][$code])) $last_v = $course['yesterday']['data'][$code]; $dyn = number_format($v - $last_v, 4, ',', ''); $value = number_format($v, 4, ',', ''); $content .= "<div class=\"valuteRow\"><div class=\"valuteABBR\">$code</div> <div class=\"valuteCourse\">$value</div><div class=\"valuteDyn\">$dyn</div></div>"; } } } $content = '<table align="center" width="214"><tr><td>' . $content . '</td></tr></table>'; return array('content' => $content, 'subject' => ''); } |
А вот каскадные стили. Обратите внимание на объявление — #block-shra_contribution-7. ID нужно прописать для вашего случая (имя модуля, номер блока).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#block-shra_contribution-7 { background-color : #1d3d78; border-radius: 7px; margin-bottom: 10px; } .block .valuteheader { color: #bce1f8; font-size: 15px; font-family: tahoma, verdana; padding: 5px 0 2px 0; line-height: 25px; text-transform: uppercase; } .block .valuteheader .valuteday { color: #fff; font-size: 12px; } .valuteheader a:hover, .valuteheader a { color: #bce1f8 !important; text-decoration: none; font-weight: normal !important; } .valuteRow { line-height: 21px; font-size: 12px; height: 23px; width: 180px; border-bottom: 1px solid #567bb0; } .valuteRow div { float:left; height: 23px; font-size: 13px; } .valuteRow .valuteABBR { width: 35%; color: #d9ecff; font-size: 14px;} .valuteRow .valuteCourse { width: 30%; color: #b2e3ff; font-size: 14px;} .valuteRow .valuteDyn { width: 25%; color: #d9ecff; float: right;} |
Остались ещё вопросы? — Айда в комменты.
А как всю эту фигню отключить, она постоянно ошибку выдает?
Здравтсвуйте, Вы это сделали в виде модуля? Нельзя ли скачать готовый? Для ubercert 3 не реализовывали?
Этот код был реализован как часть модуля (у меня он назывался shra_contribution). Отдельного модуля не делал.
Скопируйте кусочки кода в свой модуль или создайте отдельный модуль, переименуйте функции. Отдельно подключите css или в тему или в модуль.
Должно работать :)
Спасибо, попробую :) Еще кое-что для 3 нашла. Буду смотреть.
Юрий, снова здравствуйте. Вас интересует работа с интернет-магазином на ubercart 2? Если да, давате по е-майл обсудим?
На страничке резюме есть вся информация для связи http://shra.ru/portfolio