Как вывести номенклатуру с нулевыми остатками по фирме в 1С 7.7, используя запросы?

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

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

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

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

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


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

Мы видим, что этот запрос использует функцию КонОст(Количество), которая возвращает конечный остаток поля Количество. Если по какой-либо номенклатуре за указанный период (С НачДата По КонДата) не было движений в регистре ОстаткиТМЦ или она никогда не числилась за указанной фирмой, функция КонОст не сможет получить по ней данные. Соответственно, такая номенклатура не попадет в результирующую выборку, даже если мы хотим видеть ее с нулевым остатком.

В ходе обсуждения на форуме прозвучало предложение добавить к группировке ключевое слово Все: |Группировка Номенклатура без групп Все; (сообщение 2). Идея этого подхода заключается в том, чтобы включить в выборку все элементы поля Номенклатура, даже если для них нет соответствующих записей в регистре. Однако на практике (сообщения 3, 4, 5) такой подход привел к тому, что в выборку попала вся номенклатура из справочника, которая хоть когда-либо имела движения, а не только та, что имеет отношение к выбранной фирме. Это может быть десятки и сотни тысяч строк, что совершенно не соответствует нашей задаче получить ограниченный список номенклатуры по конкретной фирме с нулевыми остатками, а не весь справочник.

Дальнейшее обсуждение (сообщения 8, 10) помогло нам прояснить логику проблемы. Утверждение "товар, у которого остаток по фирме 0" является неоднозначным. Если мы говорим о номенклатуре, которая никогда не проходила через выбранную фирму, то по ней всегда будет нулевой остаток. Такая номенклатура, по сути, не имеет отношения к фирме, и включать ее в отчет не всегда целесообразно. Если же нам нужен список номенклатуры, которая когда-то была в обороте у данной фирмы, но на текущий момент ее остаток равен нулю, то это уже совершенно другая задача, требующая более сложного подхода, чем простой запрос по регистру остатков. Фактически, нам нужен "базовый" список номенклатуры, по которой мы хотим отобразить остатки (включая нулевые), и этот список должен быть релевантен выбранной фирме.

Подходы к решению проблемы в 1С 7.7

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

Подход 1: Двухэтапный программный метод

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

Шаг 1: Формируем полный список номенклатуры, которая *когда-либо* была в движении по фирме

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

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


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

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Возврат; // Если по фирме не было движений вообще, выходим
КонецЕсли;

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

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

Шаг 2: Получаем текущие остатки по этой номенклатуре

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


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

Если ЗапросОстатков.Выполнить(ТекстЗапросаОстатков) = 0 Тогда
    // Если по отобранной номенклатуре нет никаких остатков, это нормально.
    // Мы все равно будем обрабатывать ТаблицуНоменклатурыФирмы, просто для всех остатки будут 0.
КонецЕсли;

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

После выполнения этого шага у нас будет две таблицы значений: ТаблицаНоменклатурыФирмы (со всеми "потенциальными" позициями, которые когда-либо были связаны с фирмой) и РезультатОстатков (с реальными остатками по тем позициям, которые их имеют на КонДата).

Шаг 3: Программная обработка и объединение результатов

Заключительный этап – это программное объединение данных из двух таблиц. Мы будем проходить по каждой номенклатуре из ТаблицаНоменклатурыФирмы и для каждой из них искать соответствующий остаток в РезультатОстатков. Если остаток найден, мы его используем; если нет – мы принудительно устанавливаем значение 0.

Для этого мы создадим итоговую таблицу значений, которую затем будем использовать для вывода, например, в XML-документ:


ИтоговаяТаблица = СоздатьОбъект("ТаблицаЗначений");
ИтоговаяТаблица.НоваяКолонка("Номенклатура", "Справочник.Номенклатура");
ИтоговаяТаблица.НоваяКолонка("Количество", "Число");
ИтоговаяТаблица.НоваяКолонка("ПродСтоимость", "Число"); // Добавляем колонку для продажной стоимости, если она нам нужна

Для Каждого СтрНоменклатура Из ТаблицаНоменклатурыФирмы Цикл
    // Ищем текущую номенклатуру в таблице с остатками
    НайденнаяСтрока = РезультатОстатков.НайтиЗначение(СтрНоменклатура.Номенклатура, "Номенклатура");
    
    // Создаем новую строку в нашей итоговой таблице
    НоваяСтрока = ИтоговаяТаблица.НоваяСтрока();
    НоваяСтрока.Номенклатура = СтрНоменклатура.Номенклатура;
    
    Если НайденнаяСтрока = 1 Тогда
        // Если номенклатура найдена в остатках, берем ее данные
        НоваяСтрока.Количество = РезультатОстатков.ПолучитьЗначение(НайденнаяСтрока, "КонОст");
        НоваяСтрока.ПродСтоимость = РезультатОстатков.ПолучитьЗначение(НайденнаяСтрока, "ПродСтоимость");
    Иначе
        // Если номенклатура не найдена в остатках, это означает, что ее остаток 0
        НоваяСтрока.Количество = 0;
        НоваяСтрока.ПродСтоимость = 0; // Или можно не выводить цену, если ее нет
    КонецЕсли;
КонецЦикла;

// Теперь ИтоговаяТаблица содержит всю номенклатуру с остатками (включая 0),
// и мы можем использовать ее для вывода в XML-документ или другой формат.

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

Подход 2: Использование Левого Внешнего Соединения (LEFT JOIN) в 1С 7.7

Данный метод, который был упомянут в дополнительных данных поиска, представляет собой более элегантный способ решения задачи, если его правильно применить. Левое внешнее соединение (ЛЕВОЕ СОЕДИНЕНИЕ) позволяет нам взять все записи из "левой" (основной) таблицы и сопоставить их с записями из "правой" (дополнительной) таблицы. Если для записи из левой таблицы нет соответствующей записи в правой, то поля, относящиеся к правой таблице, будут заполнены пустыми значениями (аналогичными NULL в SQL), которые мы впоследствии можем обработать.

Шаг 1: Формируем базовый список номенклатуры для соединения

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

Рассмотрим, как мы можем подготовить этот список:


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

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

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

// Теперь ТЗ_БазоваяНоменклатура готова к использованию в следующем запросе как источник данных.
Шаг 2: Выполняем Левое Соединение с регистром остатков

В 1С 7.7 мы можем реализовать левое соединение, используя таблицу значений, сформированную программно, как "левую" часть запроса. Это позволяет нам имитировать функциональность LEFT JOIN, свойственную полноценным SQL-базам данных.


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

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

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

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

В этом примере мы явно создаем и заполняем таблицу значений ТаблицаНоменклатуры, которая выступает в роли "левой" таблицы. Затем мы соединяем ее с данными из Регистр.ОстаткиТМЦ, используя условия по номенклатуре и фирме. Если для какой-либо номенклатуры из ТаблицаНоменклатуры не находится соответствующая запись в регистре остатков по указанной фирме, то поля Количество (КонОст) и ПродСтоимость будут содержать пустые значения. Мы программно проходим по результату и заменяем эти пустые значения на 0, чтобы получить требуемый формат данных.

Использование функции Представление: Дополнительно, если нам требуется вывести полное наименование номенклатуры, а не только ее ссылку (объект), мы можем включить в запрос функцию Представление(Номенклатура). Например: |ПредставлениеНоменклатуры = Представление(ТаблицаНоменклатуры.Номенклатура);. Это особенно полезно, когда номенклатура попадает в отчет через левое соединение, и нам нужно ее текстовое описание для вывода.

Подход 3: Комбинированный метод с расширенным периодом и последующей фильтрацией

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

В сообщении 6 предлагалось добавить в запрос другие функции, такие как Приход, Расход или НачОст. Используя эти функции вместе с достаточно широким периодом, мы можем получить все движения и начальные остатки по номенклатуре, что косвенно позволит нам выявить всю "активную" номенклатуру по фирме.


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

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

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

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

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

    // Добавляем строку в итоговую таблицу
    НоваяСтрока = ИтоговаяТаблица.НоваяСтрока();
    НоваяСтрока.Номенклатура = Стр.Номенклатура;
    НоваяСтрока.Количество = Кол;
    НоваяСтрока.ПродСтоимость = Стр.ПродСтоимость; // Аналогично обрабатываем ПродСтоимость, если она может быть пустой
КонецЦикла;

Преимущество этого подхода заключается в том, что он стремится получить максимально полный набор данных по номенклатуре и ее движениям в рамках одного запроса. Если номенклатура имела начальный остаток или движения (приход/расход) в широком периоде, она обязательно попадет в выборку. Затем мы можем извлечь из полученной таблицы КонОст, который будет корректным на КонДата. Если КонОст по какой-то причине будет пустым (что маловероятно, если номенклатура попала в выборку благодаря НачОст, Приход или Расход), мы все равно программно устанавливаем его в 0.

Особенности работы с запросами в 1С 7.7 и обработка NULL

Мы выяснили, что работа с запросами в 1С 7.7 имеет ряд особенностей, отличающихся от современных систем управления базами данных или платформы 1С 8.х. Гибкость и мощность по построению сложных запросов, особенно с множественными соединениями, подзапросами или агрегатными функциями, в 7.7 ограничены. Именно поэтому нам часто приходится прибегать к комбинации нескольких относительно простых запросов с последующей программной обработкой результатов в таблицах значений.

Критически важным аспектом является правильная обработка NULL-значений, или как их принято называть в 1С 7.7 – пустых значений. Эти значения могут появляться при использовании ЛЕВОЕ СОЕДИНЕНИЕ, когда нет соответствующих записей в правой таблице, или когда данные просто отсутствуют. Для проверки на такие значения в 1С 7.7 мы используем функцию ПустоеЗначение().

Функция ПустоеЗначение(Переменная) возвращает число 1 (Истина), если переданная переменная содержит пустое значение (например, Null, пустая строка "", число 0 для числовых типов, пустая ссылка для объектов справочников или документов) и 0 (Ложь) в противном случае. Этот механизм позволяет нам уверенно идентифицировать и заменять отсутствующие данные.

Пример использования:


Если ПустоеЗначение(Стр.Количество) = 1 Тогда
    Стр.Количество = 0; // Если остаток пустой, устанавливаем 0
КонецЕсли;

Также полезно помнить, что для получения текстового представления ссылки на объект (например, полного наименования номенклатуры из справочника) непосредственно в запросе можно использовать функцию Представление(). Например: |НаименованиеНоменклатуры = Представление(Номенклатура);. Эта функция особенно удобна, когда мы получаем ссылки на объекты через запросы или соединения и хотим сразу же получить их понятное для пользователя текстовое описание без дополнительных обращений к базе данных.

Рекомендации и лучшие практики

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

  1. Четко определите "базовый" список номенклатуры: Перед тем как приступить к написанию запроса на остатки, мы должны точно понять, какой именно набор номенклатуры должен быть включен в итоговый отчет. Это может быть:
    • Вся номенклатура, которая существует в справочнике.
    • Вся номенклатура, которая когда-либо имела движения по конкретной фирме.
    • Вся номенклатура, по которой были начальные остатки на определенную дату.
    • Номенклатура, отобранная по дополнительным критериям (например, по группе справочника, виду номенклатуры, характеристикам).
    • Выбор этого "базового" списка является ключевым и определяет сложность и структуру последующего запроса или программного кода.
  2. Используйте двухэтапный метод для ясности и контроля: Если мы сталкиваемся со сложностями в написании одного комплексного запроса, или если производительность такого запроса для больших объемов данных становится проблемой, мы рекомендуем рассмотреть двухэтапный программный подход. Он зачастую оказывается проще для отладки, понимания и дальнейшей поддержки, предоставляя полный контроль над каждым шагом обработки данных.
  3. Осторожно с ЛЕВОЕ СОЕДИНЕНИЕ: Если мы решаем использовать ЛЕВОЕ СОЕДИНЕНИЕ, крайне важно убедиться, что "левая" часть запроса (основной источник данных) содержит именно тот набор элементов, который мы хотим видеть в конечном отчете, включая "пустые" позиции. В противном случае мы рискуем получить либо слишком много ненужных данных (например, весь справочник номенклатуры), либо, наоборот, недостаточно (если "левая" часть не включает все необходимые элементы).
  4. Оптимизация производительности: Для очень больших баз данных и объемных справочников номенклатуры, выборка данных "с начала времен" может оказаться крайне медленной операцией. Мы должны стараться максимально ограничить период выборки или использовать дополнительные отборы, если это возможно, чтобы уменьшить объем обрабатываемых данных. Работа с временными таблицами значений (ТЗ) в памяти может значительно ускорить процесс по сравнению с выполнением нескольких сложных запросов к основной базе данных.
  5. Формирование XML-документа: После того как мы получили ИтоговуюТаблицу со всеми необходимыми данными (номенклатура, количество, продажная стоимость), включая корректно проставленные нулевые остатки, мы можем легко обойти эту таблицу и сформировать требуемый XML-документ, вставляя значения в соответствующие теги.

Пример кода для формирования XML-документа из нашей ИтоговойТаблицы:


// Пример формирования XML-документа из ИтоговойТаблицы
XMLДокумент = СоздатьОбъект("ДокументXML");
XMLДокумент.НовыйДокумент();
КорневойЭлемент = XMLДокумент.СоздатьЭлемент("ДанныеПоОстаткам");
XMLДокумент.ДобавитьЭлемент(КорневойЭлемент);

Для Каждого Стр Из ИтоговаяТаблица Цикл
    ЭлементНоменклатура = XMLДокумент.СоздатьЭлемент("Номенклатура");
    КорневойЭлемент.ДобавитьЭлемент(ЭлементНоменклатура);

    ЭлементИД = XMLДокумент.СоздатьЭлемент("ИД");
    ЭлементИД.Значение = Стр.Номенклатура.Код; // Или GUID, если нужен уникальный идентификатор
    ЭлементНоменклатура.ДобавитьЭлемент(ЭлементИД);

    ЭлементНаименование = XMLДокумент.СоздатьЭлемент("Наименование");
    ЭлементНаименование.Значение = Стр.Номенклатура.Наименование; // Получаем наименование номенклатуры
    ЭлементНоменклатура.ДобавитьЭлемент(ЭлементНаименование);

    ЭлементКоличество = XMLДокумент.СоздатьЭлемент("Количество");
    ЭлементКоличество.Значение = Стр.Количество;
    ЭлементНоменклатура.ДобавитьЭлемент(ЭлементКоличество);

    ЭлементЦена = XMLДокумент.СоздатьЭлемент("ЦенаПродажи");
    ЭлементЦена.Значение = Стр.ПродСтоимость;
    ЭлементНоменклатура.ДобавитьЭлемент(ЭлементЦена);
КонецЦикла;

// Теперь XMLДокумент готов. Мы можем сохранить его в файл или передать как строку.
XMLДокумент.Записать("ОстаткиНоменклатуры.xml");

Заключение

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

← На главную