Обновление цен из Excel в 1С и затем через REST-API на 1С-Битрикс
Задача: обновлять цены в 1С УТ11 и на сайте 1С-Битрикс из Excel файла.
Создал обработчик 1С платформы который берет данные из Excel создает документ с новыми ценами для торговли и затем отправляет массив данных в JSON формате на REST-API сайта.
Реализация в 1С:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПриОткрытииСервер();
КонецПроцедуры
&НаСервере
Процедура ПриОткрытииСервер()
ВидЦены = Справочники.ВидыЦен.НайтиПоНаименованию("Прайс-лист");
КонецПроцедуры
&НаКлиенте
Процедура ИмпортироватьЦены(Команда)
Если ПутьКExcel = "" Тогда
Сообщить("Не указан путь к файлу");
Возврат;
КонецЕсли;
Если ПроверитьСуществованиеФайла(ПутьКExcel) = Ложь Тогда
Сообщить("Не найден файл импорта");
Возврат;
КонецЕсли;
ДвоичныеДанные = Новый ДвоичныеДанные(ПутьКExcel);
Идентификатор = Новый УникальныйИдентификатор;
ИмпортироватьЦеныСервер(ПоместитьВоВременноеХранилище(ДвоичныеДанные, Идентификатор));
КонецПроцедуры
&НаСервере
Процедура ИмпортироватьЦеныСервер(ФайлВоВремХранилище)
ДвоичныеДанные = ПолучитьИзВременногоХранилища(ФайлВоВремХранилище);
ВремФайл = ПолучитьИмяВременногоФайла("xlsx");
ДвоичныеДанные.Записать(ВремФайл);
ИмпортТаблица = Новый ТабличныйДокумент;
ИмпортТаблица.Прочитать(ВремФайл);
СписокЦен = Новый Массив;
ДанныеДляЗапроса = Новый Структура;
ДанныеДляЗапроса.Вставить("items", Новый Массив);
Если НЕ ИмпортТаблица.Область("R1C1").Текст = "Наименование"
ИЛИ НЕ ИмпортТаблица.Область("R1C2").Текст = "Цена"
ИЛИ НЕ ИмпортТаблица.Область("R1C3").Текст = "УникальныйИдентификатор"
Тогда
Сообщить("Не верный формат файла");
Возврат;
КонецЕсли;
НумСтр = 2;
МассивСтроки = Новый Массив;
МассивСтроки.Добавить(ИмпортТаблица.Область("R" + НумСтр + "C1").Текст);
МассивСтроки.Добавить(ИмпортТаблица.Область("R" + НумСтр + "C2").Текст);
МассивСтроки.Добавить(ИмпортТаблица.Область("R" + НумСтр + "C3").Текст);
Пока МассивСтроки[2] <> "" Цикл
НоменклатураСсылка = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(МассивСтроки[2]));
Если ПустаяСсылка(НоменклатураСсылка) Тогда
Сообщить("Не найдена номенклатура: " + МассивСтроки[0] + "; " + МассивСтроки[2]);
Возврат;
КонецЕсли;
Попытка
НоменклатураЦена = Число(СтрЗаменить(СтрЗаменить(Формат(МассивСтроки[1], "ЧГ=0"), ",", "."), " ", ""));
Если НЕ НоменклатураЦена = "" Тогда
Если НоменклатураЦена > 0 Тогда
ТекущаяЦена = ЦенаНоменклатуры(НоменклатураСсылка, ВидЦены);
НоваяСтрока2 = Новый Структура;
НоваяСтрока2.Вставить("uid", МассивСтроки[2]);
НоваяСтрока2.Вставить("price", НоменклатураЦена);
ДанныеДляЗапроса.items.Добавить(НоваяСтрока2);
Если НоменклатураЦена <> ТекущаяЦена Тогда
НоваяСтрока = Новый Структура;
НоваяСтрока.Вставить("Номенклатура", НоменклатураСсылка);
НоваяСтрока.Вставить("Цена", НоменклатураЦена);
СписокЦен.Добавить(НоваяСтрока);
Сообщить("" + НоменклатураСсылка + "; Цена текущая: " + ТекущаяЦена + "; Цена новая: " + НоменклатураЦена);
//Возврат;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Исключение
Сообщить("" + НоменклатураСсылка + "; Не читается цена");
КонецПопытки;
НумСтр = НумСтр + 1;
МассивСтроки = Новый Массив;
МассивСтроки.Добавить(ИмпортТаблица.Область("R" + НумСтр + "C1").Текст);
МассивСтроки.Добавить(ИмпортТаблица.Область("R" + НумСтр + "C2").Текст);
МассивСтроки.Добавить(ИмпортТаблица.Область("R" + НумСтр + "C3").Текст);
КонецЦикла;
Если СписокЦен.Количество() > 0 Тогда
УстановкаЦенНоменклатурыПоступ = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
УстановкаЦенНоменклатурыПоступ.Согласован = Истина;
УстановкаЦенНоменклатурыПоступ.Дата = ТекущаяДата();
УстановкаЦенНоменклатурыПоступ.Статус = Перечисления.СтатусыУстановокЦенНоменклатуры.Согласован;
//УстановкаЦенНоменклатурыПоступ.Ответственный = ПараметрыСеанса.ТекущийПользователь;
УстановкаЦенНоменклатурыПоступ.Записать();
Для Каждого стр Из СписокЦен Цикл
ЦеныНомен = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
ЦеныНомен.Отбор.Регистратор.Установить(УстановкаЦенНоменклатурыПоступ.Ссылка);
ЦеныНомен.Прочитать();
ЗаписьНабора = ЦеныНомен.Добавить();
ЗаписьНабора.Период = УстановкаЦенНоменклатурыПоступ.Дата;
ЗаписьНабора.ВидЦены = ВидЦены;
ЗаписьНабора.Номенклатура = стр.Номенклатура;
ЗаписьНабора.Цена = стр.Цена;
ЗаписьНабора.Валюта = Константы.ВалютаУправленческогоУчета.Получить();
ЦеныНомен.Записать();
КонецЦикла;
КонецЕсли;
Если ДанныеДляЗапроса.items.Количество() > 0 Тогда
Сообщить(ПреобразоватьВJSON(ДанныеДляЗапроса));
РезультатЗапроса = ЗапросJSON("example.com",
"/rest/1/htrhrthrthtr/disweb.catalog.price.update
.json", ДанныеДляЗапроса, Неопределено, Неопределено, Ложь); Сообщить(ПреобразоватьВJSON(РезультатЗапроса)); Если ПроверкаРезультата(РезультатЗапроса) = Ложь Тогда Сообщить("Ошибка обновления цен на сайте."); Возврат; КонецЕсли; Сообщить("Цены успешно обновлены."); КонецЕсли; КонецПроцедуры &НаКлиенте Процедура ПутьКExcelНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка) СтандартнаяОбработка = Ложь; ДиалогВыбора = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие); ДиалогВыбора.ПолноеИмяФайла = "Номенклатура.xlsx"; ДиалогВыбора.Фильтр = "Файл данных (*.xlsx)|*.xlsx"; ДиалогВыбора.Заголовок = "Выберите файл"; Если ДиалогВыбора.Выбрать() Тогда ПутьКExcel = ДиалогВыбора.ПолноеИмяФайла; КонецЕсли; КонецПроцедуры &НаКлиенте Функция ПроверитьСуществованиеФайла(ПутьКФайлу) Экспорт ФайлНаДиске = Новый Файл(ПутьКФайлу); Если ФайлНаДиске.Существует() Тогда Возврат Истина; КонецЕсли; Возврат Ложь; КонецФункции // ************************************************ // Сервисные &НаСервере Функция ПустаяСсылка(ОбъектСсылка) Если Лев("" + ОбъектСсылка, 18) = "<Объект не найден>" Тогда Возврат Истина; ИначеЕсли "" + ОбъектСсылка = "" Тогда Возврат Истина; КонецЕсли; Возврат Ложь; КонецФункции &НаСервере Функция ЦенаНоменклатуры(Номенклатура, ТипЦены) Дата = ТекущаяДата(); Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ЦеныНоменклатурыСрезПоследних.Цена КАК Цена |ИЗ | РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены=&ВидЦены) |КАК | ЦеныНоменклатурыСрезПоследних |ГДЕ | ЦеныНоменклатурыСрезПоследних.Номенклатура = &Номенклатура"; Запрос.УстановитьПараметр("Номенклатура",Номенклатура); Запрос.УстановитьПараметр("Дата", Дата); Запрос.УстановитьПараметр("ВидЦены", ТипЦены); РезультатЗапроса=Запрос.Выполнить(); Выборка = РезультатЗапроса.Выбрать(); Если Выборка.Следующий() тогда Возврат Выборка.Цена; Иначе Возврат 0; КонецЕсли; КонецФункции &НаСервере Функция ПроверкаРезультата(Результат) Если Не ТипЗнч(Результат) = Тип("Структура") Тогда Сообщить("Ошибка ответа; Тип значения ответа: " + ТипЗнч(Результат)); Возврат Ложь; КонецЕсли; Если Результат.Свойство("result") = Ложь Тогда Сообщить("Ошибка ответа; result не найден"); КлючиСтруктуры = ""; Для Каждого стр из Результат Цикл Если КлючиСтруктуры <> "" Тогда КлючиСтруктуры = КлючиСтруктуры + ", "; КонецЕсли; КлючиСтруктуры = КлючиСтруктуры + стр.Ключ; КонецЦикла; Если КлючиСтруктуры <> "" Тогда Сообщить("Ключи ответа: " + КлючиСтруктуры); КонецЕсли; Возврат Ложь; КонецЕсли; Возврат Истина; КонецФункции &НаСервере процедура Запрос(ХостЗапроса, АдресЗапроса, ВыходнойФайл = неопределено, КодСостояния = неопределено, ЗаголовкиHTTP = неопределено, ФайлЗапроса = неопределено) НТТР = Новый HTTPСоединение(ХостЗапроса,, , , , 1200, Новый ЗащищенноеСоединениеOpenSSL(,)); если ВыходнойФайл = неопределено тогда ВыходнойФайл = ПолучитьимяВременногоФайла(); конецесли; Попытка если ФайлЗапроса <> неопределено тогда ОтветHTTP = НТТР.ОтправитьДляОбработки(ФайлЗапроса, АдресЗапроса, ВыходнойФайл, ЗаголовкиHTTP); иначе Запрос = Новый HTTPЗапрос(АдресЗапроса, ЗаголовкиHTTP); ОтветHTTP = НТТР.Получить(Запрос, ВыходнойФайл); конецесли; Исключение Сообщить("Неудачная попытка соединения с сервером" + Символы.ПС + ХостЗапроса + Символы.ПС + АдресЗапроса + Символы.ПС + ОписаниеОшибки()); возврат; КонецПопытки; Попытка КодСостояния = ОтветHTTP.КодСостояния; исключение КодСостояния = неопределено; конецпопытки; если КодСостояния = неопределено тогда сообщить("Не удалось получить код состояния" + Символы.ПС + ХостЗапроса + Символы.ПС + АдресЗапроса); конецесли; конецпроцедуры &НаСервере функция ЗапросJSON(ХостЗапроса, АдресЗапроса, СтруктураЗапроса = неопределено, ЗаголовкиHTTP = неопределено, ПриставкаДляКлючей = неопределено, postSend = false) if ЗаголовкиHTTP = неопределено then ЗаголовкиHTTP = Новый Соответствие; endif; ЗаголовкиHTTP.Вставить("Accept", "application/json"); Если СтруктураЗапроса = неопределено Тогда ФайлЗапроса = неопределено; //Сообщить("неопределено"); Иначе if postSend = false then if ПриставкаДляКлючей = неопределено then ПриставкаДляКлючей = новый Структура; endif; ЗаголовкиHTTP.Вставить("Content-Type", "application/json"); ФайлЗапроса = ПолучитьимяВременногоФайла("json"); //Сообщить("JSON"); Текст = Новый ЗаписьТекста; Текст.Открыть(ФайлЗапроса, "CESU-8"); Текст.ЗаписатьСтроку(ПреобразоватьВJSON(СтруктураЗапроса, ПриставкаДляКлючей)); Текст.Закрыть(); else ЗаголовкиHTTP.Вставить("Content-Type", "application/x-www-form-urlencoded"); sendPost = ""; for each str in СтруктураЗапроса do sendPost = sendPost + ?(sendPost <> "", "&", "") + str.Key + "=" + str.Value; enddo; //Сообщить("POST: " + sendPost); ФайлЗапроса = ПолучитьимяВременногоФайла(); ФайлОтправки = Новый ЗаписьДанных(ФайлЗапроса); ФайлОтправки.ЗаписатьСтроку(sendPost); ФайлОтправки.Закрыть(); endif; КонецЕсли; //Текст = Новый ЧтениеТекста;Текст.Открыть(ФайлЗапроса, "CESU-8");message(Текст.ПрочитатьСтроку());Текст.Закрыть(); ВыходнойФайл = ПолучитьимяВременногоФайла("json"); КодСостояния = неопределено; Запрос(ХостЗапроса, АдресЗапроса, ВыходнойФайл, КодСостояния, ЗаголовкиHTTP, ФайлЗапроса); если КодСостояния = неопределено тогда РезультатЗапроса = неопределено; иначе Попытка ЧтениеJSON = Новый ЧтениеJSON; ЧтениеJSON.ОткрытьФайл(ВыходнойФайл); РезультатЗапроса = ПрочитатьJSON(ЧтениеJSON); ЧтениеJSON.Закрыть(); Исключение Сообщить("Ошибка чтения JSON" + Символы.ПС + ХостЗапроса + Символы.ПС + АдресЗапроса + Символы.ПС + ОписаниеОшибки()); Сообщить(ПрочитатьФайлОшибки(ВыходнойФайл)); КонецПопытки; конецесли; Попытка УдалитьФайлы(ВыходнойФайл); УдалитьФайлы(ФайлЗапроса); Исключение КонецПопытки; возврат РезультатЗапроса; конецфункции &НаСервере Функция ПрочитатьФайлОшибки(имяВыходногоФайла) Текст = Новый ЧтениеТекста(имяВыходногоФайла, КодировкаТекста.UTF8); Стр = текст.прочитатьСтроку(); ТекстОшибки = "" + Стр+ символы.ПС; Пока Стр <> Неопределено Цикл Стр = Текст.ПрочитатьСтроку(); ТекстОшибки = ТекстОшибки + ?(Стр = Неопределено,"",Стр)+ символы.ПС; КонецЦикла; возврат ТекстОшибки; КонецФункции &НаСервере функция ПреобразоватьВJSON(ДанныеДляПреобразования, ПриставкаДляКлючей = неопределено) если ПриставкаДляКлючей = неопределено тогда ПриставкаДляКлючей = новый Структура; конецесли; если ТипЗнч(ДанныеДляПреобразования) = Тип("Массив") тогда возврат ПреобразоватьВJSONМассив(ДанныеДляПреобразования, ПриставкаДляКлючей); иначеесли ТипЗнч(ДанныеДляПреобразования) = Тип("Структура") тогда возврат ПреобразоватьВJSONСтруктура(ДанныеДляПреобразования, ПриставкаДляКлючей); иначе возврат ложь; конецесли; конецфункции &НаСервере функция ПреобразоватьВJSONМассив(ДанныеДляПреобразования, ПриставкаДляКлючей) Результат = "["; для каждого стр из ДанныеДляПреобразования цикл если не Результат = "[" тогда Результат = Результат + ","; конецесли; если ТипЗнч(стр) = Тип("Массив") тогда Результат = Результат + ПреобразоватьВJSONМассив(стр, ПриставкаДляКлючей); иначеесли ТипЗнч(стр) = Тип("Структура") тогда Результат = Результат + ПреобразоватьВJSONСтруктура(стр, ПриставкаДляКлючей); иначеесли ТипЗнч(стр) = Тип("Булево") тогда Результат = Результат + ?(стр, "true", "false"); иначеесли ТипЗнч(стр) = Тип("Число") тогда ЧислоВСтроке = "" + СтрЗаменить("" + Формат(стр, "ЧГ=0"), ",", "."); если ЧислоВСтроке = "" тогда ЧислоВСтроке = "0"; конецесли; Результат = Результат + ЧислоВСтроке; иначеесли стр = неопределено тогда Результат = Результат + "null"; иначе Результат = Результат + """" + СтрЗаменить(СтрЗаменить(СтрЗаменить(СтрЗаменить(Строка(стр), "\", "\\"), Символ(13)+Символ(10), "\n"), Символ(10), "\n"), """", "\""") + """"; конецесли; конеццикла; Результат = Результат + "]"; возврат Результат; конецфункции &НаСервере функция ПреобразоватьВJSONСтруктура(ДанныеДляПреобразования, ПриставкаДляКлючей) Результат = "{"; для каждого стр из ДанныеДляПреобразования цикл стрКлюч = стр.Ключ; для каждого стрПриставка из ПриставкаДляКлючей цикл если стрПриставка.Ключ = стрКлюч тогда стрКлюч = стрПриставка.Значение + стрКлюч; прервать; конецесли; конеццикла; если не Результат = "{" тогда Результат = Результат + ","; конецесли; если ТипЗнч(стр.Значение) = Тип("Массив") тогда Результат = Результат + """" + стрКлюч + """:" + ПреобразоватьВJSONМассив(стр.Значение, ПриставкаДляКлючей); иначеесли ТипЗнч(стр.Значение) = Тип("Структура") тогда Результат = Результат + """" + стрКлюч + """:" + ПреобразоватьВJSONСтруктура(стр.Значение, ПриставкаДляКлючей); иначеесли ТипЗнч(стр.Значение) = Тип("Булево") тогда Результат = Результат + """" + стрКлюч + """:" + ?(стр.Значение, "true", "false"); иначеесли ТипЗнч(стр.Значение) = Тип("Число") тогда ЧислоВСтроке = "" + СтрЗаменить("" + Формат(стр.Значение, "ЧГ=0"), ",", "."); если ЧислоВСтроке = "" тогда ЧислоВСтроке = "0"; конецесли; Результат = Результат + """" + стрКлюч + """:" + ЧислоВСтроке; иначеесли стр.Значение = неопределено тогда Результат = Результат + """" + стрКлюч + """:null"; иначе Результат = Результат + """" + стрКлюч + """:" + """" + СтрЗаменить(СтрЗаменить(СтрЗаменить(СтрЗаменить(Строка(стр.Значение), "\", "\\"), Символ(13)+Символ(10), "\n"), Символ(10), "\n"), """", "\""") + """"; конецесли; конеццикла; Результат = Результат + "}"; возврат Результат; конецфункции
Реализация на сайте (local/php_interface/init.php):
<?
// ...
// *********************************************************
// begin:REST API присваения цен товарам
AddEventHandler('rest', 'OnRestServiceBuildDescription', ['\DwRestApi', 'OnRestServiceBuildDescription']);
class DwRestApi
{
public static function OnRestServiceBuildDescription()
{
return [
'disweb' => [
'disweb.catalog.price.update' => [
'callback' => [__CLASS__, 'catalogPriceUpdate'],
'options' => [],
],
],
];
}
public static function catalogPriceUpdate($query, $nav, \CRestServer $server)
{
if ($query['error'])
{
throw new \Bitrix\Rest\RestException(
'Message',
'ERROR_CODE',
\CRestServer::STATUS_PAYMENT_REQUIRED
);
}
if (!isset($query['items']))
{
return [
'error' => 'ERROR_ITEMS_VAR_NOT_FOUND',
'error_description' => 'Items variable not found! ' . __LINE__,
];
}
if (count($query['items']) <= 0)
{
return [
'error' => 'ERROR_ITEMS_NOT_FOUND',
'error_description' => 'Items not found! ' . __LINE__,
];
}
if (!is_array($query['items']))
{
return [
'error' => 'ERROR_ITEMS_VAR_NO_ARRAY',
'error_description' => 'Items variable not array! ' . __LINE__,
];
}
$items = [];
foreach ($query['items'] as $item)
{
if (!isset($item['uid']) || !isset($item['price']))
{
continue;
// return [
// 'error' => 'ERROR_ITEM_DATA',
// 'error_description' => 'Item data not found! ' . __LINE__,
// ];
}
$uid = trim($item['uid']);
$price = floatval(preg_replace('/[^0-9\.]/', '', $item['price']));
if (!$price)
{
continue;
// return [
// 'error' => 'ERROR_ITEM_PRICE',
// 'error_description' => 'Item price not found! ' . __LINE__,
// ];
}
$el_res = \CIblockElement::GetList(
[],
[
'XML_ID' => $uid,
],
false,
false,
['ID']
);
if (!$el = $el_res->Fetch())
{
continue;
// return [
// 'error' => 'ERROR_ITEM_NOT_FONT',
// 'error_description' => 'Item guid: ' . $uid . ' not found! ' . __LINE__,
// ];
}
$ar_fields = [
'PRODUCT_ID' => $el['ID'],
'PRICE' => $price,
'CATALOG_GROUP_ID' => 1
];
$price_res = CPrice::GetList(
[],
[
'PRODUCT_ID' => $el['ID'],
'CATALOG_GROUP_ID' => 1,
]
);
if ($row = $price_res->Fetch())
{
\CPrice::Update($row['ID'], $ar_fields);
}
else
{
\CPrice::Add($ar_fields);
}
$items[] = [
'uid' => $uid,
'price' => $price,
'result' => 'ok',
];
}
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/bitrix/cache/s2/bitrix/catalog.section')
|| file_exists($_SERVER['DOCUMENT_ROOT'] . '/bitrix/cache/s2/bitrix/catalog.section.list'))
{
exec('rm -rf ' . $_SERVER['DOCUMENT_ROOT'] . '/bitrix/cache/s2/bitrix/catalog*');
}
return [
'status' => 'ok',
];
}
}
// end:REST API присваения цен товарам
// *********************************************************
// ...