Измерения
Номенклатура, Склад — координаты, по которым считаем остаток. Платформа индексирует их.
Регистр накопления — это учётное ядро 1С: документы пишут в него движения, а платформа отдаёт по ним остатки и обороты. Главный неочевидный механизм — как платформа считает остаток на произвольную дату быстро, не суммируя все движения с начала времён. Ответ: таблица итогов хранит предрассчитанный остаток на границах периодов, а виртуальная таблица досчитывает только движения между ближайшей границей и нужной датой.
Регистр вида «остатки» состоит из измерений (по чему учитываем), ресурсов (что накапливаем) и хранит две таблицы под капотом:
Номенклатура, Склад — координаты, по которым считаем остаток. Платформа индексирует их.
Количество (и/или Сумма) — то, что накапливается. Складывается по движениям.
Каждая запись: период, измерения, ресурсы, ВидДвижения (Приход/Расход). Пишется документом при проведении.
Предрассчитанный остаток на границах периодов (по умолчанию помесячно). Кэш для быстрого чтения.
При проведении документ в обработчике ОбработкаПроведения формирует набор записей движений и просит платформу их записать:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// 1. Разрешаем запись набора движений по регистру
Движения.ОстаткиТоваров.Записывать = Истина;
Для Каждого ТекСтрока Из Товары Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрока.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрока.Количество;
КонецЦикла;
// 2. Запись произойдёт автоматически в конце транзакции проведения,
// платформа сама обновит таблицу итогов.
КонецПроцедуры
Покрути дату запроса. Внизу видно, как платформа собирает остаток: берёт итог на ближайшей границе периода и прибавляет движения после этой границы до даты запроса. Это и есть РегистрНакопления.ОстаткиТоваров.Остатки(&Период).
Зелёная линия — running-остаток по движениям. Пунктир — границы периодов, на которых лежат итоги. Точка — дата запроса.
Состояние ресурса на момент. «Сколько лежит на складе на дату».
Сумма движений за интервал. «Сколько пришло и ушло за месяц».
И начальный/конечный остаток, и обороты за период — в одной таблице.
Остатки(&Дата, Склад = &Склад)), а не в ГДЕ снаружи. В параметрах он уходит внутрь вложенного запроса и сужает данные до расчёта итогов; снаружи — платформа сначала посчитает всё, потом отфильтрует.Оперативное проведение должно гарантировать неотрицательный остаток даже при параллельных проведениях. Правильный порядок:
// 1. Записать движения (расход)
Движения.ОстаткиТоваров.Записывать = Истина;
// ... заполнение движений ...
Движения.ОстаткиТоваров.Записать();
// 2. Под управляемой блокировкой прочитать остатки ПОСЛЕ записи
Блокировка = Новый БлокировкаДанных;
ЭлБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиТоваров");
ЭлБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// 3. Проверить виртуальную таблицу Остатки на отрицательные
// если есть минус — Отказ = Истина
ГДЕ вместо параметров виртуальной таблицы. Платформа считает итоги по всему регистру, потом фильтрует — лишняя работа на больших данных.Остатки() без указания периода, когда нужен остаток на дату документа: вернётся остаток на текущий момент, а не на момент проведения.