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

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

📊 Какой тип нумерации вам нужен чаще всего?
Статическая в таблицах
Динамическая в отчетах
Автоматическая в документах
Другое

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

1. Статическая нумерация в табличных частях документов

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

Для реализации перейдите в конфигуратор и откройте нужный документ. В табличной части добавьте новый реквизит типа Число с именем НомерСтроки. Затем в модуле объекта документа пропишите обработчик события ПередЗаписью:

Процедура ПередЗаписью(Отказ)

НомерСтроки = 1;

Для Каждого Строка Из ТабличнаяЧасть Цикл

Строка.НомерСтроки = НомерСтроки;

НомерСтроки = НомерСтроки + 1;

КонецЦикла;

КонецПроцедуры

Этот код будет автоматически проставлять номера при записи документа. Обратите внимание, что при удалении строк нумерация не будет пересчитываться — останутся "дыры" в последовательности.

⚠️ Внимание: При использовании этого метода в управляемых формах не забудьте добавить колонку НомерСтроки в настройки табличного поля на форме и установить свойство ТолькоПросмотр = Истина, чтобы пользователи не могли редактировать номера вручную.

Для более гибкого управления отображением можно использовать условное оформление. Например, чтобы выделять четные и нечетные строки разными цветами:

ЭлементыФормы.ТабличнаяЧасть.УсловноеОформление.Добавить();

НовоеОформление = ЭлементыФормы.ТабличнаяЧасть.УсловноеОформление[0];

НовоеОформление.Условие = "Выражение: НомерСтроки % 2 = 0";

НовоеОформление.ЦветФона = ВебЦвета.СветлоСерый;

2. Динамическая нумерация в отчетах и списках

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

Рассмотрим пример для отчета на СКД (Система Компоновки Данных). В настройках схемы компоновки добавьте новый ресурс типа Число с именем НомерСтроки. Затем в настройках группировки установите выражение для ресурса:

ВычисляемыйПоле = "НомерСтроки";

Выражение = "РядНомер()";

Функция РядНомер() автоматически проставит последовательные номера для каждой строки результата. Этот метод учитывает все примененные отборы и сортировки.

Для динамических списков в управляемых формах можно использовать обработчик события ПриАктивизацииСтроки:

Процедура ТаблицаПриАктивизацииСтроки(Элемент, Строка, СтандартнаяОбработка)

НомерСтроки = 1;

Для Каждого ТекСтрока Из Элемент.СписокВыбранныхСтрок Цикл

ТекСтрока.НомерСтроки = НомерСтроки;

НомерСтроки = НомерСтроки + 1;

КонецЦикла;

КонецПроцедуры

💡

Для ускорения работы с большими списками (1000+ строк) используйте механизм виртуальных таблиц или ограничивайте количество отображаемых строк через параметр ПозицияНачальная и КоличествоСтрок

3. Нумерация с учетом иерархии (для групп и подгрупп)

Когда данные имеют иерархическую структуру (например, группы и подгруппы товаров), простой последовательной нумерации недостаточно. Здесь требуется многоуровневая нумерация типа "1", "1.1", "1.2", "2" и т.д.

Реализовать это можно через рекурсивную функцию. Пример для обработки дерева значений:

Функция ПронумероватьУзлы(Узел, Префикс = "")

Если Узел.ЭтоГруппа Тогда

Узел.Номер = Префикс + Строка(Узел.Уровень) + ".";

Номер = 1;

Для Каждого ПодчиненныйУзел Из Узел.Строки Цикл

ПодчиненныйУзел.Номер = Префикс + Строка(Узел.Уровень) + "." + Номер;

Если ПодчиненныйУзел.ЭтоГруппа Тогда

ПронумероватьУзлы(ПодчиненныйУзел, Префикс + Строка(Узел.Уровень) + ".");

КонецЕсли;

Номер = Номер + 1;

КонецЦикла;

Иначе

Узел.Номер = Префикс + Строка(Узел.Уровень);

КонецЕсли;

КонецФункции

Для отображения такой нумерации в отчете настройте вычисляемое поле с формулой:

Выражение = "Строка(Уровень()) + ""."" + Строка(НомерВГруппе())";

Этот подход особенно полезен для:

  • 📊 Иерархических справочников (номенклатура, контрагенты)
  • 📋 Многоуровневых отчетов с группировкой
  • 📑 Документов с подчиненными табличными частями

Как обновить нумерацию при изменении иерархии?

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

4. Нумерация в тонком клиенте и веб-интерфейсе

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

Основные проблемы и их решения:

Проблема Причина Решение
Номера сбрасываются при прокрутке Динамическая подгрузка данных Использовать серверный метод пересчета
Медленная работа с большими списками Пересчет на клиенте Перенести логику на сервер
Неправильная нумерация при сортировке Клиентская сортировка Отключить клиентскую сортировку

Для тонкого клиента рекомендуется использовать серверные функции с асинхронным вызовом:

&НаСервере

Функция ПолучитьПронумерованныеДанные(Отбор)

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ

| НОМЕР_СТРОКИ() КАК Номер,

| ...

|ИЗ ...

|ГДЕ ...…";

Возврат Запрос.Выполнить().Выгрузить();

КонецФункции

&НаКлиенте

Процедура ОбновитьСписок(Команда)

Данные = Ожидать ПолучитьПронумерованныеДанные(Отбор) НаКлиенте;

ЭлементыФормы.Список.Список = Данные;

КонецПроцедуры

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

Запрос.Параметры.Установить("НачальнаяСтрока", (ТекущаяСтраница - 1) * РазмерСтраницы + 1);

Запрос.Текст = "ВЫБРАТЬ

| (&НачальнаяСтрока + РОВНОМЕРНОЕ_РАСПРЕДЕЛЕНИЕ(0, 1000)) КАК Номер,

| ...";

5. Нумерация с сохранением в базе данных

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

Алгоритм реализации:

  1. Добавьте реквизит НомерСтроки в табличную часть
  2. Создайте обработчик ПриЗаписи для автоматического заполнения
  3. Настройте уникальный индекс по полю НомерСтроки в таблице базы данных
  4. Реализуйте механизм пересчета при изменении порядка строк

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

Процедура ТабличнаяЧастьПеремещение(Элемент, ПеремещаемаяСтрока, ЦелеваяСтрока)

Если ПеремещаемаяСтрока.НомерСтроки > ЦелеваяСтрока.НомерСтроки Тогда

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ

| РАЗРЕШИТЬ ИЗМЕНЕНИЕ ТабличнаяЧасть.НомерСтроки КАК НомерСтроки

|ИЗ

| Документ.ИмяДокумента.ТабличнаяЧасть КАК ТабличнаяЧасть

|ГДЕ

| ТабличнаяЧасть.Ссылка = &Ссылка

| И ТабличнаяЧасть.НомерСтроки >= &ЦелевойНомер

| И ТабличнаяЧасть.НомерСтроки < &ИсходныйНомер";

Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);

Запрос.УстановитьПараметр("ЦелевойНомер", ЦелеваяСтрока.НомерСтроки);

Запрос.УстановитьПараметр("ИсходныйНомер", ПеремещаемаяСтрока.НомерСтроки);

Результат = Запрос.Выполнить();

Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл

Выборка.НомерСтроки = Выборка.НомерСтроки + 1;

КонецЦикла;

Иначе

// Аналогичная логика для перемещения вниз

КонецЕсли;

ПеремещаемаяСтрока.НомерСтроки = ЦелеваяСтрока.НомерСтроки + (Направление = НаправлениеПеремещения.Вниз ? 1 : 0);

КонецПроцедуры

💡

При сохранении нумерации в базе обязательно настройте обработку конфликтов при одновременной работе нескольких пользователей. Используйте механизм транзакций или блокировок.

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

6. Оптимизация производительности при нумерации

Некорректная реализация нумерации может значительно замедлить работу системы, особенно при обработке больших объемов данных. Вот ключевые рекомендации по оптимизации:

Для клиент-серверного варианта:

  • 🚀 Переносите логику нумерации на сервер
  • 📊 Используйте временные таблицы для промежуточных расчетов
  • 🔄 Ограничивайте количество пересчитываемых строк
  • 📈 Применяйте индексы для полей, используемых в нумерации

Пример оптимизированного запроса для нумерации:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ РАЗРЕШИТЬ ИЗМЕНЕНИЕ

| Т.Ссылка КАК Ссылка,

| (ВЫБОР

| КОГДА Т.Уровень = 1

| ТОГДА РОВНОМЕРНОЕ_РАСПРЕДЕЛЕНИЕ(1, 1000000)

| ИНАЧЕ 0

| КОНЕЦ) КАК НомерСтроки

|ПОМЕСТИТЬ ВТ_Нумерация

|ИЗ

| Документ.ИмяДокумента.ТабличнаяЧасть КАК Т

|ГДЕ

| Т.Ссылка = &Ссылка

|//////////////

|ОБЪЕДИНИТЬ ВСЕ

|//////////////

|

|ВЫБРАТЬ

| ВТ_Нумерация.Ссылка КАК Ссылка,

| РЯД_НОМЕР() КАК НомерСтроки

|ИЗ

| ВТ_Нумерация КАК ВТ_Нумерация

|УПОРЯДОЧИТЬ ПО

| Ссылка.Дата,

| Ссылка.Номер";

Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);

Результат = Запрос.Выполнить();

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

ФоновоеЗадание = ФоновыеЗадания.СоздатьФоновоеЗадание(

"ПересчетНумерации",

"Документ.ИмяДокумента.ПересчитатьНумерациюСтрок",

Структура,

Истина);

Используется серверная обработка|Запросы оптимизированы по индексам|Ограничено количество обрабатываемых строк|Реализована обработка ошибок при пересчете-->

7. Типичные ошибки и их решение

При реализации нумерации строк разработчики часто сталкиваются с типовыми проблемами. Рассмотрим наиболее распространенные случаи и способы их устранения:

Ошибка "Номер строки не уникален":

  • 🔧 Причина: Попытка присвоить одинаковые номера разным строкам
  • 🛠 Решение: Добавить проверку уникальности перед присваиванием номера или использовать составной ключ (например, СсылкаНаДокумент + НомерСтроки)

Нумерация сбрасывается при фильтрации:

  • 🔧 Причина: Пересчет номеров происходит до применения фильтра
  • 🛠 Решение: Перенести логику нумерации в обработчик события ПриОтображении или использовать функцию РядНомер() в СКД

Медленная работа при большом количестве строк:

  • 🔧 Причина: Клиентский пересчет номеров для всех строк
  • 🛠 Решение: Реализовать постраничную нумерацию или серверный пересчет

Некорректная нумерация в иерархических структурах:

  • 🔧 Причина: Не учитывается уровень вложенности при формировании номера
  • 🛠 Решение: Использовать рекурсивную функцию с передачей текущего префикса

Для диагностики проблем с нумерацией полезно использовать журнал регистрации. Добавьте запись в журнал перед началом пересчета:

ЖурналРегистрации.ЗаписатьСобытие(

"НачалоПересчетаНумерации",

УровеньЖурналаРегистрации.Информация,

,

,

"Пересчет нумерации для документа " + Объект.Ссылка);

Это поможет отследить, на каком этапе происходит сбой, особенно в распределенных системах.

8. Альтернативные подходы к нумерации

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

Использование автонумерации через СКД:

В отчетах на системе компоновки данных можно использовать встроенную функцию НОМЕР_СТРОКИ(), которая автоматически проставляет последовательные номера с учетом всех примененных отборов и сортировок. Преимущество этого метода — высокая производительность, так как нумерация происходит на уровне СУБД.

Нумерация через реквизит "Порядок":

Вместо явной нумерации можно использовать реквизит типа Число с именем Порядок, который будет определять положение строки. Это позволяет:

  • 🔢 Гибко изменять порядок строк без пересчета всех номеров
  • 📌 Сохранять пользовательскую сортировку между сеансами
  • 🔄 Легко реализовывать механизм перетаскивания строк

Пример реализации порядка через перетаскивание:

Процедура ТабличнаяЧастьПеремещение(Элемент, ПеремещаемаяСтрока, ЦелеваяСтрока)

Если ЦелеваяСтрока = Неопределено Тогда

ПеремещаемаяСтрока.Порядок = (Элемент.Список[Элемент.Список.ВГраница()] + 1).Порядок + 10;

ИначеЕсли ПеремещаемаяСтрока.Порядок > ЦелеваяСтрока.Порядок Тогда

ПеремещаемаяСтрока.Порядок = ЦелеваяСтрока.Порядок - 5;

Иначе

ПеремещаемаяСтрока.Порядок = ЦелеваяСтрока.Порядок + 5;

КонецЕсли;

Объект.Записать();

КонецПроцедуры

Визуальная нумерация через стили:

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

УсловноеОформление = ЭлементыФормы.Таблица.УсловноеОформление;

НовоеОформление = УсловноеОформление.Добавить();

НовоеОформление.Условие = "Выражение: НомерСтроки % 2 = 0";

НовоеОформление.ЦветФона = ВебЦвета.СветлоГолубой;

Этот метод не требует изменений в базе данных и работает быстро даже с большими объемами данных.

💡

Выбор метода нумерации должен основываться на бизнес-требованиях: если номера строк имеют значение для бизнес-процессов (например, порядок выполнения операций), их нужно сохранять в базе. Если нумерация нужна только для удобства просмотра — достаточно визуального отображения.

Часто задаваемые вопросы

Как сделать нумерацию строк в отчете на СКД?

В схеме компоновки данных добавьте вычисляемое поле с выражением РядНомер(). Это автоматически пронумерует строки результата с учетом всех примененных отборов и сортировок. Для многоуровневых отчетов используйте комбинацию Уровень() и НомерВГруппе().

Пример выражения для иерархического отчета: Строка(Уровень()) + "." + Строка(НомерВГруппе())

Почему нумерация сбрасывается при фильтрации таблицы?

Это происходит потому, что пересчет номеров происходит до применения фильтра. Решения:

  1. Перенесите логику нумерации в обработчик события ПриОтображении
  2. Используйте функцию РядНомер() в СКД
  3. Реализуйте серверный метод пересчета, который учитывает текущий фильтр

Для управляемых форм лучшее решение — использовать клиент-серверный вызов с передачей параметров фильтрации.

Как реализовать нумерацию с сохранением в базе данных?

Шаги реализации:

  1. Добавьте реквизит НомерСтроки в табличную часть
  2. Создайте обработчик ПередЗаписью для автоматического заполнения
  3. Настройте уникальный индекс по полю НомерСтроки в таблице БД
  4. Реализуйте механизм пересчета при изменении порядка строк

Важно: при таком подходе необходимо обрабатывать конфликты при одновременной работе нескольких пользователей.

Как сделать многоуровневую нумерацию для иерархических данных?

Используйте рекурсивную функцию, которая формирует номер строки с учетом уровня вложенности. Пример:

Функция ПолучитьНомерУзла(Узел)

Если Узел.ЭтоГруппа Тогда

Возврат Строка(Узел.Уровень);

Иначе

Возврат Строка(Узел.Уровень) + "." + Строка(Узел.НомерВГруппе);

КонецЕсли;

КонецФункции

Для отображения в отчете настройте вычисляемое поле с формулой: Строка(Уровень()) + "." + Строка(НомерВГруппе())

Как оптимизировать нумерацию для больших таблиц (100 000+ строк)?

Рекомендации по оптимизации:

  • Переносите логику на сервер
  • Используйте временные таблицы для промежуточных расчетов
  • Реализуйте постраничную обработку (по 1000-5000 строк за итерацию)
  • Применяйте фоновые задания для длительных операций
  • Отключайте ненужные индексы на время пересчета

Пример оптимизированного запроса:

Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 5000

| РАЗРЕШИТЬ ИЗМЕНЕНИЕ

| Т.Ссылка КАК Ссылка,

| (&НачальныйНомер + РОВНОМЕРНОЕ_РАСПРЕДЕЛЕНИЕ(0, 5000)) КАК НомерСтроки

|ИЗ

| Документ.ИмяДокумента.ТабличнаяЧасть КАК Т

|ГДЕ

| Т.НомерСтроки = 0

| И Т.Ссылка = &Ссылка

|УПОРЯДОЧИТЬ ПО

| Т.Дата,

| Т.Номер";