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