Задвоились и размножаются строки в заказе клиента 1С:Управление торговлей: разбираем причины и исправляем ошибку

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

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

В этой статье мы подробно разберем, почему возникает этот сбой в конфигурациях семейства ERP/КА/УТ 11, детально проанализируем взаимосвязь этого бага с платформенными механизмами, выясним роль реквизита КодСтроки и предоставим пошаговую инструкцию по локализации и исправлению этой ошибки.

Симптомы проблемы: как это выглядит на практике

Давайте рассмотрим типичную ситуацию, с которой сталкивается пользователь в программе «1С:Управление торговлей», редакция 11.4:

  1. В документе «Заказ клиента» в табличной части «Товары» внезапно появляется несколько абсолютно идентичных строк. При этом реквизит КодСтроки у всех этих строк одинаковый (например, равен «1»).
  2. Попытка интерактивного удаления лишних строк ни к чему не приводит. Пользователь удаляет дубли, оставляет одну правильную позицию, нажимает кнопку «Записать» или «Провести и закрыть». Но при повторном открытии этого же заказа лишние строки возвращаются на свои места.
  3. Если пользователь решает скопировать этот проблемный документ, то в новом (еще не записанном) заказе коды строк автоматически пересчитываются платформой и встают по порядку (от 1 до 4). Однако после первой же записи, закрытия и повторного открытия нового документа количество строк увеличивается в четыре раза — их становится 16. При следующем цикле открытия-закрытия их число возрастает до 64. Прогрессия продолжает расти при каждом перечитывании данных.

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

Выясняем причину: почему строки множатся в геометрической прогрессии?

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

Давайте разберем по шагам, как именно работает ошибочный алгоритм при открытии документа:

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

Критическая важность реквизита «КодСтроки»

Отдельно рассмотрим, почему у всех дублирующихся строк реквизит КодСтроки оказывается равен «1» (или любому другому фиксированному значению). В современных конфигурациях 1С на базе ERP-платформы (УТ 11, Комплексная автоматизация, ERP) этот реквизит имеет фундаментальное значение:

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

Рассмотрим пример типичного НЕПРАВИЛЬНОГО кода, который приводит к подобной проблеме:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    
    // НЕПРАВИЛЬНО: Алгоритм обходит строки и добавляет новые без проверки на дубли
    Для Каждого СтрокаТовара Из ТекущийОбъект.Товары Цикл
        Если СтрокаТовара.Номенклатура.Акционная Тогда
            // Программа пытается добавить подарок к каждой строке
            НоваяСтрока = ТекущийОбъект.Товары.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТовара);
            НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоРеквизиту("Подарок");
            НоваяСтрока.Количество = 1;
            // Внимание: КодСтроки остался скопированным или пустым!
            // Нет проверки, был ли подарок уже добавлен ранее при предыдущем чтении!
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Как решить проблему: пошаговое руководство

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

Шаг 1. Локализация и исправление программного кода

Проанализируем систему, чтобы найти место сбоя, и исправим его:

  1. Проверка расширений: Перейдем в меню Администрирование — Печатные формы, отчеты и обработки — Расширения. Временно отключим все активные расширения, которые могут влиять на работу документа «Заказ клиента». Проверим, прекратилось ли размножение строк при копировании. Если проблема исчезла, значит, некорректный код находится в одном из расширений.
  2. Анализ кода формы: Откроем конфигуратор, найдем форму документа ЗаказКлиента. Изучим процедуры ПриЧтенииНаСервере, ПриСозданииНаСервере и ПередЗаписьюНаСервере. Также выполним глобальный поиск по конфигурации подписок на события ПередЗаписью и ПриЗаписи для этого типа документа.
  3. Корректировка алгоритма: Перепишем логику добавления/разбиения строк. Во-первых, необходимо использовать флаги обработки, чтобы программа не обрабатывала строки повторно. Во-вторых, нужно обязательно инициализировать уникальный КодСтроки для каждой новой позиции табличной части.

Посмотрим на пример ПРАВИЛЬНОЙ реализации аналогичной задачи:


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

Шаг 2. Очистка базы данных от поврежденных документов

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

Вариант А: Интерактивный (для единичных документов)
Если пострадал всего один или два заказа клиента, мы можем исправить их вручную. Для этого необходимо запустить обработку «Групповое изменение реквизитов» (встроенная обработка 1С), выбрать поврежденный документ, временно отключить выполнение бизнес-правил и программно очистить дубли, после чего пересоздать строки заново вручную при выключенном проблемном расширении. Для прямого редактирования и очистки строк табличных частей также можно использовать универсальный редактор реквизитов и табличных частей.

Вариант Б: Программный (для массового исправления данных)
Если поврежденных документов много, напишем небольшую внешнюю обработку, которая автоматически свернет дублирующиеся строки и пересчитает их идентификаторы. Пример серверного метода для такой обработки представлен ниже:


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

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

← На главную