1C-CodeОсновы 1С → Регистры сведений: значение на дату и разрез

Регистры сведений: значение на дату и разрез

Цена на дату, оклад сотрудника, курс валюты. Там, где значение не складывается, а заменяется.

Глава 20 из 36 · чтение примерно 22 минут

Регистр накопления отвечает «сколько всего». Регистр сведений — «какое значение действует». Цена товара на первое марта, оклад сотрудника с начала года, курс доллара на вчера.

Разница принципиальна: у накопления значения складываются, у сведений — заменяются. Два оклада за один месяц не дают сумму двух окладов: действует последний.

Главный метод

ПолучитьПоследнее(Дата, Отбор) — то, ради чего регистр сведений и существует. Он отдаёт запись, действовавшую на указанную дату: последнюю не позже неё.

Есть и ПолучитьПервое — он нужен реже, но на собеседовании про него спрашивают ровно затем, чтобы проверить, понимаете ли вы, что «первое» и «последнее» считаются относительно даты, а не относительно таблицы.

Из чего состоит регистр сведений

ЧастьВходит в ключЗачем
Периодда, если регистр периодическийс какого момента значение действует
Измерениедапо чему ищем: номенклатура, сотрудник, тип цены
Ресурснетсамо значение: цена, оклад, курс
Реквизитнетпояснение к записи: комментарий, автор, документ-основание

Ключ записи: период плюс измерения

Это главное устройство регистра сведений, и половина ошибок растёт из непонимания именно его.

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

Отсюда правило выбора: по чему ищем — измерение, что нашли — ресурс. Цену ищут по номенклатуре и типу цены, значит они измерения, а сама цена — ресурс.

И отсюда же частая беда. Сделали тип цены реквизитом, а не измерением — и розничная цена стала затирать оптовую: ключ у них теперь один и тот же, а платформа хранит по одной записи на ключ.

Запись с такими ключевыми полями уже существует

Самая частая ошибка при записи в регистр сведений. Значит, в наборе оказались две строки с одинаковым набором «период + измерения».

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

Лечение не «поймать исключение», а свернуть данные до записи: сгруппировать в запросе или свернуть таблицу значений. Тогда в наборе по строке на ключ, и ошибке неоткуда взяться.

Независимый и подчинённый регистратору

Ещё одно свойство, из-за которого ошибаются.

Независимый — записи заводят руками или кодом. Цены, курсы, оклады: никакой документ их не «делает».

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

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

Периодический или непериодический

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

Непериодический — просто таблица «ключ → значение». Единица измерения номенклатуры, ответственный за склад, настройка пользователя. История не нужна, значение одно.

Периодический — то же самое, но у каждой записи есть период, и значений может быть много: цена на январь, цена на март. Вопрос к нему — не «какое значение», а «какое значение на дату».

Записи с одинаковыми измерениями и разными периодами — это история: платформа умеет отдать значение, действовавшее на любой момент.

Признак, что нужен периодический: в задаче есть слова «действует с», «менялась», «на момент документа».

Периодичность: с точностью до чего

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

Поставили «В пределах дня» — две записи за одно число невозможны, вторая заменит первую. И наоборот: поставили «В пределах секунды», а значение меняется раз в месяц — ничего не сломается, но период придётся заполнять полной датой со временем, и записи «на март» будут отличаться друг от друга минутами.

Платформа при записи обрезает период до выбранной точности сама. Записали 15 марта в регистр с периодичностью «В пределах месяца» — в базе окажется 1 марта. Это не ошибка и не потеря: так устроено. Но человек, который ищет свою запись по 15 марта, её не находит и решает, что запись не прошла.

Какую периодичность выбирают

ПериодичностьКогда подходитЧто будет в поле «Период»
В пределах секундыкурс валюты, показания датчикадата со временем как есть
В пределах дняцена, скидка, ответственный00:00 того же дня
В пределах месяцаоклад, норма часов, лимит1-е число месяца
В пределах годаучётная политика, ставка налога1 января
По позиции регистраторазаписи пишет документмомент времени документа

Регистр сведений или реквизит справочника

Вопрос, который решают каждый раз заново, а правило простое: нужна ли история и нужен ли разрез.

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

Артикул лежит реквизитом — и правильно: он один, история его изменений никому не нужна. Регистр здесь был бы лишней сложностью на пустом месте.

Проверочный вопрос: «может ли у одного объекта быть два таких значения одновременно или в разное время?» Да — регистр. Нет — реквизит.

Срез последних

ВЫБРАТЬ
	Номенклатура,
	Цена
ИЗ
	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
		&НаДату,
		Номенклатура В (&Список))
ЧастьЧто означает
СрезПоследнихпо одной записи на каждый набор измерений — последняя НЕ ПОЗЖЕ указанной даты
&НаДатуобычно дата документа, а не текущая: цена берётся та, что действовала тогда
второй параметротбор — как и у регистров накопления, внутри скобок, а не в ГДЕ
СрезПервыхзеркальная таблица: первая запись НЕ РАНЬШЕ даты; нужна реже

Срез последних на пустую дату

Оставили дату пустой — платформа поймёт это как «на самое начало времён», и срез окажется пустым. Не «на сейчас», как ожидают.

Отсюда классическая картина: цены в регистре есть, а в документе подставляется ноль — потому что в параметр приехала пустая дата из незаполненного реквизита.

Проверка на заполненность даты перед запросом стоит одну строку и снимает этот вопрос навсегда.

Отбор — внутри скобок, а не в ГДЕ

Срез последних — не таблица, а виртуальная таблица: платформа строит её на лету, перебирая историю и оставляя по одной записи на ключ. И вот что важно: параметры в скобках она учитывает во время построения, а условие в ГДЕ применяет после.

Разница огромна. Отбор в скобках — платформа просматривает историю только нужных товаров. Условие в ГДЕ — платформа строит срез по всей номенклатуре базы, а потом выбрасывает лишнее. На демобазе это незаметно, на боевой — минуты.

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

Соединение со срезом: цена к каждой строке документа

ВЫБРАТЬ
	Строки.Номенклатура,
	Строки.Количество,
	ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
	Документ.РеализацияТоваров.Товары КАК Строки
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
			&НаДату, ТипЦены = &ТипЦены) КАК Цены
		ПО Строки.Номенклатура = Цены.Номенклатура
ГДЕ
	Строки.Ссылка = &Документ
ЧастьЧто означает
СрезПоследних(&НаДату, …)дата документа, а не текущая: цена нужна та, что действовала тогда
ТипЦены = &ТипЦеныотбор ВНУТРИ скобок — срез строится только по нужному типу цен
ЛЕВОЕ СОЕДИНЕНИЕтовар без цены обязан остаться в результате, иначе строка документа просто исчезнет
ЕСТЬNULL(Цены.Цена, 0)у товара без цены пришёл бы NULL, и любая сумма с ним стала бы NULL

Одно значение — без запроса

Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", Товар);
Отбор.Вставить("ТипЦены", ТипЦены);

Запись = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(НаДату, Отбор);
Цена = Запись.Цена;
ЧастьЧто означает
ПолучитьПоследнеето же, что срез последних, но на ОДИН набор измерений и без запроса
Отбор — Структураключи структуры должны совпадать с именами измерений, иначе отбор молча не сработает
Запись.Ценаесли записей нет, вернётся структура с ПУСТЫМИ ресурсами — не Неопределено. Цена будет 0
когда так можноодно значение для одного объекта. На список товаров — только запрос: иначе это запрос в цикле

Ведущее измерение и подчинение регистратору

Две настройки, которые объясняют, почему записи иногда исчезают сами.

Ведущее измерение — если объект, на который ссылается это измерение, удалят, запись регистра удалится вместе с ним. Так цены исчезают вместе с номенклатурой и не остаются мусором.

Подчинение регистратору — записи принадлежат документу, как движения регистра накопления: отменили проведение — записи ушли. Без регистратора записи «независимые», и править их можно прямо в списке.

Выбор простой: значение вводит человек — независимый регистр; значение появляется при проведении — подчинённый регистратору.

Запись набором, а не по одной

У независимого регистра сведений запись выглядит необычно: берут набор записей, ставят отбор по измерениям, заполняют и записывают.

Набор.Отбор.Номенклатура.Установить(Товар) — и Записать() заменит все записи с этим отбором на те, что в наборе. Пустой набор с тем же отбором — способ удалить.

Смысл в том, что запись и удаление — одна операция с понятным результатом, а не «сначала найди и удали, потом добавь».

На чём спотыкаются

Берут цену запросом без учёта даты. «ВЫБРАТЬ Цена ИЗ РегистрСведений.Цены ГДЕ Номенклатура = &Товар» вернёт все записи истории, и какая из них попадёт в результат — дело случая. Нужен срез последних на дату.

Путают СрезПоследних и просто таблицу регистра. Таблица — вся история; срез — то, что действует на момент. Второе почти всегда и требуется.

Заводят измерением то, что меняется. Измерение — часть ключа: изменив его, вы не поправите запись, а заведёте вторую. Старая продолжит действовать.

Упражнение 1. Оклад на дату

Функция ОкладНа(Сотрудник, Год, Месяц) должна вернуть оклад, действовавший на первое число этого месяца: последнюю запись регистра Оклады с периодом не позже этой даты. Записей нет — вернуть 0.

Решается в тренажёре: код запускается и проверяется сразу.

Упражнение 2. Цена, действовавшая тогда

Функция ЦенаНаНачалоМесяца(Товар, ТипЦены, Год, Месяц) должна вернуть цену из регистра ЦеныНоменклатуры, действовавшую на первое число этого месяца. У одного товара бывает несколько типов цен — нужен именно указанный. Записей нет — вернуть 0.

Решается в тренажёре: код запускается и проверяется сразу.

Короткий свод

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

Как закрепить

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

Открыть эту главу в тренажёре

Регистры накопления: сколько и где
Модули: какой когда выполняется