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

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

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

Особенности хранения данных о продажах

В типовых конфигурациях (таких как УТ 10.3, КА 1.1 или УПП 1.3) информация о совершенных продажах хранится в регистре накопления Продажи. В отличие от регистров сведений, у которых есть виртуальная таблица СрезПоследних, регистры накопления хранят обороты. Поэтому получение «последнего» значения требует особого подхода к построению запроса.

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

Решение 1: Использование временных таблиц (оптимальный метод)

Проанализируем наиболее эффективный способ решения задачи через запрос. Мы разделим логику на два этапа: поиск максимальной даты продажи для каждой позиции номенклатуры и последующее соединение с данными регистра для получения цены.

Рассмотрим пример кода запроса:


ВЫБРАТЬ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    МАКСИМУМ(ПродажиОбороты.Период) КАК Период
ПОМЕСТИТЬ ВТ_ПоследниеДаты
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, Контрагент = &Контрагент) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
    ПродажиОбороты.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ПоследниеДаты.Номенклатура,
    ВЫБОР
        КОГДА Продажи.Количество <> 0
            ТОГДА Продажи.Стоимость / Продажи.Количество
        ИНАЧЕ 0
    КОНЕЦ КАК ЦенаПоследнейПродажи
ИЗ
    ВТ_ПоследниеДаты КАК ВТ_ПоследниеДаты
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи КАК Продажи
        ПО ВТ_ПоследниеДаты.Период = Продажи.Период
            И ВТ_ПоследниеДаты.Номенклатура = Продажи.Номенклатура
ГДЕ
    Продажи.Контрагент = &Контрагент

Разберем по шагам, что мы сделали:

  1. В первой части запроса мы обращаемся к виртуальной таблице Продажи.Обороты. Мы фильтруем данные по конкретному Контрагенту и используем группировку по Номенклатуре, выбирая МАКСИМУМ(Период). Это позволяет нам получить точное время последней продажи для каждой позиции товара. Результат сохраняем во временную таблицу ВТ_ПоследниеДаты.
  2. Во второй части мы соединяем нашу временную таблицу с основной таблицей регистра накопления Продажи. Соединение происходит по двум полям: Номенклатура и Период.
  3. В поле выбора мы производим расчет цены. Обязательно используем конструкцию ВЫБОР, чтобы избежать ошибки деления на ноль, если в системе по какой-то причине зафиксирована продажа с нулевым количеством.

Решение 2: Получение цены перед текущим документом

Часто в печатной форме документа РеализацияТоваровУслуг требуется вывести цену, которая была до текущей продажи. Если мы просто возьмем последнюю дату из регистра, запрос вернет цену из того самого документа, который мы сейчас печатаем.

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


// В параметрах запроса устанавливаем границу периода
Запрос.УстановитьПараметр("КонецПериода", Новый Граница(Объект.Ссылка.МоментВремени(), ВидГраницы.Исключая));

Используя объект Граница с параметром ВидГраницы.Исключая, мы указываем системе, что нужно анализировать все записи регистра вплоть до текущего документа, но не включая его самого. Это гарантирует, что МАКСИМУМ(Период) найдет именно предыдущую продажу.

Решение 3: Получение цены для конкретной позиции (в цикле)

Если задача стоит максимально просто — например, при изменении номенклатуры в строке документа нужно быстро подтянуть цену — можно использовать упрощенный запрос с конструкцией ВЫБРАТЬ ПЕРВЫЕ 1. Хотя выполнение запросов в цикле считается плохим тоном в 1С, для единичных операций (интерактивная работа пользователя) это допустимо.

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


ВЫБРАТЬ ПЕРВЫЕ 1
    Продажи.Стоимость / Продажи.Количество КАК Цена
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Контрагент = &Контрагент
    И Продажи.Номенклатура = &Номенклатура
    И Продажи.Период < &ТекущийМомент
УПОРЯДОЧИТЬ ПО
    Продажи.Период УБЫВ

Здесь мы используем физическую таблицу регистра, сортируем записи по Период УБЫВ и берем самую первую (то есть самую свежую) запись. Этот метод прост в реализации, но менее производителен при работе с большими списками товаров.

Важные технические нюансы

При реализации этих алгоритмов следует учитывать несколько критических моментов:

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

← На главную