Разработка механизмов универсального обмена данными — это одна из фундаментальных задач программиста 1С. В данной статье мы подробно разберем, как создать внешнюю обработку, которая позволяет выгрузить любой документ базы данных в текстовый файл (TXT) и загрузить его обратно, создав точную копию. Мы будем соблюдать жесткие ограничения: запрет на использование XML, JSON, а также системных функций ЗначениеВСтрокуВнутр() и УникальныйИдентификатор().
Рассмотрим основные требования. Нам необходимо реализовать выбор любого типа документа, доступного в конфигурации, и обеспечить "исчерпывающую" выгрузку данных. Это означает, что в файл должны попасть не только основные реквизиты (Дата, Организация, Контрагент), но и все строки всех табличных частей. Поскольку нам запрещено использовать стандартные форматы сериализации и уникальные идентификаторы, мы должны разработать собственный протокол передачи данных, основанный на метаданных и естественных ключах (таких как Код или Наименование для справочников).
Проанализируем, как предоставить пользователю возможность выбрать любой документ из базы. Если мы просто создадим реквизит с типом ДокументСсылка, нам придется выбирать конкретный вид документа. Чтобы сделать поле универсальным, воспользуемся объектом ОписаниеТипов.
Разберем программный способ настройки такого поля при создании формы:
// В модуле формы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
МассивТипов = Новый Массив;
Для Каждого МетаДанныеДокумента Из Метаданные.Документы Цикл
МассивТипов.Добавить(Тип("ДокументСсылка." + МетаДанныеДокумента.Имя));
КонецЦикла;
// Устанавливаем составной тип данных для реквизита "ВыбранныйДокумент"
Элементы.ВыбранныйДокумент.ОграничениеТипа = Новый ОписаниеТипов(МассивТипов);
КонецПроцедуры
Этот подход позволяет полю выбора динамически подстраиваться под все виды документов, существующие в вашей конфигурации.
Перейдем к самой сути задачи — универсальному чтению данных документа. Чтобы код работал для любого документа, мы не можем обращаться к полям напрямую (например, Объект.Контрагент). Вместо этого мы проанализируем коллекцию Метаданные.
Рассмотрим алгоритм выгрузки основных реквизитов:
// Пример получения данных через метаданные
СсылкаНаДок = Объект.ВыбранныйДокумент;
ИмяДокумента = СсылкаНаДок.Метаданные().Имя;
// Выгрузка основных реквизитов
Для Каждого РеквизитМетаданных Из Метаданные.Документы[ИмяДокумента].Реквизиты Цикл
ИмяПоля = РеквизитМетаданных.Имя;
ЗначениеПоля = СсылкаНаДок[ИмяПоля];
// Здесь логика преобразования ЗначениеПоля в текст (см. Шаг 4)
ЗаписатьСтрокуВФайл(ИмяПоля, ЗначениеПоля);
КонецЦикла;
Документ — это не только шапка, но и табличные части. Проанализируем, как выгрузить их универсально. Нам потребуется вложенный цикл: внешний по списку табличных частей, средний по строкам каждой части и внутренний по колонкам (реквизитам) табличной части.
Для Каждого ТЧ_Метаданные Из Метаданные.Документы[ИмяДокумента].ТабличныеЧасти Цикл
ИмяТЧ = ТЧ_Метаданные.Имя;
ТаблицаДанных = СсылкаНаДок[ИмяТЧ];
Для Каждого СтрокаТЧ Из ТаблицаДанных Цикл
Для Каждого Колонка Из ТЧ_Метаданные.Реквизиты Цикл
ИмяКолонки = Колонка.Имя;
ЗначениеКолонки = СтрокаТЧ[ИмяКолонки];
// Запись в файл: ИмяТЧ, НомерСтроки, ИмяКолонки, Значение
КонецЦикла;
КонецЦикла;
КонецЦикла;
Выясним причину сложности использования простого текста: 1С хранит ссылки на справочники, другие документы и перечисления как внутренние идентификаторы. По условию задачи нам запрещено их использовать. Как же передать информацию о "Контрагенте" или "Складе"?
Решение заключается в использовании естественных ключей. Для каждого типа данных мы должны продумать свой способ представления в строке:
Перечисления.Статусы.Активен превращается в строку "Активен").ГГГГММДДЧЧММСС).Рассмотрим пример функции преобразования ссылки в текстовое описание:
Функция ЗначениеВТекст(Значение)
Если Значение = Неопределено Тогда
Возврат "NULL";
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Или ТипЗнч(Значение) = Тип("Булево") Тогда
Возврат Строка(Значение);
ИначеЕсли ТипЗнч(Значение) = Тип("Дата") Тогда
Возврат Формат(Значение, "ДФ=ггггММддЧЧммсс");
ИначеЕсли Метаданные.Справочники.Содержит(Значение.Метаданные()) Тогда
// Используем Код как наиболее надежный ключ
Возврат Значение.Код;
ИначеЕсли Метаданные.Перечисления.Содержит(Значение.Метаданные()) Тогда
Возврат Строка(Значение);
КонецЕсли;
Возврат Строка(Значение);
КонецФункции
Поскольку XML под запретом, разработаем свою структуру файла. Посмотрим на пример простого формата "Ключ|Тип|Значение":
[Header]
DocumentType|ПоступлениеТоваровУслуг
Date|20231025120000
[Attributes]
Организация|Справочник.Организации|000000001
Контрагент|Справочник.Контрагенты|ООО "Поставщик"
Склад|Справочник.Склады|Основной
[Table|Товары]
1|Номенклатура|Справочник.Номенклатура|00005|Количество|10|Цена|500
2|Номенклатура|Справочник.Номенклатура|00008|Количество|5|Цена|1200
Для работы с таким файлом идеально подходит объект ТекстовыйДокумент. Мы можем использовать метод ДобавитьСтроку() при выгрузке и перебор строк при загрузке.
Проанализируем процесс восстановления документа из файла. Нам нужно выполнить действия в обратном порядке:
НовыйДок = Документы[ИмяВида].СоздатьДокумент();.Справочники.Организации.НайтиПоКоду("000000001").НовыйДок[ИмяРеквизита] = НайденнаяСсылка;.НовыйДок[ИмяТЧ].Добавить().Важный момент: Номер документа выгружать и загружать не нужно (или нужно игнорировать при записи), так как по условию он должен оставаться уникальным, а система присвоит его автоматически при вызове метода Записать().
Для глубокого понимания того, как работает платформа с данными, мы рекомендуем изучить книгу С.А. Митичкина "Разработка в системе 1С:Предприятие 8". Там подробно разобраны механизмы объектной модели. Также обратите внимание на синтакс-помощник в разделе "Метаданные", чтобы изучить все доступные свойства объектов конфигурации.
Помните, что данный метод обмена через текстовые файлы с поиском по наименованию/коду является учебным. В реальных задачах предпочтительнее использовать УникальныйИдентификатор или специализированные средства (Конвертация данных), так как наименования справочников могут дублироваться, что приведет к ошибкам при загрузке.