Спецификация это конструкторский документ, введенный ответственным специалистом конструктором, потому организация рабочего места конструктора без выполнения повторяющихся рутинных операций в "Управление нашей фирмой", без использования двойного ввода одной и той же информации спецификаций и без ручного редактирования реквизитов нескольких справочников 1С послужит сокращению трудоемкости и минимизации механических ошибок оператора, снижению налога на автоматизацию.
Пример спецификации 205-15.pdf
Конструкторский документ является точным документом и первичным источником данных для заполнения спецификации. За правильность этого документа отвечает конструктор, поэтому правильным решением является позволить вводить данные конструктору именно в таком виде, как этого требует его профессиональная деятельность, а не заставлять его дважды вводить данные в 1С. Ведь по служебным обязанностям он отвечает за правильность чертежа в SolidWorks, а конструкторов со знанием 1С не бывает, надо их либо учить либо делегировать их работу не ответственным, которые не стремятся к чужой работе.
Конвертер PDF в CSV (PDF2CSV) tabula
На текущий момент типовая загрузка спецификаций из PDF и из конструкторской документации невозможна, она не реализована. Ее надо писать и я представляю Вашему вниманию свой вариант с использованием вот такого промежуточного конвертера.
На гитхаб есть бесплатный кроссплатформенный ява-конвертер Табула, https://github.com/tabulapdf/tabula-java/releases который идеально без ошибок перевел из распечатки PDF в стандартные таблицы формата CSV тридцать спецификаций готовой продукции, что позволило составить и применить данный самописный парсер конструкторских спецификаций.
На компьютере должна быть установлена ява, ее установить не сложно. Для запуска достаточно скопировать как есть и запустить в терминале скачанный ява-скрипт в каталоге с хранилищем документов PDF, устанавливать и прописывать права не нужно. Чтобы превратить все файлы PDF выбранного каталога в стандартные таблицы формата CSV, надо выполнить команду в каталоге с PDF-ками.
java -jar tabula-1.0.5-jar-with-dependencies.jar -b. -l
Это работает безотказно, в каталоге появятся файлы CSV, и теперь на этот самый каталог можно натравить мою обработку.
Небольшое отступление с описанием типового механизма загрузки спецификации (для понимания процесса)
Рассмотрим типовой порядок создания (загрузки) спецификаций. Номенклатуру (продукцию) в УНФ нужно создать вручную. Над табличной частью есть кнопка загрузки из табличного источника.
Типовой функционал заполнения спецификации из Эксель не затейлив (функциональность УНФ выгодна, она подкупает простотой - позволяет быстро делать такие вещи, как эта). В момент чтения файла делается попытка сопоставления, УНФ предлагает составить файл соответствия (который можно записать и открыть - прочитать), либо воспользоваться импортом через табличный документ.
Затем в процедуре ЗагрузкаДанныхИзВнешнегоИсточникаОбработкаРезультата() происходит заполнение свойств реквизитов табличной части спецификации в соответствии со значениями шаблона сопоставления.
фрагмент формы элемента справочника спецификаций, процедура ЗагрузкаДанныхИзВнешнегоИсточникаОбработкаРезультата.
Для каждого СтрокаТаблицы Из ТаблицаСопоставленияДанных Цикл
Если СтрокаТаблицы[ЗагрузкаДанныхИзВнешнегоИсточника.ИмяСлужебногоПоляЗагрузкаВПриложениеВозможна()] Тогда
ЗаполнитьЗначенияСвойств(Объект.Состав.Добавить(), СтрокаТаблицы);
КонецЕсли;
КонецЦикла;
ТаблицаСопоставленияДанных имеет следующий формат.
Номенклатура
|
Штрихкод
|
Артикул
|
НоменклатураНаименование
|
НоменклатураНаименованиеПолное
|
Характеристика
|
ХарактеристикаНаименование
|
ХарактеристикаАртикул
|
Количество
|
Количество_ВходящиеДанные
|
ТипСтрокиСостава
|
ТипСтрокиСостава_ВходящиеДанные
|
ЕдиницаИзмерения
|
ЕдиницаИзмерения_ВходящиеДанные
|
ДоляСтоимости
|
ДоляСтоимости_ВходящиеДанные
|
КоличествоПродукции
|
КоличествоПродукции_ВходящиеДанные
|
Спецификация
|
Спецификация_ВходящиеДанные
|
_ЗагрузкаВПриложениеВозможна
|
_СтрокаСопоставлена
|
_ЗаполненыНеПолностью
|
_КлючСвязи
|
|
|
|
""
|
""
|
"Упаковочная единица"
|
""
|
|
""
|
""
|
1
|
"1"
|
Узел
|
"Узел"
|
шт
|
"шт"
|
1
|
"1.0"
|
1
|
"1"
|
|
"Состав упаковки"
|
Истина
|
Ложь
|
Ложь
|
0
|
|
|
|
""
|
""
|
"Основной блок Ветерок"
|
""
|
|
"7,5 кВт"
|
""
|
1
|
"1"
|
Сборка
|
"Сборка"
|
шт
|
"шт"
|
1
|
"1.0"
|
1
|
"1"
|
|
"7,5 кВт"
|
Истина
|
Ложь
|
Ложь
|
0
|
|
|
|
""
|
""
|
"Пульт управления"
|
""
|
|
""
|
""
|
1
|
"1"
|
Материал
|
"Материал"
|
шт
|
"шт"
|
1
|
"1.0"
|
1
|
"1"
|
|
""
|
Истина
|
Ложь
|
Ложь
|
0
|
|
|
|
""
|
""
|
"Электронный блок управления"
|
""
|
|
""
|
""
|
1
|
"1"
|
Материал
|
"Материал"
|
шт
|
"шт"
|
1
|
"1.0"
|
1
|
"1"
|
|
""
|
Истина
|
Ложь
|
Ложь
|
0
|
|
|
|
""
|
""
|
"Саморез"
|
""
|
|
""
|
""
|
20
|
"20"
|
Материал
|
"Материал"
|
шт
|
"шт"
|
1
|
"1.0"
|
1
|
"1"
|
|
""
|
Истина
|
Ложь
|
Ложь
|
0
|
|
|
Пакетная загрузка конструкторских спецификаций, которые я предлагаю Вашему вниманию, дополнена следующими особенностями
Обработка позволяет предварительно удалить дублирующиеся по содержимому файлы PDF и CSV в каталоге во избежание попытки обработки дублей с разными именами файлов. Она контроллирует структуру файла, чтобы заголовок, шапка, данные, штамп и подвал были на своих местах - в нужной последовательности и в нужных колонках. Она позволяет заполнить список всех файлов CSV и загрузить (создать или обновить) все конструкторские спецификации из заданного каталога полностью в автоматическом режиме.
Разбор файла csv в таблицу значений прекрасно показал в публикации "7 правил RFC 4180" от Николая Кузнецова
//infostart.ru/1c/articles/541555/, которому большое спасибо. Процедуру удаления задвоенных файлов в каталоге выполнен на основе адаптации родительской обработки "Поиск дубликатов файлов на диске компьютера" //infostart.ru/public/1825332/, которую я подглядел у Владимира Токарева , Владимир, спасибо за труд!
Данный функционал я обогатил самопальным парсером конструкторской спецификации (под следующим спойлером). Правила следования элементов конструкторской спецификации описаны в парсере в виде конечного автомата, в условиях которого проверяется корректность заполнения колонок. При необходимости это правила могут быть доработаны под себя.
Функция ПарсерТаблицыСпецификации()
&НаСервере
Функция ПарсерТаблицыСпецификации(ВремТЗ, ИмяФайла = "")
ПреобрТЗ = Новый ТаблицаЗначений;
ПреобрТЗ.Колонки.Добавить("НомерСпецификации");
ПреобрТЗ.Колонки.Добавить("НомерПозицииСпецификации");
ПреобрТЗ.Колонки.Добавить("НаименованиеДетали");
ПреобрТЗ.Колонки.Добавить("РазмерыДетали");
ПреобрТЗ.Колонки.Добавить("ИсполнениеДетали");
ПреобрТЗ.Колонки.Добавить("КоличествоДеталейСпецификации");
ПреобрТЗ.Колонки.Добавить("АртикулСпецификации");
БылЗаголовок = Ложь;
БылаШапка = Ложь;
ПредыдущаяСтрокаБылаПустая = Ложь;
НачалисьДанные = Ложь;
ЗакончилиьДанные = Ложь;
БылаОсновнаяНадпись = Ложь;
БылПодвал = Ложь;
НомерСтрокиНачалаЧтенияДанных = 0;
НомерСтрокиОкончанияЧтенияДанных = 0;
АртикулСпецификации = "";
ТекущийНомерСтроки = 0;
Для Каждого СтрТЗ Из ВремТЗ Цикл
//Порядок следование секций синтаксического анализа элементов спецификации имеет значение
ТекущийНомерСтроки = ВремТЗ.Индекс(СтрТЗ)+1;
//Подвал
Если Найти(СтрТЗ.Колонка3, "Лист")>0 И Найти(СтрТЗ.Колонка6, "Дата")>0 Тогда
Если БылЗаголовок = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаШапка = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует шапка в "+ИмяФайла;
КонецЕсли;
Если НачалисьДанные = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствуют данные в "+ИмяФайла;
КонецЕсли;
Если ЗакончилиьДанные = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует кромка в "+ИмяФайла;
КонецЕсли;
Если БылаОсновнаяНадпись = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует штамп в "+ИмяФайла;
КонецЕсли;
Если БылПодвал = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку подвал в "+ИмяФайла;
КонецЕсли;
БылПодвал = Истина;
Продолжить;
КонецЕсли;
//Заголовок
Если Найти(СтрТЗ.Колонка2, "Спецификация щитовых")>0 И СокрЛП(СтрТЗ.Колонка3)=""
И СокрЛП(СтрТЗ.Колонка4)="" И СокрЛП(СтрТЗ.Колонка5)="" И СокрЛП(СтрТЗ.Колонка6)="" И СокрЛП(СтрТЗ.Колонка7)="" Тогда
Если БылЗаголовок = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаШапка = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку заголовок в "+ИмяФайла;
КонецЕсли;
Если НачалисьДанные = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку заголовок в "+ИмяФайла;
КонецЕсли;
Если ЗакончилиьДанные = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаОсновнаяНадпись = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку заголовок в "+ИмяФайла;
КонецЕсли;
Если БылПодвал = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку заголовок в "+ИмяФайла;
КонецЕсли;
БылЗаголовок = Истина;
Продолжить;
КонецЕсли;
//Шапка
Если Найти(СтрТЗ.Колонка3, "Наименование")>0 И Найти(СтрТЗ.Колонка4, "Размеры")>0
И Найти(СтрТЗ.Колонка5, "Облицовка")>0 И Найти(СтрТЗ.Колонка6, "Кол")>0 И Найти(СтрТЗ.Колонка7, "Примечание")>0 Тогда
Если БылЗаголовок = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаШапка = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку Шапка в "+ИмяФайла;
КонецЕсли;
Если НачалисьДанные = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку Шапка в "+ИмяФайла;
КонецЕсли;
Если ЗакончилиьДанные = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку Шапка в "+ИмяФайла;
КонецЕсли;
Если БылаОсновнаяНадпись = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку Шапка в "+ИмяФайла;
КонецЕсли;
Если БылПодвал = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку Шапка в "+ИмяФайла;
КонецЕсли;
БылаШапка = Истина;
Продолжить;
КонецЕсли;
//Пропуск
Если СокрЛП(СтрТЗ.Колонка2)="" И СокрЛП(СтрТЗ.Колонка3)=""
И СокрЛП(СтрТЗ.Колонка4)="" И СокрЛП(СтрТЗ.Колонка5)="" И СокрЛП(СтрТЗ.Колонка6)="" И СокрЛП(СтрТЗ.Колонка7)="" Тогда
ПредыдущаяСтрокаБылаПустая = Истина;
Продолжить;
КонецЕсли;
//Данные
Если СокрЛП(СтрТЗ.Колонка2)>"" И СокрЛП(СтрТЗ.Колонка3)>""
И СокрЛП(СтрТЗ.Колонка4)>"" И СокрЛП(СтрТЗ.Колонка5)>"" И СокрЛП(СтрТЗ.Колонка6)>"" Тогда
Если БылЗаголовок = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаШапка = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует шапка в "+ИмяФайла;
КонецЕсли;
Если ЗакончилиьДанные = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку данные в "+ИмяФайла;
КонецЕсли;
Если БылаОсновнаяНадпись = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку данные в "+ИмяФайла;
КонецЕсли;
Если БылПодвал = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку данные в "+ИмяФайла;
КонецЕсли;
ПозИмяФайла = СтрНайти(ИмяФайла,"/",НаправлениеПоиска.СКонца);
КаталогИмяФайла = Лев(ИмяФайла,ПозИмяФайла);
ИмяИмениФайла = СтрЗаменить(ИмяФайла,КаталогИмяФайла,"");
//Делаем НомерСпецификации
НомерПервойЦифры=1000;
для СчетчикЦикла=0 По 9 цикл
н=Найти(ИмяИмениФайла,Строка(СчетчикЦикла));
Если н>0 Тогда
НомерПервойЦифры=?(НомерПервойЦифры=0,н,Мин(н,НомерПервойЦифры));
КонецЕсли;
КонецЦикла;
Если НомерПервойЦифры>1 Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", имя файла начинается не с цифры "+ИмяФайла;
КонецЕсли;
НомерПервойНеЦифры = 0;
ДлинаСтрокиИмениФайла = СтрДлина(ИмяИмениФайла);
Для СчетчикЦикла = 1 по ДлинаСтрокиИмениФайла Цикл
СимволИмениФайла = Сред(ИмяИмениФайла, СчетчикЦикла, 1);
н=Найти("-012345679",СимволИмениФайла);
Если н=0 Тогда
НомерПервойНеЦифры=СчетчикЦикла;
Прервать;
КонецЕсли;
КонецЦикла;
Если НомерПервойНеЦифры = 0 Тогда
РазмерНомераСпецификации = СтрДлина(ИмяИмениФайла);
Иначе
РазмерНомераСпецификации = НомерПервойНеЦифры-1;
КонецЕсли;
НомерСпецификации = Лев(ИмяИмениФайла,РазмерНомераСпецификации);
СтрПреобрТЗ = ПреобрТЗ.Добавить();
СтрПреобрТЗ.НомерСпецификации=НомерСпецификации;
СтрПреобрТЗ.НомерПозицииСпецификации=СтрТЗ.Колонка2;
СтрПреобрТЗ.НаименованиеДетали=СтрТЗ.Колонка3;
СтрПреобрТЗ.РазмерыДетали=СтрТЗ.Колонка4;
СтрПреобрТЗ.ИсполнениеДетали=СтрТЗ.Колонка5;
СтрПреобрТЗ.КоличествоДеталейСпецификации=Вычислить(СтрТЗ.Колонка6);
СтрПреобрТЗ.АртикулСпецификации="";
НачалисьДанные = Истина;
Продолжить;
КонецЕсли;
//Кромка
Если СокрЛП(СтрТЗ.Колонка2)="" И Найти(нрег(СтрТЗ.Колонка3), "кромка")>0
И СокрЛП(СтрТЗ.Колонка4)="" И СокрЛП(СтрТЗ.Колонка5)="" И СокрЛП(СтрТЗ.Колонка6)="" И СокрЛП(СтрТЗ.Колонка7)="" Тогда
Если БылЗаголовок = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаШапка = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует шапка в "+ИмяФайла;
КонецЕсли;
Если НачалисьДанные = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствуют данные в "+ИмяФайла;
КонецЕсли;
Если ЗакончилиьДанные = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку кромка в "+ИмяФайла;
КонецЕсли;
Если БылаОсновнаяНадпись = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку кромка в "+ИмяФайла;
КонецЕсли;
Если БылПодвал = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку кромка в "+ИмяФайла;
КонецЕсли;
ЗакончилиьДанные = Истина;
Продолжить;
КонецЕсли;
//Основная надпись
Если СокрЛП(СтрТЗ.Колонка2)="" И СокрЛП(СтрТЗ.Колонка3)=""
И СокрЛП(СтрТЗ.Колонка4)="" И СокрЛП(СтрТЗ.Колонка5)="" И СокрЛП(СтрТЗ.Колонка6)="" И СокрЛП(СтрТЗ.Колонка7)>"" Тогда
Если БылЗаголовок = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует заголовок в "+ИмяФайла;
КонецЕсли;
Если БылаШапка = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует шапка в "+ИмяФайла;
КонецЕсли;
Если НачалисьДанные = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствуют данные в "+ИмяФайла;
КонецЕсли;
Если ЗакончилиьДанные = Ложь Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", отсутствует кромка в "+ИмяФайла;
КонецЕсли;
//номер листа сбился, не на своем месте
//Если БылаОсновнаяНадпись = Истина Тогда
// Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку штамп в "+ИмяФайла;
//КонецЕсли;
Если БылПодвал = Истина Тогда
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", не по порядку штамп в "+ИмяФайла;
КонецЕсли;
АртикулСпецификации = СокрЛП(СтрТЗ.Колонка7);
БылаОсновнаяНадпись = Истина;
Продолжить;
КонецЕсли;
КонецЦикла;
Если БылПодвал = Истина Тогда
Для Каждого Стр Из ПреобрТЗ Цикл
Стр.АртикулСпецификации = АртикулСпецификации;
КонецЦикла;
Возврат ПреобрТЗ;
Иначе
Возврат "Ошибка в стр."+СокрЛП(ТекущийНомерСтроки)+", нарушена структура файла "+ИмяФайла;
КонецЕсли;
КонецФункции
Данный Парсер конструкторской спецификации проверяет соответствие CSV-файла правилам составления конструкторской спецификации, и если данные правила соблюдены, извлекает из файла данные для загрузки в 1С. Такое отсутствует в типовой конфигурации. Он реализует многочисленный контроль возможных ошибок, при этом сразу безотказно отработал на загрузке 30 конструкторских спецификаций, благодаря чему нет необходимости их открывать и просматривать. Конструкторская спецификация это стандартный элемент единой системы конструкторской документации (ЕСКД), он не может ошибаться. Данное обстоятельство позволяет ввести однозначное соответствие колонок файла и документа непосредственно в коде обработки. У меня оно задается непосредственно при создании спецификации продукции в УНФ. Конструктор как специалист по конструированию не должен быть перегружен управляемыми формами. Жаль, что это не поддержано типовыми решениями.
Версия 1 сделана для Виндоуз, код работающий на Линукс закомментирован.
Версия 2 имеет дополнительные возможности - может выполняться одинаково и на Линукс и на Виндоуз, конвертер запускается непосредственно из 1С как внешнее приложение при нажатии кнопки "Конвертировать".
Версия 3 (окончательная) имеет дополнительные возможности -
- в ней есть есть флажок, позволяющий игнорировать строгие правила к составу спецификации, если нет записи о толщине кромки щитовых деталей или не соблюдены требования к штампу (основной надписи спецификации) загрузка будет выполнена успешно.
- реализован обход дерева вложенных каталогов для конвертирования из PDF в CSV (не надо выьирать и перетаскивать файлы, собирать в целевой каталог вручную) и реализована загрузка спецификаций из всех вложенных подкаталогов целевого каталога.
Протестировано на Управление нашей фирмой, редакция 3.0 (3.0.2.215).