Связь один-ко-многим в 1С 7.7 и 8.х (часть 1)

2010-06-29 papirosnik Базы данных

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

Дело в том, что предложенная автороами 1С концепция групп справочников, на практике не совсем удобна и редко используется в чистом виде. Сама идея, что название группы есть также равноправный элемент справочника вносит немало путаницы в код, является потенциальным источником ошибок, разночтений и недопониманий между разработчиком конфигурации и её пользователями.

Авторы предлагают так называемую связь "один-к-одному", Этот термин из области баз данных означает, что одной записи из таблицы баз данных соответствует ровно одна запись из подчинённого справочника. Например, в списке стран есть поле "СТОЛИЦА", указывающее на список городов. У каждой страны может быть только одна столица. Или у каждого сотрудника только одна должность (ссылка из списка сотрудников на список должностей). А вот если всё-таки не одна? Трудолюбивый человек занимает две должности на пол-ставки или по-совместительству. Как это отразить в нашей базе данных? Заводить ещё одного сотрудника и ему присваивать новую должность? Это отвратительное решение, приводящее к избыточности данных, ненужным связям и т.п. Одним словом — дурной тон. Чтобы этого избежать, необходимо грамотно спроектировать структуру данных на начальном этапе.

Что же предлагает нам в этом плане 1С 7.7. Первое и очевидное, которое объясняют на всех уроках новичкам — это группы. Мол, механизм очень удобен, поддержка реализована быстрая и пользователям всё интуитивно понятно. Не верьте этому. В моём представлении группы в 1С есть вещь ненужная, и даже больше — вредная. Представтье список сотрудников, рассортированный по группам. Допустим, у нас есть группы Администрация, ИТР, Рабочие. В ней должны быть подгруппы Отдел Кадров, Канцелярия, Бухгалтерия, Техническая служба и т.п. Куда отнести Главного инженера? В администрацию? или в ИТР? или в Техническую службу? А если начальника цеха перевели в фрезеровщики 🙂 — надо его двигать по группам? А если Кладовщик цеха готовой продукции работает по совместительству и уборщиком производственных помещений? К к какой группе тогда её относить? А вдруг сокращается какая-то должность, и людей переводят на другие…. Вопросов без очевидного ответа уйма.

Справделивости ради отметим, что 1С предлагает и другой механизм для реализации таких задач: Подчинённые справочники. Не вдаваяь подробнее, скажу для наших примеров , что можно завести справочник Столицы, который будет подчинён справочнику Страны.  Для этого примера всё вроде выглядит прилично… Но вдруг в какой-либо стране окажется две столицы ). Похоже на шутку — но в каждой шутке, как известно, есть доля правды. А для примера с должностями этот механизм примерно так же полезен, как и группы справочников. Его надо расширять.

Вот тут на сцену выступает "связь один-ко-многим". Гораздо удобнее хранить: Список сотрудниокв, Список должностей, Таблицу с должностями сотрудников. Поясняю почему: В списке сотрудников хранятся все сведения от сотрудниках, но нет никаких связей с должностями. В списке должностей — вся информация о должности (код, оклад, льготы и т.п.), но нет никакой привязки к конкретному человеку. А привязка производится в списке Должностей сотрудников.Этот список физически реализован в виде таблицы, в ктороой всего три поля: 1) ID записи (зачем оно надо — объяснять не стану. Это тема из учебника по теории БД(. 2) ID сотрудника (код или номер записи из таблицы Сотрудников); 3) ID должности (код или номер должности из справочника Должностей).

Таким образом связь между сотрудником и занимаемой им должностью физически реализована в виде отдельной таблицы, а не полей типа Должность в  списке Сотрудников. Благодаря этому и один сотрудник может занимать несколько должностей и это будет очевидным образом отражено в базе данных. Если вдруг человека примут ещё на одну должность, программисту не придётся вводить новое поле Должность2, или сотруднику отдела кадров дублировать данные по этому сотруднику в новую запись. Достаточно отметить, что теперь он "принадлежит" и ещё одной должности.

Это пример показывает, что одна запись как бы "принадлежит" многим. Здесь не устанавливается жёсткая привзяка, а просто отмечается, какие связи существуют между справочниками.  Можно привести ещё множество примеров, раскрывающих суть такого подхода. Какой либо товар может принадлежать к группе "импортный" или "отечествееный". Но также он может быть "однократного применения" и "многократно используемый"… Чтобы не плодить различные взаимоподчинённые или хитропереплетённые справочники, вводится один — ГруппаДлятовара. А в нём код товара и код группы, которй он принадлежит — и принадлежать он может к бесконечному числу групп. В качестве примере также можно привести ситуацию когда какая-либо организация прнадлежит группе Поставщики. А вдруг так случится, что они станут и получателем товара. Дублировать тогда запись и в группе Получатели? Или всех пользователей предупредить, чтобы такого-то получателя искали в группе Поставщики? Проще сделать связь "один-ко-многим" и пользоваться очевидными логическими преимуществами (а в 1С версии 8 и выше — ещё и тем, что всё это реализовано фактически).

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

Связанные справочники

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

Первое, я ввожу процедуру ЗаполнитьТЗВ. Она будет применяться и при первом открытии формы и при добавлении или удалении нового вида в таблицу:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Процедура ЗаполнитьТЗВ()
   ТЗВ.УдалитьСтроки();
   Спр = СоздатьОбъект("Справочник.ВидыДляТовара");
   Спр.ИспользоватьВладельца(ТекущийЭлемент());
   Спр.ВыбратьЭлементы();
   Пока Спр.ПолучитьЭлемент()=1 Цикл
     Если Спр.ПометкаУдаления()=1 Тогда
       Продолжить;
     КонецЕсли;
     ТЗВ.НоваяСтрока();
     ТЗВ.Вид = Спр.Вид.Наименование;
   КонецЦикла;
   ТЗВ.Сортировать("Вид");
   ТЗВ.ТекущаяСтрока(-1);
КонецПроцедуры

Теперь вызов этой процедуры просто вставим в тело предопределйнной процедуры ПриОткрытии() и тогда, открывая карточку МЦ мы сразу будем видеть в таблице, к каким группам эта матценность принадлежит.

1
2
3
4
5
Процедура ПриОткрытии()
   //Здесь возможно Ваш какой-то код
   ЗаполнитьТЗВ();
  //Здесь возможно Ваш какой-то код
КонецПроцедуры;

Далее необходимо запрограммировать реакцию на кнопку Добавить и Удалить. Думаю, если Вы осили предыдущий код, то поймёте и смысл обработчиков этих кнопок:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Процедура ЧекЗаписи()
       Если Выбран() = 0 Тогда
	Если Вопрос("Эту операцию можно выполнить только после записи товара.
	|Записать?", "Да+Нет") = "Да" Тогда
   	    Записать();
	КонецЕсли;
 
     КонецЕсли;
КонецПроцедуры
 
Процедура ПоКнопкеДобавить()
    ЧекЗаписи();
    Если Выбран() = 1 Тогда
	ОткрытьПодбор("Справочник.ВидыТоваров",,,0);
    КонецЕсли;
КонецПроцедуры
 
Процедура ПоКнопкеУдалить()
    Если ТЗВ.КоличествоСтрок()=0 Тогда Возврат; КонецЕсли;
   ЧекЗаписи();
   Если Выбран()<>1 Тогда Возврат; КонецЕсли;
   Вид = ТЗВ.ПолучитьЗначение(ТЗВ.ТекущаяСтрока(),"Вид");
   Спр = СоздатьОбъект("Справочник.ВидыТоваров");
   Если Спр.НайтиПоНаименованию(Вид,0,0)=1 Тогда
	Зн = Спр.ТекущийЭлемент();
	Спр = СоздатьОбъект("Справочник.ВидыДляТовара");
	Спр.ИспользоватьВладельца(ТекущийЭлемент());
	Если Спр.НайтиПоРеквизиту("Вид",Зн,0)=1 Тогда
  	   Если Вопрос("Дейтсвительно удалить связь товара с &#39;" +Зн+"&#39;?","Да+Нет") ="Да" Тогда
 	   Спр.Удалить();
 	   ЗаполнитьТЗВ();
	КонецЕсли;
	Иначе
	   Сообщить("Не найдено: "+Зн);
	КонецЕсли;
	Иначе
  	   Сообщить("Вид товара не найден: "+Вид);
	КонецЕсли
КонецПроцедуры

Вроде всё. Эти манипуляции позволят Вам иметь удобный иеррархический справочник чего- или кого-либо (Сотрудников, Товаров, Организаций и т.д. и.т.п.). Конечно же, применение этой иеррархии не ограничивается только видимой сферой. Ей можно использовать для удобной группировки в отчётах. К примеру Вам надо получить сумму реализованного "импоротного" товара однократного применения за какой-то период. Просто в форме отчёта перечисляете требуемые группы (можно сказать "метки") и вперёд. Конечно же, поддержка такой иеррархии должна быть запрограммирована в процедуре Сформировать() модуля отчёта. Как это проделать — возможно расскажу в последующих статьях.

Но скорее что объяснять дальше для версии 7.7 не буду, т.к. весь этот функционал встроен в 1С  начиная с версии 8  и носит название "Табличная часть справочника". Я рад что Всевышний внял моим мольбам и послал рарзработчикам 1С просветление реализовать эту возможность в 1С. В следующей статье я покажу как её задействовать и какие простые, элегантные и удобные получаются при этом коды.

, БД, Подчинённые справочники, Табличная часть справочника,


Добавить комментарий

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

Powered by WordPress. Designed by elogi.