Регистр сведений 1С хранит данные, которые определяются сочетанием признаков: цена товара у поставщика на дату, курс валюты на день, штрихкод товара. Создать его в конфигураторе несложно, а ошибки проектирования — лишнее измерение, не та периодичность или режим записи — всплывают, когда данных уже много. Ниже — как спроектировать регистр сведений в 1С 8.3, что класть в измерения, ресурсы и реквизиты и как писать и читать записи кодом.
Регистр сведений — таблица с составным ключом: одному сочетанию «поставщик + товар» на дату соответствует ровно одна запись. Данные вида «значение X для сочетания A и B на дату D» — его случай.
| Задача | Что выбрать | Почему |
|---|---|---|
| Цена товара у поставщика, меняется со временем | Периодический регистр сведений | История по датам, значение на любую дату |
| Курс валюты на день | Периодический регистр сведений | Типовой пример — регистр КурсыВалют в конфигурациях на БСП |
| Штрихкоды товаров, сопоставление кодов внешней системы и ссылок | Непериодический регистр сведений | История не нужна, ключ — сочетание полей |
| Контрагенты, договоры, склады | Справочник | Нужна собственная ссылка, табличные части, иерархия |
| Остатки товаров, взаиморасчёты, продажи за период | Регистр накопления | Значения складываются, нужны остатки и обороты |
Главный признак регистра накопления — числа, которые имеет смысл суммировать. Цену складывать бессмысленно. Цены, курсы и ставки живут в регистре сведений, остатки и обороты — в регистре накопления. Второй признак — ссылка: на запись регистра сведений нельзя сослаться из реквизита документа. Если на данные нужно ссылаться, это справочник.
ЦеныПоставщиков) и синоним — его увидит пользователь.В типовой конфигурации на поддержке новый регистр лучше добавить в расширение: собственные регистры сведений в расширениях можно создавать с платформы 8.3.11, а типовая обновляется без ручного сравнения. Если у товара нужна пара дополнительных полей без истории, регистр не нужен — хватит дополнительных реквизитов и сведений.
Периодичность отвечает на вопрос, нужна ли история. У непериодического регистра на сочетание измерений хранится одно значение: записали новое — старое потеряно. У периодического появляется стандартное поле Период, и на каждую дату хранится своё значение.
При записи платформа приводит период к началу интервала: у регистра «в пределах дня» запись с периодом 14:35 сохранится на 00:00:00. Два изменения цены за день в таком регистре невозможны — второе заменит первое. Если цена меняется несколько раз в день, выбирайте «в пределах секунды», но тогда внимательнее с датой среза.
Для периодического регистра в запросах доступны виртуальные таблицы СрезПоследних (последнее значение на дату включительно) и СрезПервых (первое значение не раньше даты). На больших регистрах срез на текущий момент ускоряют свойства «Разрешить итоги среза последних» и «Разрешить итоги среза первых»: платформа хранит готовые итоги и не ищет последнюю запись по всей истории.
Независимый регистр пишут откуда угодно: форма записи, обработка, регламентное задание, загрузка из файла. Уникальность контролируется по периоду (если он есть) и всем измерениям.
Подчинённый регистратору регистр заполняют документы при проведении — так же, как регистр накопления. У записей появляются поля Регистратор, НомерСтроки и Активность, при отмене проведения записи удаляются. Документ становится регистратором, когда регистр отмечен на закладке «Движения» в окне редактирования документа. Проведение выглядит так:
// Модуль объекта документа УстановкаЦенПоставщика
// (вариант, когда регистр ЦеныПоставщиков подчинён регистратору)
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения.ЦеныПоставщиков.Записывать = Истина;
// Номенклатура -> номер строки, где товар встретился впервые
НомераСтрок = Новый Соответствие;
Для Каждого СтрокаТоваров Из Товары Цикл
ПерваяСтрока = НомераСтрок[СтрокаТоваров.Номенклатура];
Если ПерваяСтрока <> Неопределено Тогда
// Вторая запись с тем же ключом сорвёт запись движений
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Строка %1: товар %2 уже указан в строке %3",
СтрокаТоваров.НомерСтроки, СтрокаТоваров.Номенклатура, ПерваяСтрока);
Сообщение.Сообщить();
Отказ = Истина;
Продолжить;
КонецЕсли;
НомераСтрок.Вставить(СтрокаТоваров.Номенклатура, СтрокаТоваров.НомерСтроки);
Движение = Движения.ЦеныПоставщиков.Добавить();
Движение.Период = Дата;
Движение.Поставщик = Контрагент;
Движение.Номенклатура = СтрокаТоваров.Номенклатура;
Движение.Цена = СтрокаТоваров.Цена;
КонецЦикла;
КонецПроцедуры
Соответствие НомераСтрок ловит товар, указанный в табличной части дважды: обе строки дали бы записи с одинаковым ключом, и проведение упало бы с ошибкой. Ту же проверку можно перенести в ОбработкаПроверкиЗаполнения. Функция СтрШаблон есть в платформе с 8.3.6.
Подчинённый режим нужен, когда у изменения должен быть первичный документ: видно, кто и когда поменял цену. Независимый — когда данные справочные: курсы валют, штрихкоды, настройки.
У подчинённого регистра в ключ записи входит регистратор, поэтому два документа могут записать одинаковые период и измерения — и значение на эту дату становится неоднозначным. Спасает периодичность «Позиция регистратора» или проверка при проведении.
Измерения — ключ записи: по ним ищут и отбирают, их сочетание вместе с периодом уникально. Ресурсы — хранимые значения: цена, курс, процент. Реквизиты — сопутствующая информация, которая в ключ не входит: комментарий, источник данных, кто внёс.
Правило выбора: если записи с разными значениями поля должны существовать одновременно — это измерение, если новое значение заменяет старое — ресурс или реквизит. Розничная и оптовая цена товара действуют одновременно, значит вид цены — измерение. Между ресурсом и реквизитом разница скорее смысловая: ресурсы — то, ради чего регистр создан, и именно их возвращает метод менеджера ПолучитьПоследнее.
У измерений есть свойства, которые стоит настроить сразу:
| Свойство | Что делает | Когда включать |
|---|---|---|
| Ведущее | Запись нужна, пока существует объект: при удалении объекта записи с ним удаляются, в форме объекта появляется переход к записям регистра | Измерение ссылочного типа, а данные без объекта не нужны: цены товара, штрихкоды |
| Основной отбор | По этим измерениям (и по периоду, если у регистра включён «Основной отбор по периоду») регистрируются изменения для планов обмена — это минимальная порция данных при обмене | Измерения, по которым записи логично выгружать и отбирать порциями |
| Запрет незаполненных значений | Не даёт записать запись с пустым измерением | Почти всегда для ссылочных измерений |
| Индексировать | Создаёт дополнительный индекс по полю | Для измерений, по которым часто отбирают, но которые стоят не первыми |
Порядок измерений тоже важен: от него зависит структура основного индекса таблицы в СУБД. Первым ставьте то измерение, по которому отбирают чаще всего.
| Элемент | Настройка |
|---|---|
| Периодичность | В пределах дня |
| Режим записи | Независимый, «Основной отбор по периоду» включён |
Измерение Поставщик | СправочникСсылка.Контрагенты; ведущее, основной отбор, запрет незаполненных значений |
Измерение Номенклатура | СправочникСсылка.Номенклатура; ведущее, основной отбор, запрет незаполненных значений |
Ресурс Цена | Число 15.2, неотрицательное |
Реквизит Комментарий | Строка 200 |
Код — общий модуль ЦеныПоставщиковСервер с флажком «Сервер» без «Вызов сервера». На клиенте объекты регистра недоступны, поэтому из формы вызывайте его в процедуре с директивой &НаСервере.
// Общий модуль ЦеныПоставщиковСервер
// Свойства модуля: Сервер = Истина, Вызов сервера = Ложь.
// Регистр сведений ЦеныПоставщиков: периодичность «В пределах дня», режим записи «Независимый».
// Измерения: Поставщик (СправочникСсылка.Контрагенты), Номенклатура (СправочникСсылка.Номенклатура).
// Ресурс: Цена (Число 15.2). Реквизит: Комментарий (Строка 200).
#Область ПрограммныйИнтерфейс
// Записывает цену поставщика на дату. Запись с тем же периодом и измерениями заменяется.
//
// Параметры:
// Поставщик - СправочникСсылка.Контрагенты
// Номенклатура - СправочникСсылка.Номенклатура
// Цена - Число
// Дата - Дата - дата начала действия цены, время отбрасывается
// Комментарий - Строка
//
Процедура УстановитьЦену(Поставщик, Номенклатура, Цена, Дата, Комментарий = "") Экспорт
МенеджерЗаписи = РегистрыСведений.ЦеныПоставщиков.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = НачалоДня(Дата);
МенеджерЗаписи.Поставщик = Поставщик;
МенеджерЗаписи.Номенклатура = Номенклатура;
МенеджерЗаписи.Цена = Цена;
МенеджерЗаписи.Комментарий = Комментарий;
МенеджерЗаписи.Записать(Истина);
КонецПроцедуры
// Загружает прайс поставщика на дату: все цены этого поставщика на эту дату
// заменяются содержимым прайса.
//
// Параметры:
// Поставщик - СправочникСсылка.Контрагенты
// Дата - Дата
// Прайс - ТаблицаЗначений - колонки Номенклатура, Цена
//
Процедура ЗагрузитьПрайс(Поставщик, Дата, Прайс) Экспорт
ДатаЦены = НачалоДня(Дата);
НаборЗаписей = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Период.Установить(ДатаЦены);
НаборЗаписей.Отбор.Поставщик.Установить(Поставщик);
Добавленные = Новый Соответствие;
Для Каждого СтрокаПрайса Из Прайс Цикл
// В наборе не может быть двух записей с одинаковыми ключевыми полями
Если Не ЗначениеЗаполнено(СтрокаПрайса.Номенклатура)
Или Добавленные[СтрокаПрайса.Номенклатура] <> Неопределено Тогда
Продолжить;
КонецЕсли;
Добавленные.Вставить(СтрокаПрайса.Номенклатура, Истина);
Запись = НаборЗаписей.Добавить();
Запись.Период = ДатаЦены;
Запись.Поставщик = Поставщик;
Запись.Номенклатура = СтрокаПрайса.Номенклатура;
Запись.Цена = СтрокаПрайса.Цена;
КонецЦикла;
НаборЗаписей.Записать(Истина);
КонецПроцедуры
// Возвращает цену одного товара, действующую на конец дня (0, если цены нет).
//
// Параметры:
// Поставщик - СправочникСсылка.Контрагенты
// Номенклатура - СправочникСсылка.Номенклатура
// Дата - Дата
//
// Возвращаемое значение:
// Число
//
Функция ЦенаНаДату(Поставщик, Номенклатура, Дата) Экспорт
Отбор = Новый Структура("Поставщик, Номенклатура", Поставщик, Номенклатура);
Значения = РегистрыСведений.ЦеныПоставщиков.ПолучитьПоследнее(КонецДня(Дата), Отбор);
Возврат Значения.Цена;
КонецФункции
// Возвращает цены по списку товаров, действующие на дату, одним запросом.
//
// Параметры:
// Поставщик - СправочникСсылка.Контрагенты
// СписокНоменклатуры - Массив из СправочникСсылка.Номенклатура
// Дата - Дата
//
// Возвращаемое значение:
// ТаблицаЗначений - колонки Номенклатура, Цена, ДатаЦены
//
Функция ЦеныНаДату(Поставщик, СписокНоменклатуры, Дата) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена,
| Цены.Период КАК ДатаЦены
|ИЗ
| РегистрСведений.ЦеныПоставщиков.СрезПоследних(
| &Дата,
| Поставщик = &Поставщик
| И Номенклатура В (&СписокНоменклатуры)) КАК Цены";
Запрос.УстановитьПараметр("Дата", КонецДня(Дата));
Запрос.УстановитьПараметр("Поставщик", Поставщик);
Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
// Удаляет цену, установленную на конкретную дату. Записи на другие даты не меняются.
//
// Параметры:
// Поставщик - СправочникСсылка.Контрагенты
// Номенклатура - СправочникСсылка.Номенклатура
// Дата - Дата
//
Процедура УдалитьЦену(Поставщик, Номенклатура, Дата) Экспорт
НаборЗаписей = РегистрыСведений.ЦеныПоставщиков.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Период.Установить(НачалоДня(Дата));
НаборЗаписей.Отбор.Поставщик.Установить(Поставщик);
НаборЗаписей.Отбор.Номенклатура.Установить(Номенклатура);
НаборЗаписей.Записать(Истина); // пустой набор с отбором удаляет записи по отбору
КонецПроцедуры
#КонецОбласти
МенеджерЗаписи.Записать(Истина) — параметр «Замещать». С Истина запись с тем же периодом и измерениями перезаписывается, с Ложь платформа выдаст ошибку, если такая запись уже есть. Для сотен записей удобнее набор.НаборЗаписей.Отбор...Установить() — отбор определяет, какие записи «покрывает» набор. При Записать(Истина) платформа удаляет все записи под отбором и пишет содержимое набора. Товары, которых нет в новом прайсе, исчезнут из цен на эту дату.Добавленные — защита от дублей: две строки прайса с одним товаром дают одинаковый ключ, и запись набора упадёт.ПолучитьПоследнее — метод менеджера для одного значения. Возвращает структуру ресурсов; если цены нет, в ней значение по умолчанию для типа, то есть 0. Дата — КонецДня(Дата), как в ЦеныНаДату: после перехода на периодичность «в пределах секунды» обе функции вернут одну цену. Для списка товаров вызывайте ЦеныНаДату — один запрос вместо сотни.СрезПоследних(&Дата, Условие) — условие по измерениям стоит в параметрах виртуальной таблицы, и платформа отбирает записи до вычисления среза. КонецДня гарантирует, что цена, установленная в этот же день, попадёт в результат.Записать(Истина) и убирайте дубли до записи.НаборЗаписей.Записать() без установленного отбора заменяет весь независимый регистр содержимым набора, а пустой набор без отбора очищает регистр целиком.КонецДня(Дата) или момент времени документа.СрезПоследних(&Дата, Цена > 0) вернёт последнюю ненулевую цену, даже если после неё цену обнулили. Нужна действующая цена — ставьте условие по ресурсу в ГДЕ после среза.&НаКлиенте. Перенесите работу с регистром в серверную процедуру.Измерение входит в ключ: записи с разными значениями измерения существуют одновременно. Реквизит в ключ не входит — новое значение перезаписывает старое в той же записи. Отбирать в запросах можно по обоим.
Обычно причин две: регистр не включён ни в одну подсистему или у роли пользователя нет права на просмотр. Проверьте подсистемы и права ролей, затем обновите конфигурацию базы данных. Если права раздаются через профили групп доступа, роль нужно добавить в профиль.
Дополнительные сведения из БСП добавляют объекту поля без доработки конфигурации, но истории по датам и ключа из нескольких полей там нет. Если значение зависит от даты или от сочетания объектов, нужен свой регистр.
Дорого обходятся ошибки проектирования, которые выясняются на живых данных: перенос истории после смены ключа, дубли при проведении, медленные срезы. Если нужно спроектировать хранение цен, настроек или сопоставлений под вашу схему учёта или исправить уже работающий регистр, посмотрите услуги по доработке 1С или разместите задачу на площадке: оценка бесплатная, ответ — в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.