Как получить актуальные розничные цены в разрезе складов в «1С:Торговля и склад 7.7»

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

Разработчики, приходящие в среду «1С:Предприятие 7.7» из более современных версий платформы (таких как 1С 8.3), часто сталкиваются с трудностями при попытке получить актуальные розничные цены. В платформе 1С 8.х задача получения цен решается в одну строчку через срез последних виртуальной таблицы регистра сведений. В «семерке» же аналогичного простого механизма нет, а сама архитектура розничного учета имеет ряд критически важных особенностей.

В этой статье мы подробно разберем, как устроен учет розничных цен в типовой конфигурации «Торговля и склад 7.7» (ред. 9.2), почему возникают расхождения между справочниками и регистрами, а также рассмотрим четыре практических способа решения этой задачи с примерами кода.

---

Архитектура розничного учета в ТиС 7.7: Регистр vs Справочник

Для начала проанализируем, где именно в системе хранятся цены и почему у одного и того же товара на разных складах розничная цена может отличаться, даже если для них задан один и тот же тип цен.

В «1С:Торговля и склад 7.7» учет на складах с признаком «Розничный» ведется принципиально иначе, чем на оптовых. Ключевые особенности этой схемы:

  1. Учет остатков в продажных ценах. В регистре ОстаткиТМЦ остатки товаров на розничных складах хранятся не только в количественном выражении, но и в разрезе конкретных цен продажи. Для этого в регистре предусмотрено специальное измерение — ЦенаПрод.
  2. Роль документа «Переоценка товаров в розницу». Если вы изменили цену в подчиненном справочнике Цены, стоимость остатков товара на розничном складе автоматически не изменится. Чтобы новая розничная цена вступила в силу, необходимо обязательно создать и провести документ Переоценка товаров в розницу. Этот документ списывает остатки по старой цене продажи и приходует их по новой цене из справочника.
  3. Приоритет регистра при продажах. Когда оформляется продажа через розничный склад (например, документом Чек ККМ или Реализация (розница)), система берет цену продажи исключительно из регистра ОстаткиТМЦ (измерение ЦенаПрод). Продать товар по цене из справочника, если она не совпадает с регистром, стандартными средствами розницы невозможно.

Именно поэтому возникает ситуация, когда цена в справочнике («бумажная» цена) не совпадает с реальной ценой продажи на конкретной торговой точке (зафиксированной в регистре).

---

Почему расходятся цены в справочнике и регистре?

Выясним основные причины, из-за которых розничные цены в справочнике и регистре начинают различаться:

Ниже мы подробно разберем четыре практических метода получения цен, каждый из которых подходит под свои задачи.

---

Способ 1. Стандартный запрос по регистру «ОстаткиТМЦ» (цены по наличию товара)

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

Напишем стандартный запрос «семерки» для получения остатков товаров в разрезе складов и цен продажи:


Процедура ПолучитьЦеныИзРегистра()
    Перем Запрос, ТекстЗапроса, ТабЗнач;
    
    Запрос = СоздатьОбъект("Запрос");
    ТекстЗапроса = 
    "//{{ЗАПРОС(РозничныеЦеныРегистр)
    |Период с РабочаяДата() по РабочаяДата();
    |Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
    |Склад = Регистр.ОстаткиТМЦ.Склад;
    |ЦенаПрод = Регистр.ОстаткиТМЦ.ЦенаПрод;
    |Количество = Регистр.ОстаткиТМЦ.Количество;
    |Функция КолОст = КонОст(Количество);
    |Группировка Склад;
    |Группировка Номенклатура без групп;
    |Группировка ЦенаПрод;
    |Условие(Склад.ВидСклада = Перечисление.ВидыСкладов.Розничный);
    |Условие(Количество > 0);
    |//}}ЗАПРОС"
    ;
    
    Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
        Предупреждение("Ошибка выполнения запроса по регистру ОстаткиТМЦ!");
        Возврат;
    КонецЕсли;
    
    // Выгружаем результаты в таблицу значений для последующего анализа
    ТабЗнач = СоздатьОбъект("ТаблицаЗначений");
    Запрос.Выгрузить(ТабЗнач, 1, 0);
    
    // Выведем результат в окно сообщений
    ТабЗнач.ВыбратьСтроки();
    Пока ТабЗнач.ПолучитьСтроку() = 1 Цикл
        Сообщить("Склад: " + ТабЗнач.Склад + " | " +
                 "Товар: " + ТабЗнач.Номенклатура + " | " +
                 "Цена в регистре: " + ТабЗнач.ЦенаПрод + " | " +
                 "Остаток: " + ТабЗнач.КолОст);
    КонецЦикла;
КонецПроцедуры

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

Недостатки: Не выводит товары, у которых нулевой остаток на складе.

---

Способ 2. Получение цен из периодического справочника «Цены» (срез последних)

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

Для получения единичных значений цен на определенную дату мы можем воспользоваться встроенным объектом Периодический или стандартной глобальной функцией конфигурации ТиС глПолучитьЦену().

Рассмотрим пример обхода справочника с получением цен на определенную дату:


Процедура ПолучитьЦеныИзСправочника()
    Перем СпрНом, СпрЦены, ОбъектПериодический, ВыбТипЦен, АктуальнаяЦена;
    
    СпрНом = СоздатьОбъект("Справочник.Номенклатура");
    СпрЦены = СоздатьОбъект("Справочник.Цены");
    ОбъектПериодический = СоздатьОбъект("Периодический");
    
    // Предположим, у нас есть выбранный тип цен (например, розничные)
    ВыбТипЦен = Константа.РозничныйТипЦен; 
    
    СпрНом.ВыбратьЭлементы();
    Пока СпрНом.ПолучитьЭлемент() = 1 Цикл
        // Пропускаем группы номенклатуры
        Если СпрНом.ЭтоГруппа() = 1 Тогда
            Продолжить;
        КонецЕсли;
        
        // Настраиваем выборку подчиненного справочника Цены
        СпрЦены.ИспользоватьВладельца(СпрНом.ТекущийЭлемент());
        СпрЦены.ВыбратьЭлементы();
        
        Пока СпрЦены.ПолучитьЭлемент() = 1 Цикл
            // Ищем только цены нужного нам типа
            Если СпрЦены.ТипЦен = ВыбТипЦен Тогда
                // Инициализируем периодический объект для получения значения на дату
                ОбъектПериодический.ИспользоватьОбъект("Цена", СпрЦены.ТекущийЭлемент());
                АктуальнаяЦена = ОбъектПериодический.ЗначениеНаДату(РабочаяДата());
                
                Сообщить("Товар: " + СпрНом.Наименование + " | " +
                         "Тип цен: " + ВыбТипЦен + " | " +
                         "Цена из справочника: " + АктуальнаяЦена);
            КонецЕсли;
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

Важное предупреждение о производительности: Использование циклов с обращением к периодическим реквизитам через объект Периодический или функцию глПолучитьЦену() для большого объема данных (более 1000 позиций номенклатуры) работает крайне медленно. Происходит так называемая проблема "N+1 запросов", когда база данных перегружается огромным количеством мелких одиночных обращений. Для построения больших прайс-листов этот способ не подходит.

---

Способ 3. Прямые SQL-запросы через 1С++ (профессиональное решение)

Если база данных работает под управлением Microsoft SQL Server (или используется DBF-версия с установленным драйвером SQLite), оптимальным решением для быстрого среза последних цен является использование внешней компоненты 1С++ (1C-Плюс-Плюс).

С помощью класса ПрямойЗапрос мы можем обратиться к SQL-таблицам напрямую. В компоненте реализована мощнейшая виртуальная функция $СрезПоследних, которая мгновенно собирает актуальные периодические реквизиты цен из таблицы истории значений (_1sconst) по всему справочнику.

Проанализируем пример построения прямого запроса для получения цен:


// Пример концептуального построения прямого запроса с помощью 1С++
Процедура ПолучитьЦеныЧерезПрямойЗапрос()
    Перем ЗапросSQL, ТекстЗапроса, ТабРезультат;
    
    ЗапросSQL = СоздатьОбъект("ПрямойЗапрос");
    
    // Формируем SQL-запрос с виртуальной таблицей $СрезПоследних
    ТекстЗапроса = "
    |SELECT 
    |    спрНом.descr as Товар,
    |    срезЦен.Цена as РозничнаяЦена
    |FROM 
    |    $Справочник.Номенклатура as спрНом
    |INNER JOIN 
    |    $Справочник.Цены as спрЦены ON спрЦены.parentid = спрНом.id
    |LEFT JOIN 
    |    $Периодический.Цена.СрезПоследних(:ВыбДата, ) as срезЦен ON срезЦен.objid = спрЦены.id
    |WHERE 
    |    спрЦены.$ТипЦен = :ВыбТипЦен AND спрНом.isfolder = 0
    |";
    
    ЗапросSQL.УстановитьПараметр("ВыбДата", РабочаяДата());
    ЗапросSQL.УстановитьПараметр("ВыбТипЦен", Константа.РозничныйТипЦен);
    
    ТабРезультат = ЗапросSQL.ВыполнитьИнструкцию(ТекстЗапроса);
    
    // Моментально получаем готовую таблицу значений с актуальными ценами
    ТабРезультат.ВыбратьСтроки();
    Пока ТабРезультат.ПолучитьСтроку() = 1 Цикл
        Сообщить("Товар: " + ТабРезультат.Товар + " | Цена: " + ТабРезультат.РозничнаяЦена);
    КонецЦикла;
КонецПроцедуры

Преимущества метода: Скорость работы возрастает в десятки и сотни раз. Построение прайс-листа на 50 000 позиций занимает менее одной секунды.

Недостатки: Требуется установка внешней компоненты 1С++ и базовые навыки написания SQL-запросов.

---

Способ 4. Анализ документов переоценки (метод автора темы)

Если нам нужен чистый «срез последних» розничных цен, установленных на складах (даже если товара сейчас нет в наличии), но при этом мы не можем использовать внешние компоненты вроде 1С++, мы можем пойти альтернативным путем. Разберем логику этого метода:

  1. Запросом собираем все документы, которые делают движения по регистру цен или устанавливают цены на розничных складах за определенный прошлый период (например, за последние 3–6 месяцев).
  2. Основными документами-источниками здесь выступают: Переоценка товаров в розницу, Поступление ТМЦ (в розницу) и Перемещение ТМЦ (на розничный склад).
  3. Полученные строки документов сортируем в обратном хронологическом порядке (по дате и времени).
  4. Для каждого уникального сочетания «Товар + Склад» берем цену из самого свежего найденного документа.

Этот способ позволяет сымитировать механизм работы виртуальной таблицы среза последних на чистом встроенном языке 1С 7.7. Именно это решение в итоге применил автор темы для получения корректного отчета.

---

Практические советы по поддержанию актуальности розничных цен

Чтобы избежать расхождения данных в ваших отчетах и облегчить написание запросов, мы рекомендуем придерживаться следующих правил регламентного учета в «1С:Торговля и склад 7.7»:

← На главную