Как получить цену последней закупки номенклатуры через таблицу оборотов в 1С

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

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

Постановка задачи и выбор метода

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

Рассмотрим два подхода к решению этой задачи:

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

Решение через физическую таблицу регистра

Прежде чем переходить к виртуальным таблицам, проанализируем наиболее эффективный с точки зрения производительности метод — работу с основной таблицей регистра Закупки. Рассмотрим по шагам, как строится такой запрос.

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


ВЫБРАТЬ
    взПериодПоследнейЗакупки.Номенклатура КАК Номенклатура,
    взПериодПоследнейЗакупки.ХарактеристикаНоменклатуры,
    Закупки.Количество,
    Закупки.Стоимость,
    ВЫБОР
        КОГДА Закупки.Количество = 0
            ТОГДА 0
        ИНАЧЕ ВЫРАЗИТЬ(Закупки.Стоимость / Закупки.Количество КАК ЧИСЛО(15, 2))
    КОНЕЦ КАК Цена
ПОМЕСТИТЬ ВТ_ПоследниеЗакупки
ИЗ
    (ВЫБРАТЬ
        Закупки.Номенклатура КАК Номенклатура,
        МАКСИМУМ(Закупки.Период) КАК Период,
        Закупки.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры
    ИЗ
        РегистрНакопления.Закупки КАК Закупки
    СГРУППИРОВАТЬ ПО
        Закупки.Номенклатура,
        Закупки.ХарактеристикаНоменклатуры) КАК взПериодПоследнейЗакупки
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Закупки КАК Закупки
        ПО взПериодПоследнейЗакупки.Период = Закупки.Период
            И взПериодПоследнейЗакупки.Номенклатура = Закупки.Номенклатура
            И взПериодПоследнейЗакупки.ХарактеристикаНоменклатуры = Закупки.ХарактеристикаНоменклатуры
ГДЕ
    взПериодПоследнейЗакупки.Период МЕЖДУ &НачалоПериода И &КонецПериода;

Важное замечание: При использовании МАКСИМУМ(Период) может возникнуть ситуация, когда в одну и ту же секунду оформлено несколько поступлений. В этом случае запрос вернет несколько строк. Чтобы этого избежать, рекомендуется использовать поле Регистратор или связку с уникальным моментом времени.

Реализация через виртуальную таблицу Обороты

Если специфика задачи или требования архитектуры обязывают использовать виртуальную таблицу Обороты, алгоритм несколько меняется. Виртуальная таблица Обороты эффективна, когда нам нужны итоговые данные, но для получения цены конкретного документа нам придется «раскрыть» таблицу до регистратора.

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

  1. Обратимся к виртуальной таблице РегистрНакопления.Закупки.Обороты.
  2. В параметрах виртуальной таблицы укажем периодичность Регистратор. Это критически важно, так как без этого поля мы не сможем определить конкретную последнюю закупку.
  3. Поместим все движения периода во временную таблицу.
  4. Вторым шагом выберем из этой временной таблицы максимальный период или регистратор для каждой номенклатуры.
  5. Соединим результат со всеми движениями, чтобы получить данные о цене.

Проанализируем структуру такого подхода:


// Шаг 1: Получаем все обороты с детализацией до документа
ВЫБРАТЬ
    ЗакупкиОбороты.Номенклатура,
    ЗакупкиОбороты.ХарактеристикаНоменклатуры,
    ЗакупкиОбороты.Регистратор,
    ЗакупкиОбороты.Период,
    ЗакупкиОбороты.КоличествоОборот КАК Количество,
    ЗакупкиОбороты.СтоимостьОборот КАК Стоимость
ПОМЕСТИТЬ ВТ_ВсеДвижения
ИЗ
    РегистрНакопления.Закупки.Обороты(&Начало, &Конец, Регистратор, ) КАК ЗакупкиОбороты;

// Шаг 2: Находим последние даты закупки
ВЫБРАТЬ
    ВТ.Номенклатура,
    ВТ.ХарактеристикаНоменклатуры,
    МАКСИМУМ(ВТ.Период) КАК МаксПериод
ПОМЕСТИТЬ ВТ_Максимумы
ИЗ
    ВТ_ВсеДвижения КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Номенклатура,
    ВТ.ХарактеристикаНоменклатуры;

// Шаг 3: Получаем финальные данные
ВЫБРАТЬ
    Т.Номенклатура,
    Т.Стоимость / Т.Количество КАК Цена
ИЗ
    ВТ_Максимумы КАК М
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ВсеДвижения КАК Т
    ПО М.Номенклатура = Т.Номенклатура 
    И М.ХарактеристикаНоменклатуры = Т.ХарактеристикаНоменклатуры
    И М.МаксПериод = Т.Период;

Рассмотрим преимущества этого метода: использование параметров виртуальной таблицы позволяет системе 1С на уровне СУБД отсечь лишние записи еще до начала группировок, что может быть полезно на очень больших базах данных при наличии правильно настроенных индексов.

Автоматизация создания документов «Установка цен номенклатуры»

Выясним, как использовать полученные данные в программном коде. После того как запрос выполнен и данные выгружены в таблицу значений, нам нужно обойти результат и сравнить новую цену с текущей ценой в системе (которую можно получить через РегистрСведений.ЦеныНоменклатуры.СрезПоследних).

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


Установка = Документы.УстановкаЦенНоменклатуры.СоздатьДокумент();
Установка.Дата = ТекущаяДата();
Установка.Комментарий = "Установлено обработкой на основании последних закупок";

Для Каждого СтрокаТЗ из ТЗ_РезультатЗапроса Цикл
    // Проверяем, отличается ли цена (с округлением)
    Если Окр(Число(СтрокаТЗ.ЦенаУстановки), 2) <> Окр(Число(СтрокаТЗ.ЦенаЗакупки), 2) Тогда
        
        НоваяСтрока = Установка.Товары.Добавить();
        НоваяСтрока.Номенклатура = СтрокаТЗ.Номенклатура;
        НоваяСтрока.ХарактеристикаНоменклатуры = СтрокаТЗ.ХарактеристикаНоменклатуры;
        НоваяСтрока.Цена = СтрокаТЗ.ЦенаЗакупки;
        НоваяСтрока.Валюта = ВалютаРубли; // Желательно добавить пересчет валют, если закупка была в USD
        НоваяСтрока.ЕдиницаИзмерения = СтрокаТЗ.ЕдиницаХранения;
        НоваяСтрока.ТипЦен = НужныйТипЦен;
        
    КонецЕсли;
КонецЦикла;

Если Установка.Товары.Количество() > 0 Тогда
    Установка.Записать(РежимЗаписиДокумента.Проведение);
КонецЕсли;

Рекомендации по оптимизации

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

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

← На главную