Как правильно использовать переменные в модуле объекта и реализовать алгоритм ФИФО без глобальных переменных

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

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

Разбор синтаксиса: почему возникает ошибка при объявлении переменной

Рассмотрим ситуацию, с которой столкнулся автор темы. При попытке объявить переменную в самом верху модуля объекта кодом Перем Гл_Перем = 0; система выдает синтаксическую ошибку. Проанализируем причину. В языке 1С оператор Перем служит только для объявления переменной и определения ее области видимости. Присваивать значение переменной непосредственно в строке объявления запрещено.

Чтобы правильно инициализировать переменную модуля объекта, нам необходимо разделить этот процесс на два этапа. Разберем правильный код:


// В самом начале модуля, вне процедур и функций
Перем МояПеременная; 

Процедура ОбработкаПроведения(Отказ, Режим)
    // Инициализация значения происходит внутри процедуры
    МояПеременная = 0;
    // Дальнейшая логика
КонецПроцедуры

Важно помнить: переменная, объявленная в начале модуля без ключевого слова Экспорт, доступна во всех процедурах и функциях этого конкретного модуля, но не видна за его пределами.

Жизненный цикл переменных модуля объекта на сервере

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

Рассмотрим сценарий:

  1. Пользователь нажимает кнопку «Провести».
  2. На сервере создается экземпляр объекта документа.
  3. Выполняется процедура ОбработкаПроведения.
  4. Как только процедура завершается, объект уничтожается, а вместе с ним очищаются и значения всех переменных, объявленных через Перем.

Если вы рассчитываете, что переменная сохранит свое значение после того, как управление вернулось на клиент, или при следующем серверном вызове — это ошибка. На сервере «состояние» объекта между вызовами не сохраняется автоматически. Каждый раз, когда вы вызываете метод объекта с клиента, 1С заново инициализирует модуль.

Альтернатива переменным: ДополнительныеСвойства

Если нам необходимо передать какую-то информацию между различными событиями (например, из ПередЗаписью в ОбработкаПроведения или в подписку на событие), использование Перем не всегда удобно. Проанализируем более гибкий инструмент — свойство ДополнительныеСвойства. Это структура, которая есть у каждого программного объекта (документа, справочника).

Рассмотрим пример использования:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Сохраняем промежуточное значение, которое понадобится позже
    ЭтотОбъект.ДополнительныеСвойства.Вставить("ВажныйПараметр", Истина);
КонецПроцедуры

Процедура ОбработкаПроведения(Отказ, Режим)
    // Извлекаем значение
    Если ЭтотОбъект.ДополнительныеСвойства.Свойство("ВажныйПараметр") Тогда
        // Выполняем логику
    КонецЕсли;
КонецПроцедуры

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

Реализация алгоритма ФИФО: уходим от глобальных переменных

Часто необходимость в «глобальных переменных» возникает при реализации алгоритмов списания партий (ФИФО). Программист хочет хранить остаток, который нужно списать, в переменной и обновлять его по мере обхода выборки. Однако, как показано в итоговом решении автора темы, правильнее использовать локальные переменные внутри процедур и передавать их как параметры в другие процедуры.

Разберем структуру решения для задачи списания товаров по принципу «первым пришел — первым ушел» (FIFO). Основная логика заключается в том, чтобы получить остатки товаров, упорядоченные по дате поступления, и последовательно уменьшать требуемое количество.

Посмотрим на пример реализации процедуры списания:


&НаСервере
Процедура РеализоватьСписаниеФИФО(ДатаДокумента, НазваниеТовара, НужноСписать, СтоимостьПродажи)
    
    // 1. Получаем данные о приходах (партиях) с помощью запроса
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	РегистрПриходОбороты.Дата КАК ДатаПартии,
    |	РегистрПриходОбороты.КоличествоОборот КАК ОстатокПартии,
    |	РегистрПриходОбороты.СтоимостьОборот КАК СтоимостьПартии
    |ИЗ
    |	РегистрНакопления.РегистрПриход.Обороты(,, День, Наименование = &Наименование) КАК РегистрПриходОбороты
    |УПОРЯДОЧИТЬ ПО
    |	ДатаПартии";
    
    Запрос.УстановитьПараметр("Наименование", НазваниеТовара);
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Локальная переменная для отслеживания того, сколько еще осталось списать
    ОсталосьСписать = НужноСписать;
    
    Пока Выборка.Следующий() И ОсталосьСписать > 0 Цикл
        
        Если Выборка.ОстатокПартии > 0 Тогда
            
            Если Выборка.ОстатокПартии >= ОсталосьСписать Тогда
                // Текущей партии хватает для закрытия всей потребности
                ЗаполнитьДвиженияФИФО(Выборка.ДатаПартии, ОсталосьСписать, Выборка.СтоимостьПартии);
                ОсталосьСписать = 0; // Потребность закрыта
            Иначе
                // Забираем весь остаток из текущей партии и идем к следующей
                СписываемИзПартии = Выборка.ОстатокПартии;
                ЗаполнитьДвиженияФИФО(Выборка.ДатаПартии, СписываемИзПартии, Выборка.СтоимостьПартии);
                
                // Уменьшаем остаток, который нужно списать в следующих итерациях
                ОсталосьСписать = ОсталосьСписать - СписываемИзПартии;
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

Анализ кода и важные замечания

Проанализируем приведенный выше алгоритм. Мы видим, что переменная ОсталосьСписать является локальной для процедуры РеализоватьСписаниеФИФО. Нам не нужно объявлять ее на уровне модуля объекта через Перем, так как она используется только в рамках одного процесса списания конкретной строки товара. Это делает код более надежным и защищает от случайного изменения данных из других частей программы.

Важные моменты при работе с регистрами в таких задачах:

Заключение

Мы выяснили, что использование глобальных переменных в модуле объекта чаще всего является следствием непонимания синтаксиса или архитектуры платформы «1С:Предприятие 8». Для корректной работы достаточно следовать правилам:

  1. Объявлять переменные модуля через Перем без инициализации.
  2. Помнить о кратковременности жизни контекста объекта на сервере.
  3. Использовать ДополнительныеСвойства для передачи данных между событиями.
  4. При реализации алгоритмов вроде ФИФО отдавать предпочтение локальным переменным и передаче параметров в процедуры.

Соблюдение этих принципов сделает ваш код чище, предсказуемее и легче в отладке.

← На главную