При разработке и поддержке конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются со специфическим поведением элементов интерактивного интерфейса. Одна из классических проблем связана с обработкой формул в колонках табличной части документа. Пользователи вводят данные, программа их обрабатывает, но при переходе на следующую строку или нажатии клавиши Enter расчеты неожиданно производятся заново, приводя к порче введенных данных.
В рамках этой статьи мы подробно разберем, почему возникает эффект многократного деления и округления числовых показателей (например, при переводе килограммов в тонны), и рассмотрим несколько надежных практических способов обхода этого ограничения платформы.
Для начала проанализируем ситуацию и поймем, почему стандартный код работает некорректно. Представим процедуру, которая вызывается из формулы колонки ВесБрутто при изменении значения:
Процедура ВводКоличества()
ВесБруттоТн = Окр(ВесБрутто/1000, 2); // Используется в дальнейшем расчете
ВесБрутто = ВесБруттоТн; // Переприсвоение для отображения на форме
КонецПроцедуры
На первый взгляд логика верна: пользователь вводит вес в килограммах (например, 3151.3), система делит его на 1000, округляет до двух знаков после запятой (получается 3.15) и записывает обратно в реквизит ВесБрутто, чтобы на экране сразу отобразилось значение в тоннах.
Однако на практике при нажатии клавиши Enter или попытке перейти в другую строку значение в ячейке внезапно превращается в 0.01 или 0.00. Выясним, почему это происходит:
ПриИзменении срабатывает строго один раз в момент фиксации ввода пользователем, формула колонки в 1С 7.7 работает гораздо более агрессивно.Форма.Обновить(), если курсор находится на редактируемой строке.3151.3 и нажимает Enter, процедура ВводКоличества() выполняется в первый раз. Она делит значение на 1000 и записывает в ячейку 3.15.ВесБрутто уже лежит число 3.15! Система берет его, снова делит на 1000, получает 0.00315, округляет до двух знаков и записывает результат 0.00.Кроме того, при таком подходе абсолютно невозможно исправить ошибку ввода. Если пользователь попытается вручную скорректировать получившиеся 3.15 на 3.20, система применит алгоритм деления к значению 3.20 и превратит его в ничтожно малую величину.
Рассмотрим четыре эффективных метода решения этой проблемы, которые обычно применяются практикующими разработчиками 1С.
Самый надежный и архитектурно правильный способ — разделить физический ввод данных пользователем и расчетный показатель, который используется в формулах и выводится в печатные формы. Это полностью исключит рекурсивные расчеты и путаницу.
Сделаем следующие шаги:
ВесБруттоКг (числовой, точность 1 или 2 знака). Именно в это поле пользователь будет вводить исходный вес в килограммах.ВесБруттоТн сделаем доступным только для просмотра (снимем флаг "Доступность" в свойствах колонки на форме). В нем будет храниться и отображаться итоговый вес в тоннах.ВесБруттоКг поместим вызов процедуры перерасчета.Посмотрим на пример программного кода для реализации этого механизма:
Процедура ВводКоличества()
// Рассчитываем значение в тоннах на основе введенных килограммов
ВесБруттоТн = Окр(ВесБруттоКг / 1000, 2);
// Рассчитываем сумму без НДС
СуммаБНДС = Цена * Дни * ВесБруттоТн;
КонецПроцедуры
При таком подходе пользователь всегда видит исходные данные, которые он ввел (в килограммах), и рядом — расчетный результат в тоннах. Если допущена ошибка, ее легко исправить: достаточно изменить значение в колонке килограммов, и система корректно обновит результат в тоннах без риска повторного деления.
Если бизнес-требования или дизайн формы жестко ограничивают добавление новых колонок, и нам необходимо производить ввод и отображение в пределах одного и того же поля, мы можем задействовать флаг состояния. В качестве такого флага отлично подойдет единица измерения.
Разберем этот вариант по шагам:
ЕдИзм (тип "Строка" или ссылка на справочник единиц измерения).ЕдИзм = "кг".Проанализируем пример кода:
Процедура ВводКоличества()
// Проверяем, в каких единицах сейчас введены данные
Если СокрЛП(ЕдИзм) = "кг" Тогда
ВесБрутто = Окр(ВесБрутто / 1000, 2);
ЕдИзм = "тн"; // Меняем единицу измерения, фиксируя перевод в тонны
КонецЕсли;
// Рассчитываем сумму
СуммаБНДС = Цена * Дни * ВесБрутто;
КонецПроцедуры
Важное примечание: Чтобы дать пользователю возможность исправить ошибочно введенный вес, нам потребуется механизм сброса флага обратно в "кг". Для этого можно использовать предопределенную процедуру формы ПриВыбореСтроки(). Если пользователь дважды кликает по ячейке веса для редактирования, мы принудительно возвращаем единицу измерения в "кг" и умножаем текущее значение на 1000, возвращая его к исходному виду для редактирования.
Процедура ПриВыбореСтроки()
Если Форма.ТекущаяКолонка() = "ВесБрутто" Тогда
Если СокрЛП(ЕдИзм) = "тн" Тогда
// Возвращаем значение в кг для редактирования
ВесБрутто = ВесБрутто * 1000;
ЕдИзм = "кг";
Форма.Обновить(0); // Обновляем форму без вызова формул
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот подход часто применяется в типовых конфигурациях линейки 1С:Предприятие 7.7 (например, в "Торговле и Складе"). Он полностью исключает использование формулы колонки для обработки ввода, перекладывая эту задачу на стандартные диалоговые окна платформы.
Разберем алгоритм реализации данного метода:
ВесБрутто на форме недоступной для прямого редактирования в табличной части (снимаем галочку "Редактирование в списке").ПриВыбореСтроки().ВвестиЧисло(), запрашивая у пользователя вес строго в килограммах.Реализуем это в коде:
Процедура ПриВыбореСтроки()
Если Форма.ТекущаяКолонка() = "ВесБрутто" Тогда
ВременныйВесКг = 0;
// Открываем модальное окно ввода числа
Если ВвестиЧисло(ВременныйВесКг, "Введите вес груза в килограммах (кг):", 12, 2) = 1 Тогда
// Расчет производится ровно один раз при нажатии кнопки "ОК"
ВесБрутто = Окр(ВременныйВесКг / 1000, 2);
СуммаБНДС = Цена * Дни * ВесБрутто;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот способ гарантирует абсолютную стабильность данных, так как платформа физически не может запустить процедуру перерасчета повторно при перемещении курсора по табличной части.
Для разработчиков, которые не боятся использовать внешние библиотеки расширения возможностей 1С 7.7, идеальным решением станет использование популярной компоненты FormEx.
Эта компонента позволяет перехватывать расширенные события элементов управления формы. В штатном функционале 1С 7.7 нет события, аналогичного "ПослеРедактированияЯчейки", но FormEx предоставляет его. Перехватив это событие, мы можем выполнять процедуру перевода килограммов в тонны и перерасчет суммы только тогда, когда пользователь действительно завершил интерактивный ввод нового значения в конкретную ячейку, игнорируя системные обновления экрана или обычные перемещения фокуса.
Мы рассмотрели различные подходы к устранению проблемы циклического расчета в табличной части на платформе 1С 7.7. Подведем итоги: