Как исправить ошибку заполнения табличной части по спецификации в 1С:УПП 1.3?

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

При разработке и кастомизации конфигураций на платформе «1С:Предприятие 8» (особенно таких крупных систем, как «Управление производственным предприятием, редакция 1.3») программисты часто сталкиваются с задачами автоматического заполнения табличных частей документов. Одной из типовых задач является заполнение материалов на основании выпущенной продукции (или брака).

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

Давайте сначала проанализируем исходный код, который привел к некорректному поведению системы.


Процедура ЗаполнитьМатериалыБракаНажатие(Элемент)
    Ответ = Вопрос("Проверьте правильность заполнения номенклатуры!!!" + Символы.ПС + "Продолжить?" + Символы.ПС, РежимДиалогаВопрос.ДаНет);
    Если Не Ответ = КодВозвратаДиалога.Да Тогда
        Возврат;
    КонецЕсли;
    
    Для каждого Стр из ПродукцияБрака Цикл
        Если Стр.Подразделение.Наименование = "2.Участок ТЦ" 
            ИЛИ Стр.Подразделение.Наименование = "3.Участок 1 исп." 
            ИЛИ Стр.Подразделение.Наименование = "5.Участок ПСИ" 
            ИЛИ Стр.Подразделение.Наименование = "6.Участок ок.сб." 
            ИЛИ Стр.Подразделение.Наименование = "7.Участок Вых.контр." Тогда
            
            НоваяСтрока = МатериалыБрака.Добавить;
            НоваяСтрока.Количество = Стр.Количество;
            НоваяСтрока.Номенклатура = Стр.Номенклатура;
            НоваяСтрока.Заказ = Стр.Заказ;
            НоваяСтрока.ЕдиницаИзмерения = Стр.ЕдиницаИзмерения;
            НоваяСтрока.Коэффициент = Стр.Коэффициент;
            НоваяСтрока.Подразделение = Стр.Подразделение;
            
        ИначеЕсли Стр.Подразделение.Наименование = "1.Участок ТП" 
            ИЛИ Стр.Подразделение.Наименование = "4.Участок сборки" Тогда
            
            ЗаполнитьМатериалыБракаПоСпецификации(МатериалыБрака, "МатериалыБрака");
        КонецЕсли;
    КонецЦикла;
    
    Этаформа.ТекущийЭлемент = ЭлементыФормы.МатериалыБрака;
КонецПроцедуры

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

---

Причина 1: Вызов групповой процедуры заполнения внутри построчного цикла

Главная логическая ошибка кроется в этой строке, расположенной внутри цикла Для каждого Стр из ПродукцияБрака Цикл:

ЗаполнитьМатериалыБракаПоСпецификации(МатериалыБрака, "МатериалыБрака");

Типовая общая процедура ЗаполнитьМатериалыБракаПоСпецификации предназначена для работы со всем контекстом документа. Она не знает, на какой именно строчке цикла сейчас находится выполнение. «Под капотом» эта процедура выполняет следующие действия:

  1. Очищает целевую табличную часть МатериалыБрака.
  2. Считывает все строки из исходной таблицы ПродукцияБрака.
  3. Для каждой строки находит спецификацию по умолчанию, рассчитывает нормативы и добавляет новые строки в МатериалыБрака.

Когда мы помещаем такой вызов внутрь построчного цикла, происходит конфликт логики:

В результате мы получаем хаотичное смешивание данных и некорректное заполнение.

---

Причина 2: Жесткое кодирование наименований подразделений (Hardcode)

Проверка условий вида Стр.Подразделение.Наименование = "2.Участок ТЦ" является серьезной архитектурной ошибкой по двум причинам:

  1. Ненадежность: если пользователь переименует подразделение в справочнике (например, исправит опечатку или удалит префикс «2.»), код молча перестанет работать. Программа не выдаст ошибку, но условия в блоках Если всегда будут возвращать значение Ложь.
  2. Низкая производительность: обращение через точку Стр.Подразделение.Наименование заставляет платформу выполнять неявный SQL-запрос к базе данных для получения наименования при обработке каждой отдельной строки. На больших документах это приведет к зависанию интерфейса.

---

Решение проблемы: Правильная архитектура заполнения

Чтобы решить поставленную задачу красиво и надежно, нам нужно разделить заполнение на два независимых этапа:

  1. Выделить продукцию, требующую заполнения по спецификации, в отдельный временный массив или таблицу и обработать её групповым методом.
  2. Перенести строки прямого копирования отдельно.

Давайте рассмотрим подробный пример реализации этого алгоритма с оптимизацией обращений к базе данных.


Процедура ЗаполнитьМатериалыБракаНажатие(Элемент)
    
    Ответ = Вопрос("Проверьте правильность заполнения номенклатуры!!!" + Символы.ПС + "Продолжить?" + Символы.ПС, РежимДиалогаВопрос.ДаНет);
    Если Не Ответ = КодВозвратаДиалога.Да Тогда
        Возврат;
    КонецЕсли;
    
    // Очищаем целевую таблицу перед заполнением
    МатериалыБрака.Очистить();
    
    // Создаем массивы для быстрой проверки соответствия подразделений без обращений к БД в цикле
    СписокПрямогоПереноса = Новый Массив;
    СписокПоСпецификации  = Новый Массив;
    
    // Заполняем массивы ссылками на подразделения (поиск по коду надежнее, чем по наименованию)
    СписокПрямогоПереноса.Добавить(Справочники.Подразделения.НайтиПоКоду("00002")); // Участок ТЦ
    СписокПрямогоПереноса.Добавить(Справочники.Подразделения.НайтиПоКоду("00003")); // Участок 1 исп.
    СписокПрямогоПереноса.Добавить(Справочники.Подразделения.НайтиПоКоду("00005")); // Участок ПСИ
    СписокПрямогоПереноса.Добавить(Справочники.Подразделения.НайтиПоКоду("00006")); // Участок ок.сб.
    СписокПрямогоПереноса.Добавить(Справочники.Подразделения.НайтиПоКоду("00007")); // Участок Вых.контр.
    
    СписокПоСпецификации.Добавить(Справочники.Подразделения.НайтиПоКоду("00001"));  // Участок ТП
    СписокПоСпецификации.Добавить(Справочники.Подразделения.НайтиПоКоду("00004"));  // Участок сборки
    
    // Шаг 1: Создаем временную таблицу-копию продукции для расчета по спецификациям
    ТаблицаДляСпецификаций = ПродукцияБрака.ВыгрузитьКолонки();
    
    // Шаг 2: Распределяем строки по правилам обработки
    Для каждого Стр Из ПродукцияБрака Цикл
        
        СсылкаПодразделения = Стр.Подразделение;
        
        // Проверяем вхождение ссылки в наши списки (работает мгновенно в оперативной памяти)
        Если СписокПрямогоПереноса.Найти(СсылкаПодразделения) <> Неопределено Тогда
            
            // Прямой перенос данных
            НоваяСтрока = МатериалыБрака.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, Стр);
            
        ИначеЕсли СписокПоСпецификации.Найти(СсылкаПодразделения) <> Неопределено Тогда
            
            // Запоминаем строки, которые нужно отправить на расчет спецификаций
            ЗаполнитьЗначенияСвойств(ТаблицаДляСпецификаций.Добавить(), Стр);
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Шаг 3: Если есть продукция для расчета спецификаций, обрабатываем её
    Если ТаблицаДляСпецификаций.Количество() > 0 Тогда
        
        // Создаем временную структуру для передачи в типовой механизм
        // Мы временно подменяем исходную таблицу документа отфильтрованной таблицей
        СохраненнаяТаблицаПродукции = ПродукцияБрака.Выгрузить();
        
        ПродукцияБрака.Загрузить(ТаблицаДляСпецификаций);
        
        // Вызываем типовой механизм для заполнения отфильтрованной части
        // Метод допишет рассчитанные строки в конец таблицы МатериалыБрака
        ЗаполнитьМатериалыБракаПоСпецификации(МатериалыБрака, "МатериалыБрака");
        
        // Возвращаем исходную продукцию на место
        ПродукцияБрака.Загрузить(СохраненнаяТаблицаПродукции);
        
    КонецЕсли;
    
    ЭлементыФормы.МатериалыБрака.Активизировать()
    
КонецПроцедуры

---

Преимущества предложенного решения:

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

← На главную