Срез последних 1С — виртуальная таблица СрезПоследних периодического регистра сведений: по каждой комбинации измерений она возвращает самую позднюю запись не позже заданной даты. Так получают действующую цену, курс валюты на дату документа, любое значение «на момент». СрезПервых — её пара: самая ранняя запись не раньше даты. Ниже — параметры Период и Условие, готовые функции для цен и курсов и главная ошибка: почему условие в ГДЕ даёт неправильный срез. Имена регистров сверены по УТ 11.4 и КА 2.5, приём одинаков в любой конфигурации на платформе 8.3.
Срез нужен везде, где значение меняется со временем, а прочитать его надо на конкретный момент: цена на дату документа, прайс-лист в отчёте, выгрузка цен на сайт и маркетплейсы, пересчёт валюты по курсу.
ЦеныНоменклатуры: измерения Номенклатура, Характеристика, ВидЦены, ресурсы Цена, Упаковка, Валюта. Периодичность — секунда, регистр подчинён регистратору: в УТ 11.4 документ «Установка цен номенклатуры» в статусе «Согласован» пишет записи с периодом, равным дате документа.КурсыВалют из БСП: измерение Валюта, ресурсы Курс и Кратность, периодичность — день.Записи регистра видно через «Все функции» → «Регистры сведений» → «Цены номенклатуры». В КА 2.5 при «Ценообразовании 2.5» (константа ИспользуетсяЦенообразование25) цены могут лежать в регистре ЦеныНоменклатуры25 с другими измерениями — проверьте, какой регистр заполнен в вашей базе.
Синтаксис: РегистрСведений.ИмяРегистра.СрезПоследних(Период, Условие). Оба параметра необязательные, пропущенный первый обозначают запятой: СрезПоследних(, ВидЦены = &ВидЦены). В конструкторе запроса их задают в окне «Параметры виртуальной таблицы». Основы языка — в статье о синтаксисе запросов.
| Параметр | Что передавать | Если не задан |
|---|---|---|
| Период | Дата, МоментВремени или Граница; дата берётся включительно | Самые поздние записи без ограничения по дате, включая будущие |
| Условие | Условие по полям регистра: измерениям, ресурсам, реквизитам, Регистратор — без псевдонима таблицы | Срез строится по всем записям |
Главное из синтакс-помощника: условие применяется к исходным записям, а не к уже отобранным. Платформа сначала оставляет записи, подходящие под Период и Условие, потом по каждой комбинации измерений берёт самую позднюю. Незафиксированные измерения размножают строки: без отбора по ВидЦены срез вернёт строку на каждый вид цен товара. В 1С 8.3 срез последних берёт только активные записи, а в режиме совместимости 8.3.17 и ниже — и неактивные (у УТ 11.4.14 режим 8.3.18, у КА 2.5.20 — 8.3.24).
Пример — цены на дату по списку товаров в валюте цены и в рублях. Функция серверная: Запрос на тонком клиенте недоступен.
// Цены вида цен на дату: в валюте цены и в рублях по курсу на ту же дату.
// Серверный код: общий модуль, модуль менеджера или модуль объекта обработки.
// Регистры ЦеныНоменклатуры и КурсыВалют - УТ 11.4 и КА 2.5.
// СписокНоменклатуры - массив или список значений со ссылками на номенклатуру.
Функция ЦеныНаДату(ВидЦены, Дата, СписокНоменклатуры) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Характеристика КАК Характеристика,
| Цены.Упаковка КАК Упаковка,
| Цены.Цена КАК Цена,
| Цены.Валюта КАК Валюта,
| Цены.Период КАК ЦенаДействуетС,
| ВЫБОР
| КОГДА ЕСТЬNULL(Курсы.Кратность, 0) = 0
| ТОГДА 0
| ИНАЧЕ ВЫРАЗИТЬ(Цены.Цена * Курсы.Курс / Курсы.Кратность КАК ЧИСЛО(15, 2))
| КОНЕЦ КАК ЦенаВРублях
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
| &Дата,
| ВидЦены = &ВидЦены
| И Номенклатура В (&СписокНоменклатуры)) КАК Цены
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&Дата) КАК Курсы
| ПО Цены.Валюта = Курсы.Валюта";
// Цены в регистре заданы с точностью до секунды, курсы - по дням.
// Для "цены на день" передайте КонецДня(Дата), для цены документа - его дату.
Запрос.УстановитьПараметр("Дата", Дата);
Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);
// Товара без цены этого вида в результате не будет: срез возвращает только
// существующие записи. Нужен полный список - соединяйте срез с товарами.
Возврат Запрос.Выполнить().Выгрузить();
КонецФункцииСрезПоследних(&Дата, ВидЦены = &ВидЦены И Номенклатура В (&СписокНоменклатуры)) — дата и отборы в параметрах: срез строится только по нужным записям. Вместо списка подойдёт подзапрос к временной таблице, как в общем модуле Ценообразование УТ 11.4.КурсыВалют.СрезПоследних(&Дата) — второй срез на ту же дату, без условия: валют в базе немного.ЛЕВОЕ СОЕДИНЕНИЕ и ЕСТЬNULL(Курсы.Кратность, 0) = 0 — цена без курса не теряется и не вызывает деления на ноль, а получает 0 в колонке ЦенаВРублях.Цены.Цена * Курсы.Курс / Курсы.Кратность — курс в БСП указан за Кратность единиц валюты. Для рубля есть запись 1/1: БСП добавляет её на 01.01.1980 при записи валюты. Цена в УТ 11.4 задана за Упаковку — для базовой единицы учитывайте коэффициент.Виртуальная таблица вычисляется раньше, чем срабатывает ГДЕ. Условие в параметрах решает, из каких записей выбирать последнюю, условие в ГДЕ — какие из найденных последних записей оставить. Для измерений результат совпадает, для периода и ресурсов — нет. Пример: по розничному виду цен кофе стоит 500 с 01.03 и 550 с 20.03, чай — 300 с 05.03; нужны цены на 10.03.
| Запрос | Кофе | Чай |
|---|---|---|
СрезПоследних(&Дата, ВидЦены = &ВидЦены) | 500 | 300 |
СрезПоследних без параметров + ГДЕ Цены.Период <= &Дата | строки нет | 300 |
Во втором варианте срез взят без даты: последняя запись кофе — от 20.03, и ГДЕ её отбросил. Ошибки нет, товар молча пропадает. Пакет ниже показывает этот случай и условие на ресурс — выполните его в консоли запросов с параметрами Дата и ВидЦены.
// Пакет для консоли запросов. Параметры: Дата, ВидЦены.
// Запрос 1. НЕПРАВИЛЬНО: срез без даты (самые поздние записи вообще), дата - в ГДЕ.
// Товар, цену которого меняли после &Дата, пропадёт из результата.
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Характеристика КАК Характеристика,
Цены.Цена КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
ГДЕ
Цены.Период <= &Дата
И Цены.ВидЦены = &ВидЦены
;
// Запрос 2. ПРАВИЛЬНО: дата и вид цен - в параметрах виртуальной таблицы.
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Характеристика КАК Характеристика,
Цены.Цена КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ВидЦены) КАК Цены
;
// Запрос 3. Ресурс в ГДЕ: товары, у которых ПОСЛЕДНЯЯ цена больше нуля.
// Если цену обнулили, товара в выборке не будет.
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Характеристика КАК Характеристика,
Цены.Цена КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ВидЦены) КАК Цены
ГДЕ
Цены.Цена > 0
;
// Запрос 4. Ресурс в Условии: последняя НЕНУЛЕВАЯ цена.
// Нулевые записи отбрасываются до среза - вернётся прежняя цена и её дата.
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Характеристика КАК Характеристика,
Цены.Цена КАК Цена,
Цены.Период КАК ЦенаОт
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ВидЦены И Цена > 0) КАК ЦеныГДЕ — строки те же, но срез строится по всему регистру и лишь потом фильтруется. Стандарт 1С «Обращения к виртуальным таблицам» требует передавать такие условия в параметры.ГДЕ Цены.Цена > 0: «последняя цена, если она не нулевая» — товар с обнулённой ценой выпадает. Цена > 0 в Условии: «последняя ненулевая цена» — нулевые записи отброшены до среза, и вернётся старая цена. Для прайс-листа обычно нужен первый вариант.Регистратор <> &Документ в Условии даёт срез на дату документа без его собственных записей — старую цену для печатной формы установки цен. В ГДЕ это условие убрало бы строку целиком.У ЦеныНоменклатуры и КурсыВалют в УТ 11.4 и КА 2.5 включено «Разрешить итоги: срез последних» — отдельная таблица последних значений для быстрого среза на текущий момент. По синтакс-помощнику она не используется, если в Условии есть поля кроме измерений и разделителей.
Чтобы получить срез последних на дату в запросе 1С, дату передают первым параметром. Нюансы такие.
КурсыВалют она дневная, время не влияет. У ЦеныНоменклатуры — секунда: цену, установленную в 18:00, документ от 10:00 того же дня не увидит. Для «цены на день» передают конец дня — так в УТ 11.4 берутся цены поставщиков: КОНЕЦПЕРИОДА(&Дата, ДЕНЬ). Функции дат — в статье о дате в запросе 1С.ТекущаяДатаСеанса().Новый Граница(Дата, ВидГраницы.Исключая) — срез строго до даты. Конструктор Граница на тонком клиенте недоступен, параметр собирают на сервере.Пример с границей: какие цены изменились за день.
// Какие цены вида цен изменились за день: старая и новая цена.
// Срез на конец дня сравнивается со срезом строго до начала дня.
// Серверный код; регистр ЦеныНоменклатуры - УТ 11.4 и КА 2.5.
Функция ИзмененияЦенЗаДень(ВидЦены, ДатаДня) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Новые.Номенклатура КАК Номенклатура,
| Новые.Характеристика КАК Характеристика,
| ЕСТЬNULL(Старые.Цена, 0) КАК СтараяЦена,
| Новые.Цена КАК НоваяЦена,
| Новые.Период КАК ВремяИзменения
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&КонецДня, ВидЦены = &ВидЦены) КАК Новые
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДоНачалаДня, ВидЦены = &ВидЦены) КАК Старые
| ПО Новые.Номенклатура = Старые.Номенклатура
| И Новые.Характеристика = Старые.Характеристика
|ГДЕ
| Новые.Период >= &НачалоДня
| И (Старые.Цена ЕСТЬ NULL
| ИЛИ Старые.Цена <> Новые.Цена
| ИЛИ Старые.Упаковка <> Новые.Упаковка)
|
|УПОРЯДОЧИТЬ ПО
| ВремяИзменения";
Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
Запрос.УстановитьПараметр("НачалоДня", НачалоДня(ДатаДня));
Запрос.УстановитьПараметр("КонецДня", КонецДня(ДатаДня));
// Исключая: записи ровно на начало дня в "старый" срез не попадут.
// Конструктор Граница на тонком клиенте недоступен - только сервер.
Запрос.УстановитьПараметр("ДоНачалаДня", Новый Граница(НачалоДня(ДатаДня), ВидГраницы.Исключая));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункцииУсловие на Период здесь стоит в ГДЕ намеренно: нужен не срез на начало дня, а отбор готового среза — «последняя цена установлена сегодня».
СрезПервых(Период, Условие) устроен зеркально: по каждой комбинации измерений — самая ранняя запись с периодом не раньше даты (включительно), без даты — самая первая запись за всю историю. Типичных применений два: когда значение появилось впервые и какое изменение запланировано следующим.
// СрезПервых без даты: самая ранняя запись по каждому товару за всю историю.
// Новинки прайса - товары, которым ПЕРВУЮ цену вида назначили не раньше даты.
// Дата стоит в ГДЕ намеренно: СрезПервых(&НачалоПериода, ...) вернул бы
// и старые товары, у которых цена просто менялась после этой даты.
Функция НовинкиПрайса(ВидЦены, НачалоПериода) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПервыеЦены.Номенклатура КАК Номенклатура,
| ПервыеЦены.Характеристика КАК Характеристика,
| ПервыеЦены.Период КАК ПерваяЦенаОт,
| ПервыеЦены.Цена КАК ПерваяЦена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПервых(, ВидЦены = &ВидЦены) КАК ПервыеЦены
|ГДЕ
| ПервыеЦены.Период >= &НачалоПериода
|
|УПОРЯДОЧИТЬ ПО
| ПерваяЦенаОт УБЫВ";
Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
// СрезПервых после даты: ближайшее запланированное изменение цены
// и цена, которая действует сейчас. Исключая - запись ровно на момент
// Дата уже действует и в "будущие" не попадает.
Функция ЗапланированныеЦены(ВидЦены, Дата) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Будущие.Номенклатура КАК Номенклатура,
| Будущие.Характеристика КАК Характеристика,
| ЕСТЬNULL(Текущие.Цена, 0) КАК ТекущаяЦена,
| Будущие.Цена КАК НоваяЦена,
| Будущие.Период КАК ДействуетС
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПервых(&ПослеДаты, ВидЦены = &ВидЦены) КАК Будущие
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ВидЦены = &ВидЦены) КАК Текущие
| ПО Будущие.Номенклатура = Текущие.Номенклатура
| И Будущие.Характеристика = Текущие.Характеристика
|
|УПОРЯДОЧИТЬ ПО
| ДействуетС";
Запрос.УстановитьПараметр("ВидЦены", ВидЦены);
Запрос.УстановитьПараметр("Дата", Дата);
Запрос.УстановитьПараметр("ПослеДаты", Новый Граница(Дата, ВидГраницы.Исключая));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункцииДата в НовинкиПрайса стоит в ГДЕ по той же логике: параметры выбирают записи до среза, ГДЕ фильтрует готовый результат. СрезПервых(&НачалоПериода, ...) вернул бы и старые товары, у которых цена просто менялась.
Для одного курса или одной цены запрос не обязателен: у менеджера регистра срез последних есть в виде методов. СрезПоследних(Дата, Отбор) возвращает таблицу значений, ПолучитьПоследнее(Дата, Отбор) — структуру ресурсов одной записи; для первых записей есть СрезПервых и ПолучитьПервое. Отбор — структура с ключами по именам измерений. Методы работают на сервере, в толстом клиенте и во внешнем соединении. На тонком клиенте свойства РегистрыСведений нет, поэтому из формы срез берут серверной функцией.
// Модуль формы документа или обработки с реквизитами Валюта и Дата.
&НаКлиенте
Процедура ПоказатьКурс(Команда)
ДанныеКурса = КурсНаДату(Объект.Валюта, Объект.Дата);
Если ДанныеКурса.Курс = 0 Тогда
ПоказатьПредупреждение(, "Курс валюты на дату не найден.");
Иначе
ПоказатьПредупреждение(, СтрШаблон("Курс: %1 руб. за %2 ед. валюты",
ДанныеКурса.Курс, ДанныеКурса.Кратность));
КонецЕсли;
КонецПроцедуры
// На тонком клиенте нет свойства РегистрыСведений: срез берём на сервере.
// Структура с числами без проблем возвращается на клиент.
&НаСервереБезКонтекста
Функция КурсНаДату(Валюта, Дата)
// Дата не заполнена - берём текущую дату сеанса.
ДатаСреза = ?(ЗначениеЗаполнено(Дата), Дата, ТекущаяДатаСеанса());
// ПолучитьПоследнее возвращает структуру ресурсов Курс и Кратность.
// Записи на дату нет - ресурсы пустые: Курс = 0, Кратность = 0.
Отбор = Новый Структура("Валюта", Валюта);
Возврат РегистрыСведений.КурсыВалют.ПолучитьПоследнее(ДатаСреза, Отбор);
КонецФункцииНет записи на дату — ПолучитьПоследнее вернёт пустые ресурсы (Курс = 0), так проверяет результат и сама БСП. Курс 1 при кратности 1 у доллара — курсы до этой даты не загружались вовсе, и срез нашёл служебную запись от 01.01.1980. В БСП есть обёртка РаботаСКурсамиВалют.ПолучитьКурсВалюты(Валюта, ДатаКурса) — в УТ 11.4 и КА 2.5. Для списка валют или товаров методы в цикле не годятся: каждый вызов — отдельное обращение к базе.
ГДЕ вместо параметра — пропадают товары с новой ценой после даты.ВидЦены срез даёт строку на каждый вид цен.Цены.ВидЦены) или поле другой таблицы; доступны поля самого регистра, параметры и вложенные запросы.УстановитьПараметр.НачалоДня при периодичности «секунда».&НаСервереБезКонтекста.ПЕРВЫЕ 1 с УПОРЯДОЧИТЬ ПО Период УБЫВ вернёт одну запись на весь запрос — это для одного товара. Срез даёт запись на каждую комбинацию измерений.
В наборе данных «Запрос» срез пишут так же, параметр &Дата СКД добавит в параметры отчёта сама. Если дата своя у каждой строки — см. статью о срезе последних в СКД.
Нет, срезы есть только у периодических регистров сведений. У регистра накопления значения на дату дают таблицы Остатки и ОстаткиИОбороты — см. статью об остатках в запросе.
Причин обычно две. Время: период записи равен дате документа с точностью до секунды, и срез на более ранний момент её не видит. Статус: в УТ 11.4 «Установка цен номенклатуры» пишет цены в регистр только в статусе «Согласован».
Если цены на дату расходятся с документами, срез тормозит на большом регистре или нужен прайс с пересчётом валют и упаковок, задачу быстрее решит программист, который пишет такие запросы каждый день. Профильная услуга — отчёты и обработки 1С. Разместите задачу — оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.