Главная и подчиненная таблицы в 1С — связывание

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

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

Идея:

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

Реализация:

В поисках такой универсальной связки наткнулся на конструкцию вида:

Новый УникальныйИдентификатор;

Как раз его и будем использовать для связывания. В обе табличные части документа добавляем поле с типом УникальныйИдентификатор. После этого необходимо в модуле формы обработать несколько событий, а именно:

ПриНачалеРедактирования — Табличного поля главной таблицы и Табличного поля подчиненной таблицы.

ПередУдалением — Табличного поля главной таблицы для очистки связанных строк подчиненной.

ПриАктивизацииСтроки — для отбора строк подчиненной таблицы.

Вот и все…

А теперь примеры обработчиков, кстати, они универсальны для любых решений.

 

Процедура ГлавнаяТаблицаПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)

Данные = ЭлементыФормы.ГлавнаяТаблица.ТекущаяСтрока;
Если НоваяСтрока Тогда
Данные.ИД = Новый УникальныйИдентификатор;
КонецЕсли;

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

Процедура ПодчиненнаяТаблицаПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)

Данные = ЭлементыФормы.ГлавнаяТаблица.ТекущаяСтрока;
ТекДанные = ЭлементыФормы.ПодчиненнаяТаблица.ТекущаяСтрока;
Если НоваяСтрока Тогда
ТекДанные.ИД = Данные.ИД;
КонецЕсли;

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

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

Данные = ЭлементыФормы.ГлавнаяТаблица.ТекущаяСтрока;
Отбор = Новый Структура("ИД",Данные.ИД);
Масс = ПодчиненнаяТаблица.НайтиСтроки(Отбор);
Для каждого Строка из Масс Цикл
ПодчиненнаяТаблица.Удалить(Строка);
КонецЦикла;

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

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

Данные = ЭлементыФормы.ГлавнаяТаблица.ТекущаяСтрока;
Если Данные = Неопределено Тогда
Возврат;
КонецЕсли;
ЭлементыФормы.ПодчиненнаяТаблица.ОтборСтрок.ИД.Значение = Данные.ИД;
ЭлементыФормы.ПодчиненнаяТаблица.ОтборСтрок.ИД.Использование = истина;

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

И совсем небольшое дополнение — полю ИД устанавливаем Свойство:Индексировать = индексировать.

Поле Табличного Поля можно (и нужно во избежание) сделать недоступным для пользователя.

Жду ваших отзывов и критики :).

20 Comments

  1. kote

    Какую практическую задачу Вы решали, где Вам пришлось это применить?

    Reply ↓
  2. BorisMor

    (1) kote, автор не открыл Америке. Иногда и правда требуется хранить «сложные данные» и первичный ключ (primary key) — это выход.

    Но я предпочитаю использовать для связки осмысленный ключ (ссылку на справочник или значение).

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

    Reply ↓
  3. alex_shkut

    (1) Спасибо за признание решения. Просто в тех решениях, которые достались мне по наследству это связывание выполнялось по НомеруСтроки!!!. Представьте, что будет, если я отсортирую Главную таблицу?

    Пример: ГлавнаяТаблица — поля (которые пашут и сеют — земля)

    ПодчиненнаяТаблица — координаты этого Поля с GPS-Агромер.

    Итак — к одному «полю» привязано до 500 координат ПО НОМЕРУ СТРОКИ. Нвадеюсь Вы меня поняли…

    При использовании моего метода можно совсем забыть про синхронизацию.

    Я использовал это решение в связке ВыполненнаяОперация — МногоИспользованыхРесурсов. Такая себе СводкаЗаПериод.

    Reply ↓
  4. alex_shkut

    (2) Я выполнял поиск решения данной проблемы и не нашел однозначного решения, а мне оно было необходимо. Приведите мне, пожалуйста, хотя-бы пару ссылок с решением данного вопроса. Я вспомнил Access и искал аналогии в 1С. Поскольку я копал Access довольно подробно, а там он используется для синхронизации двух БД — UID был очевидным решением. В 1С на уровне движка можно организовать такое связывание генерируя UID для таблицы со Свойством:Главная и Подчиненная. Для решения многокаскадности достаточно два поля в каждой таблице — MUID SUID, и подчеркиваю только с установленным Свойством.

    Reply ↓
  5. BorisMor

    (4) думаю ссылки не нужны. Про примори кей знают все кто работал с базами. Сочувствую что ваш предшественник оказался таким «уником».

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

    Reply ↓
  6. KulSer

    Я тоже делал аналогичное связывание по уникальному идентификатору, но, как теперь вижу, несколько перемудрил. Но Ваш код лаконичнее и с первого взгляда понятнее. Спасибо, Александр.

    Reply ↓
  7. Sabfir

    Если сначала добавить строку во вторую таблицу при отсутствии строки в первой таблице, то эти строки не свяжутся.

    Reply ↓
  8. gaglo

    Так и не понял, почему тип УникальныйИдентификатор показался настолько выгодным. Раз поле для организации связки пришлось добавлять, почему не сделать его числовым? Конечно, не НомерСтроки, а какой-нибудь НомерДляСвязи, уникальный в пределах документа. Код обработчиков практически неизменен, только вместо одной строки «Новый УникальныйИдентификатор» пришлось бы написать пяток операторов, чтоб выяснить максимальный НомерДляСвязи да прибавить к нему 1. (Ну да, и еще УникальныйИдентификатор будет уникальным не только в пределах документа, а по всей базе и окрестностях…)

    Reply ↓
  9. catena

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

    Reply ↓
  10. ashvik

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

    Reply ↓
  11. Den_D

    Делал так еще в 2007 году, сейчас тоже так делаю, очень удобно. Как сказали в (10) не все ситуации рассмотрены в данном примере кода.

    Reply ↓
  12. zqzq

    Конфигурация Управление небольшой фирмой — документ, например, ПриемНаРаботу — используются подчиненные таб. части. Функции для их поддержки в общем модуле УправлениеНебольшойФирмойКлиент. Так что да, автор изобрел велосипед.

    Там, кстати, и такое обработано:

     Если ФормаДокумента.Элементы[ФормаДокумента.ИмяТабличнойЧасти].ТекущиеДанные = Неопределено Тогда
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = НСтр(«ru = ‘Не выбрана строка основной табличной части!'»);
    Сообщение.Сообщить();

    Для связи используется числовой инкрементный ключ КлючСвязи (число(5,0)) — занимает меньне иеста чем UUID (вроде бы), но ваше решение более устойчивое (если забыть, например, обработчик после удалеия, строки не переназначатся… хотя всё равно будут висеть невидимые и создавать косяки).

    Reply ↓
  13. alex_shkut

    Спасибо за отзывы. Я не старался изобрести велосипед. Эта статья опубликована для тех, кто ищет решение и не находит. А примеры я привел здесь — в обсуждении. И причем это довольно известная компания :). А Уникальный идентификатор ценен тем, что он останется уникальным всегда и везде — как бы вы в последующем не разбрасывали информацию по базе — всегда можно отследить связи между данными.

    Я постараюсь дописать базовый код так, чтобы он был минимально достаточным.

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

    Reply ↓
  14. alex_shkut
    Если НоваяСтрока Тогда
    Элемент.ТекущаяСтрока.Сотрудник = ЭлементыФормы.РаботникиОрганизации.ТекущаяСтрока.Сотрудник;
    КонецЕсли;

    Вот такое-вот решение при приеме на работу в моей конфигурации. Я специально не буду называть разработчика, т.к. это будет совсем неэтично. Мы здесь призваны улучшать то, что есть и создавать правильное новое.

    В данном варианте УникальныйИдентификатор — это и есть Ссылка на Сотрудника.

    Reply ↓
  15. alex_shkut

    Спасибо за голоса. Перечитал я все посты и понял. Я показал велосипед без колес :). Колеса поставит программист сам — какие ему нужно. Шоссейные (без копирования подчиненных) или горные (с ньюансами). Это достаточный минимум для тех, кто вообще не знает, как это сделать…

    Reply ↓
  16. slawanix

    Спасибо автору, плюсанул.

    Reply ↓
  17. ehcore

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

    &НаКлиенте
    Процедура ГлавнаяТаблицаПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    
    Данные = Элементы.ГлавнаяТаблица.ТекущиеДанные;
    Если НоваяСтрока Тогда
    Данные.ID = Новый УникальныйИдентификатор;
    КонецЕсли;
    
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ПодчиненнаяТаблицаПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    
    Данные = Элементы.ГлавнаяТаблица.ТекущиеДанные;
    
    ТекДанные = Элементы.ПодчиненнаяТаблица.ТекущиеДанные;
    Если НоваяСтрока Тогда
    ТекДанные.ID = Данные.ID;
    КонецЕсли;
    
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ГлавнаяТаблицаПередУдалением(Элемент, Отказ)
    
    Данные = Элементы.ГлавнаяТаблица.ТекущиеДанные;
    Отбор = Новый Структура(«ID»,Данные.ID);
    Масс = Объект.ПодчиненнаяТаблица.НайтиСтроки(Отбор);
    Для каждого Строка из Масс Цикл
    Объект.ПодчиненнаяТаблица.Удалить(Строка);
    КонецЦикла;
    
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ГлавнаяТаблицаПриАктивизацииСтроки(Элемент)
    
    Данные = Элементы.ГлавнаяТаблица.ТекущиеДанные;
    Если Данные = Неопределено Тогда
    Возврат;
    КонецЕсли;
    Элементы.ПодчиненнаяТаблица.ОтборСтрок = Новый ФиксированнаяСтруктура(«ID», Данные.ID);
    
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ПодчиненнаяТаблицаПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, Группа, Параметр)
    
    Данные = Элементы.ГлавнаяТаблица.ТекущиеДанные;
    Если Данные = Неопределено Тогда
    Отказ = Истина;
    КонецЕсли;
    
    КонецПроцедуры
    

    Показать

    Ну и код модуля объекта(очищает подчиненную и присваивает новые ID строкам главной):

    Процедура ПриКопировании(ОбъектКопирования)
    
    ПодчиненнаяТаблица.Очистить();
    Для Каждого Строка Из ГлавнаяТаблица Цикл
    Строка.ID = Новый УникальныйИдентификатор;
    КонецЦикла;
    
    КонецПроцедуры
    

    Показать

    Reply ↓
  18. SmArtist

    (17), Спасибо, работает. Только нужно в процедуру

    ГлавнаяТаблицаПриНачалеРедактирования()

    добавить одну строку:

    Данные = Элементы.ГлавнаяТаблица.ТекущиеДанные;
    Если НоваяСтрока Тогда
    Данные.ИдентификаторСтроки = Новый УникальныйИдентификатор;
    Элементы.ПодчиненнаяТаблица.ОтборСтрок = Новый ФиксированнаяСтруктура(«ИдентификаторСтроки», Данные.ИдентификаторСтроки);
    КонецЕсли;
    

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

    Reply ↓
  19. ImHunter

    Чем плохи УИДы.

    Их нельзя передать в запрос внешней таблицей значений. Списком вроде можно. А вот в ТЗ — увы.

    Reply ↓
  20. tp_home@mail.ru

    Спасибо! То что надо при моей задаче.

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *