При работе с платформой 1С:Предприятие 8, особенно когда мы имеем дело с данными, полученными из Системы Компоновки Данных (СКД), часто возникает задача обхода таблиц значений. В процессе этой работы программисты-новички (и не только) сталкиваются с рядом типовых ошибок, связанных как с синтаксисом доступа к данным, так и с проблемами сравнения различных типов значений. Давайте подробно рассмотрим эти сложности и выясним, как их корректно преодолевать, основываясь на практическом примере с форума.
Предположим, у нас есть динамический список, данные из которого необходимо вывести в таблицу значений, а затем обработать каждую строку этой таблицы. Изначальный код для получения таблицы значений из СКД выглядит следующим образом:
Схема = Элементы.СписокСтандартныйПоискНоменклатура.ПолучитьИсполняемуюСхемуКомпоновкиДанных;
Настройки = Элементы.СписокСтандартныйПоискНоменклатура.ПолучитьИсполняемыеНастройкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, , ,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
Результат = ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Переменная Результат в данном случае является объектом ТаблицаЗначений, содержащим данные, сформированные на основе настроек динамического списка. Теперь нам необходимо обойти эту таблицу и выполнить определенные действия для каждой ее строки.
Первая распространенная ошибка возникает при попытке получить значение колонки внутри цикла обхода. Автор темы столкнулся с проблемой, когда пытался обратиться к колонке Продажи следующим образом:
Для Каждого ЭлементаСписка из Результат ЦИКЛ
Добавляем = Объект.Корзина.Добавить; // Синтаксическая ошибка: нужно Добавить()
Если Результат.Продажи > 1 Тогда // Ошибка: Результат - это вся таблица, а не строка
Добавляем.Номенклатура = Результат.Колонки.Наименование; // Ошибка: Результат - это вся таблица
КонецЕсли;
КонецЦикла;
В этом фрагменте кода есть сразу несколько критических недочетов, которые приведут к ошибке:
Добавляем = Объект.Корзина.Добавить; является синтаксически некорректной. Для добавления новой строки в коллекцию (табличную часть или другую коллекцию значений) необходимо вызывать метод Добавить(). Правильно будет Добавляем = Объект.Корзина.Добавить();.Если Результат.Продажи > 1 Тогда. Переменная Результат является самой таблицей значений, а не ее текущей строкой. Когда мы используем конструкцию Для Каждого ЭлементаСписка из Результат ЦИКЛ, в каждой итерации цикла переменная ЭлементаСписка содержит ссылку на текущую строку таблицы значений. Соответственно, для доступа к значению колонки Продажи в текущей строке необходимо использовать синтаксис ЭлементаСписка.Продажи. Аналогичная ошибка допущена в строке Добавляем.Номенклатура = Результат.Колонки.Наименование;, где также следовало бы использовать ЭлементаСписка.Наименование.Правильный подход к доступу к данным внутри цикла выглядит так:
Для Каждого ЭлементаСписка из Результат ЦИКЛ
// Теперь ЭлементаСписка - это текущая строка таблицы значений
Добавляем = Объект.Корзина.Добавить(); // Корректный вызов метода
Если ЭлементаСписка.Продажи > 1 Тогда // Правильный доступ к колонке текущей строки
Добавляем.Номенклатура = ЭлементаСписка.Наименование; // Правильный доступ к колонке текущей строки
КонецЕсли;
КонецЦикла;
После исправления синтаксиса доступа к колонкам, автор темы столкнулся с новой проблемой, описанной в сообщении 7.
Когда мы исправили доступ к колонкам, могла возникнуть следующая ошибка:
Операции сравнения на больше-меньше допустимы только для значений совпадающих примитивных типов (Булево, Число, Строка, Дата) {ВнешняяОбработка.ПомощникПереноса.Форма.ФормаПодбора.Форма(7153)}: Если ЭлементаСписка.Продажи > 1 Тогда
Эта ошибка указывает на то, что мы пытаемся сравнить значение колонки ЭлементаСписка.Продажи (которое, по всей видимости, не является числом или имеет неопределенный тип) с числом 1. Причины могут быть разными:
Продажи в СКД не нашлось данных для какой-либо строки, то в соответствующей ячейке таблицы значений может оказаться Неопределено. Сравнение Неопределено > 1 вызовет ошибку.Неопределено, если СКД возвращает значения NULL из базы данных, они также могут вызвать ошибку при прямом сравнении.""). Сравнение "" > 1 также приведет к ошибке.Для решения этой проблемы нам необходимо убедиться, что значение колонки является числом и заполнено, прежде чем выполнять операцию сравнения. Для этого используются функции 1С:
ЗначениеЗаполнено(Значение): Возвращает Истина, если значение не является пустым для своего типа (например, не 0 для числа, не пустая строка для строки, не Неопределено).ТипЗнч(Значение): Возвращает тип значения переменной. Это позволяет нам явно проверить, является ли значение числом.Число(Значение): Позволяет попытаться преобразовать значение к числовому типу. Если преобразование невозможно, вернет 0.Теперь, когда мы понимаем как правильно обращаться к колонкам и какие проблемы могут возникнуть с типами данных, давайте рассмотрим полноценное и надежное решение. Мы будем использовать функции ЗначениеЗаполнено() и ТипЗнч() для проверки значений перед их использованием и сравнением.
Для Каждого ЭлементаСписка из Результат ЦИКЛ
// Шаг 1: Проверяем, заполнено ли значение в колонке "Продажи" и является ли оно числом
Если ЗначениеЗаполнено(ЭлементаСписка.Продажи) И ТипЗнч(ЭлементаСписка.Продажи) = Тип("Число") Тогда
// Шаг 2: Проверяем, заполнено ли значение в колонке "ВНаличииОстатокСкладаОтгрузки" и является ли оно числом
Если ЗначениеЗаполнено(ЭлементаСписка.ВНаличииОстатокСкладаОтгрузки) И ТипЗнч(ЭлементаСписка.ВНаличииОстатокСкладаОтгрузки) = Тип("Число") Тогда
// Шаг 3: Выполняем логику сравнения, когда оба значения гарантированно являются числами
Если ЭлементаСписка.ВНаличииОстатокСкладаОтгрузки >= ЭлементаСписка.Продажи Тогда
Добавляем = Объект.Корзина.Добавить(); // Добавляем новую строку в коллекцию "Корзина"
// Шаг 4: Проверяем, заполнено ли Наименование перед использованием
Если ЗначениеЗаполнено(ЭлементаСписка.Наименование) Тогда
// Ищем номенклатуру по наименованию. Преобразуем наименование в строку на всякий случай.
// Важно: НайтиПоНаименованию может быть неточным, если есть несколько номенклатур с одинаковым названием.
Добавляем.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию(Строка(ЭлементаСписка.Наименование));
Иначе
// Обработка случая, когда наименование не заполнено, например, устанавливаем пустую ссылку
Добавляем.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();
КонецЕсли;
Добавляем.Количество = ЭлементаСписка.Продажи;
Добавляем.КоличествоУпаковок = ЭлементаСписка.Продажи;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Разберем этот код по шагам:
Для Каждого ЭлементаСписка из Результат ЦИКЛ: Мы начинаем итерацию по каждой строке таблицы значений Результат. Переменная ЭлементаСписка на каждой итерации будет представлять собой текущую строку.Если ЗначениеЗаполнено(ЭлементаСписка.Продажи) И ТипЗнч(ЭлементаСписка.Продажи) = Тип("Число") Тогда: Это критически важная проверка. Мы убеждаемся в двух вещах:
ЗначениеЗаполнено(ЭлементаСписка.Продажи): гарантирует, что значение в колонке Продажи не является Неопределено, NULL, пустой строкой, или нулем (для числовых типов).ТипЗнч(ЭлементаСписка.Продажи) = Тип("Число"): явно проверяем, что тип значения является числовым. Это дает дополнительную гарантию от ошибок сравнения, если ЗначениеЗаполнено() для каких-то нечисловых типов возвращает Истину.ВНаличииОстатокСкладаОтгрузки.Если ЭлементаСписка.ВНаличииОстатокСкладаОтгрузки >= ЭлементаСписка.Продажи Тогда: Только после успешных проверок на заполненность и тип мы выполняем операцию сравнения. Теперь мы можем быть уверены, что сравниваем два числа.Добавляем = Объект.Корзина.Добавить();: Мы корректно добавляем новую строку в табличную часть или коллекцию значений Корзина объекта Объект и получаем ссылку на эту новую строку в переменную Добавляем.Добавляем.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию(Строка(ЭлементаСписка.Наименование));:
Строка() для явного преобразования значения из колонки Наименование в строку. Это полезно, если наименование по какой-то причине пришло не в строковом формате.Справочники.Номенклатура.НайтиПоНаименованию() ищет элемент справочника по полному наименованию. Важно учитывать, что этот метод может быть не всегда точным, если в справочнике есть несколько элементов с одинаковым наименованием или если наименование отличается регистром. Для более надежного поиска рекомендуется использовать поиск по коду или по уникальному идентификатору (GUID), если они доступны. Также мы добавили проверку ЗначениеЗаполнено(ЭлементаСписка.Наименование), чтобы избежать передачи пустого значения в метод поиска, что может привести к непредсказуемому поведению или ошибкам.Добавляем.Количество = ЭлементаСписка.Продажи; и Добавляем.КоличествоУпаковок = ЭлементаСписка.Продажи;: Мы присваиваем значения числовых колонок из таблицы СКД в соответствующие поля новой строки корзины.Для обеспечения максимальной стабильности и надежности кода при работе с таблицами значений, полученными из СКД, рассмотрим дополнительные рекомендации:
Число(Значение). Если ожидаете строку, используйте Строка(Значение). Помните, что Число(Неопределено) вернет 0, а Число("") также вернет 0, что может быть не всегда желательным. В таких случаях сначала используйте ЗначениеЗаполнено() или ТипЗнч().Неопределено или NULL (в зависимости от источника данных и настроек). Всегда предусматривайте логику обработки таких значений, чтобы ваш код не завершался ошибкой.НайтиПоНаименованию() является удобным, но не всегда надежным. Если возможно, используйте поиск по уникальным идентификаторам (например, НайтиПоКоду() или НайтиПоУникальномуИдентификатору()), чтобы избежать неоднозначности.ЭлементаСписка.Продажи, ТипЗнч(ЭлементаСписка.Продажи)) на каждой итерации цикла. Это поможет точно определить, какое значение или какой тип вызывает проблему.Следуя этим рекомендациям, вы сможете создавать более надежный и отказоустойчивый код при работе с таблицами значений, формируемыми Системой Компоновки Данных в 1С:Предприятии, минимизируя вероятность возникновения ошибок, связанных с типами данных и доступом к элементам коллекций.