Как исправить ошибку «Поле объекта не обнаружено» и правильно заполнить документ «Операция (бухгалтерский учет)» в 1С

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

При разработке внешних обработок в среде 1С:Предприятие 8 начинающие программисты часто сталкиваются с типовыми ошибками. Две самые распространенные из них — это неправильная адресация реквизитов на управляемой форме и некорректное обращение к аналитике (субконто) при создании бухгалтерских проводок. В этой статье мы подробно разберем, как исправить ошибку «Поле объекта не обнаружено (ДатаКорректировки)», как правильно работать с планом счетов и субконто, а также как оптимизировать код для быстрой записи проводок в документ «ОперацияБух».

Причина 1. Ошибка обращения к реквизитам формы через «Объект»

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

Выясним причину этой ошибки. В управляемом приложении 1С данные формы разделены на данные самой формы и данные основного реквизита объекта (в данном случае — обработки). Переменная Объект внутри модуля формы ссылается на структуру данных самой обработки. Если реквизит ДатаКорректировки был создан непосредственно в списке «Реквизиты» на вкладке формы (а не в дереве метаданных самой обработки), то обращаться к нему через точку от слова Объект нельзя.

Посмотрим на пример ошибочного кода:


// ОШИБКА: Система ищет реквизит внутри метаданных обработки, где его нет
Док.Дата = Объект.ДатаКорректировки; 

Для исправления этой ошибки достаточно убрать обращение к объекту. Если реквизит принадлежит непосредственно форме, к нему нужно обращаться напрямую по имени:


// ПРАВИЛЬНО: Прямое обращение к реквизиту формы
Док.Дата = ДатаКорректировки; 

Если же вы хотите, чтобы реквизит был доступен через Объект.ИмяРеквизита, вам необходимо перенести его из реквизитов формы в реквизиты самой внешней обработки в дереве метаданных.

Причина 2. Ошибка «Поле объекта не обнаружено (Субконто1)» и работа со счетами

После создания документа возникает задача заполнить его проводками. Здесь новичков подстерегают сразу три серьезные трудности:

  1. Передача счета в виде строки. Попытка присвоить счету значение в виде текстовой строки (например, Проводка.СчетДт = "94") приведет к ошибке, так как система ожидает получить ссылку на элемент плана счетов.
  2. Обращение к субконто по индексу. Попытка написать Проводка.СубконтоКт.Субконто1 = ... вызовет ошибку Поле объекта не обнаружено (Субконто1).
  3. Несоответствие имен субконто. Попытка жестко прописать имена субконто вручную может привести к сбоям при изменении настроек счетов в режиме «1С:Предприятие».

Проанализируем ситуацию подробнее. В планах счетов 1С субконто представляют собой коллекцию с типом значения РегистрБухгалтерииСубконто. Доступ к элементам этой коллекции осуществляется не по номерам (Субконто1, Субконто2), а по объектам видов характеристик (например, ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура) либо по именам видов субконто, определенным для конкретного счета.

Чтобы избежать ошибок при заполнении аналитики, в конфигурации «Бухгалтерия предприятия 3.0» предусмотрен специальный общий модуль. Мы рекомендуем использовать функцию БухгалтерскийУчет.УстановитьСубконто(). Она автоматически определяет, к какому именно слоту аналитики привязан данный вид субконто на выбранном счете учета, и безопасно записывает значение.

Также важно правильно получать ссылки на счета учета. Вместо поиска по коду внутри цикла, который создает избыточные запросы к базе данных, ссылку на счет следует получать один раз до начала цикла.

Посмотрим, как выглядит правильное получение счета:


// Получаем ссылку на счет один раз вне цикла
СчетДт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("94");

Причина 3. Критическая ошибка производительности — запись в цикле

Очень опасной ошибкой в коде является вызов метода Проводки.Записать() внутри цикла обхода остатков. Разберем, почему так делать категорически нельзя:

Каждый вызов метода Записать() инициирует полноценную транзакцию записи в базу данных. Если в результате запроса у вас будет 1000 строк номенклатуры, система выполнит 1000 транзакций записи на диск. Это приведет к колоссальному замедлению работы системы и длительным блокировкам таблиц базы данных. Кроме того, при стандартных настройках каждый вызов метода записи набора без флага добавления будет полностью перезаписывать предыдущие строки, из-за чего в документе в итоге останется только одна (последняя) проводка.

Правило разработки в 1С: Набор записей всегда должен формироваться полностью в оперативной памяти в цикле, а его запись в базу данных (метод Записать()) должна выполняться один раз строго за пределами цикла.

Причина 4. Почему в документе «Операция» не заполняется сумма операции?

При программном заполнении проводок через набор записей регистра бухгалтерии разработчики замечают, что в списке документов поле «Сумма» (реквизит СуммаОперации) остается пустым, хотя проводки внутри документа успешно созданы и видны в форме.

Дело в том, что документ ОперацияБух устроен отлично от стандартных документов конфигурации. Его реквизит шапки СуммаОперации автоматически не пересчитывается на основе движений. Эту сумму необходимо рассчитать программно в процессе обхода выборки запроса, после чего явно записать в реквизит созданного документа.

Решение проблемы: правильный и оптимизированный алгоритм

Рассмотрим детальный и оптимизированный шаблон кода для создания документа ОперацияБух, заполнения его проводок остатками со счета 10 и списания их на счет 94 с корректным расчетом итоговой суммы:


Процедура СоздатьДокументНаСервере()
    
    // Шаг 1. Создаем и записываем документ для получения ссылки (Регистратора)
    Док = Документы.ОперацияБух.СоздатьДокумент();
    Док.Дата = ДатаКонецДня(ДатаКорректировки); // Используем конец дня для корректных остатков
    Док.Организация = НаименованиеОрганизации;
    Док.Комментарий = "Создана автоматически для обнуления остатков";
    Док.Содержание = "Обнуление остатков";
    Док.Ответственный = ПараметрыСеанса.ТекущийПользователь;
    Док.Записать(); // Документ должен быть записан в ИБ, чтобы у него появилась Ссылка
    
    // Шаг 2. Создаем набор записей регистра бухгалтерии и устанавливаем отбор по регистратору
    Проводки = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
    Проводки.Отбор.Регистратор.Установить(Док.Ссылка);
    
    // Шаг 3. Подготавливаем запрос для получения остатков
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ХозрасчетныйОстатки.Субконто1 КАК Номенклатура,
        |    ХозрасчетныйОстатки.КоличествоОстаток КАК КоличествоОстаток,
        |    ХозрасчетныйОстатки.СуммаОстаток КАК СуммаОстаток,
        |    ХозрасчетныйОстатки.Субконто2 КАК ДокументПартии,
        |    ХозрасчетныйОстатки.Субконто3 КАК Склад
        |ИЗ
        |    РегистрБухгалтерии.Хозрасчетный.Остатки(&Дата, Счет В ИЕРАРХИИ (&Счет), , Организация = &Организация) КАК ХозрасчетныйОстатки
        |ГДЕ
        |    ХозрасчетныйОстатки.КоличествоОстаток > 0";
    
    // Устанавливаем параметры запроса
    Запрос.УстановитьПараметр("Дата", Док.Дата);
    Запрос.УстановитьПараметр("Организация", Док.Организация);
    // СчетУчета должен содержать ссылку на ПланСчетов.Хозрасчетный (например, счет 10)
    Запрос.УстановитьПараметр("Счет", СчетУчета); 
    
    РезультатЗапроса = Запрос.Выполнить();
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    
    // ОПТИМИЗАЦИЯ: Получаем ссылки на счета один раз до начала цикла
    СчетДт = ПланыСчетов.Хозрасчетный.НайтиПоКоду("94");
    СчетКт = СчетУчета;
    
    // Переменная для подсчета общей суммы операции
    ИтоговаяСумма = 0;
    
    // Шаг 4. Обходим результаты запроса и наполняем набор записей в памяти
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        
        Проводка = Проводки.Добавить();
        Проводка.Регистратор = Док.Ссылка;
        Проводка.Организация = Док.Организация;
        Проводка.Период = Док.Дата;
        
        Проводка.СчетДт = СчетДт;
        Проводка.СчетКт = СчетКт;
        
        // Безопасное заполнение аналитики (субконто) с помощью механизмов БСП
        БухгалтерскийУчет.УстановитьСубконто(Проводка.СчетКт, Проводка.СубконтоКт, "Номенклатура", ВыборкаДетальныеЗаписи.Номенклатура);
        БухгалтерскийУчет.УстановитьСубконто(Проводка.СчетКт, Проводка.СубконтоКт, "Партии", ВыборкаДетальныеЗаписи.ДокументПартии);
        БухгалтерскийУчет.УстановитьСубконто(Проводка.СчетКт, Проводка.СубконтоКт, "Склады", ВыборкаДетальныеЗаписи.Склад);
        
        // Заполняем ресурсы проводки
        Проводка.Сумма = ВыборкаДетальныеЗаписи.СуммаОстаток;
        Проводка.КоличествоКт = ВыборкаДетальныеЗаписи.КоличествоОстаток;
        
        // Аккумулируем сумму для реквизита документа
        ИтоговаяСумма = ИтоговаяСумма + ВыборкаДетальныеЗаписи.СуммаОстаток;
        
    КонецЦикла;
    
    // Шаг 5. Записываем весь пакет проводок за одно обращение к базе данных
    Если Проводки.Количество() > 0 Тогда
        
        Проводки.Записать();
        
        // Шаг 6. Обновляем реквизит СуммаОперации в документе и сохраняем изменения
        ДокОбъект = Док.Ссылка.ПолучитьОбъект();
        ДокОбъект.СуммаОперации = ИтоговаяСумма;
        ДокОбъект.Записать();
        
    КонецЕсли;

КонецПроцедуры

Подведем итоги работы

Мы проанализировали логику программного взаимодействия с бухгалтерскими регистрами в 1С и исправили ключевые ошибки кода. Запомните основные правила:

← На главную