При выгрузке данных в форматы обмена (например, в XML или JSON) разработчики часто сталкиваются с необходимостью параллельного подсчета итоговых сумм и количеств. В процессе отладки таких алгоритмов нередко возникают две взаимосвязанные проблемы: аварийное завершение с ошибкой «Преобразование значения к типу Число не может быть выполнено» или неожиданный результат Ложь вместо рассчитанного числа при проверке строки в табло отладчика. Разберем подробно, почему это происходит и как правильно организовать структуры данных и циклы в 1С.
Проанализируем ситуацию, когда разработчик в окне «Вычислить выражение» (Shift+F9) или в «Табло» вводит строку целиком из модуля, например:
Количество = Количество + Массив[Счетчик][4]
В кодовой базе платформы 1С символ = выполняет две функции в зависимости от контекста:
Если ... Тогда).В окне отладчика знак = всегда воспринимается исключительно как операция сравнения. Отладчик не выполняет присваивание, а проверяет, равно ли текущее значение переменной Количество (допустим, 0) сумме этой же переменной и элемента массива (0 + 1 = 1). Так как 0 не равен 1, отладчик возвращает булево значение Ложь. Это создает ложное впечатление, будто в саму переменную записывается логический тип вместо числа.
Чтобы корректно проверить результат вычисления в отладчике, необходимо передавать только правую часть выражения без присваивания: Количество + Массив[Счетчик][4].
Если в процессе выполнения кода платформа выдает исключение «Преобразование значения к типу Число не может быть выполнено» на строке сложения, выясним возможные источники проблемы:
+) завершается критической ошибкой, если один из операндов имеет тип Null, Неопределено, пустую ссылку или нечисловую строку. Если массив заполняется результатами запроса, в котором использовались левые соединения, ненайденные значения вернут Null. Для предотвращения таких ситуаций в запросе следует обязательно использовать функцию ЕСТЬNULL(Поле, 0).Массив[299][9] в условии цикла Пока Счетчик <> Массив[299][9] Цикл приводит к тому, что при изменении размерности данных или опечатке в индексе считывается некорректная ячейка либо возникает ошибка «Индекс находится за границами массива».Количество крайне нежелательно, так как оно совпадает с именем встроенного метода коллекций платформы 1С (.Количество()). Это ухудшает читаемость кода и провоцирует скрытые ошибки. Для итоговых показателей безопаснее использовать имена ИтогоКоличество или ВсегоКоличество.Хранение табличных данных в многомерных массивах с цифровыми индексами (Массив[Счетчик][0], Массив[Счетчик][4]) делает код уязвимым к любым изменениям структуры запроса. Оптимальным решением является использование объекта ТаблицаЗначений или массива структур с явными именами колонок.
Рассмотрим, как преобразовать исходный алгоритм формирования XML в надежный и читаемый вид:
// Инициализация записи XML
ЗаписьXML.ЗаписатьНачалоЭлемента("DocumentLines");
// Получаем таблицу значений с типизированными колонками из запроса
ТаблицаТоваров = ЗапросТоваров(Элемент.Значение);
// Безопасный обход строк с помощью цикла Для Каждого
Для Каждого СтрокаТовара Из ТаблицаТоваров Цикл
ЗаписьXML.ЗаписатьНачалоЭлемента("Line");
ЗаписьXML.ЗаписатьНачалоЭлемента("GroupName");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.GroupName));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("LineNumber");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.LineNumber));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("LotNumber");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.LotNumber));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("GoodCode");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.GoodCode));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("NameGoods");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.NameGoods));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("NumberGoods");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.Количество));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("UnitName");
ЗаписьXML.ЗаписатьТекст(XMLСтрока(СтрокаТовара.UnitName));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("UnitPrice");
ЗаписьXML.ЗаписатьТекст(Формат(СтрокаТовара.Цена, "ЧРД=.; ЧГ=0"));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("Currency");
ЗаписьXML.ЗаписатьТекст(СтрЗаменить(СтрокаТовара.Currency, "*", ""));
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьКонецЭлемента(); // Line
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // DocumentLines
// Подсчет общего количества без ручного сложения в цикле
ИтогоКоличество = ТаблицаТоваров.Итог("Количество");
Для Каждого автоматически обходит все строки коллекции от первой до последней, исключая выход за границы массива или бесконечное зацикливание при сбое условий.ТаблицаЗначений.Итог("ИмяКолонки") выполняет суммирование платформенными средствами на уровне C++, что работает быстрее ручного сложения в цикле и защищает от ошибок несовпадения типов.XMLСтрока() обеспечивает правильное приведение встроенных типов 1С к стандартам XML без необходимости вручную вызывать Строка().