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