В практике разработки на платформе 1С часто возникает задача нормализации данных в документах. Одна из таких задач — разделение одной строки табличной части (ТЧ) с большим количеством товара на несколько строк, где количество в каждой равно единице. Это может потребоваться для ведения поштучного учета, работы с серийными номерами, марками («Честный ЗНАК») или исправления ошибок предыдущих загрузок данных.
В этой статье мы подробно разберем, как реализовать такой алгоритм, избежав типичных ошибок новичков, связанных с итерацией по коллекциям и потерей данных в интерфейсе.
Рассмотрим типичную ошибку. Разработчик пытается использовать цикл Для Каждого непосредственно по табличной части документа и внутри этого цикла добавлять новые строки. Проанализируем, к чему это приведет: коллекция, по которой идет обход, динамически меняется. Это либо вызывает пропуск строк, либо приводит к бесконечному циклу, так как индекс текущей строки смещается.
Кроме того, при программном изменении данных в управляемой форме платформа «не замечает» модификации, если они сделаны не интерактивно. В итоге пользователь может закрыть документ, и все изменения будут потеряны без предупреждения.
Наиболее надежный и производительный способ — перенести обработку на сервер, выгрузить табличную часть в массив или таблицу значений, обработать её и загрузить обратно. Рассмотрим этот алгоритм по шагам.
ТаблицаЗначений.Проанализируем пример кода для реализации этой логики:
&НаСервере
Процедура РазбитьСтрокиНаСервере()
// Получаем таблицу значений из табличной части объекта
ТаблицаСостава = Объект.Состав.Выгрузить();
// Создаем копию структуры таблицы для формирования нового состава
НоваяТаблицаСостава = ТаблицаСостава.СкопироватьКолонки();
Для Каждого СтрокаТЧ Из ТаблицаСостава Цикл
Если СтрокаТЧ.Количество > 1 Тогда
// Запоминаем исходное количество
ИсходноеКолво = СтрокаТЧ.Количество;
Для Счетчик = 1 По ИсходноеКолво Цикл
НоваяСтрока = НоваяТаблицаСостава.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЧ);
// Устанавливаем единичное количество
НоваяСтрока.Количество = 1;
// Важно: пересчитываем сумму, если это необходимо
// НоваяСтрока.Сумма = СтрокаТЧ.Цена * 1;
КонецЦикла;
Иначе
// Если количество и так 1 или меньше (например, 0), просто копируем строку
НоваяСтрока = НоваяТаблицаСостава.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЧ);
КонецЕсли;
КонецЦикла;
// Загружаем полученный результат обратно в объект
Объект.Состав.Загрузить(НоваяТаблицаСостава);
КонецПроцедуры
При разбивке строки с количеством, например, 3 и общей суммой 100 рублей, простое деление может дать 33.33 рубля на строку. В сумме три такие строки дадут 99.99 рублей. Потеряется 1 копейка, что критично для бухгалтерского учета. Чтобы своевременно обнаружить возможные расхождения после подобных доработок программы, можно использовать автоматическое сравнение бухгалтерских проводок между базами 1С.
Рассмотрим, как правильно распределять суммы. Мы должны рассчитывать сумму для каждой строки, а для последней строки — присваивать остаток от общей суммы исходной строки.
// Пример логики распределения суммы
Если СтрокаТЧ.Количество > 1 Тогда
ТекущаяСумма = СтрокаТЧ.Сумма;
СуммаДляНовыхСтрок = Окр(ТекущаяСумма / ИсходноеКолво, 2);
НакопленнаяСумма = 0;
Для Счетчик = 1 По ИсходноеКолво Цикл
НоваяСтрока = НоваяТаблицаСостава.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЧ);
НоваяСтрока.Количество = 1;
Если Счетчик = ИсходноеКолво Тогда
// В последнюю строку записываем остаток
НоваяСтрока.Сумма = ТекущаяСумма - НакопленнаяСумма;
Иначе
НоваяСтрока.Сумма = СуммаДляНовыхСтрок;
НакопленнаяСумма = НакопленнаяСумма + СуммаДляНовыхСтрок;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если вы хотите изменить табличную часть «на месте», не прибегая к выгрузке в таблицу значений, необходимо использовать обход коллекции с конца. Это позволит добавлять или удалять строки, не нарушая индексы еще не обработанных элементов.
&НаКлиенте
Процедура РазбитьСтрокиОбратнымЦиклом()
КолвоСтрок = Объект.Состав.Количество();
// Обходим с последней строки к первой
Для Индекс = -(КолвоСтрок - 1) По 0 Цикл
ТекущаяСтрока = Объект.Состав[ -Индекс ];
Если ТекущаяСтрока.Количество > 1 Тогда
ИсходноеКолво = ТекущаяСтрока.Количество;
// Сначала меняем количество в текущей строке на 1
ТекущаяСтрока.Количество = 1;
// Добавляем остальные строки (n-1 штук)
Для Счетчик = 1 По (ИсходноеКолво - 1) Цикл
НоваяСтрока = Объект.Состав.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекущаяСтрока);
НоваяСтрока.Количество = 1;
КонецЦикла;
КонецЕсли;
КонецЦикла;
// Устанавливаем флаг модифицированности, так как изменения программные
ЭтаФорма.Модифицированность = Истина;
КонецПроцедуры
Выясним причину, по которой 1С не предлагает сохранить документ после работы такой обработки. При интерактивном вводе данных (пользователь нажал кнопку, ввел текст) флаг Модифицированность устанавливается платформой автоматически. При программном изменении реквизитов Объект внутри формы этот флаг не взводится.
Решение: Всегда явно указывайте ЭтаФорма.Модифицированность = Истина; после завершения программных манипуляций с данными в контексте формы. Если же вы работаете в обработке, которая открывает документ в фоне, вам необходимо вызвать метод Объект.Записать().
Проанализируем ситуацию в сложных конфигурациях (ERP, УТ 11, КА 2). В таких системах строки основной табличной части часто связаны с другими таблицами (например, ЭтапыГрафикаОплаты или ВидыЗапасов) через реквизит КлючСвязи.
Если вы просто скопируете строку методом ЗаполнитьЗначенияСвойств, то у всех 5 новых строк будет один и тот же КлючСвязи. При проведении документа это вызовет критическую ошибку, так как система не сможет сопоставить данные. В таких случаях для каждой новой строки необходимо генерировать новый уникальный номер ключа связи (обычно это целое число) и следить за целостностью данных в подчиненных таблицах.
Если в вашей конфигурации внедрена БСП, стоит рассмотреть использование общего модуля ОбщегоНазначения. В нем могут присутствовать вспомогательные функции для копирования структур данных. Однако для специфической задачи «разбивки по количеству» чаще всего пишется самописный цикл, так как логика пересчета сумм и весов индивидуальна для каждого документа.
Подведем итог: Для безопасной разбивки строк используйте серверный метод с временной таблицей значений, не забывайте про распределение копеек в суммах и обязательно устанавливайте признак модифицированности формы, чтобы пользователь мог сохранить результат работы обработки.