Как реализовать копирование номенклатуры вместе со спецификацией и выбором количества копий в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

Ограничения стандартных событий и модулей платформы

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

  1. Обработчик ПриКопировании в модуле объекта: процедура ПриКопировании(ОбъектКопирования) выполняется исключительно в контексте сервера. На сервере невозможно вызвать диалоговые окна с вопросами к пользователю (функция Вопрос()), открыть форму выбора спецификаций или запросить число копий. Любой интерактивный ввод должен происходить на стороне клиента до начала серверной записи.
  2. Событие ПередНачаломДобавления в форме списка: перехват нажатия кнопки добавления или копирования срабатывает только при интерактивных действиях непосредственно в данной динамической таблице. Если пользователь открывает карточку или выполняет операцию из другого места системы, код не сработает. Кроме того, прерывание стандартного процесса для запуска серии копирований усложняет логику формы списка.

Посмотрим на пример первой попытки перехвата события в динамическом списке, где при копировании открывается форма списка спецификаций:


Процедура СписокПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, ЭтоГруппа)
    Если Копирование = Истина Тогда
        Ответ = Вопрос("Необходимо ли скопировать спецификацию?", РежимДиалогаВопрос.ДаНет, 0, КодВозвратаДиалога.Нет);
        Если Ответ = КодВозвратаДиалога.Да Тогда
            ФормаОбъекта = Справочники.СпецификацииНоменклатуры.ПолучитьФорму("ФормаСписка");
            ФормаОбъекта.Открыть();
        КонецЕсли; 
    КонецЕсли;
КонецПроцедуры

Такой подход быстро показывает свои минусы: форма спецификаций открывается без отбора по исходной номенклатуре, а сам процесс создания копий не автоматизирован. Наиболее гибким решением в этой ситуации является создание отдельной формы-помощника (обработки или вспомогательной формы списка).

Организация отбора спецификаций для копируемой номенклатуры

Для удобства пользователя в новой форме размещаются реквизиты: исходная Номенклатура (полученная в параметрах открытия), поле выбора Спецификация и числовое поле Количество (желаемое число создаваемых копий).

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


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Номенклатура = Параметры.СсылкаНом;
КонецПроцедуры

&НаКлиенте
Процедура СпецификацияНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    ФормаВыбораСпецификации = Справочники.СпецификацииНоменклатуры.ПолучитьФормуВыбора(, Элемент);
    
    // Устанавливаем отбор по исходной номенклатуре и признаку активности
    ФормаВыбораСпецификации.Отбор.СпецификацииИзделийПоНоменклатуре.ВидСравнения = ВидСравнения.Равно;
    ФормаВыбораСпецификации.Отбор.СпецификацииИзделийПоНоменклатуре.Значение = Номенклатура;
    ФормаВыбораСпецификации.Отбор.СпецификацииИзделийПоНоменклатуре.Использование = Истина;
    
    ФормаВыбораСпецификации.Отбор.Активная.Установить(Истина);
    ФормаВыбораСпецификации.РежимВыбора = Истина;
    ФормаВыбораСпецификации.Открыть();
КонецПроцедуры

Алгоритм копирования карточки номенклатуры и подчиненных объектов

Рассмотрим структуру тиражирования элементов. При создании дубликата номенклатуры нельзя просто вызвать метод Скопировать() для карточки товара. В типовых конфигурациях (таких как УПП) реквизиты ЕдиницаХраненияОстатков и ЕдиницаДляОтчетов ссылаются на подчиненный справочник ЕдиницыИзмерения, владельцем которых должна быть строго новая номенклатура.

Разберем по шагам логику, предложенную в решении:

  1. Выполняем проверку заполнения обязательных реквизитов (выбрана ли спецификация, указано ли количество).
  2. Запускаем цикл от 1 до указанного пользователем количества копий.
  3. Создаем копию исходной номенклатуры и первоначально записываем ее, чтобы получить новую валидную ссылку (Ссылка).
  4. Клонируем единицу хранения остатков исходной позиции, переназначаем реквизит Владелец на новую ссылку номенклатуры и записываем единицу.
  5. Если единица для отчетов отличается от единицы хранения, аналогично клонируем и перепривязываем ее.
  6. Обновляем ссылки на новые единицы измерения в карточке созданной номенклатуры и повторно записываем ее.
  7. Копируем выбранную спецификацию: очищаем табличную часть ВыходныеИзделия и заполняем строку данными только что созданной номенклатуры.

Посмотрим на пример клиент-серверной реализации данной логики:


&НаКлиенте
Процедура СкопироватьНоменклатуру(Команда)
    
    Если НЕ ЗначениеЗаполнено(Спецификация) Тогда
        Предупреждение("Не выбрана спецификация. Копирование невозможно!");
        Возврат;
    КонецЕсли;
    
    НоваяНом = Номенклатура.Ссылка;
    
    Для ТекущийНомер = 1 По Количество Цикл 
        КопияНом = НоваяНом.Скопировать();
        КопияНом.Записать();    
        
        КопияЕдХран = НоваяНом.ЕдиницаХраненияОстатков.Скопировать();
        КопияЕдХран.Владелец = КопияНом.Ссылка;
        КопияЕдХран.Записать();
        
        Если НоваяНом.ЕдиницаХраненияОстатков <> НоваяНом.ЕдиницаДляОтчетов Тогда 
            КопияЕдОтч = НоваяНом.ЕдиницаДляОтчетов.Скопировать();
            КопияЕдОтч.Владелец = КопияНом.Ссылка;
            КопияЕдОтч.Записать();
        КонецЕсли;    
        
        КопияНом.ЕдиницаХраненияОстатков = КопияЕдХран.Ссылка;
        Если НоваяНом.ЕдиницаХраненияОстатков <> НоваяНом.ЕдиницаДляОтчетов Тогда 
            КопияНом.ЕдиницаДляОтчетов = КопияЕдОтч.Ссылка;
        Иначе    
            КопияНом.ЕдиницаДляОтчетов = КопияЕдХран.Ссылка;
        КонецЕсли;    
        КопияНом.Записать();    
        
        Если ЗначениеЗаполнено(Спецификация) Тогда
            НоваяСпец = Спецификация.Ссылка;
            КопияСпец = НоваяСпец.Скопировать();
            КопияСпец.ВыходныеИзделия.Очистить();
            
            Стр = КопияСпец.ВыходныеИзделия.Добавить();
            Стр.Номенклатура = КопияНом.Ссылка;
            Стр.ЕдиницаИзмерения = КопияНом.Ссылка.ЕдиницаХраненияОстатков;
            Стр.Количество = 1;
            Стр.Кратность = 1;
            
            КопияСпец.Записать();
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Важные замечания по доработке и оптимизации кода

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

  1. Контекст выполнения операций: методы объектной модели (такие как Скопировать(), Записать(), прямое обращение к менеджерам справочников) недоступны на тонком клиенте в управляемом приложении. Весь цикл создания и записи объектов следует выносить в процедуру с директивой &НаСервере или &НаСервереБезКонтекста, передавая туда параметры с клиента.
  2. Использование транзакций: при массовом создании объектов в цикле обязательно оборачивайте операции в транзакцию с помощью команд НачатьТранзакцию() и ЗафиксироватьТранзакцию(). В случае непредвиденной ошибки в середине цикла конструкция ОтменитьТранзакцию() предотвратит появление «осиротевших» записей и некорректно заполненных карточек.
  3. Структура спецификаций (особенность УПП): способ привязки номенклатуры к спецификации зависит от ее вида (реквизит ВидСпецификации). Табличная часть ВыходныеИзделия используется только для вида «Полная». Для сборочных спецификаций готовая продукция хранится в реквизитах шапки (Номенклатура, Количество). Очистка табличной части для таких спецификаций приведет к нарушению целостности структуры.
  4. Назначение основной спецификации: само по себе создание копии спецификации не делает ее активной для подстановки в документы производства. Для автоматического выбора новой спецификации необходимо добавить программную запись в периодический регистр сведений ОсновныеСпецификацииНоменклатуры.

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

← На главную