1C-Code → Основы 1С → Виртуальные таблицы регистров
Откуда берутся остатки и обороты, если их никто не хранит. Пять таблиц, параметры вместо ГДЕ и граница периода, из-за которой документ не видит сам себя.
Глава 27 из 36 · чтение примерно 26 минут
В главе про регистры накопления мы выяснили: остаток не хранится, он считается по движениям. Здесь — про то, чем его считают.
Писать «сумма приходов минус сумма расходов» руками не надо: у каждого регистра есть готовые виртуальные таблицы. Виртуальные — потому что их нет в базе: платформа собирает их на лету из движений и хранимых итогов.
| Таблица | Отвечает на вопрос | Когда берут |
|---|---|---|
| Остатки | сколько на дату | проверка перед списанием, отчёт по складу |
| Обороты | сколько за период | продажи за месяц, поступления за квартал |
| ОстаткиИОбороты | было, пришло, ушло, стало | оборотно-сальдовая ведомость |
| СрезПоследних | действующее значение на дату | цена, курс, оклад |
| СрезПервых | первое значение с даты | реже; про него спрашивают на собеседовании |
Это главное правило главы, и оно про скорость, а не про красоту.
У виртуальной таблицы есть параметры: период, условие отбора, разрезы. Их пишут в скобках сразу после имени таблицы. Платформа передаёт их вглубь — и читает только нужные итоги.
Если тот же отбор поставить в ГДЕ, платформа сначала соберёт таблицу целиком, а потом выбросит лишнее. Результат одинаковый, работа разная — на большой базе в десятки раз.
ВЫБРАТЬ
О.Номенклатура,
О.КоличествоОстаток
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(
&НаДату,
Склад = &Склад) КАК О
| Часть | Что означает |
|---|---|
.Остатки( | виртуальная таблица, а не таблица движений |
&НаДату | первый параметр — момент, на который считаем |
Склад = &Склад | отбор ВНУТРИ скобок: платформа возьмёт только нужное |
КоличествоОстаток | имя ресурса плюс «Остаток» — так называются поля этой таблицы |
Граница периода
Момент времени в 1С — это не только дата, но и позиция документа внутри неё. Отсюда два вида границы: включая и исключая.
Документ, который проверяет остаток при проведении, должен смотреть на момент перед собой — иначе он увидит собственное движение и решит, что товара хватает. Для этого берут Новый Граница(МоментВремени(), ВидГраницы.Исключая).
Ошибка здесь не падает и не видна глазами: она проявляется через месяц как «минус на складе».
Поля виртуальных таблиц называются не так, как ресурсы регистра, и это сбивает.
У ресурса Количество в таблице Остатки поле называется КоличествоОстаток. В таблице Обороты — КоличествоОборот, а если регистр остаточный, то ещё КоличествоПриход и КоличествоРасход.
В ОстаткиИОбороты их четыре: КоличествоНачальныйОстаток, КоличествоПриход, КоличествоРасход, КоличествоКонечныйОстаток.
На чём спотыкаются
Берут таблицу движений вместо виртуальной. ИЗ РегистрНакопления.ОстаткиТоваров — это все движения за всю историю, а не остаток. Работать будет, считать придётся руками, и на объёме это ляжет.
Забывают, что строки может не быть. Нет остатка — нет строки. Соединение с номенклатурой обязано быть левым, а результат обёрнут в ЕСТЬNULL.
Считают обороты через остатки на две даты. Разность остатков не равна обороту, если были и приход, и расход. Для оборота есть своя таблица.
Таблицы с таким именем в базе нет. Когда вы пишете РегистрНакопления.ОстаткиТоваров.Остатки, платформа подставляет вместо этого имени целый запрос: к таблице итогов, к таблице движений, со своими соединениями и группировками.
Отсюда два следствия, которые объясняют всё остальное поведение виртуальных таблиц.
Параметры попадают внутрь этого запроса, а ГДЕ — нет. Отбор в параметрах платформа вставит в подставляемый текст, и лишние строки не будут даже посчитаны. Условие в ГДЕ применяется к уже готовому результату.
Соединять виртуальные таблицы между собой — дорого. Каждая из них разворачивается в свой подзапрос, и база соединяет два вычисленных набора, ничего не зная о них заранее.
У таблиц оборотов есть параметр Периодичность, и он отвечает на вопрос «сколько строк я хочу получить».
Период — одна строка на весь интервал. Месяц, День, Регистратор — строка на каждый месяц, день, документ. Указали Регистратор на годовом отчёте — получите строку на каждый документ за год.
Полезное следствие: график «продажи по месяцам» не требует ни группировки, ни функций работы с датой. Достаточно попросить периодичность «Месяц» — остальное сделает платформа.
ВЫБРАТЬ
Период,
Номенклатура,
КоличествоОборот
ИЗ
РегистрНакопления.Продажи.Обороты(
&Начало, &Конец, Месяц, Номенклатура В (&Список))
| Часть | Что означает |
|---|---|
&Начало, &Конец | границы периода; пусто означает «с самого начала» и «по сейчас» |
Месяц | периодичность: строка на каждый месяц, где были движения |
Номенклатура В (…) | отбор — четвёртым параметром, а не в ГДЕ |
КоличествоОборот | у оборотов имена колонок кончаются на «Оборот», у остатков — на «Остаток» |
Пустой период — это не «за всё время»
Оставить границы пустыми можно, и на демо-базе это работает. На настоящей — означает «посчитай обороты за всю историю базы», то есть по всем движениям с самого начала.
Отчёт при этом не сломается — он просто будет думать минуту вместо секунды, и никто не поймёт почему.
Поэтому границы задают всегда, даже когда «нужно всё»: у бизнеса «всё» обычно означает текущий год.
Оборотно-сальдовая ведомость — самый частый отчёт в учёте, и делается он одной виртуальной таблицей.
ОстаткиИОбороты отдаёт сразу четыре набора колонок: НачальныйОстаток, Приход, Расход, КонечныйОстаток — с именем ресурса впереди: КоличествоНачальныйОстаток, КоличествоПриход и так далее.
Считать начальный остаток отдельным запросом и вычитать руками не надо — это ровно то, что делают, не зная про эту таблицу.
Почему в отчёте «приход есть, остатка нет»
Знакомая картина: в обороты товар попал, а в остатки — нет. Обычно виноват не регистр, а границы.
Остатки считаются НА момент, и момент этот — мгновение. Остаток «на 31 марта» без времени означает «на 31 марта в 00:00», то есть до всех документов этого дня.
Отсюда правило из главы про даты: конец периода приводят к КонецДня. А в паре «остатки и обороты» платформа делает это сама — ещё одна причина брать её, а не собирать из кусков.
Есть случай, когда правильнее взять сами движения, а не итоги: когда нужен регистратор — кто это сделал.
«Покажи, из каких документов сложился этот остаток» — вопрос не к остаткам, а к движениям. В виртуальной таблице регистратора нет вовсе: она уже всё сложила.
Второй случай — когда движений мало и они все нужны: расшифровка по одному документу, проверка своего же проведения.
Упражнение 2. Обороты, а не остатки
Напишите ВыручкаЗаВсё(): сумма по колонке ВыручкаОборот регистра Продажи через виртуальную таблицу .Обороты, без ограничения периода. Ожидается 26300.
Решается в тренажёре: код запускается и проверяется сразу.
Упражнение 1. Позиций на остатке
Функция ПозицийНаОстатке() должна вернуть, по скольким позициям номенклатуры есть остаток. Возьмите виртуальную таблицу РегистрНакопления.ОстаткиТоваров.Остатки и посчитайте строки — писать «приход минус расход» руками не нужно.
Решается в тренажёре: код запускается и проверяется сразу.
Виртуальные таблицы собираются на лету из движений и итогов. Остатки — на дату, Обороты — за период, ОстаткиИОбороты — всё сразу, СрезПоследних — действующее значение. Отбор передают параметром в скобках, не через ГДЕ. Момент времени бывает включающим и исключающим: документ при проведении смотрит на «перед собой». Имена полей — ресурс плюс «Остаток», «Оборот», «Приход», «Расход».
Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.