Как программно получить цены на услуги из регистра сведений "ServicePrices" в 1С:Отель?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

Приветствуем вас, коллеги! Многие начинающие разработчики в 1С сталкиваются с трудностями при попытке получить данные, которые, на первый взгляд, должны храниться в справочниках, но на самом деле находятся в других объектах конфигурации. В этой статье мы подробно рассмотрим, как правильно получать цены на услуги в конфигурации "1С:Отель", используя программный код и язык запросов.

Наш читатель столкнулся с проблемой, когда он мог получить наименования услуг из справочника Services, но никак не мог достать их цены. Это распространенная ситуация, поскольку цены являются динамическими данными, зависящими от времени, типа цены и других факторов, и для их хранения в 1С используются специальные объекты — регистры сведений.

Понимание структуры хранения данных в 1С

Прежде чем перейти к решению, давайте кратко разберем, как вообще устроено хранение данных в системе 1С. Это поможет нам лучше понять, почему цены находятся именно в регистре сведений, а не напрямую в справочнике.

  1. Справочники: Эти объекты используются для хранения условно-постоянной информации, то есть объектов учета. Примеры: товары, услуги, контрагенты, организации. Они представляют собой список уникальных элементов, каждый из которых имеет свои свойства (наименование, код и т.д.). В нашем случае, справочник Services хранит информацию об услугах.
  2. Документы: Документы предназначены для регистрации событий хозяйственной деятельности. Это могут быть поступления товаров, продажи, оплаты, перемещения и так далее. Они фиксируют факт совершения какой-либо операции в определенный момент времени.
  3. Регистры сведений: Это очень важный объект для хранения различных свойств объектов, которые могут меняться со временем, или любой другой вспомогательной информации. Цены — это классический пример таких данных. Регистры сведений позволяют хранить историю изменений (периодические регистры) или просто набор дополнительных характеристик. В нашем случае, именно здесь мы и найдем цены услуг.
  4. Регистры накопления: Эти регистры используются для учета количественных и/или суммовых показателей в разрезе аналитик. Например, остатки товаров на складах, взаиморасчеты с контрагентами. Данные в них формируются при проведении документов.
  5. Регистры бухгалтерии: Специализированные регистры для хранения бухгалтерских проводок, то есть движений по счетам бухгалтерского учета.

Итак, мы выяснили, что справочники хранят сами объекты (услуги), а регистры сведений хранят данные об их свойствах (цены), которые могут меняться со временем. Теперь давайте рассмотрим подробнее, как это реализовано в "1С:Отель".

Структура хранения цен в 1С:Отель

Как и было предположено нашим читателем, цены на услуги в конфигурации "1С:Отель" хранятся в периодическом Регистре сведений "ServicePrices". Слово "периодический" означает, что этот регистр способен хранить историю изменения цен во времени. Каждая запись в нем имеет привязку к дате, начиная с которой установленная цена становится актуальной.

Типичная структура этого регистра включает:

  1. Измерения (то, в разрезе чего хранится цена):
    • Service: Это ссылка на элемент справочника "Services" (конкретная услуга). Именно это измерение связывает цену с нашей услугой.
    • PriceType: Это ссылка на справочник "Типы цен" (например, "Базовый тариф", "Тариф выходного дня", "Корпоративный тариф"). Наличие этого измерения позволяет хранить несколько цен для одной и той же услуги, но для разных тарифов или типов цен.
  2. Ресурсы (то, что мы хотим узнать):
    • Price: Числовое значение самой цены.
    • Currency: Ссылка на справочник "Валюты", указывающая, в какой валюте установлена цена.

Ваш первоначальный код, который обращался к справочнику Services для получения цен, был некорректным, потому что справочник услуг не хранит цену напрямую. Он лишь является одним из "ключей" (измерений), по которому цена ищется в регистре сведений.

Способ 1: Получение цен с помощью запроса (рекомендуемый метод)

Запрос — это наиболее правильный, гибкий и производительный способ получения данных в 1С, особенно когда вам нужно получить информацию для нескольких элементов сразу, или когда данные хранятся в связанных объектах. Для получения актуальных на определенную дату цен из периодического регистра сведений мы будем использовать его специальную виртуальную таблицу СрезПоследних. Эта таблица позволяет на лету сформировать "срез" — то есть выбрать самые последние (актуальные) записи на указанную нами дату.

Давайте рассмотрим полный пример программного кода, который демонстрирует получение цен на услуги с помощью запроса:


// Массив, в котором будем хранить коды нужных нам услуг
КодыУслуг = Новый Массив;
КодыУслуг.Добавить("ДОПМBB");
КодыУслуг.Добавить("ДОПМHB");
КодыУслуг.Добавить("ДОПМFB");
КодыУслуг.Добавить("АРЗАЛ");

// Создаем объект Запрос
Запрос = Новый Запрос;

// Устанавливаем текст запроса
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ЦеныУслугСрезПоследних.Service.Наименование КАК НаименованиеУслуги,
    |    ЦеныУслугСрезПоследних.Service.Код КАК КодУслуги,
    |    ЦеныУслугСрезПоследних.Price КАК Цена,
    |    ЦеныУслугСрезПоследних.Currency КАК Валюта
    |ИЗ
    |    РегистрСведений.ServicePrices.СрезПоследних(&НаДату, Service.Код В (&КодыУслуг) И PriceType = &ТипЦен) КАК ЦеныУслугСрезПоследних";

// Устанавливаем параметры запроса. Это очень важный шаг,
// поскольку параметры делают запрос более гибким и безопасным.
Запрос.УстановитьПараметр("НаДату", ТекущаяДата()); // Получаем цены, актуальные на текущий момент

// Передаем массив кодов услуг для отбора. 
// Условие "Service.Код В (&КодыУслуг)" позволяет выбрать цены только для указанных нами услуг.
Запрос.УстановитьПараметр("КодыУслуг", КодыУслуг); 

// Важно: Укажите нужный вам Тип Цен. Обычно он хранится в справочнике "ТипыЦен".
// Если тип цен не важен или у вас его нет, вы можете убрать эту строку 
// и условие "И PriceType = &ТипЦен" из текста запроса.
ТипЦенОбъект = Справочники.PriceTypes.НайтиПоНаименованию("Базовый тариф");
Если ТипЦенОбъект.Пустая() Тогда
    Сообщить("Тип цен 'Базовый тариф' не найден. Проверьте данные в справочнике 'Типы цен'.");
    Возврат; // Прерываем выполнение, если не можем найти тип цен
КонецЕсли;
Запрос.УстановитьПараметр("ТипЦен", ТипЦенОбъект);

// Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();

// Проверяем, не пустой ли результат, и выводим полученные данные
Если РезультатЗапроса.Пустой() Тогда
    Сообщить("Цены для указанных услуг на текущую дату и тип цен не найдены.");
Иначе
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщение = "Услуга: " + Выборка.НаименованиеУслуги + 
                    " (Код: " + Выборка.КодУслуги + "), " +
                    "Цена: " + Выборка.Цена + 
                    " " + Выборка.Валюта;
        Сообщить(Сообщение);
    КонецЦикла;
КонецЕсли;

Давайте подробнее разберем каждый аспект этого запроса:

  1. ВЫБРАТЬ ...: Здесь мы указываем, какие поля нам нужны из результата запроса. Мы получаем наименование и код услуги (через точку ЦеныУслугСрезПоследних.Service.Наименование, так как Service — это ссылка на справочник), саму цену и валюту.
  2. ИЗ РегистрСведений.ServicePrices.СрезПоследних(...): Это обращение к виртуальной таблице СрезПоследних регистра сведений ServicePrices. Эта таблица принимает два необязательных параметра:
    • Первый параметр (&НаДату) — это дата, на которую нужно получить срез. Мы передаем ТекущаяДата(), чтобы получить актуальные цены на сейчас.
    • Второй параметр (Service.Код В (&КодыУслуг) И PriceType = &ТипЦен) — это условие для отбора записей внутри среза. Мы отбираем цены только для тех услуг, коды которых содержатся в нашем массиве КодыУслуг, и только для определенного типа цен.
  3. КАК ЦеныУслугСрезПоследних: Мы даем псевдоним нашей виртуальной таблице, чтобы удобнее к ней обращаться в тексте запроса.
  4. Параметры запроса (УстановитьПараметр): Использование параметров запроса (&НаДату, &КодыУслуг, &ТипЦен) позволяет нам передавать значения из кода 1С в текст запроса, что делает запрос универсальным и защищенным от SQL-инъекций.
  5. Обработка результата (Выполнить, Выбрать, Пока Выборка.Следующий() Цикл): После выполнения запроса мы получаем объект РезультатЗапроса. Метод Выбрать() возвращает объект Выборка, по которому мы можем итерироваться, чтобы получить каждую найденную запись.

Этот пример является наиболее полным и учитывает такую важную деталь, как тип цены, что является стандартной практикой в большинстве конфигураций 1С.

Способ 2: Получение цены для одной услуги без запроса (упрощенный метод)

Если перед нами стоит задача получить цену только для одной конкретной услуги и на определенную дату, мы можем обойтись без написания полного текста запроса, используя специальные методы менеджера регистра сведений. Это может быть немного проще для понимания новичком, но менее производительно при работе с большим количеством данных.

Рассмотрим пример кода для этого способа:


// 1. Найдем нужную услугу по коду в справочнике "Services"
КодИскомойУслуги = "ДОПМBB";
УслугаОбъект = Справочники.Services.НайтиПоКоду(КодИскомойУслуги);

// Проверяем, найдена ли услуга. Это критически важный шаг для избежания ошибок.
Если УслугаОбъект.Пустая() Тогда
    Сообщить("Услуга с кодом '" + КодИскомойУслуги + "' не найдена!");
    Возврат; // Прерываем выполнение, если услуга не найдена
КонецЕсли;

// 2. Найдем нужный тип цен. 
// Если у вас нет типов цен или он не важен, этот шаг можно пропустить,
// но тогда нужно будет убрать "PriceType" из структуры отбора.
НаименованиеТипаЦен = "Базовый тариф";
ТипЦенОбъект = Справочники.PriceTypes.НайтиПоНаименованию(НаименованиеТипаЦен);

Если ТипЦенОбъект.Пустая() Тогда
    Сообщить("Тип цен '" + НаименованиеТипаЦен + "' не найден!");
    Возврат; // Прерываем выполнение, если тип цен не найден
КонецЕсли;

// 3. Создаем структуру для отбора по измерениям регистра сведений.
// В эту структуру мы помещаем значения тех измерений, по которым хотим получить цену.
Отбор = Новый Структура;
Отбор.Вставить("Service", УслугаОбъект);      // Отбор по услуге
Отбор.Вставить("PriceType", ТипЦенОбъект);  // Отбор по типу цены

// 4. Получаем срез последних записей на текущую дату с указанным отбором.
// Метод ПолучитьПоследнее() возвращает Структуру с ресурсами регистра или Неопределено, 
// если запись не найдена.
ДанныеЦены = РегистрыСведений.ServicePrices.ПолучитьПоследнее(ТекущаяДата(), Отбор);

// 5. Проверяем результат и выводим цену
Если ДанныеЦены = Неопределено Тогда
    Сообщить("Цена для услуги '" + УслугаОбъект.Наименование + "' с типом цен '" + ТипЦенОбъект.Наименование + "' не установлена на текущую дату.");
Иначе
    Сообщить("Цена: " + ДанныеЦены.Price + " " + ДанныеЦены.Currency);
КонецЕсли;

Разберем основные шаги этого метода:

  1. Справочники.Services.НайтиПоКоду("ДОПМBB"): Сначала нам нужно получить ссылку на сам объект "Услуга" из справочника Services. Мы используем метод НайтиПоКоду, но можно также использовать НайтиПоНаименованию или просто получить ссылку другим способом.
  2. Справочники.PriceTypes.НайтиПоНаименованию("Базовый тариф"): Аналогично, получаем ссылку на нужный нам объект "Тип цены" из справочника PriceTypes.
  3. Новый Структура: Для формирования условий отбора в ПолучитьПоследнее мы используем объект Структура. В эту структуру мы вставляем пары "ключ-значение", где ключами являются имена измерений регистра ("Service", "PriceType"), а значениями — ссылки на соответствующие объекты.
  4. РегистрыСведений.ServicePrices.ПолучитьПоследнее(ТекущаяДата(), Отбор): Это ключевой метод. Он обращается к менеджеру регистра сведений ServicePrices и запрашивает самую последнюю (актуальную) запись на указанную дату (ТекущаяДата()) с учетом заданных в структуре условий отбора. Если запись найдена, метод возвращает Структуру, содержащую ресурсы регистра (Price, Currency). Если записи нет, возвращается Неопределено.
  5. Проверка на Неопределено: Всегда необходимо проверять результат вызова ПолучитьПоследнее(), так как регистр может не содержать данных, соответствующих нашим условиям.

Ключевые выводы и рекомендации

Исходя из нашего анализа и рассмотренных примеров, мы можем сделать несколько важных выводов и дать рекомендации:

  1. Используйте регистры сведений для изменяемых данных: Для работы с данными, которые меняются со временем (такими как цены, курсы валют, ответственные лица), в 1С всегда используются периодические регистры сведений. Это фундаментальный принцип архитектуры 1С.
  2. Предпочитайте запросы: Доступ к данным из регистров сведений наиболее эффективно осуществляется через запросы к виртуальным таблицам (СрезПоследних, СрезПервых). Этот подход обеспечивает лучшую производительность и гибкость, особенно при обработке большого объема данных или необходимости получения информации для множества элементов.
  3. Учитывайте измерения регистра: Всегда помните об измерениях регистра (в нашем случае Service и PriceType). Именно они определяют уникальность записи и являются "ключами" для поиска нужных данных.
  4. Используйте консоль запросов: Если у вас возникают трудности с написанием запроса, настоятельно рекомендуем использовать консоль запросов. Это мощный инструмент разработчика 1С, который позволяет интерактивно строить, выполнять и отлаживать запросы, видеть их результат и проверять правильность получения данных. Начните с простого запроса к регистру ServicePrices.СрезПоследних без условий, чтобы убедиться, что данные в регистре вообще есть, а затем постепенно добавляйте нужные измерения и отборы.
  5. Изучайте основы: Если вы новичок, посвятите время изучению основ платформы 1С:Предприятие 8. Концепции справочников, документов, регистров сведений и языка запросов являются базовыми, и их глубокое понимание значительно ускорит ваше развитие как программиста 1С.

Надеемся, что это подробное руководство помогло вам разобраться, как получать цены на услуги в "1С:Отель", и дало четкое понимание принципов работы с регистрами сведений. Успехов в вашем программировании!

← На главную