1C-Code → Основы 1С → Регистры накопления: сколько и где
Остатки и обороты не хранятся в документах — их считают по движениям. Главный механизм учёта и главный источник вопросов на собеседовании.
Глава 19 из 36 · чтение примерно 30 минут
Вопрос «сколько сейчас товара на складе» кажется простым, пока не задумаешься, где хранить ответ.
Первое, что приходит в голову, — держать поле «Остаток» в справочнике «Номенклатура» и менять его при каждом движении. Так делать нельзя, и понять почему — значит понять устройство всего учёта в 1С.
Почему поле «Остаток» — плохая идея
Нельзя посмотреть прошлое. «Сколько было на первое апреля» — ответа нет, поле хранит только «сейчас».
Нельзя разобраться, откуда взялось. Остаток разошёлся с фактом — и непонятно, какой документ виноват.
Нельзя перепровести. Исправили накладную задним числом — а поле уже дважды изменено.
Двое одновременно ломают его гарантированно. Прочитали оба по 10, оба записали 7 — вместо 4.
1С хранит не остаток, а историю изменений. Каждое проведение документа пишет строки в регистр накопления: что, куда, сколько и в какую сторону.
Остаток — это не хранимое значение, а результат подсчёта: сумма приходов минус сумма расходов на нужную дату. Платформа считает его сама и хранит промежуточные итоги, чтобы не пересчитывать всю историю каждый раз.
Отсюда все свойства, за которые этот механизм и любят: видно прошлое на любую дату, видно, каким документом сделано каждое движение, и перепроведение просто переписывает строки этого документа.
| Часть | Что это | Пример |
|---|---|---|
| Период | дата движения | 05.01.2024 |
| Регистратор | документ, который его сделал | Поступление 1 |
| ВидДвижения | приход или расход | Приход |
| Измерения | разрезы: в каком сочетании считаем | Номенклатура, Склад |
| Ресурсы | то, что складывается | Количество, Сумма |
| Реквизиты | справочно, в итоги не идут | Комментарий |
Измерение, ресурс, реквизит
Различие простое и его спрашивают почти всегда.
Ресурс складывается. Количество, сумма. Это то, что вы хотите видеть в итоге.
Измерение — разрез, по которому складывается. Остаток «по товару и складу» означает: измерения Номенклатура и Склад.
Реквизит не участвует в итогах вообще. Он для того, чтобы потом посмотреть глазами: комментарий, ответственный.
Ошибка «сделал измерением то, что должно быть реквизитом» дорогая: каждое лишнее измерение размножает строки итогов и замедляет всё.
Остатки — когда нужен ответ «сколько сейчас». Товары на складах, деньги в кассе, долги контрагентов. У движений есть приход и расход.
Обороты — когда «сколько за период», а остаток бессмыслен. Продажи, выручка, количество звонков. Расхода у таких движений нет: только накопление.
Выбирают по вопросу, на который отвечает регистр. «Сколько продали в марте» — обороты. «Сколько лежит на складе» — остатки. Сделать остаточный регистр там, где нужны только обороты, значит заставить платформу считать и хранить итоги, которые никто не спросит.
К регистру обращаются не напрямую, а через виртуальные таблицы: платформа собирает их из движений и хранимых итогов.
Остатки — сколько на дату. Обороты — сколько за период. ОстаткиИОбороты — начальный остаток, приход, расход, конечный остаток одной таблицей.
Главное правило: отбор передают параметром виртуальной таблицы, а не условием ГДЕ. Разница не в красоте — в количестве прочитанных строк. С параметром платформа возьмёт нужные итоги; с условием ГДЕ — сначала соберёт всё, потом выбросит лишнее.
На чём спотыкаются
Фильтруют через ГДЕ вместо параметров. Работает одинаково, отличается в десятки раз. Это первое, что смотрят, когда «отчёт тормозит».
Забывают, что остатка может не быть. Товара на складе не было — строки в виртуальной таблице тоже нет, а не ноль. Соединение обязано быть левым, а результат — обёрнут в ЕСТЬNULL.
Считают остаток на дату документа, а не на момент. Граница периода бывает включающей и исключающей; из-за этого документ то видит собственное движение, то нет.
Пишут движения в обход регистра. Прямых записей в таблицу итогов не бывает: только движения документа. Всё остальное — способ получить неверные итоги.
Регистр даёт не одну таблицу, а несколько. Обращаться к ним по-разному — и выбор между ними определяет, будет отчёт работать секунду или минуту.
| Таблица | Что отдаёт | Когда берут |
|---|---|---|
| РегистрНакопления.Имя | сами движения, по одной строке на запись | когда нужно «кто это сделал» — ссылка на регистратор |
| ….Остатки(Период, Отбор) | остаток на момент | «сколько сейчас» и «сколько было на дату» |
| ….Обороты(Начало, Конец, Период, Отбор) | приход и расход за период | «сколько продали за месяц» |
| ….ОстаткиИОбороты(…) | остаток на начало, обороты, остаток на конец | оборотно-сальдовая ведомость — всё сразу |
| ….Обороты у оборотного регистра | только обороты | у регистра оборотов остатков нет вовсе |
ВЫБРАТЬ
Номенклатура,
КоличествоОстаток
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(
&НаДату,
Склад = &Склад И Номенклатура В (&СписокТоваров))
| Часть | Что означает |
|---|---|
&НаДату | первый параметр — момент; пусто означает «на сейчас» |
второй параметр | отбор ВНУТРИ виртуальной таблицы — не путать с ГДЕ |
КоличествоОстаток | имя ресурса плюс «Остаток»: так платформа называет колонки итогов |
без соединения | остаток уже посчитан — складывать движения руками не надо |
Отбор в параметрах, а не в ГДЕ
Это главная ошибка производительности во всех отчётах по регистрам, и спрашивают о ней на собеседовании постоянно.
ГДЕ Склад = &Склад после виртуальной таблицы работает так: платформа считает остатки по всем складам и всей номенклатуре, а потом выбрасывает лишнее. Отбор в параметрах — считает только по нужному.
Разница на демо-базе незаметна. На базе с миллионом движений это разница между «секунда» и «полторы минуты», и находят её обычно замером, когда отчётом уже пользуются.
Упражнение 2. Остаток товара на складе
Напишите Остаток(Товар, Склад): количество на складе через виртуальную таблицу .Остатки, отбор — в параметрах, а не в ГДЕ. Проверка спрашивает про Гвозди 100мм на Основном складе. Если ничего не нашлось, вернуть 0, а не NULL.
Решается в тренажёре: код запускается и проверяется сразу.
Регистр накопления не запрещает отрицательный остаток. Он честно складывает движения: пришло 100, ушло 300 — останется минус 200.
Запрет — это не свойство регистра, а ваш код в проведении. Нет проверки — будет минус, и никто не заметит, пока кладовщик не придёт с вопросом.
В демо-базе такой товар есть. Следующее упражнение его находит — и это ровно тот запрос, которым начинают разбирательство на настоящей базе.
Упражнение 3. Найти минус на складе
Напишите ВМинусе(): верните наименование первого товара, у которого остаток отрицательный. Если таких нет — пустую строку.
Решается в тренажёре: код запускается и проверяется сразу.
Если остаток складывается из движений, то на миллионе движений он должен считаться минуту. А считается мгновенно — потому что платформа хранит итоги.
У регистра есть служебные таблицы итогов: остаток на конец каждого месяца и «текущие итоги» на последний момент. Запрос к виртуальной таблице берёт ближайший готовый итог и досчитывает по движениям только остаток пути.
Из этого следуют две практические вещи, про которые спрашивают:
Запрос на середину месяца медленнее, чем на границу: досчитывать приходится больше.
Итоги можно пересчитать и отключить. При массовой загрузке документов итоги отключают, загружают и пересчитывают один раз — иначе каждая запись обновляет их заново.
Регистратор и набор записей
У каждой записи регистра есть регистратор — ссылка на документ, который её сделал. Движения без регистратора в регистре накопления невозможны.
Отсюда способ работы с движениями программно: их не добавляют по одной, а берут набор записей с отбором по регистратору, меняют и записывают целиком. НаборЗаписей.Записать() заменяет ВСЕ движения этого документа разом.
Это и ответ на вопрос «как удалить движения»: записать пустой набор с тем же отбором.
Период движения — дата документа, а не «сейчас»
В движении есть поле Период, и заполнять его надо датой документа. Написали ТекущаяДата() — и документ от первого марта лёг в остатки сегодняшним числом.
Проявляется это не сразу: «сейчас» отчёты покажут верно. Сломается отчёт на дату — и разбираться с ним будут через полгода, когда автора уже не спросить.
Начинающий пытается сделать один регистр «на всё»: и количество, и сумма, и партии, и резерв.
Правило простое: один регистр отвечает на один вопрос. Остатки товаров, взаиморасчёты с контрагентами, товары в резерве — три разных регистра, потому что это три разных вопроса с разными измерениями.
Признак, что регистров надо два: часть измерений у половины движений пустая. Если «Договор» заполняется только для взаиморасчётов, а для товарных движений пуст — значит, в один регистр свели две сущности.
Почему нельзя просто добавить измерение
Соблазн есть: добавить в существующий регистр ещё одно измерение — «и заказ тоже». Цена этого решения не видна сразу.
Каждое измерение дробит остатки. Был остаток «товар × склад», стал «товар × склад × заказ» — и строк в итогах стало в сто раз больше, а «сколько всего на складе» теперь требует группировки.
Поэтому измерения добавляют, только когда по ним действительно нужен ОСТАТОК. Если нужна просто справка «из какого заказа» — это реквизит, а не измерение.
Упражнение 1. Остаток по движениям
Функция ОстатокТовара(Товар, Склад) должна пройти по движениям регистра ОстаткиТоваров и вернуть остаток: приходы плюс, расходы минус. Считайте только строки с нужным товаром и складом.
Решается в тренажёре: код запускается и проверяется сразу.
Остаток не хранят — его считают по движениям. Движение состоит из периода, регистратора, вида, измерений и ресурсов: ресурс складывается, измерение — разрез, реквизит в итоги не идёт. Регистры бывают остаточные и оборотные, и выбор между ними определяется вопросом, на который они отвечают. К регистру ходят через виртуальные таблицы, отбор передают параметром, а не через ГДЕ. Отсутствие остатка — это отсутствие строки, а не ноль.
Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.
← Документы и проведение: что случилось
→ Регистры сведений: значение на дату и разрез