Дисклеймер:
Данная статья является больше описанием моего собственного опыта решения данного вопроса, но не в кой мере каким-то универсальным решением для всех и каждого. Поэтому заранее прошу прощения за некоторые упущения и тонкости, которые я мог упустить в процессе её написания.
Предисловие:
Захотелось как-то помочь всем тем несчастным вроде меня, кто знает, что можно читать EXCEL и уже не раз загружал что-то из него в 1С. Но вот тут возникла потребность загрузить картинки из ячеек, да так, что бы понимать какой строке таблицы соответствует какая картинка, что очень даже важно, если загружаешь прайс-лист поставщика.
Покопавшись немного в интернете появилось понимание, что EXCEL 2010 никак не предоставляет возможности выдернуть из себя картинку. И многие используют сторонние библиотеки, для переноса картинки из буфера обмена в файл. Источник //infostart.ru/public/16800/ К Сожалению у меня это не заработало. Библиотека регистрируется нормально но не работает, по причине не ясной до сих пор.
Тогда было принято решение поступить радикально и работать не с объектом EXCEL, а непосредственно с табличным документом, так как есть методы позволяющие прочитать файл xlsx и загрузить его в табличный документ и работать уже конкретно с ним. К счастью у табличного документа 1С у рисунка есть метод позволяющий сохранить его в файл, что уже огромный плюс.
Но тут уже встала другая проблема. Как понять какая картинка, какой строке соответствует? Наивно пологая, что через ширину столбцов и строк можно получить координаты картинки, я «наступил на огромные грабли». Вообще мне не понятно почему так происходит? Почему координаты ячейки не соответствуют координатам картинки. Почему нет какого-то соответствия или индекса или ещё чего-то в 1С, что бы можно было это сопоставить. Это был тупик, пока я не наткнулся на маленькую статейку с кодом вот тут:
https://medium.com/1c-tricks/-c146cf0af7d1
Спасибо автору Дмитрий Марочко, теперь хоть и через костыли, но всё же я мог найти координаты картинки для нужной строки.
Дальше были только мелочные проблемы, например в Экселе есть возможность получить уровень группировки с помощью Лист1.rows(Х).OutlineLevel А вот в табличном документе такого свойства нет. Ещё возникали проблемы при загрузке картинки уже непосредственно в 1С, но это лишь от незнания особенностей конфигурации УТ 11.4
Платформа: 1С:Предприятие 8.3 (8.3.13.1690)
Конфигурация: Управление торговлей, редакция 11 (11.4.6.207)
Хватит рассусоливать, погнали код.
Я выложу основные моменты в виде текста, что бы любой мог ими воспользоваться. Но для особо ленивых будет доступна обработка, в которой уже всё готово. А так же пример прайс-листа.
-
Самое простое – это прочитать Эксель в табличный документ. Тут ничего сложно. Единственное есть нюанс, что чтение происходит на сервере и файл у вас должен быть доступен со стороны сервера. У меня это условие выполнялось, так что тут я особо не заморачивался.
EXCEL открывается в табличном документе, при этом даже не обязательно, что бы на компьютере был установлен какой-то из продуктов Microsoft Office.Объект.ТабДок.Прочитать(Объект.ПутьКФайлу);
В данном случае Объект.ТабДок – это реквизит на форме типа ТабличныйДокумент. Таким образом, у нас файл EXCEL открывается в табличном документе, при этом даже не обязательно, что бы на компьютере был установлен какой-то из продуктов Microsoft Office.
Следующий код выполняется на клиенте -
Следующим этапом надо определить, где находятся нужные нам колонки. Что бы ни подгонять каждый раз таблицу под какой-то определённый шаблон, чего конечные пользователи делать не будут, я написал алгоритм, который позволяет автоматически определять, в каких колонках какие данные находятся.
При этом, список данных, которые нужны заранее задан как колонки таблицы значений на форме. В моём случае это: Артикул Аналоги, Бренд, Номенклатура, Применяемость, Цена. Алгоритм анализирует начало таблицы в поисках имён и сам определяет по наименованию в какой колонке какие данные лежат. При этом у меня был довольно таки сложный прайс лист, так как в нём присутствовали повторяющиеся названия в колонках, например слово «номенклатура» встречалось несколько раз. Поэтому было дополнительное условие, выбирающее название колонки с наименьшей длинной.
//Создадим структуру, которая будет хранить номер строки и номер колонки табличного документа в которых расположены соответсвующие данные КолПрайсЛист = Новый Структура; Для каждого Эл из Элементы.ТабЛист.ПодчиненныеЭлементы Цикл КолИмя = НРЕГ(СтрЗаменить(Эл.Имя,"ТабЛист","")); КолПрайсЛист.Вставить(КолИмя+"Строка",0); КолПрайсЛист.Вставить(КолИмя+"Колонка",0); КолПрайсЛист.Вставить(КолИмя+"Длинна",99);//Так как названия колонок могут повторятся, нам нужно выбрать название с минимальной длинной КонецЦикла; Для Х=1 По КоличествоКолонок Цикл //что бы не по всей таблице ведь мы ищем только шапку а она где-то вверху Для У=1 По КоличествоКолонок Цикл ТекстХУ = НРЕГ(Объект.ТабДок.Область(Х,У,Х,У).Текст); Если СтрНайти(ТекстХУ,"бренд")<>0 тогда КолПрайсЛист.брендСтрока = Х; КолПрайсЛист.брендКолонка = У; КонецЕсли; Если СтрНайти(ТекстХУ,"номенклатура")<>0 тогда //как раз таки слово "Номенклатура" у нас встречается несколько раз Если СтрДлина(ТекстХУ)<=КолПрайсЛист.номенклатураДлинна Тогда КолПрайсЛист.номенклатураСтрока = Х; КолПрайсЛист.номенклатураКолонка = У; КолПрайсЛист.номенклатураДлинна = СтрДлина(ТекстХУ); КонецЕсли; КонецЕсли; Если СтрНайти(ТекстХУ,"артикул")<>0 тогда КолПрайсЛист.артикулСтрока = Х; КолПрайсЛист.артикулКолонка = У; КонецЕсли; Если СтрНайти(ТекстХУ,"аналог")<>0 тогда КолПрайсЛист.аналогиСтрока = Х; КолПрайсЛист.аналогиКолонка = У; КонецЕсли; Если СтрНайти(ТекстХУ,"применяемость")<>0 тогда КолПрайсЛист.применяемостьСтрока = Х; КолПрайсЛист.применяемостьКолонка = У; КонецЕсли; Если СтрНайти(ТекстХУ,"цена")<>0 тогда //Так же как и цена, так же может быть не одна Если СтрДлина(ТекстХУ)<=КолПрайсЛист.ценаДлинна Тогда КолПрайсЛист.ценаСтрока = Х; КолПрайсЛист.ценаКолонка = У; КолПрайсЛист.ценаДлинна = СтрДлина(ТекстХУ); КонецЕсли; КонецЕсли; Если СтрНайти(ТекстХУ,"картинка")<>0 тогда КолПрайсЛист.картинкаСтрока = Х; КолПрайсЛист.картинкаКолонка = У; КонецЕсли; КонецЦикла; КонецЦикла;
-
Таким образом мы находим всю шапку табличного документа и здесь же понимаем с какой строки начинать загрузку непосредственно данных. Конкретно в моём случае данные начинались через две строки после шапки, так как и сама шапка высотой в две ячейки.
максимальныйX = 1; Для каждого Эл из КолПрайсЛист Цикл Если СтрНайти(Эл.Ключ,"Строка")<>0 Тогда Если максимальныйX<Эл.Значение Тогда максимальныйX = Эл.Значение; КонецЕсли; КонецЕсли; КонецЦикла; Сообщить("Обход начинается с "+Строка(максимальныйX)+"+2");
-
Дальше происходит непосредственно обход табличного документа по строкам с изъятием данных из колонок, которые мы нашли и добавлением в таблицу значений. Всё это происходит на клиенте.
максимальныйX = максимальныйX + 2; ТабЛист.Очистить(); ВремФ = КаталогВременныхФайлов()+"tempIMG."; УровеньГруппировки = 0; //обход по таблице с данными до конца прайс листа. //Предполагается что у прайс листа нет никаких подвалов, //тем не менее этот момент тоже предусмотрен и если подвал всё же есть, //то в таблицу значений не будут добавлены пустые строки Для Х=максимальныйX По КоличествоСтрок Цикл Если ПустаяСтрока(Объект.ТабДок.Область(Х,КолПрайсЛист.номенклатураКолонка,Х,КолПрайсЛист.номенклатураКолонка).текст) Тогда //Если колонка с номенклатурой пуста, то это скорее всего начало группы. А значит добавляем наименование группы Если НЕ ПустаяСтрока(Объект.ТабДок.Область(Х,КолПрайсЛист.брендКолонка,Х,КолПрайсЛист.брендКолонка).текст) Тогда //Если нет названия группы, то и не будем ничего создавать Нов = ТабЛист.Добавить(); Нов.группа = Объект.ТабДок.Область(Х,КолПрайсЛист.брендКолонка,Х,КолПрайсЛист.брендКолонка).текст; //вот тут интересно. У Экселя есть Лист1.rows(Х).OutlineLevel //а у табличного документа такого нет, но есть "Отступ (Indent)" который может, хоть и косвенно, но указывать на иерархию, если выгрузка прайс листа идёт из 1С УровеньГруппировки = Объект.ТабДок.Область(Х,КолПрайсЛист.брендКолонка,Х,КолПрайсЛист.брендКолонка).Отступ/2;//каждый уровень иерархии прибавляет по 2 символа отступа Нов.УровеньГруппировки = УровеньГруппировки; КонецЕсли; Иначе Нов = ТабЛист.Добавить(); Нов.Бренд = Объект.ТабДок.Область(Х,КолПрайсЛист.брендКолонка).текст; Нов.Номенклатура = Объект.ТабДок.Область(Х,КолПрайсЛист.номенклатураКолонка).текст; Нов.Артикул = Объект.ТабДок.Область(Х,КолПрайсЛист.артикулКолонка).текст; Нов.Аналоги = Объект.ТабДок.Область(Х,КолПрайсЛист.аналогиКолонка).текст; Нов.Применяемость = Объект.ТабДок.Область(Х,КолПрайсЛист.применяемостьКолонка).текст; Попытка Нов.Цена = Объект.ТабДок.Область(Х,КолПрайсЛист.ценаКолонка).text; Исключение Сообщить("Не число в "+Строка(Х)+" "+ОписаниеОшибки()); КонецПопытки; Нов.УровеньГруппировки = УровеньГруппировки+1; //уровень группировки элемента на уровень больше группы //Создадим временную картинку. ВремКартинка = Объект.ТабДок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Текст); ВремКартинка.Имя = "Линейка"+Формат(Х,"ЧН=; ЧГ="); //расположим её в следующей колонке после самой последней, в строке, в которой сейчас находимся ВремКартинка.Расположить(Объект.ТабДок.Область(Х,КоличествоКолонок+1,Х,КоличествоКолонок+1)); //Таким образом мы получаем точный отступ сверху для картинки, которая содержит рисунок нашей номенклатуры ТочноеПоложениеКартинки = ВремКартинка.Верх; //Временная картинка больше не нужна Объект.ТабДок.Рисунки.Удалить(ВремКартинка); //Обходим коллекцию рисунков и сравниваем отступ сверху с тем, который мы запомнили для данной строки Для каждого Изо ИЗ Объект.ТабДок.Рисунки Цикл Если ИЗО.Верх = ТочноеПоложениеКартинки Тогда ФК = Изо.Картинка.Формат(); Нов.Расширение = Строка(ФК); Изо.Картинка.Записать(ВремФ+Нов.Расширение); //Когда нашли, сохраняем картинку в файл Нов.Картинка = Новый Картинка(ВремФ+Нов.Расширение); //после этого из файла добавляем её в нашу таблицу значений Прервать;//Для экономии времени, если картинка нашлась, не нужно дальше обходить оставшиеся элементы коллекции КонецЕсли; КонецЦикла; КонецЕсли; КонецЦикла;
-
После всего этого весь прайс лист у нас в таблице значений на клиенте. И дальше мы можем обойти таблицу значений и создать как группы справочника, так и элементы с нужными значениями. Заострять на этом внимания я не буду. Тут многое зависит от конфигурации. Лишь покажу, как извлечь картинку из таблицы значений и поместить её в справочник номенклатура. Хоть данный код и справедлив только для УТ 11.4.6.207 но я думаю многим он будет полезен. (Потому что раньше это было просто. Добавляешь объект в ХранилищеДополнительнойИнформации и всё. А вот тут оказалось всё по другому)
Если Не ПустаяСтрока(РасширениеКартинки) Тогда //Это колонка Стр.расширение,, если она не пустая, значит есть картинка в колонке Стр.Картинка Найд = Справочники.НоменклатураПрисоединенныеФайлы.НайтиПоРеквизиту("ВладелецФайла",НовНом.Ссылка); Если ЗначениеЗаполнено(Найд) Тогда //Если вообще какой-то файл или картинка был загружен ранее, то он удаляется. Найд.ПолучитьОбъект().Удалить(); КонецЕсли; //Помещаем двоичные данные картинки во временное хранилище АдресФайлаВХранилище = ПоместитьВоВременноеХранилище(КартинкаСтроки.ПолучитьДвоичныеДанные(), УникальныйИдентификатор); //Создаём структуру ПараметрыФайлаКартинки = Новый Структура(); ПараметрыФайлаКартинки.Вставить("Автор", Пользователи.АвторизованныйПользователь()); ПараметрыФайлаКартинки.Вставить("ВладелецФайлов", НовНом.Ссылка); ПараметрыФайлаКартинки.Вставить("ИмяБезРасширения", НовНом.Наименование); ПараметрыФайлаКартинки.Вставить("РасширениеБезТочки", "PNG"); ПараметрыФайлаКартинки.Вставить("ВремяИзмененияУниверсальное", ТекущаяДата()); //И используем один из механизмов, доступных в УТ 11.4.6.207 ПрисоединенныйФайл = РаботаСФайлами.ДобавитьФайл( ПараметрыФайлаКартинки, АдресФайлаВХранилище, "", "НоменклатураПрисоединенныеФайлы"); //Который возвращает нам ссылку на Справочники.НоменклатураПрисоединенныеФайлы НовНом.ФайлКартинки = ПрисоединенныйФайл; //Это значение присваивается реквизиту справочника номенклатура с именем "ФайлКартинки" КонецЕсли;
-
По большей части всё. Дальше просто создаёте номенклатуру, затем номенклатуру поставщика, создаёте между ними связь. А затем регистрируете цены поставщика.
Надеюсь, мой пример сэкономит кому-то время и нервы. Я собирал всю информацию около недели, так как нет всего этого в одном месте. Где-то одно, где-то другое. Много всего перепробовал, много не получилось, много ошибок допустил и исправил.R03;R03;R03;R03;R03;R03;R03;