Главная и подчиненная таблицы в 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 не будет опубликован. Обязательные поля помечены *