При разработке и кастомизации механизмов проведения документов в 1С разработчики часто сталкиваются с необходимостью программного заполнения или корректировки аналитики (субконто) в сформированных проводках. Типичная проблема, с которой можно столкнуться: при попытке напрямую записать значение в субконто оно сбрасывается, а тип значения становится Неопределено.
В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем внутреннее устройство хранения субконто в платформе 1С и рассмотрим несколько надежных способов программного заполнения аналитики как в таблице значений, так и непосредственно в наборе записей регистра бухгалтерии.
Для начала разберем природу субконто в платформе 1С. В регистре бухгалтерии субконто представляют собой коллекцию значений с составным типом данных, определяемым планом видов характеристик (обычно это ВидыСубконтоХозрасчетные).
Когда мы выгружаем движения (проводки) в промежуточную Таблицу значений для последующего анализа или модификации, эта сложная структура трансформируется («сворачивается») в плоскую таблицу со следующими стандартными колонками:
ВидСубконтоДт1, ВидСубконтоДт2, ВидСубконтоДт3 — хранят метаданные (ссылки на элементы плана видов характеристик).СубконтоДт1, СубконтоДт2, СубконтоДт3 — хранят непосредственные значения (ссылки на справочники, документы и т.д.).Аналогичные тройки колонок существуют и для кредитовой стороны (ВидСубконтоКт1, СубконтоКт1 и т.д.).
Главная ошибка начинающих разработчиков заключается в попытке заполнить только значение аналитики, например, написать ЗаписьБУ.СубконтоКт2 = СсылкаНаДоговор. Поскольку колонка ВидСубконтоКт2 остается пустой, платформа не знает, к какому типу из сотен возможных в характеристике нужно привести записываемое значение. Происходит конфликт типов, и платформа сбрасывает значение колонки в дефолтное состояние — Неопределено.
Ниже мы подробно рассмотрим четыре практических способа решения этой задачи.
Если в вашем алгоритме проводки получаются в виде таблицы значений (например, перед вызовом процедуры записи движений), вам необходимо жестко и последовательно заполнять пару колонок: сначала вид субконто, а затем его значение.
Рассмотрим пример кода, к которому пришел автор темы в ходе обсуждения:
Процедура КЗ_ОтражениеСписаниеОС(ДокументСсылка, ТаблицаПроводок)
Для каждого ЗаписьБУ из ТаблицаПроводок Цикл
ЗапросОС = Новый Запрос;
ЗапросОС.Текст =
"ВЫБРАТЬ
| МестонахождениеОССрезПоследних.ОсновноеСредство КАК ОсновноеСредство,
| МестонахождениеОССрезПоследних.Арендатор КАК Арендатор
|ИЗ
| РегистрСведений.МестонахождениеОС.СрезПоследних(, ) КАК МестонахождениеОССрезПоследних
|ГДЕ
| МестонахождениеОССрезПоследних.ОсновноеСредство = &ОсновноеСредство";
ЗапросОС.УстановитьПараметр("ОсновноеСредство", ЗаписьБУ.СубконтоДт1);
РезультатОС = ЗапросОС.Выполнить().Выбрать();
Если РезультатОС.Следующий() Тогда
Если ЗаписьБУ.SчетКт = ПланыСчетов.Хозрасчетный.ПрочиеДоходныеВложения Тогда
// Сначала указываем вид субконто для первой позиции Кт
ЗаписьБУ.ВидСубконтоКт1 = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты;
// Затем записываем само значение
ЗаписьБУ.СубконтоКт1 = РезультатОС.Арендатор;
// Повторяем операцию для второй позиции субконто Кт
ЗаписьБУ.ВидСубконтоКт2 = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.ОсновныеСредства;
ЗаписьБУ.СубконтоКт2 = РезультатОС.ОсновноеСредство;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Важное предупреждение: Данный способ полностью рабочий, однако он имеет существенный архитектурный минус — жесткое кодирование индексов субконто (1 и 2). Если в конфигураторе на счете ПрочиеДоходныеВложения поменяется порядок субконто (например, «Основные средства» станут первыми, а «Контрагенты» вторыми), этот код начнет записывать некорректные данные или вызовет ошибку проведения. Ниже мы разберем, как сделать код более гибким.
Если вы работаете не с плоской промежуточной таблицей значений, а непосредственно с коллекцией движений документа (объектом типа РегистрБухгалтерииНаборЗаписей), то обращение к субконто происходит гораздо проще и безопаснее.
В наборе записей субконто хранятся в виде коллекции СубконтоДт и СубконтоКт. Обращаться к ним можно напрямую по ключу — объекту плана видов характеристик. При этом платформа сама определит нужный порядковый номер субконто на счете.
Проанализируем пример кода:
Для Каждого ЗаписьБУ Из НаборДвижений Цикл
Если ЗаписьБУ.СчетКт = ПланыСчетов.Хозрасчетный.ПрочиеДоходныеВложения Тогда
// Получаем нужные виды субконто
ВидКонтрагенты = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты;
ВидОС = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.ОсновныеСредства;
// Напрямую заполняем значения по ключу вида субконто
ЗаписьБУ.СубконтоКт[ВидКонтрагенты] = СсылкаНаАрендатора;
ЗаписьБУ.СубконтоКт[ВидОС] = СсылкаНаОС;
КонецЕсли;
КонецЦикла;
При таком подходе вам не нужно беспокоиться о том, является ли субконто первым, вторым или третьим на счете. Платформа выполнит всю внутреннюю работу за вас. Если на данном счете вообще нет вида субконто «Контрагенты», 1С просто проигнорирует эту строку и не вызовет аварийного завершения работы программы.
Если вы ведете разработку в типовой конфигурации 1С (например, 1С:Бухгалтерия предприятия, ERP или Комплексная автоматизация), то изобретать велосипед не нужно. В составе Библиотеки Стандартных Подсистем (БСП) уже есть готовые общие модули для работы с субконто.
Чаще всего используется метод БухгалтерскийУчет.УстановитьСубконто(). Разберем синтаксис его вызова:
// Метод сам проверит наличие вида субконто на счете и корректно заполнит значение
БухгалтерскийУчет.УстановитьСубконто(
ЗаписьБУ.СчетКт,
ЗаписьБУ.СубконтоКт,
ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты,
СсылкаНаАрендатора
);
Этот способ является стандартом разработки для современных конфигураций. Он делает ваш код лаконичным, легко читаемым и полностью защищенным от ошибок изменения структуры метаданных.
Что делать, если вам всё-таки крайне необходимо работать именно с таблицей значений (например, в универсальном модуле обмена данными), но вы хотите избежать жесткого кодирования индексов СубконтоКт1, СубконтоКт2?
Решение — динамический обход видов субконто конкретного счета. Напишем универсальный алгоритм:
Если ЗаписьБУ.СчетКт = ПланыСчетов.Хозрасчетный.ПрочиеДоходныеВложения Тогда
// Перебираем виды субконто, которые прикреплены к счету в пользовательском режиме или конфигураторе
Для Каждого СтрокаСубконто Из ЗаписьБУ.СчетКт.ВидыСубконто Цикл
Индекс = СтрокаСубконто.НомерСтроки; // Получаем реальный индекс (1, 2 или 3)
ТекущийВидСубконто = СтрокаСубконто.ВидСубконто;
// Динамически определяем имя колонки по индексу
ИмяКолонкиВида = "ВидСубконтоКт" + Индекс;
ИмяКолонкиЗначения = "СубконтоКт" + Индекс;
Если ТекущийВидСубконто = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты Тогда
ЗаписьБУ[ИмяКолонкиВида] = ТекущийВидСубконто;
ЗаписьБУ[ИмяКолонкиЗначения] = СсылкаНаАрендатора;
ИначеЕсли ТекущийВидСубконто = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.ОсновныеСредства Тогда
ЗаписьБУ[ИмяКолонкиВида] = ТекущийВидСубконто;
ЗаписьБУ[ИмяКолонкиЗначения] = СсылкаНаОС;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Этот способ объединяет в себе гибкость работы с плоской таблицей значений и безопасность динамического определения индексов метаданных.
Подводя итог, отметим: если вы работаете в типовой конфигурации, всегда отдавайте приоритет встроенным функциям БСП (Способ 3). При написании собственных уникальных механизмов на чистой платформе используйте работу через набор записей напрямую (Способ 2) и избегайте жесткого обращения к колонкам субконто по их номерам, если это возможно.