При работе с отчетами на системе компоновки данных (СКД) в 1С часто возникает задача получить данные из табличной части документа, которые связаны с движениями регистра. Иногда разработчики пытаются использовать НомерСтроки табличной части для связывания с записями регистра, полагаясь на кажущееся соответствие. Однако, как мы выясним, такой подход крайне ненадежен и может привести к серьезным ошибкам в данных. Давайте разберем эту проблему подробно и предложим надежные решения.
Мы проанализируем ситуацию, чтобы понять, почему прямое использование номера строки нежелательно, и рассмотрим несколько эффективных и безопасных способов получения необходимых данных.
На первый взгляд может показаться, что реквизит НомерСтроки табличной части документа напрямую соответствует некому "номеру строки" в записях регистра, сформированных этим документом. Однако это заблуждение, которое может привести к серьезным проблемам:
НомерСтроки в табличной части документа — это порядковый номер строки в этой конкретной табличной части. А "номер строки" или "номер записи" в регистре — это порядковый номер записи в наборе движений, сформированных документом. Эти понятия не идентичны и могут не совпадать.Поэтому, мы категорически не рекомендуем использовать НомерСтроки для связывания табличной части документа с записями регистра в отчетах СКД. Давайте рассмотрим проверенные и надежные подходы.
Это самое надежное и рекомендуемое решение, особенно если вы являетесь разработчиком конфигурации или имеете возможность ее модифицировать. Мы добавим в регистр специальное поле, которое будет однозначно идентифицировать строку табличной части-источника.
Шаги по реализации:
Модифицируем структуру регистра:
РегистромНакопления (например, регистр оборотов), добавьте новое Измерение. Назовем его, например, ИдентификаторСтрокиДокумента. Тип данных для этого измерения можно выбрать Число, если вы планируете хранить номер строки, или Строка, если будете формировать более сложный идентификатор. Более надежным будет использование типа Строка, куда можно записывать, например, УникальныйИдентификатор строки табличной части (если таковой есть) или комбинацию полей.РегистромСведений или РегистромБухгалтерии, или же РегистромНакопления (регистр остатков), где добавление измерения нецелесообразно, добавьте новый Реквизит с аналогичным названием и типом.Мы предлагаем использовать тип Строка для ИдентификаторСтрокиДокумента, так как он более универсален. Если вы используете УникальныйИдентификатор строки, то тип должен быть Строка.
Изменяем логику проведения документа:
Теперь нам нужно модифицировать код проведения документа, чтобы при записи движений в регистр заполнялось наше новое поле. Рассмотрим пример, где мы используем НомерСтроки для примера, но помним о его ограничениях. В реальной жизни лучше использовать комбинацию полей или уникальный идентификатор, если он есть.
Допустим, у нас есть табличная часть Товары и регистр накопления ОстаткиТоваров.
// Предположим, что Движения.ОстаткиТоваров - это коллекция движений регистра
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Объект.Дата;
Движение.Номенклатура = СтрокаТЧ.Номенклатура;
Движение.Количество = СтрокаТЧ.Количество;
// Здесь мы заполняем наше новое измерение/реквизит
Движение.ИдентификаторСтрокиДокумента = СтрокаТЧ.НомерСтроки; // Пример, для иллюстрации
// В более надежном варианте:
// Движение.ИдентификаторСтрокиДокумента = СтрокаТЧ.УникальныйИдентификатор;
// Или комбинация полей:
// Движение.ИдентификаторСтрокиДокумента = Строка(СтрокаТЧ.Номенклатура) + "_" + Строка(СтрокаТЧ.Характеристика);
КонецЦикла;
Мы должны убедиться, что выбранный идентификатор действительно уникален для каждой строки табличной части и стабилен. Если в табличной части нет уникального идентификатора, и вы не хотите его добавлять, можно использовать комбинацию ключевых полей, которые однозначно идентифицируют строку (например, Номенклатура + Характеристика + Серия и т.д.).
Используем новое поле в СКД:
Теперь в отчете СКД мы можем легко связать данные регистра с табличной частью документа по нашему новому измерению/реквизиту ИдентификаторСтрокиДокумента. Мы включим его в запрос к регистру и в запрос к табличной части документа, а затем выполним соединение.
ВЫБРАТЬ
Регистр.Номенклатура,
Регистр.Количество,
Регистр.ИдентификаторСтрокиДокумента КАК ИдентификаторИзРегистра
ИЗ
РегистрНакопления.ОстаткиТоваров КАК Регистр
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходнаяНакладная.Товары КАК ТабличнаяЧасть
ПО Регистр.Регистратор = ТабличнаяЧасть.Ссылка
И Регистр.ИдентификаторСтрокиДокумента = ТабличнаяЧасть.НомерСтроки // Если НомерСтроки был использован
// ИЛИ
// И Регистр.ИдентификаторСтрокиДокумента = ТабличнаяЧасть.УникальныйИдентификатор
Таким образом, мы создаем явную и надежную связь между данными. Это решение требует изменений в конфигурации, но обеспечивает максимальную точность и устойчивость к будущим изменениям.
Если нет возможности или желания изменять структуру регистра и логику проведения, мы можем попытаться связать табличную часть с регистром, используя существующие поля, которые совместно образуют уникальный ключ для строки. Это часто измерения регистра.
Проанализируем ситуацию:
Мы выясним, какие реквизиты табличной части документа соответствуют измерениям регистра. Например, если в табличной части документа "Поступление товаров" есть поля Номенклатура, Характеристика, Склад, и эти же поля являются измерениями в регистре накопления "Остатки товаров", то мы можем использовать их для связывания.
Шаги по реализации:
Определяем связующие поля:
Мы должны составить "табличку" соответствия: реквизиты табличной части документа против измерений регистра. Цель — найти такой набор полей, который однозначно идентифицирует каждую строку табличной части и который также присутствует в регистре как измерения. Например:
Номенклатура, Характеристика, Серия.Номенклатура, Характеристика, Серия.В этом случае, связка по этим трем полям будет надежной.
Мы также можем посмотреть на типовые механизмы. В стандартных конфигурациях 1С часто используются специальные поля для связи строк табличной части с записями регистров, например, Распоряжение и КодСтрокиРаспоряжения. Если такие поля есть в вашем регистре (часто как измерения или реквизиты) и в табличной части, используйте их. Поле Распоряжение, как правило, указывает на документ-основание или конкретный этап, а КодСтрокиРаспоряжения — на уникальный идентификатор строки этого распоряжения.
Формируем запрос в СКД:
В запросе СКД мы будем соединять данные табличной части документа с данными регистра по этим общим полям. Рассмотрим пример:
ВЫБРАТЬ
Регистр.Номенклатура,
Регистр.Характеристика,
Регистр.Количество КАК КоличествоИзРегистра,
ТабличнаяЧасть.Цена КАК ЦенаИзТЧ,
ТабличнаяЧасть.Сумма КАК СуммаИзТЧ
ИЗ
РегистрНакопления.ОстаткиТоваров КАК Регистр
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходнаяНакладная.Товары КАК ТабличнаяЧасть
ПО Регистр.Регистратор = ТабличнаяЧасть.Ссылка
И Регистр.Номенклатура = ТабличнаяЧасть.Номенклатура
И Регистр.Характеристика = ТабличнаяЧасть.Характеристика
// Добавляем все поля, которые однозначно идентифицируют строку
// И Регистр.Серия = ТабличнаяЧасть.Серия
Этот подход менее инвазивен, так как не требует изменений в конфигурации, но его применимость зависит от наличия соответствующих полей в регистре и их способности однозначно идентифицировать строку табличной части.
В некоторых особо сложных случаях, когда ни один из предыдущих подходов не подходит (например, если нет уникальных полей для связи или требуется специфическая агрегация), мы можем прибегнуть к более сложным техникам внутри СКД.
1. Объединение нескольких наборов данных:
Мы можем создать два отдельных набора данных: один для регистра, другой для табличной части документа. Затем в СКД мы свяжем их через "Связи наборов данных" по общим полям (например, по ссылке на документ и тем полям, которые хоть как-то могут быть связаны, если нет идеального уникального ключа). Этот подход позволяет более гибко управлять связями, но требует тщательного анализа, чтобы избежать дублирования или пропуска данных.
2. Использование временных таблиц в запросах СКД:
Для очень сложных случаев, когда требуется предварительная обработка или агрегация данных, прежде чем их можно будет связать, мы можем использовать временные таблицы в запросах СКД. Это позволяет нам подготовить данные из табличной части и регистра по отдельности, а затем объединить их.
// Пример использования временной таблицы для табличной части
ВЫБРАТЬ
Товары.Ссылка КАК СсылкаДокумента,
Товары.Номенклатура КАК НоменклатураТЧ,
Товары.Количество КАК КоличествоТЧ,
Товары.НомерСтроки КАК НомерСтрокиТЧ // Используем для примера, но с осторожностью
ПОМЕСТИТЬ ВТ_ТабличнаяЧасть
ИЗ
Документ.ПриходнаяНакладная.Товары КАК Товары
ГДЕ
Товары.Ссылка В (&СписокДокументов)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Регистр.Регистратор,
Регистр.Номенклатура,
Регистр.Количество,
ВТ_ТабличнаяЧасть.КоличествоТЧ,
ВТ_ТабличнаяЧасть.НомерСтрокиТЧ
ИЗ
РегистрНакопления.ОстаткиТоваров КАК Регистр
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТабличнаяЧасть КАК ВТ_ТабличнаяЧасть
ПО Регистр.Регистратор = ВТ_ТабличнаяЧасть.СсылкаДокумента
И Регистр.Номенклатура = ВТ_ТабличнаяЧасть.НоменклатураТЧ
// Дополнительные условия связи, если есть
Использование временных таблиц дает большую мощность в запросах, но может усложнить отладку и настройку СКД.
3. Вычисляемые поля с функциями общего модуля:
Если нам нужно получить какое-то агрегированное значение из табличной части для каждой записи регистра, и это значение не может быть получено прямым соединением, мы можем использовать вычисляемые поля в СКД с функциями из общих модулей. Например, если нам нужно объединить несколько текстовых значений из строк табличной части в одну строку.
// В общем модуле (например, в ОбщегоНазначенияКлиентСервер)
Функция СоединитьЗначенияСтрокТЧ(СсылкаДокумента, Номенклатура) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Комментарий
|ИЗ
| Документ.ПриходнаяНакладная.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &СсылкаДокумента
| И Товары.Номенклатура = &Номенклатура";
Запрос.УстановитьПараметр("СсылкаДокумента", СсылкаДокумента);
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Результат = Запрос.Выполнить().Выгрузить();
Возврат Результат.ВыгрузитьКолонку("Комментарий").Соединить(", ");
КонецФункции
Затем в вычисляемом поле СКД:
ОбщегоНазначенияКлиентСервер.СоединитьЗначенияСтрокТЧ(Регистратор, Номенклатура)
Этот подход следует использовать с осторожностью, так как он может негативно сказаться на производительности отчета, особенно при больших объемах данных, поскольку функция будет вызываться для каждой записи.
Мы рассмотрели несколько подходов к решению проблемы получения значений из табличной части документа в отчетах СКД. Выбирайте наиболее подходящий для вашей ситуации, исходя из возможностей модификации конфигурации и сложности задачи. Главное — всегда помните о ненадежности прямого использования НомерСтроки и стремитесь к созданию устойчивых и корректных отчетов.