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

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

В процессе работы с конфигурацией 1С:Управление торговлей (ред. 11) программисты часто сталкиваются с необходимостью доработки типовых механизмов, которые работают некорректно или недостаточно быстро. В рамках данной статьи мы разберем несколько практических кейсов: исправление ошибки заполнения серий при вводе Реализации товаров и услуг на основании Возврата товаров от клиента, подходы к созданию многопоточных обработок и нюансы работы с Перечислениями при импорте данных.

Исправление подбора серий при вводе реализации на основании возврата

Одной из часто встречающихся проблем в типовой УТ 11 является ситуация, когда при создании документа РеализацияТоваровУслуг на основании документа ВозвратТоваровОтКлиента серии товаров не переносятся автоматически, хотя в исходном документе они указаны. Рассмотрим решение, реализованное через механизм расширений с использованием аннотации &Вместо для процедуры ЗаполнитьДокументНаОснованииВозвратаТоваровОтКлиента.

Проанализируем алгоритм исправления. Суть заключается в том, чтобы программно извлечь данные о сериях из документа-основания и корректно распределить их по строкам нового документа. Для этого используется структура запроса к табличным частям Товары и Серии исходного документа ВозвратТоваровОтКлиента.

Разберем по шагам код реализации этого механизма:


&Вместо("ЗаполнитьДокументНаОснованииВозвратаТоваровОтКлиента")
Процедура АР_ЗаполнитьДокументНаОснованииВозвратаТоваровОтКлиента(Знач ДокументОснование)
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ДокументОснование", ДокументОснование);
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ВозвратТоваровОтКлиента.Ссылка КАК Ссылка,
    |    ВозвратТоваровОтКлиента.Организация КАК Организация,
    |    ВозвратТоваровОтКлиента.Партнер КАК Партнер,
    |    ВозвратТоваровОтКлиента.Склад КАК Склад,
    |    ВозвратТоваровОтКлиента.Товары.(
    |        Номенклатура, Характеристика, Упаковка, КоличествоУпаковок, Количество,
    |        Цена, Сумма, СтавкаНДС, СуммаНДС, Склад, Серия, СтатусУказанияСерий
    |    ) КАК Товары,
    |    ВозвратТоваровОтКлиента.Серии.(
    |        Номенклатура, Характеристика, Серия, Количество
    |    ) КАК Серии
    |ИЗ
    |    Документ.ВозвратТоваровОтКлиента КАК ВозвратТоваровОтКлиента
    |ГДЕ
    |    ВозвратТоваровОтКлиента.Ссылка = &ДокументОснование";
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Не Выборка.Следующий() Тогда
        Возврат;
    КонецЕсли;

    // Проверка возможности ввода на основании через типовой механизм БСП
    ОбщегоНазначенияУТ.ПроверитьВозможностьВводаНаОсновании(Выборка.Ссылка);
    
    // Заполнение реквизитов шапки
    ЗаполнитьЗначенияСвойств(ЭтотОбъект, Выборка);
    
    // Работа с табличной частью Товары и Серии
    ТоварыОснования = Выборка.Товары.Выгрузить();
    СерииОснования = Выборка.Серии.Выгрузить();
    
    ИндексыСтрок = Новый Массив();
    Для Каждого СтрокаТовары Из ТоварыОснования Цикл
        // СтатусУказанияСерий = 10 означает, что серии указаны в ТЧ Серии
        Если СтрокаТовары.СтатусУказанияСерий = 10 Тогда
            ИндексыСтрок.Добавить(ТоварыОснования.Индекс(СтрокаТовары));
        КонецЕсли;
    КонецЦикла;
    
    Если ИндексыСтрок.Количество() > 0 Тогда
        КлючСерии = "Номенклатура, Характеристика, Склад";
        // Вызов серверного метода для сопоставления серий
        ОбеспечениеСервер.ПеренестиСерииИзТаблицыВСтроки(ТоварыОснования, ИндексыСтрок, СерииОснования, КлючСерии);
    КонецЕсли;
    
    ЭтотОбъект.Товары.Загрузить(ТоварыОснования);
    
    // Загрузка ТЧ Серии, если она используется в документе
    Для Каждого СтрокаСерии Из СерииОснования Цикл
        НоваяСтрока = ЭтотОбъект.Серии.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаСерии);
        НоваяСтрока.Склад = Выборка.Склад;
    КонецЦикла;
КонецПроцедуры

Важно отметить, что в данном примере мы используем метод ОбеспечениеСервер.ПеренестиСерииИзТаблицыВСтроки. Это штатная функция 1С, которая автоматизирует процесс распределения серий из вспомогательной таблицы в основную таблицу товаров. Это позволяет избежать написания сложных циклов перебора и ручного сопоставления остатков по сериям.

Организация многопоточной обработки данных

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

Основные принципы многопоточности в 1С:

  1. Разделение данных: Весь объем данных делится на порции (кванты). Делить можно по периодам (например, по месяцам), по ответственным, по складам или по диапазонам внутренних идентификаторов UUID.
  2. Фоновые задания: Для каждой порции данных запускается отдельное фоновое задание через метод ФоновыеЗадания.Выполнить().
  3. Минимизация блокировок: Чтобы потоки не конфликтовали, необходимо следить, чтобы разные потоки не обрабатывали одни и те же объекты метаданных одновременно. В идеале потоки должны записывать данные в независимые регистры или разные документы.

Проанализируем пример логики запуска многопоточной обработки:


Процедура ЗапуститьОбработкуВПотоках(МассивДокументов)
    МаксимальноеКоличествоПотоков = 8;
    ПорцияДанных = Цел(МассивДокументов.Количество() / МаксимальноеКоличествоПотоков);
    
    Для НомерПотока = 1 По МаксимальноеКоличествоПотоков Цикл
        НачальныйИндекс = (НомерПотока - 1) * ПорцияДанных;
        КонечныйИндекс = ?(НомерПотока = МаксимальноеКоличествоПотоков, МассивДокументов.Количество() - 1, НомерПотока * ПорцияДанных - 1);
        
        ТекущаяПорция = Новый Массив;
        Для и = НачальныйИндекс По КонечныйИндекс Цикл
            ТекущаяПорция.Добавить(МассивДокументов[и]);
        КонецЦикла;
        
        ПараметрыЗадания = Новый Массив;
        ПараметрыЗадания.Добавить(ТекущаяПорция);
        
        ФоновыеЗадания.Выполнить("МойОбщийМодуль.ОбработатьПорциюДанных", ПараметрыЗадания);
    КонецЦикла;
КонецПроцедуры

Как показывает практика, использование 8–10 потоков на современных серверных мощностях позволяет увеличить скорость обработки данных в 5–8 раз. Однако следует учитывать Блокировки на уровне СУБД, особенно при записи в регистры бухгалтерии или регистры накопления с высокой нагрузкой.

Работа с перечислениями и поиском объектов при загрузке

Разберем типичную ошибку начинающих разработчиков при попытке загрузить данные из внешних файлов (Excel, CSV). Часто возникает проблема при заполнении реквизитов типа ПеречислениеСсылка и поиске существующих объектов.

Посмотрим на пример правильной логики сопоставления ставки НДС при создании новой номенклатуры. Вместо прямого присваивания числа или строки, необходимо обращаться к метаданным перечисления:


Если СтрокаДанных.СтавкаНДС = "20%" Тогда
    НоваяПозиция.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
ИначеЕсли СтрокаДанных.СтавкаНДС = "10%" Тогда
    НоваяПозиция.СтавкаНДС = Перечисления.СтавкиНДС.НДС10;
Иначе
    НоваяПозиция.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
КонецЕсли;

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


Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
    НайденнаяСсылка = Справочники.Номенклатура.НайтиПоНаименованию(СтрокаДанных.Наименование, Истина);
    
    Если НайденнаяСсылка.Пустая() Тогда
        ОбъектНоменклатуры = Справочники.Номенклатура.СоздатьЭлемент();
        ОбъектНоменклатуры.Наименование = СтрокаДанных.Наименование;
    Иначе
        ОбъектНоменклатуры = НайденнаяСсылка.ПолучитьОбъект();
    КонецЕсли;
    
    // Корректное заполнение перечисления через условие или соответствие
    Если СтрокаДанных.СтавкаНДСЧислом = 20 Тогда
        ОбъектНоменклатуры.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
    КонецЕсли;
    
    ОбъектНоменклатуры.Записать();
КонецЦикла;

Полезный совет: Для повышения производительности при массовой загрузке рекомендуется использовать ЗагрузитьВременнуюТаблицу и выполнять поиск соответствий одним запросом ко всей базе, вместо выполнения поиска в цикле. Это существенно сокращает количество обращений к базе данных.

Заключение

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

← На главную