При разработке в среде 1С:Предприятие 8 часто возникает задача копирования или переноса данных между документами. Типичный пример: перенос дополнительных сведений (например, специальной цены закупки) из "Коммерческого предложения" в "Заказ клиента". Несмотря на кажущуюся простоту, здесь есть множество нюансов — от правильной обработки дублей номенклатуры до обеспечения высокой производительности системы. Разберем по шагам, как решить эту задачу правильно. Если первичный документ необходимо сформировать на основе заказа из электронной почты, можно автоматизировать создание документов в 1С по данным входящих писем.
Рассмотрим ситуацию, когда нам нужно получить данные из документа-основания с помощью запроса и сопоставить их с текущим документом. Это эффективный способ, так как он позволяет получить только нужные поля, не загружая объект целиком в память. Однако на этом пути часто встречаются ошибки типизации.
Проанализируем пример кода, в котором выполняется запрос к табличной части документа-основания:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КПТовары.ЦенаЗакупкиПоставщика КАК ЦенаЗакупкиПоставщика,
| КПТовары.Номенклатура КАК Номенклатура
|ИЗ
| Документ.КоммерческоеПредложениеКлиенту.Товары КАК КПТовары
|ГДЕ
| КПТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Объект.ДокументОснование);
РезультатЗапроса = Запрос.Выполнить().Выгрузить(); // Получаем Таблицу Значений
Для Каждого СтрокаЗК Из Объект.Товары Цикл
Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", СтрокаЗК.Номенклатура);
НайденныеСтроки = РезультатЗапроса.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
// ОШИБКА: НайденныеСтроки — это МАССИВ, а не строка
// Нужно обращаться к первому элементу массива
СтрокаЗК.Цена = НайденныеСтроки[0].ЦенаЗакупкиПоставщика;
КонецЕсли;
КонецЦикла;
Важный момент: метод НайтиСтроки() всегда возвращает объект типа Массив. Даже если найдена всего одна строка, вы не можете обратиться к ее полям напрямую через точку от переменной НайденныеСтроки. Обязательно нужно указывать индекс элемента, например, НайденныеСтроки[0]. Также обратите внимание, что метод НайтиСтроки() никогда не возвращает Неопределено, он возвращает пустой массив, поэтому проверку следует делать через НайденныеСтроки.Количество() = 0.
Если документов-оснований немного, а количество строк в табличной части не превышает нескольких десятков, можно воспользоваться простым перебором. Этот метод не требует написания запросов и интуитивно понятен.
Разберем логику сопоставления по номеру строки и номенклатуре:
// Перебираем товары в документе-источнике
Для Каждого СтрокаТбКП Из Объект.ДокументОснование.Товары Цикл
// Перебираем товары в текущем документе
Для Каждого СтрокаТбЗК Из Объект.Товары Цикл
// Сверяем номенклатуру и позиции (если порядок строк важен)
Если СтрокаТбКП.Номенклатура = СтрокаТбЗК.Номенклатура
И СтрокаТбКП.НомерСтроки = СтрокаТбЗК.НомерСтроки Тогда
СтрокаТбЗК.Цена = СтрокаТбКП.ЦенаЗакупкиПоставщика;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Выясним причину, почему этот метод может быть опасен:
СтрокаТбКП.Номенклатура.Наименование. Сравнение строк работает медленнее, чем сравнение ссылок. Кроме того, обращение к Наименованию через точку вызывает дополнительный неявный запрос к базе данных для каждой строки. Сравнивайте ссылки напрямую: СтрокаТбКП.Номенклатура = СтрокаТбЗК.Номенклатура.НомерСтроки рискованно, так как пользователь мог переместить строки или добавить новые, что нарушит синхронизацию.Для того чтобы сделать поиск мгновенным даже в документах на тысячи строк, проанализируем использование индексов. Мы можем выгрузить табличную часть источника в ТаблицаЗначений и добавить индекс по ключевому полю.
Рассмотрим пример реализации:
ТЗ_Источник = Объект.ДокументОснование.Товары.Выгрузить();
// Индексируем колонку для ускорения метода Найти()
ТЗ_Источник.Индексы.Добавить("Номенклатура");
Для Каждого СтрокаЦель Из Объект.Товары Цикл
// Метод Найти() на индексированной таблице работает очень быстро
СтрокаИсточник = ТЗ_Источник.Найти(СтрокаЦель.Номенклатура, "Номенклатура");
Если СтрокаИсточник <> Неопределено Тогда
ЗаполнитьЗначенияСвойств(СтрокаЦель, СтрокаИсточник, "Цена");
КонецЕсли;
КонецЦикла;
Здесь мы использовали полезную функцию ЗаполнитьЗначенияСвойств(). Она позволяет скопировать данные из одной строки в другую по совпадающим именам колонок, что сокращает объем кода и уменьшает вероятность ошибок при опечатках в именах полей.
В современных конфигурациях, таких как ERP, УТ 11 или КА, для связи строк между разными табличными частями (например, "Товары" и "Скидки") используется служебное поле КлючСвязи. Если ваша задача — перенести данные между документами, которые были созданы "на основании", проверьте, не заполнен ли КлючСвязи (или ИдентификаторСтроки).
Если такие идентификаторы есть, сопоставление нужно делать именно по ним, а не по номенклатуре. Это гарантирует точность, если в документе присутствуют две одинаковые позиции номенклатуры, но с разными условиями (например, разными характеристиками или ценами). Для работы с подчиненными строками в интерфейсе пригодится пример настройки связанных табличных частей на управляемых формах.
Подведем итог и выберем оптимальный подход в зависимости от условий:
Найти() по таблице значений.ОбработкаЗаполнения в модуле объекта. Там вы можете сразу обработать параметр ДанныеЗаполнения, который содержит ссылку на документ-основание.Номенклатуру, но и Характеристику, Серию и Упаковку, если они используются в учете. Иначе вы рискуете перенести цену от "первого попавшегося" похожего товара.Соблюдение этих правил позволит вам создавать надежные и быстрые алгоритмы переноса данных, которые будут корректно работать даже при интенсивном росте базы данных. Чтобы проконтролировать корректность результата и отсутствие расхождений в учете, можно автоматически сверять бухгалтерские и налоговые проводки между базами 1С.