При переходе к разработке на современных конфигурациях 1С, таких как «Управление торговлей 11», «Комплексная автоматизация 2» или «ERP Управление предприятием», программисты часто сталкиваются с ошибками при проведении документов, созданных программно. Для этого есть модуль интеграции 1С с маркетплейсами. Одна из самых распространенных проблем — сообщение «Не найден объект расчетов для договора», хотя сам договор в документе заполнен корректно.
В этой статье мы подробно разберем, почему возникает эта ошибка, как устроена архитектура взаиморасчетов в новых конфигурациях и как правильно написать код, чтобы документ проводился без ошибок.
Рассмотрим ситуацию на примере. Разработчик пишет обработку для загрузки накладных от маркетплейсов (Wildberries, Ozon, Яндекс.Маркет). В задачах, где требуется автоматическая загрузка из Excel/CSV с созданием документа поступления, при создании документа ПриобретениеТоваровУслуг часто используется следующий код:
НовоеПоступление = Документы.ПриобретениеТоваровУслуг.СоздатьДокумент();
НовоеПоступление.Дата = ТекущаяДата();
НовоеПоступление.Партнер = Объект.Поставщик;
// ... заполнение организации, склада и прочего ...
НовоеПоступление.Договор = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию("Закупка1", Истина);
НовоеПоступление.Записать();
При попытке проведения этого документа (программно или интерактивно после записи) система выдает ошибку: «Не найден объект расчетов для договора...». При этом, если создать такой же документ вручную и выбрать тот же договор, все работает.
Давайте разберемся, почему так происходит.
В старых конфигурациях (например, УТ 10.3 или БП 3.0) основным разрезом взаиморасчетов был Договор. Если поле Договор заполнено, система знала, куда писать движения по регистрам.
В современных решениях (УТ 11, КА 2, ERP) архитектура сложнее. В регистрах накопления (таких как РасчетыСПоставщиками) существует измерение Объект расчетов. Это измерение является определяющим для системы, и именно по нему строится реестр платежей поставщикам с детализацией по документам и объектам расчетов.
Важно понимать, что Объект расчетов — это не всегда договор. В зависимости от настроек договора, объектом расчетов может выступать:
В документе ПриобретениеТоваровУслуг есть скрытый реквизит ОбъектРасчетов. Когда пользователь работает вручную, при выборе договора срабатывает событийный обработчик ПриИзменении, который анализирует настройки договора и автоматически заполняет скрытый реквизит ОбъектРасчетов.
При программном создании через метод СоздатьДокумент() обработчики событий формы не запускаются. Вы просто присваиваете ссылку в реквизит Договор, но реквизит ОбъектРасчетов остается пустым. Именно поэтому при проведении система не может определить, как закрывать долги, что в дальнейшем сделает невозможным автоматическое групповое создание документов взаимозачета задолженности.
Самый простой способ исправить ошибку, если мы точно знаем, что расчеты по данному договору ведутся «По договорам», — это продублировать ссылку на договор в реквизит объекта расчетов.
Дополним код следующим образом:
// Находим договор
ТекДоговор = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию("Закупка1", Истина);
If ЗначениеЗаполнено(ТекДоговор) Тогда
НовоеПоступление.Договор = ТекДоговор;
// ВАЖНО: Заполняем объект расчетов явно
// Это работает, если детализация расчетов в договоре стоит "По договорам"
НовоеПоступление.ОбъектРасчетов = ТекДоговор;
КонецЕсли;
Если же расчеты ведутся «По накладным», то объектом расчетов является сам документ. В этом случае заполнение происходит сложнее, так как ссылка на новый документ появляется только после метода УстановитьСсылкуНового() или первой записи.
НовоеПоступление.УстановитьСсылкуНового(Документы.ПриобретениеТоваровУслуг.ПолучитьСсылку());
// ... заполнение реквизитов ...
НовоеПоступление.ОбъектРасчетов = НовоеПоступление.Ссылка;
Чтобы не гадать, какой именно объект должен попасть в реквизит (договор, заказ или накладная), правильнее использовать типовые механизмы заполнения. В УТ 11 и ERP существует общий модуль для работы с взаиморасчетами.
В разных версиях конфигураций название процедур может отличаться, но логика остается прежней. Рассмотрим универсальный подход — использование заполнения данных документа.
Вместо ручного присвоения каждого поля, мы можем подготовить структуру данных и вызвать метод заполнения, который отработает так же, как если бы пользователь вводил данные вручную.
Однако, если мы остаемся в рамках прямого присвоения свойств, нам необходимо убедиться в корректности заполнения вспомогательных полей. Давайте посмотрим на более надежный код:
НовоеПоступление = Документы.ПриобретениеТоваровУслуг.СоздатьДокумент();
// ...
НовоеПоступление.ХозяйственнаяОперация = Перечисления.ХозяйственныеОперации.ЗакупкаУПоставщика;
НовоеПоступление.Партнер = Объект.Поставщик;
НовоеПоступление.Контрагент = Объект.Поставщик; // Часто требуется заполнить и контрагента
НовоеПоступление.Организация = Объект.Организация;
// Заполняем договор
НовоеПоступление.Договор = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию("Закупка1", Истина);
// Если используются соглашения, их тоже ОБЯЗАТЕЛЬНО нужно заполнять
// НовоеПоступление.Соглашение = ...;
// Вызов типового механизма для актуализации расчетов
// В зависимости от версии БСП/УТ название модуля может меняться.
// Часто используется такой подход:
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("Партнер", НовоеПоступление.Партнер);
ПараметрыЗаполнения.Вставить("Договор", НовоеПоступление.Договор);
// Данный метод (примерный) должен определить объект расчетов
ВзаиморасчетыСервер.ЗаполнитьОбъектРасчетов(НовоеПоступление);
Если доступа к процедуре ЗаполнитьОбъектРасчетов нет или вы не хотите усложнять код зависимостями от общих модулей, вернитесь к Решению 1, но обязательно проверьте настройки самого договора в режиме «1С:Предприятие».
Помимо основной ошибки с объектом расчетов, в исходном примере есть несколько моментов, которые следует улучшить для стабильной работы.
1. Поиск по наименованию
Использование НайтиПоНаименованию("Закупка1") — это "мина замедленного действия". Как только пользователь исправит название договора на "Закупка 1" (с пробелом) или "Основной договор", ваша обработка перестанет работать.
Рекомендуем искать договоры по:
2. Заполнение валют
В коде автора используется поиск валюты по коду внутри цикла (предположительно):
НовоеПоступление.Валюта = Справочники.Валюты.НайтиПоКоду("643");
Это создает лишнюю нагрузку на базу данных. Правильнее получить ссылку на рубли один раз перед началом создания документов или использовать кэшированные значения:
ВалютаРубль = Константы.ВалютаРегламентированногоУчета.Получить();
// ...
НовоеПоступление.Валюта = ВалютаРубль;
3. Использование Соглашений
В коде автора полностью игнорируется реквизит Соглашение. В УТ 11 использование соглашений может быть обязательным. Если в настройках системы включена опция «Использовать соглашения с поставщиками», то незаполненное соглашение также приведет к ошибкам проведения или некорректному заполнению цен. Чтобы избежать таких проблем при работе с документами закупки, удобно использовать рабочее место отдела закупок – цены предыдущих поставок.
Ошибка «Не найден объект расчетов» возникает из-за того, что при программном создании документа не заполняется служебный реквизит ОбъектРасчетов, который в интерфейсе заполняется автоматически.
Для исправления ситуации необходимо:
ОбъектРасчетов (ссылку на договор или на сам документ).Соглашение и Контрагент.Чтобы проверить корректность отражения данных в учете, рекомендуем использовать анализ продаж и оплат по объектам расчётов или сформировать подробный реестр оплат от покупателей с детализацией по документам и объектам расчетов.