При разработке обработок загрузки данных из внешних источников (в частности, из файлов Microsoft Excel) программисты часто сталкиваются с ситуацией, когда числовые данные загружаются некорректно, выборочно или вызывают ошибки при записи объектов. Особые трудности могут возникнуть при работе с реквизитами, имеющими тип ОпределяемыйТип (например, ОпределяемыйТип.ДенежноеПолеНеотрицательное или ОпределяемыйТип.ДенежнаяСуммаНеотрицательная), которые широко используются в конфигурациях 1С:ERP, Комплексная автоматизация и Управление торговлей.
В этой статье мы подробно разберем, почему возникает подобная проблема при чтении данных через COM-объект Excel, в чем заключается разница между свойствами ячейки .Text и .Value, а также как правильно приводить типы данных и обрабатывать исключения, чтобы гарантировать 100% загрузку данных.
Рассмотрим классическую ситуацию. Программист пытается считать значение из ячейки Excel и записать его в реквизит табличной части документа (например, НакопленнаяАмортизацияБУ в документе ВводОстатковВнеоборотныхАктивов2_4):
НоваяСтрока.НакопленнаяАмортизацияБУ = ExcelЛист.Cells(Row, 18).Text;
При таком подходе данные переносятся успешно лишь в редких случаях (около 10%). Выясним причину такого поведения системы. Дело в том, что свойство .Text возвращает строковое представление значения ячейки с учетом примененного к ней визуального форматирования в самом Excel:
"12 500,00"), свойство .Text вернет именно эту строку с пробелом и запятой."###", свойство .Text вернет строку "###".Число. Попытка записать такую строку в числовой реквизит или реквизит определяемого типа вызывает тихий сбой (значение просто не устанавливается или сбрасывается в 0) либо приводит к ошибке времени выполнения.Для того чтобы избежать искажения данных из-за визуального оформления ячеек, необходимо считывать их внутреннее (чистое) значение. Для этого вместо свойства .Text следует использовать свойство .Value.
Свойство .Value возвращает реальное содержимое ячейки в исходном типе данных COM. Если в ячейке находится число, COM-объект вернет тип Double (число с плавающей точкой), которое платформа 1С без труда преобразует в свой внутренний тип Число.
Рассмотрим пример правильного считывания:
Износ = ExcelЛист.Cells(Row, 18).Value;
НоваяСтрока.НакопленнаяАмортизацияБУ = Износ;
Профессиональный совет: В сценариях автоматизации Excel еще более эффективным является использование свойства .Value2. В отличие от .Value, свойство .Value2 не выполняет внутреннюю проверку и конвертацию форматов даты и времени, а также денежных форматов на стороне Excel. Оно возвращает чистые числа или строки намного быстрее, что существенно ускоряет загрузку больших файлов.
Иногда формат файла Excel вынуждает разработчика считывать данные как текст (например, если ячейки имеют текстовый формат, но содержат числа с разделителями разрядов). Обычная попытка очистить строку с помощью стандартной функции СтрЗаменить(Строка, " ", "") не принесет результата.
Разберем, почему это происходит. В качестве разделителя групп разрядов в Excel используется не обычный пробел (код ASCII 32), а неразрывный пробел (код ASCII 160, или Символы.НПП в 1С). Чтобы гарантированно преобразовать такую строку в число, выполним предобработку текста:
ИсходныйТекст = ExcelЛист.Cells(Row, 18).Text;
// Удаляем обычные пробелы
ОчищеннаяСтрока = СтрЗаменить(ИсходныйТекст, " ", "");
// Удаляем неразрывные пробелы (код 160)
ОчищеннаяСтрока = СтрЗаменить(ОчищеннаяСтрока, Символы.НПП, "");
// Заменяем точку на запятую для корректного приведения типов в локализованной версии 1С
ОчищеннаяСтрока = СтрЗаменить(ОчищеннаяСтрока, ".", ",");
Попытка
НоваяСтрока.НакопленнаяАмортизацияБУ = Число(ОчищеннаяСтрока);
Исключение
НоваяСтрока.НакопленнаяАмортизацияБУ = 0; // значение по умолчанию при ошибке формата
КонецПопытки;
Объект конфигурации Определяемый тип в 1С предназначен для централизованного описания типов данных. На уровне платформы определяемый тип всегда сводится к одному или нескольким базовым (примитивным) типам. Для типа ОпределяемыйТип.ДенежноеПолеНеотрицательное базовым типом является Число (обычно с длиной 15, точностью 2 и признаком неотрицательности).
Работа с таким реквизитом в программном коде абсолютно идентична работе с обычным числовым реквизитом. Тем не менее, хорошим тоном является строгое явное приведение типов данных перед записью в базу данных:
ЗначениеИзЭкселя = ExcelЛист.Cells(Row, 18).Value;
// Защита от пустых значений (Неопределено, NULL или пустая строка)
Если ЗначениеИзЭкселя = Неопределено Тогда
ЧисловоеЗначение = 0;
Иначе
Попытка
ЧисловоеЗначение = Число(ЗначениеИзЭкселя);
// Дополнительная валидация на знак, так как тип реквизита "Неотрицательное"
Если ЧисловоеЗначение < 0 Тогда
ЧисловоеЗначение = -ЧисловоеЗначение; // или 0, в зависимости от бизнес-логики
КонецЕсли;
Исключение
ЧисловоеЗначение = 0;
КонецПопытки;
КонецЕсли;
НоваяСтрока.НакопленнаяАмортизацияБУ = ЧисловоеЗначение;
Проанализируем архитектурные изменения в типовых решениях фирмы "1С". Начиная с выпусков линеек 2.5 (ERP 2.5, КА 2.5, УТ 11.5), разработчики провели глобальный рефакторинг метаданных системных конфигураций.
Многие определяемые типы, использовавшиеся годами, были упразднены или переименованы:
ОпределяемыйТип.ДенежноеПолеНеотрицательное и ОпределяемыйТип.ДенежноеПолеЛюбойЗнак были полностью удалены из состава метаданных.ОпределяемыйТип.ДенежнаяСуммаНеотрицательная и ОпределяемыйТип.ДенежнаяСуммаЛюбогоЗнака.Если вы разрабатываете внешнюю обработку, предназначенную для работы в разных версиях ERP или КА, избегайте жесткой привязки к конкретным определяемым типам в коде. Всегда преобразуйте считываемые значения к универсальному типу Число. Это обеспечит обратную совместимость вашего программного решения и предотвратит возникновение критических ошибок компиляции при обновлении конфигурации клиента.