Как реализовать дополнительный артикул с автонумерацией по папкам в 1С:Управление торговлей 11?

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

В процессе организации складского учета нередко возникает потребность в коротких, удобных для чтения и поиска артикулах вида «X001», где «X» — буква родительской папки, а «001» — порядковый номер позиции внутри группы. При этом стандартные поля Код и Артикул в справочнике Номенклатура часто уже заняты для синхронизации с бухгалтерскими системами или хранения номеров поставщиков. Разберем, как правильно подойти к решению этой задачи: от использования штатных механизмов конфигурации до надежной программной реализации собственного нумератора.

Анализ типовых возможностей конфигурации

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

  1. Хранение данных поставщиков: Если поле Артикул занято кодами контрагентов, правильнее использовать штатный справочник НоменклатураКонтрагентов (подсистема «Номенклатура поставщиков»). Перенос информации поставщиков в предназначенную для этого структуру позволяет освободить основной реквизит Артикул для складских нужд предприятия без изменения конфигурации.
  2. Внутреннее штрихкодирование: Если артикул требуется исключительно для маркировки полок, товаров и быстрой идентификации при подборе, рассмотрим использование регистра сведений ШтрихкодыНоменклатуры. Платформа поддерживает генерацию коротких кодов (Code 128, EAN-8), которые без доработок поддерживаются складскими терминалами и шаблонами печати этикеток.

Ошибки и риски при наивной реализации нумератора

Если специфика учета все же требует создания собственного дополнительного реквизита с маской «Символ + Порядковый номер», выясним, с какими подводными камнями сталкивается разработчик.

Часто предлагается вычислять следующий номер простым подсчетом количества элементов в группе через функцию КОЛИЧЕСТВО в запросе. Выясним причину, почему такой подход опасен в рабочей базе:

  1. Дублирование при удалении: Если товар из середины списка был помечен на удаление, перемещен в другую группу или физически удален, общее число записей уменьшится. Следующий созданный товар получит номер, который уже присвоен существующей позиции.
  2. Состояние гонки (Race Condition): В многопользовательском режиме, когда несколько операторов одновременно вводят новые позиции, оба сеанса могут одновременно прочитать одно и то же число элементов и сформировать одинаковый артикул.
  3. Ограничения строковых функций в запросах: Если номер хранится в виде строки, агрегатная функция МАКСИМУМ в языке запросов сравнивает строки лексикографически. Без строгой длины и ведущих нулей строка «X10» окажется меньше строки «X2», что приведет к сбою генерации.

Архитектурно правильное решение: нумератор на регистре сведений

Чтобы гарантировать уникальность артикулов в транзакции и избежать коллизий, разберем по шагам создание счетчика на базе независимого регистра сведений в расширении конфигурации.

Создадим регистр сведений НумераторАртикулов со следующей структурой:

  1. Измерение: Префикс (Тип: Строка, длина 10) — хранит букву группы или ссылку на папку номенклатуры.
  2. Ресурс: ТекущийНомер (Тип: Число, длина 10, точность 0).

Реализуем процедуру генерации в модуле менеджера или общем серверном модуле. Обязательно установим исключительную управляемую блокировку:


Функция ПолучитьСледующийАртикул(Знач Префикс) Экспорт

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

КонецФункции

Подключение автогенерации в событии ПередЗаписью

Разберем подключение вызова генерации нового кода через расширение конфигурации. Перехватим обработчик ПередЗаписью справочника Номенклатура.

Проанализируем обязательные проверки, которые необходимо выполнить перед формированием значения:

  1. Исключить группы справочника с помощью проверки свойства ЭтоГруппа.
  2. Проверить признак ОбменДанными.Загрузка: если выполняется синхронизация с другой базой (например, с 1С:Бухгалтерией), перезаписывать код нельзя.
  3. Проверить, не заполнен ли артикул ранее, чтобы не менять номер у существующих позиций при повторном сохранении.

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

КонецПроцедуры

Первичное заполнение существующих данных

Для первоначальной нумерации 5000 уже созданных позиций напишем внешнюю обработку. Проанализируем алгоритм ее работы:

  1. Выбираем выборкой все элементы справочника Номенклатура, сгруппированные по родительским папкам.
  2. Для каждой папки инициализируем счетчик с единицы.
  3. В цикле присваиваем строковый артикул, записываем объект и фиксируем последнее числовое значение в регистре НумераторАртикулов.

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

← На главную