Как в 1С 7.7 получить цену закупки на момент реализации?

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

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

Ограничения штатного языка запросов 1С 7.7 («черных запросов»)

Рассмотрим, почему нельзя решить эту задачу простым единственным запросом встроенного языка 1С 7.7:

  1. Отсутствие подзапросов и произвольных соединений (JOIN): В штатном языке запросов 7.7 нельзя связать табличную часть документов реализации с документами поступления по условию Поступление.Дата <= Реализация.Дата.
  2. Отсутствие ограничения выборки (аналога TOP 1 / LIMIT): В штатном запросе нельзя заставить систему прекратить поиск после нахождения первой подходящей записи при сортировке по убыванию. Запрос всегда обходит весь указанный диапазон данных.
  3. Необходимость учета точной позиции документа: Если поступление и реализация оформлены в один день (например, поступление в 11:00, а продажа в 12:00), сравнения по одной лишь дате недостаточно. В 1С 7.7 хронология определяется позицией документа (12-символьная строка, включающая дату, время и внутренний идентификатор).

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

Способ 1. Алгоритмическая обработка через Таблицу Значений (без внешних компонент)

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

Рассмотрим алгоритм реализации по шагам:

  1. Составляем один запрос, который выбирает все документы поступления и реализации за требуемый период по нужным позициям номенклатуры.
  2. Выгружаем результат запроса в Таблицу Значений методом Выгрузить(ТЗ).
  3. Добавляем в ТЗ колонку для точной позиции документа с помощью функции СформироватьПозициюДокумента().
  4. Сортируем Таблицу Значений по колонкам Товар и ПозицияDoc в порядке возрастания.
  5. Обходим ТЗ последовательно в цикле:
    • Если текущая строка — документ поступления, запоминаем его цену как текущую цену закупа для данного товара.
    • Если текущая строка — документ реализации, записываем ранее запомненную цену закупа как цену на момент этой продажи.

Посмотрим на пример реализации данного алгоритма на языке 1С 7.7:


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

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Возврат;
КонецЕсли;

// 2. Выгружаем в Таблицу Значений
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ);

// 3. Добавляем колонку для сортировки по точной хронологической позиции
ТЗ.НоваяКолонка("ПозицияDoc", "Строка");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    ТЗ.ПозицияDoc = СформироватьПозициюДокумента(ТЗ.Док, 0);
КонецЦикла;

// 4. Сортируем по Товару и точной хронологии
ТЗ.Сортировать("Товар,ПозицияDoc");

// 5. Вычисляем закупочные цены на момент реализации
ТекущийТовар = ПолучитьПустоеЗначение("Справочник.Номенклатура");
АктуальнаяЦенаЗакупа = 0;

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

Способ 2. Прямые SQL-запросы через компоненту 1С++

Если ваша база 1С 7.7 работает на базе MS SQL Server и используется внешняя компонента 1С++, проблему можно решить на стороне СУБД с помощью коррелированных подзапросов или оконных функций.

Рассмотрим пример использования конструкции OUTER APPLY (доступна в MS SQL Server 2005 и новее):


SELECT 
    DocSale.IDDOC as [Реализация $Документ.Реализация],
    DocSaleLines.SP1234 as [Товар $Справочник.Номенклатура],
    LastPurchase.Price as [ЦенаЗакупа]
FROM 
    [1S_DOCS] DocSale (NOLOCK)
INNER JOIN 
    [DT123] DocSaleLines (NOLOCK) ON DocSaleLines.IDDOC = DocSale.IDDOC
OUTER APPLY (
    SELECT TOP 1 
        DocInLines.SP5678 as Price
    FROM 
        [1S_DOCS] DocIn (NOLOCK)
    INNER JOIN 
        [DT567] DocInLines (NOLOCK) ON DocInLines.IDDOC = DocIn.IDDOC
    WHERE 
        DocInLines.SP1234 = DocSaleLines.SP1234 -- тот же товар
        AND DocIn.DATE_TIME_IDDOC <= DocSale.DATE_TIME_IDDOC -- до момента продажи
    ORDER BY 
        DocIn.DATE_TIME_IDDOC DESC
) As LastPurchase
WHERE 
    DocSale.IDDOCDEF = $ВидДокумента.Реализация

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

Способ 3. Использование периодических реквизитов справочника

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

В таком случае при проведении поступления обновляется значение реквизита ЗакупочнаяЦена на дату документа. А при проведении реализации или построении отчета значение цены считывается методом Получить(ДатаДокумента) на момент совершения продажи.

Важные замечания

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

← На главную