Почему номера субконто в проводках (наборе записей) не совпадают с планом счетов 1С и как это исправить?

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

При разработке и кастомизации конфигураций на платформе 1С:Предприятие 8.3 разработчики часто сталкиваются с загадочным поведением регистров бухгалтерии. Типичный сценарий: на счете 41.01 («Товары на складах») в пользовательском интерфейсе и в карточке счета субконто расположены в привычном порядке:

  1. Номенклатура (Субконто 1)
  2. Склады (Субконто 2)
  3. Партии (Субконто 3)

Однако при чтении движений документа через объектную технику РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей() в свойствах проводки внезапно обнаруживается совершенно иная картина. Например, в СубконтоКт1 оказывается записана Партия, в СубконтоКт2 — Номенклатура, а в СубконтоКт3 — Склад. Почему платформа меняет их местами на физическом уровне и как написать отказоустойчивый код, который не зависит от этих изменений? Давайте детально разберем физические механизмы работы СУБД в 1С.

Физический уровень хранения (Набор записей) против логического представления (Интерфейс и Запросы)

Чтобы понять первопричину проблемы, нам необходимо четко разграничить два уровня абстракции, которыми оперирует платформа 1С:

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

Особенности счета 41.01: Динамическое управление аналитикой

Выясним, почему физический порядок субконто на счете 41.01 («Товары на складах») в 1С:Бухгалтерии предприятия 3.0 может отличаться в разных базах данных.

Если мы откроем конфигуратор БП 3.0 и найдем в плане счетов предопределенный счет 41.01, мы обнаружим, что список видов субконто у него... пуст. Разработчики фирмы «1С» не задают виды субконто для счетов учета запасов жестко на этапе разработки. Состав аналитики определяется программно при первом запуске или при изменении настроек в режиме Предприятие (меню Администрирование — Параметры учета — Настройки плана счетов — Учет запасов).

Здесь пользователь может включить или отключить:

Проанализируем, что происходит в базе данных под капотом, когда мы включаем эти флаги в разной последовательности.

Влияние исторической предыстории базы на порядок полей

Каждый раз, когда включается очередная настройка учета запасов, специальный программный алгоритм конфигурации добавляет соответствующий вид субконто в табличную часть счета 41.01. Порядок добавления строк жестко определяет физический индекс субконто в таблице движений СУБД.

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

Сценарий А (Сначала включили склады, потом партии):

  1. При создании базы по умолчанию создается субконто Номенклатура (занимает физическую позицию №1).
  2. Бухгалтер заходит в настройки и включает «Учет по складам». На счет добавляется субконто Склады на первую свободную позицию (физическая позиция №2).
  3. Спустя полгода компания решает вести партионный учет. Бухгалтер включает «Учет по партиям». На счет добавляется субконто Партии (физическая позиция №3).
  4. Физический порядок в СУБД: 1 - Номенклатура, 2 - Склады, 3 - Партии.

Сценарий Б (Сначала включили партии, потом склады):

  1. В новой базе сначала активировали партионный учет. Субконто Партии записалось на свободную физическую позицию №2.
  2. Через некоторое время решили детализировать учет до складов. Субконто Склады записалось на свободную физическую позицию №3.
  3. Физический порядок в СУБД: 1 - Номенклатура, 2 - Партии, 3 - Склады.

Но почему тогда в режиме Предприятия (в плане счетов) мы всегда видим одинаковый порядок (Номенклатура -> Склады -> Партии)?

Все очень просто: форма списка плана счетов и карточка счета в БП 3.0 программно сортируют строки табличной части перед выводом их на экран пользователя. Это сделано для эстетики и удобства восприятия информации. Однако физический порядок строк в системных таблицах базы данных при этом умышленно не изменяется. Переиндексация и перезапись физических колонок в регистрах огромных баз данных приводили бы к колоссальным нагрузкам на СУБД, блокировкам таблиц и потенциальным сбоям в работе сотен пользователей.

Как правильно работать с субконто в коде без привязки к номерам

Завязываться в программном коде на жесткие номера субконто типа Запись.СубконтоКт1 или Запись.СубконтоКт2 — это грубейшая архитектурная ошибка, которая обязательно приведет к багам при переносе кода на другие базы данных или при изменении настроек параметров учета.

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

Решение №1: Объектная техника работы с Набором Записей

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

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


// 1. Сначала кэшируем необходимые виды субконто в локальные переменные
ВидНоменклатура = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура;
ВидСклады       = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Склады;
ВидПартии       = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Партии;

// 2. Создаем и считываем набор записей по регистратору
Набор = РегистрыБухгалтерии.Хозрасчетный.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(ДокументСсылка);
Набор.Прочитать();

Для Каждого Запись Из Набор Цикл
    
    // Проверяем, что счет проводки по Кредиту — это счет 41.01
    Если Запись.СчетКт = ПланыСчетов.Хозрасчетный.ТоварыНаСкладах Тогда
        
        // БЕЗОПАСНОЕ ЧТЕНИЕ: Обращаемся по ключу ПВХ
        Товар  = Запись.СубконтоКт[ВидНоменклатура];
        Склад  = Запись.СубконтоКт[ВидСклады];
        Партия = Запись.СубконтоКт[ВидПартии];
        
        // Выполняем логику анализа
        Если ЗначениеЗаполнено(Товар) Тогда
            // Например, выводим информацию в журнал регистрации
            ЗаписьЖурналаРегистрации("АнализПроводки",,,, "Товар: " + Товар + ", Склад: " + Склад);
        КонецЕсли;
        
        // БЕЗОПАСНАЯ ЗАПИСЬ: Установка значений также производится по ключу ПВХ
        // Платформа сама запишет данные в нужную физическую колонку СУБД (будь то СубконтоКт1, 2 или 3)
        Запись.СубконтоКт[ВидНоменклатура] = НоваяСсылкаНаТовар;
        
    КонецЕсли;
    
КонецЦикла;

// Если вносили изменения, записываем набор
// Набор.Записать();

Используя этот подход, вы полностью абстрагируетесь от того, под каким индексом хранятся данные в физической таблице базы данных. Код будет стабильно работать и на пустой демо-базе, и на рабочей базе клиента с десятилетней историей.

Решение №2: Правильное получение субконто в Запросах

Если вам необходимо прочитать данные из регистра с помощью запроса и гарантировать, что в результатах запроса Субконто1 — это всегда Номенклатура, а Субконто2 — это всегда Склад, обязательно используйте параметр &ВидыСубконто в виртуальных таблицах.

Проанализируем пример правильного построения запроса:


// 1. Формируем строго упорядоченный массив видов субконто
МассивВидов = Новый Массив;
МассивВидов.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура); // Индекс 0 -> Будет в Субконто1
МассивВидов.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Склады);      // Индекс 1 -> Будет в Субконто2
МассивВидов.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Партии);      // Индекс 2 -> Будет в Субконто3

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ХозрасчетныйОбороты.Субконто1 КАК Товар,
    |	ХозрасчетныйОбороты.Субконто2 КАК Склад,
    |	ХозрасчетныйОбороты.Субконто3 КАК Партия,
    |	ХозрасчетныйОбороты.СуммаОборотКт КАК СуммаОборотКт
    |ИЗ
    |	РегистрБухгалтерии.Хозрасчетный.Обороты(&НачДата, &КонДата, , Счет = &ВыбСчет, &ВидыСубконто, , , ) КАК ХозрасчетныйОбороты";

Запрос.УстановитьПараметр("ВыбСчет", ПланыСчетов.Хозрасчетный.ТоварыНаСкладах);
Запрос.УстановитьПараметр("НачДата", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("КонДата", КонецДня(ТекущаяДата()));

// Передаем наш массив в пятый параметр виртуальной таблицы Обороты
Запрос.УстановитьПараметр("ВидыСубконто", МассивВидов);

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    // Мы можем быть на 100% уверены, что в поле "Товар" попала именно Номенклатура,
    // а в поле "Склад" — именно Склад, независимо от структуры физической БД.
    Сообщить("Склад: " + Выборка.Склад + " -> " + Выборка.Товар);
КонецЦикла;

Если бы мы не передали массив МассивВидов в качестве параметра &ВидыСубконто, платформа сопоставила бы поля по умолчанию на основе физического размещения, и в поле Субконто1 в разных базах данных могли бы оказаться совершенно разные сущности.

Резюме и чек-лист для разработчика 1С

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

  1. Никогда не доверяйте номерам полей: Забудьте про конструкции СубконтоКт1, СубконтоДт2 в объектной технике, если вы обращаетесь к ним по числовым индексам или именам свойств.
  2. Используйте обращение по ключу: Всегда извлекайте и записывайте значения субконто в проводках через квадратные скобки, передавая в качестве ключа конкретную ссылку из ПВХ ВидыСубконтоХозрасчетные.
  3. Всегда типизируйте виртуальные таблицы: При обращении к виртуальным таблицам остатков и оборотов в запросах передавайте строго определенный массив видов субконто. Это гарантирует неизменность структуры колонок выборки результата запроса.
  4. Помните про настройки учета: Порядок субконто на счетах учета запасов (41, 10, 43), расчетов с контрагентами (60, 62) и многих других в типовых решениях определяется настройками параметров учета в режиме Предприятие и зависит от истории конкретной базы данных.
← На главную