При обработке данных после выполнения запроса разработчики 1С нередко сталкиваются с ситуацией: в результирующую таблицу значений необходимо записать отрицательное число, однако платформа либо преобразует его в ноль, либо не позволяет скорректировать свойства типа колонки, выдавая ошибку «Поле недоступно для записи» (или «Поле только для чтения»). Разберем подробно, почему возникает такое поведение платформы и какими способами можно корректно настроить допустимый знак числовой колонки.
Проанализируем механизм типизации в платформе 1С:Предприятие. Когда выполняется запрос, поля выборки наследуют типы данных и их квалификаторы непосредственно из объектов метаданных (например, ресурсов регистров накопления, реквизитов документов или табличных частей). Если в конфигураторе для ресурса был установлен квалификатор числа Неотрицательное, то при выгрузке результата запроса в таблицу значений колонка создается с объектом ОписаниеТипов, содержащим ДопустимыйЗнак.Неотрицательный.
Попытка изменить свойство напрямую через код:
РезультатЗапроса.Колонки.КоличествоОборот.ТипЗначения.КвалификаторыЧисла.ДопустимыйЗнак = ДопустимыйЗнак.Любой;
приводит к ошибке выполнения. Это связано со следующими правилами платформы:
ОписаниеТипов и КвалификаторыЧисла являются неизменяемыми после создания. Все их свойства доступны только для чтения.ТипЗначения у объекта КолонкаТаблицыЗначений также доступно только для чтения и не может быть переопределено «на лету».ДопустимыйЗнак.Неотрицательный, платформа автоматически выполняет приведение к допустимому диапазону — число обращается в 0.Рассмотрим основные способы решения этой задачи — как на уровне текста запроса, так и средствами встроенного языка при работе с таблицей значений.
Наиболее эффективный и производительный подход — задать нужный тип еще на этапе формирования запроса. В языке запросов 1С функция ВЫРАЗИТЬ() для числового типа всегда возвращает поле с допустимым знаком Любой.
Посмотрим на пример текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВЫРАЗИТЬ(ТоварыНаСкладахОбороты.КоличествоОборот КАК ЧИСЛО(15, 3)) КАК КоличествоОборот,
| ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ТоварыНаСкладахОбороты";
РезультатТаблица = Запрос.Выполнить().Выгрузить();
После выгрузки такого запроса колонка КоличествоОборот в полученной таблице значений будет иметь квалификатор знака Любой, что позволит свободно записывать в нее как положительные, так и отрицательные значения в последующих циклах обработки.
Если знак числа или логика его расчета известны заранее, операцию можно перенести непосредственно в запрос. Любое математическое выражение (умножение, вычитание) или использование конструкции ВЫБОР КОГДА ... ТОГДА ... КОНЕЦ автоматически снимает ограничение на неотрицательность поля.
Разберем пример условной смены знака в запросе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура,
| ВЫБОР
| КОГДА ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
| ТОГДА -Количество
| ИНАЧЕ Количество
| КОНЕЦ КАК Количество
|ИЗ
| Документ.РасходнаяНакладная.Товары";
РезультатТаблица = Запрос.Выполнить().Выгрузить();
Если запрос изменить невозможно или таблица формируется из стороннего источника, структуру таблицы значений можно скорректировать программно: добавить новую колонку с нужным ОписаниеТипов, перенести данные и удалить старую колонку.
Разберем алгоритм по шагам:
КвалификаторыЧисла с указанием необходимой разрядности, дробной части и допустимого знака ДопустимыйЗнак.Любой.ОписаниеТипов на основе числового типа и подготовленных квалификаторов.Посмотрим на программную реализацию:
// 1. Формируем описание типа с любым знаком
Квалификаторы = Новый КвалификаторыЧисла(15, 3, ДопустимыйЗнак.Любой);
НовыйТип = Новый ОписаниеТипов("Число", Квалификаторы);
// 2. Добавляем временную колонку
ТаблицаРезультат.Колонки.Добавить("КоличествоОборот_Временная", НовыйТип);
// 3. Заполняем временную колонку значениями из исходной
ТаблицаРезультат.ЗагрузитьКолонку(
ТаблицаРезультат.ВыгрузитьКолонку("КоличествоОборот"),
"КоличествоОборот_Временная"
);
// 4. Удаляем старую колонку с ограничением
ТаблицаРезультат.Колонки.Удалить("КоличествоОборот");
// 5. Переименовываем временную колонку в исходное имя
ТаблицаРезультат.Колонки.КоличествоОборот_Временная.Имя = "КоличествоОборот";
Если таблица значений заполняется вручную через выборку из результата запроса (метод Выбрать()), можно заранее создать пустую таблицу без строгой типизации колонок.
Посмотрим на пример ручного создания структуры:
// Создаем колонку без указания типа — в нее можно поместить любое значение
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура");
Таблица.Колонки.Добавить("КоличествоОборот");
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Номенклатура = Выборка.Номенклатура;
// Вычисляем или инвертируем значение без ошибок усечения
НоваяСтрока.КоличествоОборот = -Выборка.КоличествоОборот;
КонецЦикла;
Таким образом, для исключения ошибок записи отрицательных чисел оптимально применять функцию ВЫРАЗИТЬ() в запросе либо выполнять пересоздание колонки таблицы значений с корректным объектом ОписаниеТипов.