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

· 10 мин

Содержание

Субконто в проводке устанавливают не по номеру, а по виду: Движение.СубконтоДт[ВидСубконто] = Значение и Движение.СубконтоКт[ВидСубконто] = Значение. Сначала задайте счёт, потом субконто, и только для тех видов, которые есть у этого счёта в плане счетов. В типовой «Бухгалтерии предприятия» 3.0 для этого уже есть готовая процедура БухгалтерскийУчет.УстановитьСубконто.

Частая ошибка новичков — писать Движение.Субконто1Дт = Контрагент по аналогии с полями табличной части. У записи регистра бухгалтерии таких полей нет: значения хранятся в коллекции, проиндексированной видом субконто. Механизм общий для платформы 8.3, а примеры ниже даны на плане счетов Хозрасчетный из БП 3.0.

Как платформа хранит аналитику в регистре бухгалтерии

Аналитику к счёту задают три объекта метаданных. Если понять их связь, программная установка субконто перестаёт быть угадыванием.

ОбъектЧто в нём лежитПример в БП 3.0
План видов характеристикПеречень видов субконто и тип значения каждогоВидыСубконтоХозрасчетные
План счетовСчета и табличная часть ВидыСубконто у каждого счётаХозрасчетный
Регистр бухгалтерииЗаписи: счёт, сумма, значения субконто по Дт и КтХозрасчетный

Что важно для кода:

  • 📌 У плана счетов есть свойство «Максимальное количество субконто». В Хозрасчетный оно равно 3, поэтому у одного счёта не бывает больше трёх видов.
  • 📌 Порядок видов на счёте задаёт строка табличной части ВидыСубконто. Первая строка — это «Субконто 1» в отчётах, вторая — «Субконто 2».
  • 📌 Тип значения задан у вида субконто в реквизите ТипЗначения (это ОписаниеТипов). Для вида «Контрагенты» туда нельзя положить номенклатуру.
  • 📌 В записи регистра свойства СубконтоДт и СубконтоКт — это коллекции. Ключ — ссылка на вид субконто, значение — объект аналитики.

Из этого следует главное правило: значение субконто осмысленно только в паре со счётом. Один и тот же вид «Склады» на счёте 41.01 стоит третьим, а на другом счёте может стоять первым. Код, который полагается на номер позиции, ломается при первой же правке плана счетов.

Установка субконто в движениях документа

Самый частый случай — обработка проведения своего документа. Ниже поступление товара: Дт 41.01, Кт 60.01. На 41.01 в БП 3.0 стоят виды «Номенклатура», «Партии», «Склады», на 60.01 — «Контрагенты», «Договоры», «Документы расчетов с контрагентами».

Процедура ОбработкаПроведения(Отказ, РежимПроведения)

	ВидыСубконто = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные;

	Движения.Хозрасчетный.Записывать = Истина;

	Для Каждого СтрокаТовара Из Товары Цикл

		Движение = Движения.Хозрасчетный.Добавить();
		Движение.Период      = Дата;
		Движение.Организация = Организация;

		// 1. Сначала счета
		Движение.СчетДт = ПланыСчетов.Хозрасчетный.ТоварыНаСкладах;
		Движение.СчетКт = ПланыСчетов.Хозрасчетный.РасчетыСПоставщиками;

		// 2. Потом субконто по видам
		Движение.СубконтоДт[ВидыСубконто.Номенклатура] = СтрокаТовара.Номенклатура;
		Движение.СубконтоДт[ВидыСубконто.Склады]       = Склад;

		Движение.СубконтоКт[ВидыСубконто.Контрагенты] = Контрагент;
		Движение.СубконтоКт[ВидыСубконто.Договоры]    = ДоговорКонтрагента;
		Движение.СубконтоКт[ВидыСубконто.ДокументыРасчетовСКонтрагентами] = Ссылка;

		Движение.Сумма = СтрокаТовара.Сумма;
		Движение.Содержание = "Поступление товаров";

	КонецЦикла;

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

Пара пояснений к коду:

  1. Флаг Записывать = Истина говорит платформе записать набор после проведения. Без него движения сформируются в памяти и пропадут.
  2. Субконто «Партии» здесь не заполнено. Это допустимо: незаполненное субконто — пустая аналитика, а не ошибка. Но если учёт партий включён, остатки по ним разъедутся.
  3. Вид субконто передаётся ссылкой на предопределённый элемент. На клиенте и в общих модулях с клиентским контекстом используйте ПредопределенноеЗначение("ПланВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты").

Если счёт в записи меняется после того, как субконто уже заданы, перепроверьте аналитику. Значения по видам, которых нет у нового счёта, в проводке не нужны и только запутают отладку.

Готовая процедура УстановитьСубконто и своя обёртка

Писать проверки в каждой строке проведения утомительно. Поэтому и в типовых, и в самописных решениях установку субконто выносят в одну процедуру — своего рода конструктор аналитики.

Процедура из типовой БП 3.0

В общем модуле БухгалтерскийУчет есть процедура УстановитьСубконто. Типичный вызов:

БухгалтерскийУчет.УстановитьСубконто(
	Движение.СчетКт, Движение.СубконтоКт, "Контрагенты", Контрагент);

Третий параметр принимает имя предопределённого вида строкой, номер субконто на счёте или ссылку на вид. Процедура сама проверяет, есть ли такой вид у счёта, и приводит значение к нужному типу. Точную сигнатуру и необязательные параметры посмотрите в Конфигураторе: откройте общий модуль и найдите процедуру поиском по модулю (Ctrl+F).

Своя обёртка для нетиповой конфигурации

Если типового модуля нет, напишите короткую процедуру в своём общем модуле с флагом «Сервер»:

Процедура УстановитьСубконтоПоВиду(Счет, Субконто, ВидСубконто, Значение) Экспорт

	Если Не ЗначениеЗаполнено(Счет) Или Не ЗначениеЗаполнено(ВидСубконто) Тогда
		Возврат;
	КонецЕсли;

	// Вид должен быть на счёте, иначе значение в проводке не нужно
	Если Счет.ВидыСубконто.Найти(ВидСубконто, "ВидСубконто") = Неопределено Тогда
		Возврат;
	КонецЕсли;

	// Приводим значение к типу вида: чужой тип превратится в пустое значение
	Субконто[ВидСубконто] = ВидСубконто.ТипЗначения.ПривестиЗначение(Значение);

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

Обёртка решает три задачи сразу: не пишет аналитику «мимо» счёта, не падает на пустом счёте и не кладёт значение чужого типа. Обращение Счет.ВидыСубконто читает счёт из базы — для единичных документов это нормально, для массовой записи ниже есть вариант с кэшем.

Установка нескольких субконто через структуру и соответствие

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

КоллекцияКлючКогда удобна
СтруктураИмя предопределённого вида строкойЧитаемый код, виды известны заранее
СоответствиеСсылка на вид субконтоВиды приходят из данных, есть непредопределённые виды

Вариант со структурой

Процедура УстановитьСубконтоИзСтруктуры(Счет, Субконто, Значения) Экспорт

	Для Каждого Элемент Из Значения Цикл
		ВидСубконто = ПредопределенноеЗначение(
			"ПланВидовХарактеристик.ВидыСубконтоХозрасчетные." + Элемент.Ключ);
		УстановитьСубконтоПоВиду(Счет, Субконто, ВидСубконто, Элемент.Значение);
	КонецЦикла;

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

// Вызов
Значения = Новый Структура("Контрагенты, Договоры", Контрагент, ДоговорКонтрагента);
Значения.Вставить("ДокументыРасчетовСКонтрагентами", Ссылка);
УстановитьСубконтоИзСтруктуры(Движение.СчетКт, Движение.СубконтоКт, Значения);

Плюс подхода: в структуру можно положить «лишние» виды. Для счёта 60.01 сработают все три, а если тот же набор передать для 76.05, у которого другой состав аналитики, обёртка молча пропустит то, чего на счёте нет.

Вариант с соответствием

Ключ структуры — только строка-идентификатор, поэтому пользовательские виды субконто, добавленные в справочник вручную, в неё не положить. Здесь выручает Соответствие:

Значения = Новый Соответствие;
Значения.Вставить(ВидСубконтоПроекты, Проект); // непредопределённый вид из настроек
Значения.Вставить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты, Контрагент);

Для Каждого Элемент Из Значения Цикл
	УстановитьСубконтоПоВиду(Движение.СчетДт, Движение.СубконтоДт, Элемент.Ключ, Элемент.Значение);
КонецЦикла;

Субконто в табличных частях документов

В табличной части аналитика хранится иначе, чем в регистре. Там нет коллекции по видам — обычно это три реквизита Субконто1, Субконто2, Субконто3 с типом «Характеристика.ВидыСубконтоХозрасчетные». Так устроены, например, строки ручной операции и многие самописные документы.

Настройка реквизитов в Конфигураторе

Чтобы поле само подстраивало тип под счёт строки, у реквизита Субконто1 задайте свойство «Связь по типу»: связь — реквизит Счет этой строки, элемент связи — 1. Для Субконто2 элемент связи 2, для Субконто3 — 3. После этого пользователь в поле «Субконто 1» увидит выбор только из значений первого вида на счёте.

Программное заполнение строки

При заполнении из кода номер поля надо сопоставить с позицией вида на счёте:

Процедура ЗаполнитьСубконтоСтроки(Строка, ЗначенияПоВидам) Экспорт

	ВидыНаСчете = Строка.Счет.ВидыСубконто;

	Для Номер = 1 По 3 Цикл

		ИмяПоля = "Субконто" + Формат(Номер, "ЧГ=0");

		Если Номер > ВидыНаСчете.Количество() Тогда
			Строка[ИмяПоля] = Неопределено; // на счёте меньше видов
			Продолжить;
		КонецЕсли;

		Вид = ВидыНаСчете[Номер - 1].ВидСубконто;
		Строка[ИмяПоля] = Вид.ТипЗначения.ПривестиЗначение(ЗначенияПоВидам[Вид]);

	КонецЦикла;

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

ЗначенияПоВидам — то же соответствие «вид → значение», что и в предыдущем разделе. Если для вида значения нет, ЗначенияПоВидам[Вид] вернёт Неопределено, и поле получит пустое значение нужного типа.

Если пользователь меняет счёт в строке, старые значения субконто остаются в полях. Очищайте их в обработчике ПриИзменении поля счёта, иначе при проведении на новый счёт уйдёт аналитика от старого.

При проведении такого документа из строки в движение значения переносят уже по видам: берут вид из ВидыНаСчете[Номер - 1].ВидСубконто и пишут Движение.СубконтоДт[Вид] = Строка[ИмяПоля].

Разные счета с разным набором видов субконто

Хуже всего на практике ведут себя документы, где счёт выбирает пользователь или он подставляется из настроек учёта. Один документ — а аналитика у каждой строки своя. Три приёма закрывают почти все такие сценарии.

Приём 1. Готовить значения «с запасом». Соберите в соответствие все объекты, которые есть в документе: контрагент, договор, номенклатура, склад, подразделение, статья затрат. Обёртка сама возьмёт только те виды, что стоят на счёте строки.

Приём 2. Различать виды с одинаковым типом. У вида «Статьи затрат» и у вида «Прочие доходы и расходы» разные справочники, но у некоторых пользовательских видов тип может совпадать. Ключом всегда должен быть вид, а не тип значения, — тогда путаницы не будет.

Приём 3. Учитывать флаги вида на счёте. В строке ВидыСубконто есть признаки ТолькоОбороты, Суммовой, Количественный, Валютный. По виду с признаком «Только обороты» остатков не бывает, и проверка «хватает ли остатка по субконто» для него не нужна.

Как ставить субконто в вашем случае?

Быстрая запись тысяч проводок с субконто

Для одного документа всё сказанное выше работает мгновенно. Проблемы начинаются при загрузке остатков, переносе данных из другой программы или пакетном перепроведении. Здесь важны четыре вещи.

1. Кэшируйте виды субконто счетов

Счет.ВидыСубконто при каждом обращении читает объект счёта. На 50 000 строк это 50 000 лишних обращений к базе. Прочитайте виды всех нужных счетов одним запросом:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	ВидыСубконто.Ссылка КАК Счет,
|	ВидыСубконто.НомерСтроки КАК Номер,
|	ВидыСубконто.ВидСубконто КАК ВидСубконто
|ИЗ
|	ПланСчетов.Хозрасчетный.ВидыСубконто КАК ВидыСубконто
|ГДЕ
|	ВидыСубконто.Ссылка В (&Счета)
|УПОРЯДОЧИТЬ ПО
|	Счет, Номер";
Запрос.УстановитьПараметр("Счета", МассивСчетов);

КэшВидов = Новый Соответствие; // Счет -> Массив видов по порядку
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Виды = КэшВидов[Выборка.Счет];
	Если Виды = Неопределено Тогда
		Виды = Новый Массив;
		КэшВидов.Вставить(Выборка.Счет, Виды);
	КонецЕсли;
	Виды.Добавить(Выборка.ВидСубконто);
КонецЦикла;

Дальше проверка «есть ли вид на счёте» превращается в КэшВидов[Счет].Найти(Вид) <> Неопределено — без обращений к базе. Проверку на Неопределено для счёта без аналитики не забудьте.

2. Пишите набором, а не по одной записи

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

3. Делите загрузку на порции в транзакциях

Одна транзакция на весь массив — это долгие блокировки и откат всего при одной ошибке. Одна транзакция на документ — медленно. Разумная середина — порции, например по 100–500 документов, подберите размер на своих данных.

4. Отключайте итоги только на время загрузки

У менеджера регистра есть УстановитьИспользованиеИтогов(Ложь). Запись без пересчёта итогов идёт заметно быстрее. После загрузки включите итоги обратно и при необходимости выполните ПересчитатьИтоги().

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

Ошибки при программной установке субконто

Ошибки здесь редко сопровождаются понятным сообщением. Чаще проводка просто записывается «не туда» или без аналитики, а выясняется это в оборотно-сальдовой ведомости.

Диагностика: выберите симптом

Ещё одна ловушка — значения субконто от старой проводки в копируемом документе. При копировании табличной части значения полей Субконто1–3 переносятся как есть. Если после этого сменить счёт, поля нужно очистить: «Связь по типу» ограничивает выбор в форме, но не очищает уже введённое значение.

Частые вопросы

Как прочитать значения субконто проводки в запросе?
Используйте виртуальную таблицу ДвиженияССубконто регистра: в ней есть поля СубконтоДт1, СубконтоДт2, СубконтоДт3 и такие же по кредиту, а также ВидСубконтоДт1 и далее.
Можно ли установить субконто на счёт, у которого нет такого вида?
Нет смысла: аналитика существует только для видов, заданных у счёта в плане счетов. Сначала добавьте вид на счёт в Конфигураторе или через пользовательскую настройку плана счетов, если она разрешена.
Как узнать, сколько субконто у счёта?
Через Счет.ВидыСубконто.Количество(). Порядок строк в этой табличной части совпадает с номером субконто в отчётах.
Можно ли получить больше трёх субконто на счёте?
В плане счетов Хозрасчетный типовой БП ограничение — три. Его задаёт свойство плана счетов «Максимальное количество субконто», и менять его в типовой конфигурации — значит снимать её с поддержки.
Чем ПредопределенноеЗначение лучше обращения через менеджер?
Функция работает и на клиенте, и на сервере. Обращение ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Контрагенты доступно только в серверном контексте.