При разработке обработок загрузки данных из внешних источников (например, Excel) программисты часто сталкиваются с необходимостью автоматического создания не только документов, но и сопутствующих элементов справочников. Одной из самых частых задач является создание договора контрагента «на лету», если он отсутствует в базе. В этой статье мы разберем типичные ошибки при реализации этой логики и составим правильный алгоритм работы.
Рассмотрим ситуацию: при загрузке строк из файла Excel программа должна создать документ ПоступлениеТоваровУслуг. Для этого документа обязательным реквизитом является ДоговорКонтрагента. Если договора нет, его нужно создать программно. Посмотрим на критические ошибки, которые часто допускают новички (на основе примера из обсуждения):
Записать() вызывается после завершения цикла обхода строк Excel, то в базе будет создан только один договор (последний из обработанных), либо возникнет ошибка.Ссылка на объект справочника, а не сам Объект. Ссылка появляется только после метода Записать().ДоговорыКонтрагентов является подчиненным справочнику Контрагенты. Это значит, что реквизит Владелец является обязательным для заполнения.Прежде чем приступать к созданию договора, мы должны убедиться, что контрагент найден. Рассмотрим фрагмент кода, где выполняется поиск по наименованию из ячейки Excel. Обратите внимание, что поиск по наименованию — не самый надежный способ (лучше использовать ИНН/КПП), но в рамках учебного примера воспользуемся им.
КонтрагентСсылка = Справочники.Контрагенты.НайтиПоНаименованию(Excel.Cells(СтрокаОтсчета, 23).Text, Истина);
Если КонтрагентСсылка.Пустая() Тогда
Сообщить("Контрагент не найден в строке " + СтрокаОтсчета);
Продолжить; // Переходим к следующей итерации цикла
КонецЕсли;
Проанализируем ситуацию: нам нужно создать договор с названием «Основной». Чтобы не плодить дубли, воспользуемся методом НайтиПоНаименованию, но обязательно укажем владельца (контрагента).
// Ищем существующий договор у конкретного контрагента
ДоговорСсылка = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию("Основной", Истина, , КонтрагентСсылка);
Если ДоговорСсылка.Пустая() Тогда
// Если не нашли — создаем новый
НовыйДоговорОбъект = Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
НовыйДоговорОбъект.Наименование = "Основной";
НовыйДоговорОбъект.Владелец = КонтрагентСсылка;
НовыйДоговорОбъект.Организация = Объект.Организация;
НовыйДоговорОбъект.ВидДоговора = Перечисления.ВидыДоговоровКонтрагентов.СПоставщиком;
// Устанавливаем валюту (руб.), которую нашли заранее
НовыйДоговорОбъект.ВалютаВзаиморасчетов = ВалютаДокумента;
Попытка
НовыйДоговорОбъект.Записать();
ДоговорСсылка = НовыйДоговорОбъект.Ссылка;
Исключение
Сообщить("Не удалось записать договор: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
После того как мы получили ДоговорСсылка (либо найденный старый, либо только что созданный), мы можем переходить к заполнению документа ПоступлениеТоваровУслуг. Выясним причину, по которой у автора вопроса документы создавались, но договор в них был пустой: это происходило потому, что переменная договора не обновлялась внутри цикла создания документа.
НовыйДокумент = Документы.ПоступлениеТоваровУслуг.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Объект.Организация;
НовыйДокумент.Контрагент = КонтрагентСсылка;
НовыйДокумент.ДоговорКонтрагента = ДоговорСсылка; // Присваиваем ссылку!
НовыйДокумент.ВидОперации = Перечисления.ВидыОперацийПоступлениеТоваровУслуг.Товары;
НовыйДокумент.Склад = ОсновнойСклад;
Разберем подробнее заполнение табличной части «Товары». Важный нюанс для бухгалтерии: недостаточно просто заполнить номенклатуру и количество. Необходимо также указать счета учета (например, 10.01 или 41.01). В типовых конфигурациях для этого используются специальные процедуры в модуле объекта, но при программном заполнении счета часто прописывают явно.
НСтрк = НовыйДокумент.Товары.Добавить();
НСтрк.Номенклатура = Объект.Номенклатура; // В примере автора берется из реквизита обработки
НСтрк.Количество = ТекущееЗначениеКол;
НСтрк.Цена = ТекущееЗначениеЦена;
НСтрк.Сумма = НСтрк.Количество * НСтрк.Цена;
НСтрк.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
// Прописываем счет учета вручную или через поиск
НСтрк.СчетУчета = ПланыСчетов.Хозрасчетный.НайтиПоКоду("10.01");
Сведем все действия в единую логическую цепочку, которую необходимо реализовать в коде:
Склады, Валюты и счета учета до начала цикла обхода Excel, чтобы не нагружать базу лишними запросами.Соответствие).СозданныеДокументы и записываем каждый документ в базу.Важный совет: При программном создании объектов всегда используйте блок Попытка...Исключение при вызове метода Записать(). Это защитит вашу обработку от внезапной остановки, если, например, в системе настроен запрет на создание договоров определенного вида или не заполнены обязательные дополнительные реквизиты.
Проанализировав ситуацию, мы видим, что главная тонкость работы с объектами 1С заключается в понимании разницы между Объектом (набором данных в памяти, который мы редактируем) и Ссылкой (уникальным идентификатором записи в базе данных). Помните: в реквизиты других объектов (например, в поле «Договор» документа) можно записывать только Ссылку.