В каком событии документа формировать штрихкод: ПередЗаписью или ПриЗаписи?

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

При разработке логики автоматического присвоения значений часто возникает дилемма: к какому событию модуля объекта или подписки привязать алгоритм? Типичный сценарий — необходимость сгенерировать штрихкод для заказа покупателя на основании его номера и даты. Разберем механику событий ПередЗаписью и ПриЗаписи, выясним причину появления «звёздочки» (признака модифицированности) и рассмотрим правильные варианты реализации задачи.

Почему нельзя изменять реквизиты документа в событии «ПриЗаписи»

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

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

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

Если внутри обработчика ПриЗаписи изменить реквизит через обращение к свойствам текущего объекта (ЭтотОбъект.Штрихкод = ...), значение обновится только в оперативной памяти текущего экземпляра. В СУБД эти изменения автоматически не попадут, так как фаза записи полей шапки и табличных частей объекта уже завершена. В результате свойство формы Модифицированность переходит в состояние Истина, а от пользователя требуется повторная интерактивная запись, что приводит к зацикливанию или ошибкам блокировок.

Решение 1: Генерация штрихкода в «ПередЗаписью» с получением номера

Если штрихкод обязательно должен храниться в реквизите шапки документа, правильной точкой входа является событие ПередЗаписью. Поскольку на этом шаге стандартный номер платформой еще не присвоен, его необходимо получить явно с помощью метода УстановитьНовыйНомер().

Рассмотрим пример кода в модуле документа или обработчике подписки на событие:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, что штрихкод еще не заполнен
    Если ПустаяСтрока(ЭтотОбъект.Штрихкод) Тогда
        
        // Если номер еще не присвоен, генерируем его принудительно
        Если ПустаяСтрока(ЭтотОбъект.Номер) Тогда
            ЭтотОбъект.УстановитьНовыйНомер();
        КонецЕсли;
        
        // Формируем штрихкод на основе даты и полученного номера
        ФорматированнаяДата = Формат(ЭтотОбъект.Дата, "ДФ=yyyyMMdd");
        ЭтотОбъект.Штрихкод = СокрЛП(ЭтотОбъект.Номер) + ФорматированнаяДата;
        
    КонецЕсли;
    
КонецПроцедуры

Посмотрим на технические риски и ограничения явного вызова метода УстановитьНовыйНомер():

  1. Пропуски в нумерации документов: При явном вызове номер резервируется в системе немедленно. Если далее транзакция записи будет отменена (например, сработает проверка заполнения, возникнет ошибка проведения или Отказ = Истина), этот номер будет потерян и в хронологической последовательности номеров возникнет «дыра».
  2. Блокировки в высоконагруженных базах: Ранний вызов нумератора удерживает транзакционную блокировку таблицы номеров дольше положенного, что может вызывать таймауты у других пользователей при одновременном создании заказов.
  3. Периодичность и префиксы: Метод УстановитьНовыйНомер() рассчитывает значение строго на основании текущей даты документа. Если дата будет скорректирована пользователем позже, нумерация может потребовать пересчета.

Решение 2: Сохранение штрихкода в регистр сведений в событии «ПриЗаписи»

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

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


Процедура ПриЗаписи(Отказ)
    
    Если Отказ Тогда
        Возврат;
    КонецЕсли;
    
    // Номер документа уже гарантированно сформирован платформой
    ФорматированнаяДата = Формат(ЭтотОбъект.Дата, "ДФ=yyyyMMdd");
    ЗначениеШтрихкода = СокрЛП(ЭтотОбъект.Номер) + ФорматированнаяДата;
    
    // Записываем штрихкод через набор записей в текущей транзакции
    НаборЗаписей = РегистрыСведений.ШтрихкодыДокументов.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Документ.Установить(ЭтотОбъект.Ссылка);
    
    НоваяЗапись = НаборЗаписей.Добавить();
    НоваяЗапись.Документ = ЭтотОбъект.Ссылка;
    НоваяЗапись.Штрихкод = ЗначениеШтрихкода;
    
    НаборЗаписей.Записать();
    
КонецПроцедуры

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

Решение 3: Динамическое вычисление штрихкода «на лету»

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

Проанализируем преимущества такого подхода:

  1. Отсутствие рассинхронизации: При изменении номера или даты документа штрихкод не потеряет актуальность и не потребует повторной перезаписи объекта.
  2. Оптимизация объема базы: Не расходуется дисковое пространство таблиц на дублирующую информацию.
  3. Простота формирования: Генерацию графического штрихкода можно выполнять непосредственно в процедуре печати или при выводе на форму, передавая туда готовую склейку СокрЛП(Шапка.Номер) + Формат(Шапка.Дата, "ДФ=yyyyMMdd").

Если же штрихкод требуется сканировать для быстрого поиска заказа в журнале, лучше использовать независимый идентификатор (например, хеш от Ссылка.УникальныйИдентификатор()), который не зависит от видимого номера документа и не требует вызова УстановитьНовыйНомер().

← На главную