Как решить проблему с повторным делением и округлением чисел в табличной части на 1С 7.7?

Программист 1С v7.7 Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке и поддержке конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются со специфическим поведением элементов интерактивного интерфейса. Одна из классических проблем связана с обработкой формул в колонках табличной части документа. Пользователи вводят данные, программа их обрабатывает, но при переходе на следующую строку или нажатии клавиши Enter расчеты неожиданно производятся заново, приводя к порче введенных данных.

В рамках этой статьи мы подробно разберем, почему возникает эффект многократного деления и округления числовых показателей (например, при переводе килограммов в тонны), и рассмотрим несколько надежных практических способов обхода этого ограничения платформы.

Выясняем причину: особенности работы формул колонок в 1С 7.7

Для начала проанализируем ситуацию и поймем, почему стандартный код работает некорректно. Представим процедуру, которая вызывается из формулы колонки ВесБрутто при изменении значения:


Процедура ВводКоличества()             
    ВесБруттоТн = Окр(ВесБрутто/1000, 2); // Используется в дальнейшем расчете 
    ВесБрутто   = ВесБруттоТн;            // Переприсвоение для отображения на форме
КонецПроцедуры

На первый взгляд логика верна: пользователь вводит вес в килограммах (например, 3151.3), система делит его на 1000, округляет до двух знаков после запятой (получается 3.15) и записывает обратно в реквизит ВесБрутто, чтобы на экране сразу отобразилось значение в тоннах.

Однако на практике при нажатии клавиши Enter или попытке перейти в другую строку значение в ячейке внезапно превращается в 0.01 или 0.00. Выясним, почему это происходит:

  1. В отличие от платформы 1С 8.х, где обработчик ПриИзменении срабатывает строго один раз в момент фиксации ввода пользователем, формула колонки в 1С 7.7 работает гораздо более агрессивно.
  2. Она вычисляется при любом интерактивном изменении значения, при потере фокуса ячейкой (когда пользователь нажимает Enter, Tab или кликает мышкой по другой строке), а также при программном вызове метода Форма.Обновить(), если курсор находится на редактируемой строке.
  3. Когда пользователь вводит число 3151.3 и нажимает Enter, процедура ВводКоличества() выполняется в первый раз. Она делит значение на 1000 и записывает в ячейку 3.15.
  4. При выходе из ячейки (так как фокус смещается на другую строку или реквизит) формула колонки срабатывает повторно. Но теперь в переменной ВесБрутто уже лежит число 3.15! Система берет его, снова делит на 1000, получает 0.00315, округляет до двух знаков и записывает результат 0.00.

Кроме того, при таком подходе абсолютно невозможно исправить ошибку ввода. Если пользователь попытается вручную скорректировать получившиеся 3.15 на 3.20, система применит алгоритм деления к значению 3.20 и превратит его в ничтожно малую величину.

Рассмотрим четыре эффективных метода решения этой проблемы, которые обычно применяются практикующими разработчиками 1С.

Решение 1: Разделение на реквизит ввода и реквизит расчета (Рекомендуемый способ)

Самый надежный и архитектурно правильный способ — разделить физический ввод данных пользователем и расчетный показатель, который используется в формулах и выводится в печатные формы. Это полностью исключит рекурсивные расчеты и путаницу.

Сделаем следующие шаги:

  1. Добавим в табличную часть документа новый реквизит ВесБруттоКг (числовой, точность 1 или 2 знака). Именно в это поле пользователь будет вводить исходный вес в килограммах.
  2. Существующий реквизит ВесБруттоТн сделаем доступным только для просмотра (снимем флаг "Доступность" в свойствах колонки на форме). В нем будет храниться и отображаться итоговый вес в тоннах.
  3. В формулу колонки ВесБруттоКг поместим вызов процедуры перерасчета.

Посмотрим на пример программного кода для реализации этого механизма:


Процедура ВводКоличества()             
    // Рассчитываем значение в тоннах на основе введенных килограммов
    ВесБруттоТн = Окр(ВесБруттоКг / 1000, 2); 
    
    // Рассчитываем сумму без НДС
    СуммаБНДС = Цена * Дни * ВесБруттоТн;
КонецПроцедуры

При таком подходе пользователь всегда видит исходные данные, которые он ввел (в килограммах), и рядом — расчетный результат в тоннах. Если допущена ошибка, ее легко исправить: достаточно изменить значение в колонке килограммов, и система корректно обновит результат в тоннах без риска повторного деления.

Решение 2: Использование единицы измерения в качестве триггера

Если бизнес-требования или дизайн формы жестко ограничивают добавление новых колонок, и нам необходимо производить ввод и отображение в пределах одного и того же поля, мы можем задействовать флаг состояния. В качестве такого флага отлично подойдет единица измерения.

Разберем этот вариант по шагам:

  1. Добавим в табличную часть реквизит ЕдИзм (тип "Строка" или ссылка на справочник единиц измерения).
  2. При добавлении новой строки в документ по умолчанию устанавливаем значение ЕдИзм = "кг".
  3. В процедуре обработки ввода проверяем текущую единицу измерения. Если там указано "кг", мы производим деление на 1000 и переключаем единицу измерения на "тн". Если там уже стоит "тн", деление производить не нужно.

Проанализируем пример кода:


Процедура ВводКоличества()
    // Проверяем, в каких единицах сейчас введены данные
    Если СокрЛП(ЕдИзм) = "кг" Тогда
        ВесБрутто = Окр(ВесБрутто / 1000, 2);
        ЕдИзм = "тн"; // Меняем единицу измерения, фиксируя перевод в тонны
    КонецЕсли;
    
    // Рассчитываем сумму
    СуммаБНДС = Цена * Дни * ВесБрутто;
КонецПроцедуры

Важное примечание: Чтобы дать пользователю возможность исправить ошибочно введенный вес, нам потребуется механизм сброса флага обратно в "кг". Для этого можно использовать предопределенную процедуру формы ПриВыбореСтроки(). Если пользователь дважды кликает по ячейке веса для редактирования, мы принудительно возвращаем единицу измерения в "кг" и умножаем текущее значение на 1000, возвращая его к исходному виду для редактирования.


Процедура ПриВыбореСтроки()
    Если Форма.ТекущаяКолонка() = "ВесБрутто" Тогда
        Если СокрЛП(ЕдИзм) = "тн" Тогда
            // Возвращаем значение в кг для редактирования
            ВесБрутто = ВесБрутто * 1000;
            ЕдИзм = "кг";
            Форма.Обновить(0); // Обновляем форму без вызова формул
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Решение 3: Интерактивный ввод данных через модальное окно диалога

Этот подход часто применяется в типовых конфигурациях линейки 1С:Предприятие 7.7 (например, в "Торговле и Складе"). Он полностью исключает использование формулы колонки для обработки ввода, перекладывая эту задачу на стандартные диалоговые окна платформы.

Разберем алгоритм реализации данного метода:

  1. Делаем колонку ВесБрутто на форме недоступной для прямого редактирования в табличной части (снимаем галочку "Редактирование в списке").
  2. Перехватываем двойной клик мыши по ячейке или нажатие клавиши Enter с помощью процедуры ПриВыбореСтроки().
  3. Вызываем стандартную функцию платформы ВвестиЧисло(), запрашивая у пользователя вес строго в килограммах.
  4. После успешного ввода производим разовое деление, округление и записываем результат в реквизит.

Реализуем это в коде:


Процедура ПриВыбореСтроки()
    Если Форма.ТекущаяКолонка() = "ВесБрутто" Тогда
        ВременныйВесКг = 0;
        
        // Открываем модальное окно ввода числа
        Если ВвестиЧисло(ВременныйВесКг, "Введите вес груза в килограммах (кг):", 12, 2) = 1 Тогда
            // Расчет производится ровно один раз при нажатии кнопки "ОК"
            ВесБрутто = Окр(ВременныйВесКг / 1000, 2);
            СуммаБНДС = Цена * Дни * ВесБрутто;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Этот способ гарантирует абсолютную стабильность данных, так как платформа физически не может запустить процедуру перерасчета повторно при перемещении курсора по табличной части.

Решение 4: Использование возможностей внешней компоненты FormEx

Для разработчиков, которые не боятся использовать внешние библиотеки расширения возможностей 1С 7.7, идеальным решением станет использование популярной компоненты FormEx.

Эта компонента позволяет перехватывать расширенные события элементов управления формы. В штатном функционале 1С 7.7 нет события, аналогичного "ПослеРедактированияЯчейки", но FormEx предоставляет его. Перехватив это событие, мы можем выполнять процедуру перевода килограммов в тонны и перерасчет суммы только тогда, когда пользователь действительно завершил интерактивный ввод нового значения в конкретную ячейку, игнорируя системные обновления экрана или обычные перемещения фокуса.

Резюме и рекомендации

Мы рассмотрели различные подходы к устранению проблемы циклического расчета в табличной части на платформе 1С 7.7. Подведем итоги:

← На главную