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

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

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

Выбор между ПередЗаписью и ПриЗаписи

Первый и самый важный вопрос: в каком событии присваивать артикул? Разберем два основных обработчика модуля объекта.

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

Рассмотрим обработчик ПередЗаписью. Это событие происходит внутри транзакции записи, но до того, как данные окончательно зафиксированы в таблицах базы данных. Любое изменение реквизитов объекта в этом обработчике (например, ЭтотОбъект.Артикул = НовыйНомер) будет сохранено автоматически без дополнительных вызовов метода Записать(). Поэтому для автоматического заполнения реквизитов рекомендуется всегда использовать обработчик ПередЗаписью.

Как получить данные (Родителя) без обращения к базе данных

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

Проанализируем ситуацию: когда пользователь нажимает кнопку «Записать», все данные из формы уже перенесены в объект в оперативной памяти. Это означает, что реквизит Родитель уже заполнен в самом объекте. Нам не нужно делать запрос к базе по ссылке, чтобы узнать, в какую группу пользователь поместил товар. Мы можем обратиться напрямую к свойству ЭтотОбъект.Родитель.

Посмотрим на пример кода в модуле объекта:


Процедура ПередЗаписью(Отказ)
    // Проверяем, что объект новый и артикул еще не заполнен
    Если ЭтоНовый() И НЕ ЗначениеЗаполнено(Артикул) Тогда
        // Получаем родителя прямо из свойств текущего объекта
        ТекущийРодитель = ЭтотОбъект.Родитель;
        
        // Вызываем функцию поиска максимального артикула в этой группе
        ЭтотОбъект.Артикул = СформироватьНовыйАртикул(ТекущийРодитель);
    КонецЕсли;
КонецПроцедуры

Проблема параллельного ввода и транзакционные блокировки

Выясним причину возможной ошибки при работе нескольких пользователей. Если два оператора одновременно создают товар в одной и той же группе, их сеансы могут одновременно выполнить запрос на получение «максимального артикула». Оба получат одинаковое число (например, 100) и попытаются записать артикул 101. В результате мы получим дублирование артикулов.

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

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

Использование механизма ДополнительныеСвойства для обхода рекурсии

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

Проанализируем пример кода, предотвращающего цикл:


Процедура ПриЗаписи(Отказ)
    // Проверяем флаг-предохранитель
    Если ДополнительныеСвойства.Свойство("АртикулУжеПрисвоен") Тогда
        Возврат;
    КонецЕсли;

    // Устанавливаем флаг перед повторной записью
    ДополнительныеСвойства.Вставить("АртикулУжеПрисвоен", Истина);
    
    // Выполняем присвоение и записываем объект
    ЭтотОбъект.Артикул = "АРТ-" + СокрЛП(ЭтотОбъект.Ссылка.УникальныйИдентификатор());
    ЭтотОбъект.Записать();
КонецПроцедуры

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

ОбработкаЗаполнения против ПередЗаписью

Рассмотрим еще один вариант — событие ОбработкаЗаполнения. Оно срабатывает в момент нажатия кнопки «Создать». Кажется удобным сразу показать пользователю будущий артикул. Однако здесь кроется ловушка: между моментом создания карточки и моментом её записи пользователем может пройти много времени. За этот период другой сотрудник может успеть создать и записать свой товар, заняв этот номер.

Оптимальная стратегия:

Оптимизация производительности

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

  1. Автонумерация: использование стандартного механизма кодов 1С (реквизит Код), который платформа умеет инкрементировать самостоятельно и очень быстро.
  2. Регистр сведений: хранение последнего использованного номера в специальном регистре. Чтение одной записи из регистра гораздо быстрее, чем выполнение агрегатного запроса МАКСИМУМ() по всему справочнику.

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

← На главную