Почему функция меняет дату документа в 1С и как это исправить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: почему меняется дата?

Представим ситуацию: в модуле документа описана функция, которая должна найти остаток по определенному активу на указанную дату. Если остатка нет, функция начинает «отматывать» время назад, чтобы найти ближайшую дату с ненулевым остатком. На первый взгляд, код выглядит логично, но при его выполнении разработчик замечает, что сама дата документа в форме или в базе данных внезапно изменилась.

Проанализируем исходный фрагмент кода, который вызвал проблему:


Функция ПоискОстаткаМонетыНаДату(Монета, ДатаЧ)
    Флаг = 0;
    ОстатокМ = ПоискОстаткаМонетыНаДатуЗапрос(Монета, ДатаЧ);
    
    Пока ОстатокМ = 0 Цикл
        ДатаЧ = ДатаЧ - 24*60*60; // Вычитаем одни сутки
        Флаг = Флаг + 1;
        ОстатокМ = ПоискОстаткаМонетыНаДатуЗапрос(Монета, ДатаЧ);
        
        Если Флаг > 10 Тогда
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ОстатокМ;
КонецФункции

В данном примере, если мы вызываем функцию и передаем в качестве второго параметра Дата (реквизит текущего документа), то строка ДатаЧ = ДатаЧ - 24*60*60; напрямую воздействует на данные документа. Выясним причину этого поведения: по умолчанию в языке 1С параметры в процедуры и функции передаются по ссылке.

Механизм передачи параметров: По ссылке vs По значению

Разберем по шагам, что происходит в памяти компьютера. Когда параметр передается по ссылке, переменная ДатаЧ внутри функции не становится независимой копией. Она превращается в своеобразный «псевдоним» или указатель на ту же область памяти, где хранится исходный реквизит Дата.

Следовательно, любое изменение значения ДатаЧ внутри цикла немедленно отражается на исходном объекте. Если мы вычли 10 дней в цикле, то и документ «постареет» на 10 дней прямо в процессе записи или проведения, что абсолютно недопустимо для бизнес-логики.

Чтобы решить эту задачу, необходимо использовать ключевое слово Знач. Оно заставляет платформу создать локальную копию значения в момент вызова функции. В этом случае все манипуляции с ДатаЧ останутся внутри функции и не затронут исходный документ.

Правильное решение проблемы

Для исправления ошибки нам нужно изменить заголовок функции, добавив Знач перед параметром, который мы собираемся изменять внутри кода. Посмотрим на исправленный вариант:


// Теперь ДатаЧ передается по значению, исходный объект защищен
Функция ПоискОстаткаМонетыНаДату(Монета, Знач ДатаЧ)
    Флаг = 0;
    ОстатокМ = ПоискОстаткаМонетыНаДатуЗапрос(Монета, ДатаЧ);
    
    Пока ОстатокМ = 0 Цикл
        ДатаЧ = ДатаЧ - 24*60*60; 
        Флаг = Флаг + 1;
        ОстатокМ = ПоискОстаткаМонетыНаДатуЗапрос(Монета, ДатаЧ);
        
        Если Флаг > 10 Тогда
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ОстатокМ;
КонецФункции

Теперь, когда мы вызываем ПоискОстаткаМонетыНаДату(Монета, ЭтотОбъект.Дата), платформа создает временную копию даты. Цикл меняет эту копию, а реквизит Дата самого документа остается неизменным.

Особенности работы с коллекциями и сложными типами

Важно помнить, что ключевое слово Знач работает по-разному для разных типов данных. Проанализируем эти нюансы, чтобы избежать ошибок в будущем:

  1. Примитивные типы (Число, Строка, Дата, Булево): Ключевое слово Знач полностью защищает переменную. Создается копия, и оригинал изменить невозможно.
  2. Объекты и коллекции (Массив, Структура, ТаблицаЗначений, СправочникОбъект): Здесь ситуация сложнее. При использовании Знач система запрещает переопределение самой переменной (вы не сможете присвоить ей новый массив), но она не защищает содержимое. Если вы передадите Массив через Знач и внутри функции добавите в него элементы через метод Добавить(), эти изменения отобразятся в исходном массиве. Для полной защиты коллекций необходимо использовать метод Скопировать().

Влияние на клиент-серверное взаимодействие

Рассмотрим ситуацию с точки зрения производительности. Когда мы вызываем серверную функцию с клиента и передаем параметры по ссылке (без Знач), платформа вынуждена выполнять двойную работу:

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

Использование Знач оптимизирует этот процесс: платформе не нужно «возвращать» значение параметра обратно на клиент. Это экономит трафик и время выполнения вызова. Согласно стандартам разработки «1С:Совместимо», использование Знач является обязательным для всех входных параметров, которые не предназначены для возврата результата через изменение самого параметра.

Нюансы работы в модулях формы

Если подобная функция вызывается в модуле формы, отсутствие Знач может привести к неожиданным визуальным эффектам. Например, если параметром выступает реквизит формы, связанный с полем ввода, пользователь может увидеть, как дата в поле «скачет» или меняется прямо во время выполнения расчетов. Кроме того, это может спровоцировать срабатывание обработчиков событий ПриИзменении, что приведет к зацикливанию или избыточным серверным вызовам.

Рекомендации по написанию кода

Подводя итог, выделим несколько правил «хорошего тона» для программиста 1С:

Понимание механизмов работы платформы с памятью и параметрами позволяет создавать стабильный и предсказуемый код, избегая трудноуловимых ошибок, связанных с автоматическим изменением данных объекта.

← На главную