Как исправить ошибку «Поле объекта не обнаружено» при заполнении Расходной накладной на основании Банковской выписки в 1С 8.3

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке механизмов заполнения табличных частей документов в системе «1С:Предприятие 8.3» начинающие программисты часто сталкиваются с ошибками обращения к контексту управляемой формы. Типичный пример — попытка получить данные другого документа напрямую через свойство Объект без предварительного выбора или передачи ссылки.

В этой статье мы подробно разберем, почему возникает ошибка «Поле объекта не обнаружено», как правильно передавать параметры в запросы на сервере, а также рассмотрим комплексное решение классической задачи по заполнению накладной оплаченными заказами по методу FIFO (из экзамена «1С:Специалист по платформе»).

---

Анализ исходной ошибки: почему не работает Объект.БанковскаяВыписка

Рассмотрим код, который вызвал ошибку у автора темы:

Запрос.УстановитьПараметр("БанковскаяВыписка", Объект.БанковскаяВыписка);

Ошибка «Поле объекта не обнаружено (БанковскаяВыписка)» возникает по очень простой причине: контекст Объект в модуле управляемой формы документа РасходнаяНакладная содержит только те реквизиты, которые принадлежат самому этому документу (заданы в его метаданных).

Если в документе РасходнаяНакладная нет реквизита с именем БанковскаяВыписка, платформа закономерно выдает ошибку. Документ «Банковская выписка» является сторонним объектом информационной базы, и его данные нельзя прочитать «напрямую» из контекста формы накладной.

Для того чтобы заполнить накладную данными конкретной банковской выписки, нам нужно предварительно получить ссылку на этот документ. Сделать это можно двумя путями:

  1. Создать реквизит в документе-приемнике: Добавить в метаданные документа РасходнаяНакладная реквизит ДокументОснование (или БанковскаяВыписка) с типом ссылки на выписку, вывести его на форму и заполнять по нему.
  2. Интерактивный выбор пользователя: Запросить у пользователя выбор документа-основания непосредственно перед заполнением.

---

Разбор синтаксической ошибки в параметрах запроса

Пытаясь исправить ошибку, автор темы написал следующий код:

Запрос.УстановитьПараметр("Документ", "&Ссылка.Документ");

Проанализируем эту строку. Это грубая синтаксическая ошибка. Передавая в качестве второго параметра текстовую строку "&Ссылка.Документ" в кавычках, мы заставляем СУБД сравнивать уникальные идентификаторы (ссылки) документов в базе данных с простым текстом. Естественно, запрос ничего не находит и возвращает пустой результат (сообщение «Нечего добавлять!»).

Правильное правило: параметры запроса всегда должны передаваться в виде реальных объектов базы данных, ссылок, чисел, дат или булевых значений. Кавычки используются только в тексте самого запроса для обозначения имени параметра после символа амперсанда (&).

---

Решение 1: Заполнение накладной по одной конкретной Банковской выписке

Давайте реализуем корректный и безопасный алгоритм заполнения табличной части Заказы документа РасходнаяНакладная на основании выбранной банковской выписки.

Для начала создадим на форме документа РасходнаяНакладная команду «Заполнить» и свяжем ее с кнопкой. Реализуем интерактивный выбор выписки на клиенте с последующей передачей ссылки на сервер.

Код модуля управляемой формы:


&НаКлиенте
Процедура Заполнить(Команда)
    
    // Настраиваем параметры выбора для открытия формы списка выписок
    ПараметрыВыбора = Новый Структура;
    
    // Открываем форму выбора документов "БанковскаяВыписка"
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораВыписки", ЭтотОбъект);
    ОткрытьФорму("Документ.БанковскаяВыписка.ФормаВыбора", ПараметрыВыбора, ЭтаФорма, , , , ОписаниеОповещения);
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораВыписки(РезультатВыбора, ДополнительныеПараметры) Экспорт
    
    // Если пользователь выбрал документ, передаем его на сервер для обработки
    Если ЗначениеЗаполнено(РезультатВыбора) Тогда
        ЗаполнитьПоВыпискеНаСервере(РезультатВыбора);
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьПоВыпискеНаСервере(СсылкаНаВыписку)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	БанковскаяВыпискаЗаказы.Товар КАК Товар,
        |	БанковскаяВыпискаЗаказы.Количество КАК Количество,
        |	БанковскаяВыпискаЗаказы.Цена КАК Цена,
        |	БанковскаяВыпискаЗаказы.Сумма КАК Сумма
        |ИЗ
        |	Документ.БанковскаяВыписка.Заказы КАК БанковскаяВыпискаЗаказы
        |ГДЕ
        |	БанковскаяВыпискаЗаказы.Ссылка = &СсылкаНаВыписку";
    
    // Передаем реальную ссылку на документ, полученную с клиента
    Запрос.УстановитьПараметр("СсылкаНаВыписку", СсылкаНаВыписку);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если РезультатЗапроса.Пустой() Тогда
        ВызватьИсключение "В выбранном документе оплаты нет товаров для заполнения!";
    КонецЕсли;
    
    // Очищаем текущую табличную часть документа на форме
    Объект.Заказы.Очистить();
    
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        НоваяСтрока = Объект.Заказы.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
    КонецЦикла;
    
КонецПроцедуры

---

Решение 2: Сложная архитектурная задача (Заполнение по FIFO с контролем оплат)

В сообщении №12 автор приводит текст реального экзаменационного задания «1С:Специалист». По условию, документ отгрузки (Расходная накладная) должен заполняться автоматически по кнопке «Заполнить» по методу FIFO товарами из оплаченных, но еще не отгруженных заказов покупателей. При этом отгружать можно только те товары, которые физически есть на складе.

Для решения этой задачи нам потребуется использовать данные регистров накопления. Предположим, что в системе созданы три регистра:

  1. ЗаказыПокупателей (тип «Остатки») — хранит остатки неотгруженных товаров по заказам. Измерения: ЗаказПокупателя, Номенклатура. Ресурс: Количество.
  2. ОплатаЗаказов (тип «Остатки») — хранит остатки оплаченных сумм по заказам (авансы). Измерения: ЗаказПокупателя. Ресурс: Сумма.
  3. ОстаткиТоваров (тип «Остатки») — хранит текущие свободные остатки товаров на складах. Измерения: Номенклатура, Склад. Ресурс: Количество.

Проанализируем по шагам логику, которую мы должны реализовать на сервере при нажатии кнопки «Заполнить»:

Шаг 1. Запрос данных из базы

Мы должны одним пакетом запросов получить:

Шаг 2. Распределение остатков по FIFO

В коде модуля формы мы обойдем выборку заказов, упорядоченную по дате (моменту времени) заказа. Будем уменьшать доступный остаток товара на складе по мере заполнения строк накладной. Если товара на складе для очередной строки заказа не хватает, мы пропускаем эту строку (или уменьшаем количество до доступного остатка, в зависимости от трактовки учетной политики).

Рассмотрим пример реализации такого алгоритма на сервере:


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

---

Как реализовать разукомплектацию при проведении?

В сообщении №12 автор также задает важный вопрос: «При проведении документа отгрузки должны, при необходимости, создаваться движения, отражающие разукомплектацию имеющихся на складе комплектов... Как это осуществить?»

Важное архитектурное правило: все операции по проверке остатков комплектующих, расчету себестоимости и виртуальной «сборке/разукомплектации» должны производиться исключительно в модуле объекта в процедуре ОбработкаПроведения, но ни в коем случае не при нажатии кнопки «Заполнить» на форме.

Разберем логику проведения с разукомплектацией по шагам:

  1. При проведении Расходной накладной система сначала пытается списать со склада готовые комплекты.
  2. Если готовых комплектов на складе не хватает, в коде проведения мы делаем запрос к спецификациям этих комплектов (например, через регистр сведений Комплектующие), чтобы узнать их состав.
  3. Проверяем, достаточно ли на складе комплектующих для сборки недостающего количества комплектов.
  4. Если комплектующих хватает, мы формируем дополнительные движения по регистрам остатков:
    • Списываем комплектующие со склада (в дебет себестоимости комплектов).
    • Приходуем готовые комплекты на склад.
    • Обычным образом списываем собранные комплекты под нужды текущей отгрузки.
  5. Если на складе нет ни готовых комплектов, ни необходимых для сборки комплектующих — транзакция проведения отменяется с вызовом исключения и выводом информативного сообщения для пользователя.

Применение разделения логики (интерактивное заполнение формы на основе остатков и жесткий контроль остатков с разукомплектацией при проведении на сервере) гарантирует корректность транзакций и защищает базу данных от рассинхронизации информации.

← На главную