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С и проверяется сразу, без установки платформы.
← Регистры накопления: сколько и где
→ Модули: какой когда выполняется