В процессе разработки собственных конфигураций или при доработке существующих решений на платформе «1С:Предприятие» перед каждым программистом неизбежно встает вопрос выбора оптимальной архитектуры хранения данных. Один из наиболее классических и часто обсуждаемых вопросов — где правильно хранить цену номенклатуры? Допустимо ли использовать для этого регистры накопления, или же единственным верным решением является регистр сведений?
В этой статье мы подробно разберем этот архитектурный вопрос. Мы проанализируем разницу между объектами метаданных, выясним, почему попытка хранить цены в регистре накопления может привести к серьезным проблемам с производительностью и логикой учета, и рассмотрим лучшие практики проектирования на примере типовых конфигураций 1С.
Для того чтобы принять осознанное решение, давайте сначала вспомним базовую методологию платформы «1С:Предприятие» и разберем назначение каждого из этих объектов метаданных.
Регистр сведений (РС) предназначен для хранения многомерных данных о существующих состояниях объектов или о значениях их характеристик. Ключевые особенности регистра сведений:
СрезПоследних и СрезПервых, которые позволяют мгновенно получить актуальное состояние данных на любую контрольную точку времени.Регистр накопления (РН) спроектирован для учета движения ресурсов (количественных или денежных показателей). Его главная задача — накапливать остатки и обороты. Ключевые особенности регистра накопления:
Остатки, Обороты, ОстаткиИОбороты, которые хранят балансовые показатели (например, сколько товара осталось на складе или какая сумма взаиморасчетов накопилась по контрагенту).Рассмотрим ситуацию, когда разработчик решает создать регистр накопления для хранения прайс-листа, где ресурсом выступает цена. Проанализируем, к каким критическим последствиям это приведет.
1. Математическая абсурдность (проблема суммирования)
Цена является небалансовым (несводным) показателем. Сложение цены одной единицы товара с ценой другой единицы не имеет физического и экономического смысла. Если вы запишете цену в ресурс регистра накопления остатков, то при каждой записи система будет прибавлять новое значение цены к старому. На выходе в таблице итогов вы получите колоссальные суммы, не имеющие отношения к реальности. Если же использовать регистр накопления оборотов, то при сложении оборотов цен за период вы также получите бесполезные данные.
2. Отсутствие механизма «Срез последних»
Главная задача при работе с ценами в документах (например, в табличной части «Товары» документов «Реализация товаров и услуг» или «Заказ клиента») — быстро извлечь актуальную цену на дату документа.
В регистре сведений для этого существует оптимизированная на уровне СУБД виртуальная таблица СрезПоследних.
В регистре накопления такого механизма нет. Чтобы достать последнюю цену из РН, вам придется писать тяжелый запрос к реальной таблице движений, упорядочивать записи по убыванию даты и выбирать первую запись. При росте базы данных этот запрос начнет вызывать колоссальные блокировки и критическое падение производительности всей системы.
3. Зависимость от транзакций и документов
Данные в регистр накопления могут попадать только в результате проведения документов. Представьте ситуацию: руководство утвердило новые цены на следующий месяц, но продаж по этим ценам еще не было. Если вы храните цены в РН, то информации о новых ценах в системе просто не появится, пока не будет проведен первый документ продажи. Регистр сведений же является независимым (или может заполняться специальными регистраторами вроде «Установка цен номенклатуры»), что позволяет хранить прейскурант независимо от факта совершения хозяйственных операций.
4. Мусорные записи и нулевые итоги
При попытке извлечь «цену» из регистров накопления, связанных с товародвижением, разработчики часто сталкиваются с записями, содержащими нулевые значения количества или стоимости (например, при корректировках, списаниях или распределении транспортно-заготовительных расходов). Отфильтровать этот «шум» в запросах крайне сложно и неэффективно.
Тем не менее, в реальных конфигурациях мы можем встретить упоминание цены в регистрах накопления. Разберем эти специфические сценарии, чтобы не путать их с хранением прайс-листа.
В регистрах накопления (таких как «Продажи» или «Закупки») цена может быть добавлена в качестве реквизита (но ни в коем случае не ресурса!). Реквизит в регистре накопления не суммируется платформой и служит исключительно для справочной информации. Например, нам нужно зафиксировать, по какой конкретно цене была совершена данная продажа в конкретном документе для последующего построения отчетов. Но использовать этот реквизит для подстановки цены в новые документы — плохая практика, так как это замедляет работу системы и требует сканирования исторических таблиц движений.
Иногда под «ценой» понимают себестоимость единицы товара. Себестоимость действительно рассчитывается на основе данных регистра накопления (например, «Себестоимость товаров» или «Партии товаров»). В данном случае цена не хранится в готовом виде, а вычисляется динамически в запросах по формуле:
Себестоимость единицы = Стоимость остатка / Количество остатка
Этот метод используется для оценки запасов и расчета финансового результата, но не для ведения прайс-листов компании.
Еще один альтернативный вариант — хранение цены непосредственно в справочнике «Номенклатура» или в подчиненном справочнике «Цены». Рассмотрим особенности этого подхода.
Когда это применимо:
Если в вашей компании цены статичны, практически никогда не изменяются, и вам совершенно не требуется хранить историю изменения цен (то есть вам не важно, какая цена была у товара месяц назад), вы можете создать обычный реквизит Цена в справочнике Номенклатура. Это максимально упростит структуру конфигурации и ускорит разработку.
Когда это ломает логику:
Как только цена товара меняется, и вам необходимо перепровести старый документ (например, за прошлый квартал), система обратится к справочнику, возьмет оттуда новую измененную цену и исказит данные старого документа. Чтобы этого избежать, документы должны хранить цену непосредственно в своих табличных частях (что и делается во всех конфигурациях), а для подстановки цен при создании документов должна использоваться история. Справочник историю хранить не умеет.
Примечание из истории платформы: В старой платформе «1С:Предприятие 7.7» отсутствовал такой объект, как регистр сведений. Для решения этой задачи разработчики использовали механизм «периодических реквизитов справочников». В современных версиях «1С:Предприятие 8» этот механизм полностью заменен регистрами сведений, и эмулировать его через справочники считается грубой ошибкой.
Проанализируем три основных варианта хранения цен с помощью сравнительной таблицы:
| Критерий сравнения | Регистр сведений (Периодический) | Справочник (Реквизит) | Регистр накопления |
|---|---|---|---|
| Хранение истории цен | Да, автоматически в разрезе периодов | Нет (только текущее значение) | Нет (только в привязке к транзакциям документов) |
| Получение актуальной цены на дату | Мгновенно через СрезПоследних |
Да (но только текущую, без привязки к дате в прошлом) | Очень медленно (требуется обход таблицы движений) |
| Многомерность (Типы цен, Характеристики) | Да (через измерения регистра) | Сложно реализовать (требуются подчиненные справочники) | Не предназначено для этих целей |
| Влияние на производительность при подстановке | Минимальное (оптимизировано СУБД) | Минимальное | Крайне высокое (блокировки, медленные запросы) |
Рассмотрим, как правильно организовать получение актуальных цен номенклатуры на дату документа из периодического регистра сведений ЦеныНоменклатуры с помощью механизма запросов.
Представим, что наш регистр сведений ЦеныНоменклатуры имеет следующие измерения и ресурсы:
Номенклатура (Тип: СправочникСсылка.Номенклатура)ВидЦены (Тип: СправочникСсылка.ВидыЦен)Цена (Тип: Число)Валюта (Тип: СправочникСсылка.Валюты)Разберем по шагам написание запроса, который будет получать срез последних цен для списка товаров из документа:
ВЫБРАТЬ
ДокументТовары.Номенклатура КАК Номенклатура,
ДокументТовары.Количество КАК Количество,
ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) КАК Цена,
ЕСТЬNULL(ЦеныНоменклатурыСрезПоследних.Цена, 0) * ДокументТовары.Количество КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ДокументТовары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
&Период,
ВидЦены = &ВыбранныйВидЦены
) КАК ЦеныНоменклатурыСрезПоследних
ПО ДокументТовары.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
ГДЕ
ДокументТовары.Ссылка = &СсылкаНаДокумент
Проанализируем этот запрос подробнее:
РеализацияТоваровУслуг.Товары, чтобы получить список номенклатуры, для которой нужно определить цены.СрезПоследних регистра сведений. Это гарантирует, что даже если для какого-то товара цена не установлена, сам товар не исчезнет из выборки (вместо цены вернется значение NULL, которое мы обрабатываем функцией ЕСТЬNULL).&Период передается в параметры виртуальной таблицы среза последних. Это крайне важно для производительности СУБД: фильтрация и выборка последней записи происходят на уровне базы данных еще до соединения таблиц. Передача даты документа в этот параметр позволяет получить именно ту цену, которая действовала на момент совершения операции.ВидЦены непосредственно в параметрах виртуальной таблицы среза последних (ВидЦены = &ВыбранныйВидЦены). Это также является стандартом разработки «1С», существенно ускоряющим выполнение запроса.Подводя итог нашего анализа, можно сформулировать четкие правила для разработчиков конфигураций на платформе «1С:Предприятие»:
Соблюдение этих простых архитектурных принципов гарантирует высокую скорость работы вашей системы, отсутствие логических ошибок при проведении документов и простоту поддержки кода другими разработчиками.