При автоматизации складского учета и производственных процессов часто возникает задача программной генерации новых элементов справочника СерииНоменклатуры. Нередко разработчики сталкиваются с двумя сопутствующими проблемами: возникновением ошибок в типовых механизмах при вызове метода Записать() и необходимостью формирования серийных номеров по определенному шаблону (например, префикс в виде артикула номенклатуры плюс порядковый числовой номер фиксированной длины). Разберем подробно причины этих трудностей и пошагово реализуем надежный механизм генерации.
Рассмотрим типовую ситуацию: при попытке создания элемента справочника СерииНоменклатуры простым заполнением наименования система выдает исключение следующего вида:
Ошибка при выполнении обработчика - 'ПередЗаписью'
по причине:
{Справочник.СерииНоменклатуры.МодульМенеджера(221)}: Преобразование значения к типу Булево не может быть выполнено
Если ПараметрыШаблона.ИспользоватьНомерСерии Тогда
Выясним причину такого поведения конфигурации. В прикладных решениях, таких как 1С:ERP Управление предприятием, Комплексная автоматизация и Управление торговлей, логика учета серий жестко привязана к настройкам учета. Серии ведутся не произвольно, а в строгом соответствии с политикой учета серий, заданной для конкретного вида номенклатуры.
В обработчике ПередЗаписью система обращается к модулю менеджера справочника для получения параметров шаблона генерации серий. Если у создаваемого объекта реквизит ВидНоменклатуры не заполнен, структура параметров шаблона возвращается пустой (или содержит значения Неопределено), из-за чего проверка флага ПараметрыШаблона.ИспользоватьНомерСерии падает с ошибкой приведения типов.
Для устранения проблемы обязательно перед вызовом метода Записать() указываем вид номенклатуры:
НовыйОбъект = Справочники.СерииНоменклатуры.СоздатьЭлемент();
НовыйОбъект.ВидНоменклатуры = ВыбраннаяНоменклатура.ВидНоменклатуры;
НовыйОбъект.Номер = "0001";
НовыйОбъект.Записать();
Разберем следующую задачу: серийный номер должен формироваться по правилу Артикул + ЧисловойНомер (например, для артикула Я1519 номера должны иметь вид Я15190001, Я15190002 и т.д.). При этом длина артикула может варьироваться, а числовая часть номера должна иметь строго фиксированную длину (например, 4 знака) с дополнением лидирующими нулями.
Проанализируем подводные камни, возникающие при реализации поиска последнего номера через запрос к информационной базе:
МАКСИМУМ в запросе 1С при работе со строковыми полями выполняет алфавитное сравнение посимвольно слева направо. Если номера имеют разную разрядность или в них присутствуют лишние пробелы/нули, результат поиска максимума будет математически некорректным.ВЫРАЗИТЬ(Поле КАК Число) для строковых типов данных. Поэтому выделение числового значения и операцию инкремента (прибавление единицы) необходимо производить во встроенном языке после выборки.ПОДСТРОКА необходимо точно позиционировать начальный символ выборки, учитывая длину переменного артикула номенклатуры.Рассмотрим, как правильно составить запрос и обработать полученный результат во встроенном языке 1С:
СтрДлина().СерииНоменклатуры, начинающиеся с нашего артикула, используя оператор ПОДОБНО &МаскаНомера.ПОДСТРОКА выделяем числовую часть номера. Начальная позиция подстроки вычисляется как ДлинаАртикула + 1, а количество выбираемых символов соответствует разрядности номера (в нашем примере — 4 символа).Число(), что автоматически отсекает все лидирующие нули. Ни в коем случае нельзя удалять нули функцией СтрЗаменить(), так как это исказит внутренние нули числа (например, из 0050 получится 5 вместо 50).ЧЦ=4 (число целых разрядов), ЧВН= (выводить незначащие нули) и ЧГ=0 (отключить группировку разрядов пробелами).Посмотрим на готовый код серверной функции, которая находит максимальный номер, рассчитывает следующий и создает готовый элемент справочника серий:
&НаСервере
Функция СоздатьСериюНоменклатуры(НоменклатураСсылка)
СправочникСерийНомера = Справочники.СерииНоменклатуры.СоздатьЭлемент();
СправочникСерийНомера.ВидНоменклатуры = НоменклатураСсылка.ВидНоменклатуры;
Артикул = СокрЛП(НоменклатураСсылка.Артикул);
ДлинаАртикула = СтрДлина(Артикул);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(ПОДСТРОКА(СерииНоменклатуры.Номер, &ПозицияНачала, 4)) КАК НомерСтрокой
|ИЗ
| Справочник.СерииНоменклатуры КАК СерииНоменклатуры
|ГДЕ
| СерииНоменклатуры.ВидНоменклатуры = &ВидНоменклатуры
| И СерииНоменклатуры.Номер ПОДОБНО &МаскаНомера";
Запрос.УстановитьПараметр("ВидНоменклатуры", НоменклатураСсылка.ВидНоменклатуры);
Запрос.УстановитьПараметр("ПозицияНачала", ДлинаАртикула + 1);
Запрос.УстановитьПараметр("МаскаНомера", Артикул + "%");
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Если РезультатЗапроса.Следующий() И ЗначениеЗаполнено(РезультатЗапроса.НомерСтрокой) Тогда
// Преобразуем строковый номер в число для математического инкремента
ТекущийНомер = Число(РезультатЗапроса.НомерСтрокой);
СледующийНомер = ТекущийНомер + 1;
// Форматируем число: ровно 4 символа с заполнением ведущими нулями
ХвостНомера = Формат(СледующийНомер, "ЧЦ=4; ЧВН=; ЧГ=0");
СправочникСерийНомера.Номер = Артикул + ХвостНомера;
Иначе
// Первый номер для текущего артикула
СправочникСерийНомера.Номер = Артикул + "0001";
КонецЕсли;
СправочникСерийНомера.Записать();
Возврат СправочникСерийНомера.Ссылка;
КонецФункции
Разберем важные архитектурные рекомендации, если требуется сгенерировать не один элемент, а партию из сотен или тысяч серийных номеров:
Для Сч = 1 По Количество Цикл. Каждый вызов приводит к контекстному переключению между клиентом и сервером. Всю пачку номеров следует генерировать за один вызов процедуры на сервере.НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это снижает нагрузку на дисковую подсистему сервера баз данных (сброс журнала транзакций происходит однократно) и многократно ускоряет запись элементов.БлокировкаДанных на время выполнения транзакции.