Разработчики 1С часто сталкиваются с ситуацией, когда при создании нового элемента справочника (например, Номенклатура) требуется заполнить один реквизит на основании другого, который генерируется системой автоматически. Типичный пример: формирование Артикула на основе части Кода. Проблема заключается в том, что в стандартном событии ПередЗаписью код у нового объекта еще пуст, так как система присваивает его в самый последний момент перед фиксацией транзакции. В этой статье мы подробно разберем, как обойти это ограничение и правильно организовать логику присвоения значений.
Для начала разберем жизненный цикл объекта при записи в базу данных. В системе 1С:Предприятие 8 автоматическая генерация кодов и номеров происходит внутри транзакции записи. Это необходимо для обеспечения уникальности и непрерывности нумерации. Если бы код присваивался сразу при открытии формы, а пользователь нажал «Отмена», в нумерации возникали бы «дырки».
Порядок событий выглядит так:
Записать().ПередЗаписью в модуле объекта (здесь Код еще пустой для нового объекта).ПриЗаписи (код уже есть, но данные еще не окончательно в базе).ПослеЗаписи (объект зафиксирован в БД).Как мы видим, попытка прочитать Код в ПередЗаписью без дополнительных манипуляций обречена на неудачу при первом сохранении. Именно поэтому у автора вопроса артикул заполнялся только со второй попытки.
Рассмотрим самый эффективный способ решения задачи. Мы можем заставить систему сгенерировать код досрочно, используя встроенный метод УстановитьНовыйКод(). Разберем по шагам, как реализовать это в модуле объекта.
В процедуре ПередЗаписью нам нужно проверить, является ли объект новым, и если код еще не заполнен — вызвать его генерацию. Проанализируем пример кода:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, что это новый элемент
Если ЭтоНовый() Тогда
// Если код еще не заполнен системой или пользователем
Если ПустаяСтрока(Код) Тогда
// Принудительно получаем следующий свободный код
УстановитьНовыйКод();
КонецЕсли;
// Теперь Код заполнен, и мы можем использовать его для формирования артикула
СТ_УстановитьАртикулПоКоду();
КонецЕсли;
КонецПроцедуры
Важно помнить: метод УстановитьНовыйКод() обращается к базе данных для получения номера. В высоконагруженных системах с тысячами одновременных записей это может вызвать кратковременные ожидания на блокировках, но для обычных задач это стандартный и наиболее чистый путь.
Часто в ТЗ указано, что артикул должен включать в себя не весь код, а лишь его часть (например, последние 6 или 7 цифр). Здесь программисты часто совершают ошибку, используя функцию Прав() с жестко заданным количеством символов. Рассмотрим, почему это опасно и как сделать правильно.
В 1С Код — это строка фиксированной длины. Если в метаданных длина кода установлена как 11 символов, а текущий номер — «123», то переменная Код может содержать пробелы в конце или лидирующие нули. Выясним причину ошибки: функция СтрДлина(Код) вернет полную длину строки из метаданных, а не длину значащих цифр.
Проанализируем правильный подход к обработке строки:
Процедура СТ_УстановитьАртикулПоКоду()
// Обязательно убираем пробелы, которые могут быть добавлены платформой
ЧистыйКод = СокрЛП(Код);
// Проверяем префикс артикула, введенный пользователем
Если (Артикул = "7981" ИЛИ Артикул = "7980") Тогда
// Вместо Прав(Код, 7) лучше использовать логику, не зависящую от длины префиксов
// Если нужно именно последние 6 символов значащей части:
Длина = СтрДлина(ЧистыйКод);
КоличествоСимволов = ?(Длина > 6, 6, Длина);
ЧастьКода = Прав(ЧистыйКод, КоличествоСимволов);
Артикул = Артикул + ЧастьКода;
ИначеЕсли ПустаяСтрока(Артикул) Тогда
// Если артикул пустой, просто копируем код
Артикул = ЧистыйКод;
КонецЕсли;
КонецПроцедуры
Некоторые разработчики пытаются реализовать логику в событии ПриЗаписи или ПослеЗаписи. Это приводит к тому, что для сохранения обновленного артикула объект приходится записывать еще раз. Рассмотрим, к чему это может привести.
Если вызвать ЭтотОбъект.Записать() внутри процедуры ПриЗаписи, возникнет бесконечная рекурсия: метод Записать снова вызовет ПриЗаписи, и так до тех пор, пока система не аварийно завершит работу из-за переполнения стека. Чтобы этого избежать, профессионалы используют ДополнительныеСвойства.
Разберем пример безопасной повторной записи:
Процедура ПриЗаписи(Отказ)
// Проверяем, не выполняется ли эта запись повторно из нашего же кода
Если ДополнительныеСвойства.Свойство("АртикулУстановлен") Тогда
Возврат;
КонецЕсли;
// Логика изменения артикула
Если НужноОбновитьАртикул(ЭтотОбъект) Тогда
// Устанавливаем флаг, чтобы избежать рекурсии
ЭтотОбъект.ДополнительныеСвойства.Вставить("АртикулУстановлен", Истина);
ЭтотОбъект.Артикул = СформироватьНовыйАртикул(ЭтотОбъект.Код);
ЭтотОбъект.Записать();
КонецЕсли;
КонецПроцедуры
Однако, настоятельно рекомендуем использовать ПередЗаписью с методом УстановитьНовыйКод(), так как это избавляет от необходимости совершать лишние транзакции к базе данных и существенно повышает производительность системы.
Для корректной работы автоматического заполнения реквизитов на основе кода, придерживайтесь следующих правил:
ЭтоНовый(). Логика генерации кода должна срабатывать только при создании элемента, чтобы не перезаписывать данные при обычном редактировании.УстановитьНовыйКод() в ПередЗаписью. Это гарантирует наличие значения в поле Код до того, как данные уйдут в БД.СокрЛП() к кодам и артикулам, так как пробелы в строках фиксированной длины — главный источник логических ошибок при сравнении или обрезке строк.Прав(Код, 7) может начать возвращать некорректные данные. Старайтесь писать универсальные функции обработки строк.Использование этих простых, но эффективных приемов позволит вам создавать надежные механизмы автоматизации, которые будут работать корректно с первого нажатия кнопки «Записать».