В процессе разработки в среде 1С:Предприятие часто возникает задача зафиксировать номер строки в табличной части документа. Стандартное поле НомерСтроки, предоставляемое платформой, является динамическим: оно отображает текущий индекс строки в массиве. Как только мы удаляем строку, система автоматически пересчитывает индексы всех последующих строк, что приводит к изменению их номеров. Это критично, если на номер строки завязана внешняя логика, печатные формы или связи с другими таблицами. Правильная разработка и оптимизация табличных частей требует особого подхода к хранению индексов.
В рамках этой статьи мы подробно разберем, почему штатный механизм ведет себя именно так, и проанализируем несколько эффективных способов создания "неизменного" номера. Мы рассмотрим как простые варианты с добавлением реквизитов, так и продвинутые методы, используемые в типовых конфигурациях уровня ERP и Бухгалтерия предприятия 3.0.
Разберем самый надежный и часто используемый метод — добавление собственного числового реквизита в табличную часть. Назовем его, например, ФиксНомер или КодСтроки. При реализации работы с идентификаторами строк важно обеспечить их уникальность в рамках документа.
Пошаговая реализация:
ИдентификаторСтроки и типом Число (длина 10, точность 0).НомерСтроки скроем. В некоторых случаях удобно использовать редактор форм в режиме предприятия, чтобы оперативно подправить свойства элементов без изменения конфигурации.Для реализации логики заполнения в модуле формы документа в обработчике события ПриОкончанииРедактирования (или ПередЗаписью) напишем следующий код:
&НаСервере
Процедура ЗаполнитьИдентификаторСтрокиНаСервере(ТекущаяСтрока)
// Если идентификатор уже присвоен, ничего не делаем
Если ТекущаяСтрока.ИдентификаторСтроки <> 0 Тогда
Возврат;
КонецЕсли;
МаксНомер = 0;
// Обходим коллекцию строк для поиска максимального значения
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
Если СтрокаТЧ.ИдентификаторСтроки > МаксНомер Тогда
МаксНомер = СтрокаТЧ.ИдентификаторСтроки;
КонецЕсли;
КонецЦикла;
ТекущаяСтрока.ИдентификаторСтроки = МаксНомер + 1;
КонецПроцедуры
Таким образом, при удалении строки с номером "5" из списка, где было 10 строк, номера остальных (1, 2, 3, 4, 6, 7, 8, 9, 10) останутся неизменными. Новая добавленная строка получит номер 11.
Проанализируем, как аналогичная задача решена в типовых решениях, таких как БП 3.0 или ERP 2.5. Там часто используется механизм ключа связи в сложных табличных частях. Это особенно полезно, если строки основной табличной части (например, Товары) связаны со строками другой табличной части (например, Спецификации).
В таких случаях используется общая процедура для генерации ключей. Мы можем позаимствовать эту логику. Вместо ручного перебора строк в цикле, можно использовать возможности платформы по работе с таблицами значений. Если вам требуется выполнить создание кода новой таблицы значений из реквизита на форме, можно воспользоваться готовой обработкой для ускорения кодинга. Рассмотрим пример заполнения ключей перед записью документа в модуле объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Используем стандартный подход: пересчет ключей только для пустых значений
МаксимальныйКлюч = 0;
Для Каждого СтрокаТЧ Из Товары Цикл
МаксимальныйКлюч = Макс(МаксимальныйКлюч, СтрокаТЧ.КлючСвязи);
КонецЦикла;
Для Каждого СтрокаТЧ Из Товары Цикл
Если СтрокаТЧ.КлючСвязи = 0 Тогда
МаксимальныйКлюч = МаксимальныйКлюч + 1;
СтрокаТЧ.КлючСвязи = МаксимальныйКлюч;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Этот метод гарантирует, что каждый раз при сохранении документа все новые строки получат уникальные в рамках этого документа номера, а старые сохранят свои значения, независимо от того, удаляли мы что-то или нет.
Выясним причину, по которой обычное добавление реквизита может работать некорректно при интерактивной работе пользователя. Если пользователь воспользуется функцией "Скопировать строку", платформа скопирует все значения реквизитов, включая наш "уникальный" номер. Поэтому важно грамотно обрабатывать события формы 1С при работе с данными.
Чтобы этого избежать, разберем порядок действий в модуле формы. Нам нужно перехватить событие ПриКопировании для таблицы:
&НаКлиенте
Процедура ТоварыПриКопировании(Элемент, ОбъектКопирования)
// Очищаем скопированный идентификатор, чтобы система присвоила новый
Элемент.ТекущиеДанные.ИдентификаторСтроки = 0;
// Вызываем процедуру присвоения нового номера
ЗаполнитьИдентификаторСтрокиНаСервере(Элемент.ТекущиеДанные);
КонецПроцедуры
Важно: Всегда обнуляйте ключевые поля при копировании, иначе логика неизменности номеров нарушится из-за дублей.
Рассмотрим альтернативный подход, который предложили опытные участники сообщества. Вместо физического удаления строки из коллекции ДанныеФормыКоллекция, мы можем использовать механизм пометки на удаление внутри таблицы.
Алгоритм действий:
Удалена (Тип: Булево).Строка.Удалена = Истина.Этот способ идеален, если заказчик требует строгого соблюдения последовательности номеров без "дырок". Строка физически остается в базе под своим номером, но не участвует в расчетах и не видна пользователю (если настроить отбор в элементе формы "Таблица").
Если ваша задача — обеспечить связь строки с внешними системами или проанализировать граф зависимостей ссылок реквизитов табличных частей, использование уникальных идентификаторов является наиболее надежным решением. Вместо числа можно использовать УникальныйИдентификатор.
Хотя пользователю неудобно смотреть на строку вида 550e8400-e29b-41d4-a716-446655440000, этот метод является самым надежным в многопользовательской среде. Вы можете хранить GUID в скрытом реквизите, а пользователю показывать красивый порядковый номер, вычисляемый на лету, либо хранить оба значения.
Мы рассмотрели несколько подходов к решению задачи. Выбор конкретного метода зависит от целей:
КодСтроки и обработки события ПриКопировании.КлючСвязи, аналогичные тем, что реализованы в ОбщегоНазначения.ЗаполнитьКлючиСвязиТабличныхЧастей.Помните, что штатное поле НомерСтроки — это лишь проекция индекса строки в памяти, и пытаться "заставить" его не меняться программно — тупиковый путь. Создание собственного реквизита является единственно верным архитектурным решением в данной ситуации.