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

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

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

Постановка задачи и особенности платформы 1С 7.7

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

Главная трудность заключается в том, что в типовых конфигурациях 1С 7.7 (например, в Комплексной или Торговле и Складе) регистр резервов ТМЦ не хранит партионный разрез. Резерв формируется на уровне номенклатуры в целом. Встроенный язык запросов 1С 7.7 не поддерживает вложенные подзапросы и конструкции связывания JOIN между разными регистрами. Поэтому задачу приходится решать в два этапа: сначала запрашивать данные по регистрам, а затем распределять количества в коде обработки.

Способ 1. Запрос к регистру партий и табличная зачистка резервов

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

Рассмотрим пример кода на встроенном языке 1С 7.7:


// Этап 1. Получаем остатки резервов в разрезе номенклатуры
ТекстЗапросаРезервы = 
" |Номенклатура = Регистр.РезервыТМЦ.Номенклатура;
 |Количество = Регистр.РезервыТМЦ.Количество;
 |Функция КоличествоКонОст = КонОст(Количество);
 |Группировка Номенклатура без групп;";

ЗапросРезервы = СоздатьОбъект("Запрос");
Если ЗапросРезервы.Выполнить(ТекстЗапросаРезервы) <> 1 Тогда
    Возврат;
КонецЕсли;

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

// Этап 2. Получаем остатки по партиям
ТекстЗапросаПартии = 
" |Номенклатура = Регистр.ПартииНаличие.Номенклатура;
 |ПриходныйДокумент = Регистр.ПартииНаличие.Партия.ПриходныйДокумент;
 |Количество = Регистр.ПартииНаличие.Количество;
 |Функция КоличествоКонОст = КонОст(Количество);
 |Группировка Номенклатура без групп;
 |Группировка ПриходныйДокумент;";

ЗапросПартии = СоздатьОбъект("Запрос");
Если ЗапросПартии.Выполнить(ТекстЗапросаПартии) <> 1 Тогда
    Возврат;
КонецЕсли;

// Этап 3. Обход результатов и распределение резерва
Пока ЗапросПартии.Группировка(1) = 1 Цикл
    Строчка = 0;
    Если ТаблицаРезервов.НайтиЗначение(ЗапросПартии.Номенклатура, Строчка, "Номенклатура") = 1 Тогда
        КоличествоРезерва = ТаблицаРезервов.ПолучитьЗначение(Строчка, "КоличествоКонОст");
    Иначе
        КоличествоРезерва = 0;
    КонецЕсли;
    
    Пока ЗапросПартии.Группировка(2) = 1 Цикл
        Если ЗапросПартии.КоличествоКонОст > 0 Тогда
            СвободныйОстатокПоДокументу = ЗапросПартии.КоличествоКонОст - Макс(КоличествоРезерва, 0);
            КоличествоРезерва = Макс(КоличествоРезерва - ЗапросПартии.КоличествоКонОст, 0);
            
            Если СвободныйОстатокПоДокументу > 0 Тогда
                // Здесь выполняем необходимые действия с позитивным свободным остатком
                // Например, выгружаем в Таблицу Значений или выводим в печатную форму
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Способ 2. Обход группировок запроса в обратном порядке

Выясним, как управлять сортировкой результатов группировок при их выгрузке. В методе Группировка() объекта Запрос предусмотрен второй необязательный параметр, определяющий направление выборки:

  1. Значение 1 — выборка по возрастанию (по умолчанию).
  2. Значение -1 — выборка по убыванию (обратный порядок).

Посмотрим на пример применения обратной сортировки для анализа свежих партий:


Пока Запрос.Группировка(1) = 1 Цикл
    // Обходим партии в обратном порядке (от самых свежих к старым)
    Пока Запрос.Группировка(2, -1) = 1 Цикл
        ДокументПрихода = Запрос.ПриходныйДокумент;
        ОстатокПартии = Запрос.КоличествоКонОст;
    КонецЦикла;
КонецЦикла;

Причины возможного искажения данных и рекомендации

Проанализируем ситуации, которые могут привести к получению неверных результатов в реальных базах данных 1С 7.7:

  1. Нарушение последовательности партий. Проведение документов задним числом или неоперативное проведение приводит к появлению служебных партий или искажению их остатков. Перед выгрузкой отчета рекомендуется выполнять восстановление последовательности партионного учета.
  2. Отрицательные остатки («краснота»). Если в базе разрешено списание в минус, платформа создает технические партии. При анализе партионного учета эти записи требуют предварительной фильтрации.
  3. Производительность на больших объемах. При расчете остатков на давнюю прошедшую дату платформа 1С 7.7 последовательно пересчитывает движения от ближайшей точки итогов. Для работы с высоконагруженными базами данных в формате SQL эффективнее использовать прямые T-SQL запросы через внешние компоненты 1С++ или ODBC к таблицам RG* и RA*.
← На главную