Каталог решений - Собственная начальная страница с избранным, историей и сообщениями

Собственная начальная страница с избранным, историей и сообщениями

Собственная начальная страница с избранным, историей и сообщениями

В наличии

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

Категория:

Описание

Собственная начальная страница с избранным, историей и сообщениями

Сначала была мысль. И мысль была о том, что пустая начальная страница — это плохо. В разные периоды жизни программы появлялись разные бизнес-процессы, ни один из которых так и не прижился. Различные РМК в нашей сфере деятельности не требуются, а рабочие места сборщиков нужны только 5% пользователей программы. В итоге, как природа не терпит пустоты, так и я не смог дальше мириться с целой вкладкой нефункционального ничего. 

Было принято решение вывести то, что может пригодиться каждому, а любому пользователю самому виднее, что ему нужно! Поэтому, будем показывать его же избранное, но этого будет недостаточно. Начальная страница большая, а избранного у пользователя может быть 5-6 отчётов и любимое списание с кассы себе же на премию. Кстати, если пользователь никогда не обращал внимания на звёздочки, то мы заботливо нарисуем ему инструкцию.

Вспоминаем о том, сколько было слёз у сотрудников, когда они только что вышли из документа и не помнят его номер. Или же не приведи Господь, мы поменяли год, и номер тоже поменялся. Документ теперь утерян навеки и надо его искать всем отделом по обрывкам воспоминаний о комментарии и сумме «точно больше тысячи, но меньше ста». Все пользователи знают о волшебной кнопочке с часиками, но никто не вспоминает о ней без звонка в ИТ отдел, а туда звонить страшно! Значит, выведем историю последних действий пользователя.

И если уж про кнопку с часиками у бухгалтера есть шанс вспомнить, то о том, что сообщения с «колокольчика» можно пересмотреть в специальной обработочке где-то в сервисе, в какой-то там подсистеме, уже нет никакой надежды. А значит последние наши сообщения:

  • контрагент попал в новый сегмент;
  • прошла переоценка интересного нам контракта;
  • КТО-ТО поменял дату документа назад в будущее;

выведем сюда же.

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

 

 

ИЗБРАННОЕ

Начнём по порядку, а именно с "Избранного". Задачу делим на "получить избранное" и закономерно это "избранное отобразить". Как и многое в нашей жизни, избранное хранится в хранилище системных настроек, по ключу Общее/ИзбранноеРаботыПользователя. Что легко найти на сайте ИТС, просто поискав по слову "Избранное", там же полезно будет почитать, что еще можно найти в хранилище системных настроек.

Избранное = ХранилищеСистемныхНастроек.Загрузить("Общее/ИзбранноеРаботыПользователя",,, ПользовательИБ.Наименование);

ПользовательИБ –  само собой, это текущий пользователь с параметров сеанса, но по коду и в самой обработке я оставил себе возможность подглядеть в чужое избранное и сообщения. Вы же можете не указывать четвёртый параметр вовсе. Сделал я так, чтобы упростить себе жизнь в общении с пользователями об этом рабочем месте. Смотреть чужую историю возможности нет, а жаль. Опять же, для того, чтобы узнать «кто», «где» и «когда» у нас есть отдельный регистр.    

В самом избранном хранятся элементы типа "ИзбранноеРаботыПользователя", в нём же есть поля "Важное" (иконка пина, которая фиксирует элемент вверху списка), "НавигационнаяСсылка" и "Представление". Казалось бы, это всё что нам нужно, чтобы показать пользователю ссылку и куда его отправить по клику. НО, если пользователь представление своего избранного не поменял вручную, а в избранном это сделать можно, то там будет пусто. Лично у меня ровно 0 пользователей из 20 просмотренных делали представления своему избранному. Да что лукавить, и я не пользовался представлениями в "Избранном". И все же если показать пользователю вот это e1cib/data/Справочник.ВариантыОтчетов?ref=80c71866dab567cd11e7e5f3c7a20af7, он точно сюда не кликнет, только если не доказать ему лично,что именно этот текст его любимый отчёт. Нам точно нужно показывать надпись на языке бухгалтера, а не на языке навигационных ссылок.

Значит вспоминаем про ПолучитьПредставленияНавигационныхСсылок(), которая принимает массив ссылок и возвращает массив их представлений вместе с изначальным URI.

Получаем следующий код:

Избранное = ХранилищеСистемныхНастроек.Загрузить("Общее/ИзбранноеРаботыПользователя",,, ПользовательИБ);
Если ЗначениеЗаполнено(Избранное) Тогда
	СсылкиИзбранное = Новый Массив;
	Для каждого СтрИзбранное Из Избранное Цикл
		СсылкиИзбранное.Добавить(СтрИзбранное.НавигационнаяСсылка);	
	КонецЦикла;	
	ПредставленияНавигационныхСсылок = ПолучитьПредставленияНавигационныхСсылок(СсылкиИзбранное);
КонецЕсли;

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

Чтобы нарисовать элемент формы в виде ссылки нам потребуется следующий код:

Идентификатор = "Ссылка_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");
НовыйЭлемент = ЭтаФорма.Элементы.Добавить(Идентификатор, Тип("ДекорацияФормы"), Элементы.ГруппаИзбранное);
НовыйЭлемент.Вид = ВидДекорацииФормы.Надпись;
НовыйЭлемент.Заголовок = СтрПредставлениеНавигационнойСсылки.Текст;	
НовыйЭлемент.Гиперссылка = Истина;

Тут Идентификатор нам нужен не только для уникальности, но и для того, чтобы связать элемент и его навигационную ссылку. Конечно, можно было бы прямо в идентификатор добавить навигационную ссылку, но пришлось бы экранировать символы, которые нельзя использовать в наименовании. А потом еще и придётся парсить это в обратную сторону, чтобы оживить ссылку  и по ней можно было перейти. Калории на такое тратить нам жаль, поэтому нас спасёт соответствие! 

НавигационныеСсылкиСервер = Новый Соответствие;

А после рисования элемента добавим 

НавигационныеСсылкиСервер.Вставить(Идентификатор, СтрПредставлениеНавигационнойСсылки.НавигационнаяСсылка); 

Чтобы потом с клиента обращаться к этому соответствию, добавим реквизит формы "НавигационныеСсылкиСервер" с произвольным типом. И… Получаем ошибку! Мы забыли, что нельзя на клиенте хранить соответствие, но мы то хитрые и помним, что фиксированное то можно.

Меняем на форме имя соответствия на "НавигационныеСсылки", а в конце кода добавляем:

НавигационныеСсылки = Новый ФиксированноеСоответствие(НавигационныеСсылкиСервер);

Ошибок нет, осталось начать это использовать.
Добавляем на клиент обработчик события, где мы получаем ссылку из соответствия по имени элемента и просто переходим по ней.

&НаКлиенте
Процедура ПерейтиПоСсылке(Элемент, СтандартнаяОбработка)
	НавигационнаяСсылка = НавигационныеСсылки.Получить(Элемент.Имя);
	Если ЗначениеЗаполнено(НавигационнаяСсылка) Тогда
		ПерейтиПоНавигационнойСсылке(НавигационнаяСсылка);
	КонецЕсли;
КонецПроцедуры

А в код рисования формы добавляем строчку: 

НовыйЭлемент.УстановитьДействие("Нажатие", "ПерейтиПоСсылке"); 

Таким образом, у нас каждый элемент, по своему имени, сможет отправить пользователя по своей ссылочке. УРА! Мы и получили и отобразили избранное, еще и пользователь может перейти по ссылке!
Итоговый код получается следующим:

Избранное = ХранилищеСистемныхНастроек.Загрузить("Общее/ИзбранноеРаботыПользователя",,, ПользовательИБ);
Если ЗначениеЗаполнено(Избранное) Тогда
	СсылкиИзбранное = Новый Массив;
	Для каждого СтрИзбранное Из Избранное Цикл
		СсылкиИзбранное.Добавить(СтрИзбранное.НавигационнаяСсылка);	
	КонецЦикла;	
	ПредставленияНавигационныхСсылок = ПолучитьПредставленияНавигационныхСсылок(СсылкиИзбранное);
	Для каждого СтрПредставлениеНавигационнойСсылки Из ПредставленияНавигационныхСсылок Цикл
		Идентификатор = "Ссылка_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");
		НовыйЭлемент = ЭтаФорма.Элементы.Добавить(Идентификатор, Тип("ДекорацияФормы"), Элементы.ГруппаИзбранное);
		НовыйЭлемент.Вид = ВидДекорацииФормы.Надпись;
		НовыйЭлемент.Заголовок = СтрПредставлениеНавигационнойСсылки.Текст;	
		НовыйЭлемент.Гиперссылка = Истина;
		НовыйЭлемент.УстановитьДействие("Нажатие", "ПерейтиПоСсылке"); 
		НавигационныеСсылкиСервер.Вставить(Идентификатор, СтрПредставлениеНавигационнойСсылки.НавигационнаяСсылка); 
	КонецЦикла;
КонецЕсли;	

 

ВПЕРЁД ЗА ИСТОРИЕЙ ПОЛЬЗОВАТЕЛЯ!

И меня, как программиста, предал мой самый верный помощник. Гугл был явно настроен против меня, добиться от него чего-то кроме истории изменений документа как платформенной, так и средствами БСП, было очень мучительно. Но после долгих увещеваний "поисковая система номер один" сжалилась и показала мне журнал регистрации. На использование журнала я был готов также, как и на использование истории изменений объекта. Искать то надо последние действия конкретного пользователя и искать хотелось бы быстро, а ЖР вообще не про это. Тем более, если пользователь просто заглянет, не записывая, в документ, то и записей не будет ни в «тут», ни в «там». История открываемых списков, обработок и отчётов тем более там не появится. 

Но не хочется верить в невозможность этой затеи , потому что история показывается по системной кнопочке с часиками. Значит, как-то можно её откопать, я уже готов был копать и разбирать файлы пользовательского кэша, нужно было только направление. Однако, синтаксис-помощник оказался вернее западных технологий. В нём я по ключевым словам довольно быстро нашёл менеджер "ИсторияРаботыПользователя".  Что лишний раз доказывает, что всё-таки лучше сначала искать в синтаксис-помощнике и только потом в сети.

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

История = ИсторияРаботыПользователя.Получить();

В ответ функция возвращает массив навигационных ссылок с датами. Радостно получаем представления и выводим тем же кодом что и «Избранное». В принципе, оно работает, но с двумя НО:

  • Мы потеряли дату-время события, что не очень хорошо
  • При получении представлений они еще и яростно перемешались, что совсем не хорошо

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

История = ИсторияРаботыПользователя.Получить();
Если ЗначениеЗаполнено(История) Тогда
	СсылкиИстория = Новый Массив;
	Для каждого СтрИстория Из История Цикл
		СсылкиИстория.Добавить(СтрИстория.НавигационнаяСсылка);
	КонецЦикла;	
	ПредставленияНавигационныхСсылок = ПолучитьПредставленияНавигационныхСсылок(СсылкиИстория);
		
	Для каждого СтрИстория Из История Цикл
		Для каждого СтрПредставлениеНавигационнойСсылки Из ПредставленияНавигационныхСсылок Цикл
			Если СтрИстория.НавигационнаяСсылка = СтрПредставлениеНавигационнойСсылки.НавигационнаяСсылка Тогда
				Идентификатор = "Ссылка_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");
				НовыйЭлемент = ЭтаФорма.Элементы.Добавить(Идентификатор, Тип("ДекорацияФормы"), Элементы.ГруппаИстория);
				НовыйЭлемент.Вид = ВидДекорацииФормы.Надпись;
				НовыйЭлемент.Заголовок = "" + Формат(СтрИстория.Дата, "ДФ='dd.MM HH:mm'; ДЛФ=DT") + "    " + СтрПредставлениеНавигационнойСсылки.Текст;	
				НовыйЭлемент.Гиперссылка = Истина;
				НовыйЭлемент.УстановитьДействие("Нажатие", "ПерейтиПоСсылке"); 
				НавигационныеСсылкиСервер.Вставить(Идентификатор, СтрПредставлениеНавигационнойСсылки.НавигационнаяСсылка);
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
КонецЕсли;

Получилось более громоздко чем для "Избранного", но результат выводится красиво и по порядку.

 

СООБЩЕНИЯ

Остались лишь "Сообщения" и тут уже начинаются особенности нашей конфигурации. Для вывода «колокольчика» мы используем периодический регистр сведений СообщенияПользователям. В него мы закидываем сообщения и отправляем пользователю через клиентские обработчики оповещения, но сегодня не об этом. Сегодня о том, как это сообщение показать в нашей формочке.

Запрос = Новый Запрос;
Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ 100
	|	СообщенияПользователям.ТекстСообщения КАК ТекстСообщения,
	|	СообщенияПользователям.ОбъектЦель КАК ОбъектЦель,
	|	СообщенияПользователям.Период КАК Период,
	|	СообщенияПользователям.Пояснение КАК Пояснение
	|ИЗ
	|	РегистрСведений.СообщенияПользователям КАК СообщенияПользователям
	|ГДЕ
	|	СообщенияПользователям.Пользователь = &Пользователь
	|
	|УПОРЯДОЧИТЬ ПО
	|	СообщенияПользователям.Период УБЫВ";
Запрос.УстановитьПараметр("Пользователь", ?(ЗначениеЗаполнено(Пользователь), Пользователь, ПараметрыСеанса.ТекущийПользователь));

АГА! Периодический регистр сведений, а срез последних не используется, вот тут ты и попался, Носырев!

Согласен, так делать плохо, спорить не буду, просто покажу замер:

 

Видно, что со срезом всё равно было медленнее, даже если не поверить, что данные кэшировались после первого запроса. Как я понимаю, так вышло из-за количества записей в регистре и сортировки по убыванию заведомо индексируемого поля, в таких случаях построитель запроса может понять вас совсем не так, как бы вам хотелось. Лучше явно указать ему искать сразу по записям. Буду рад более научным объяснениям в комментариях. Опять же, если у вас есть подобные регистры сообщений, журналов событий и тому подобных, можете поэкспериментировать с ними.

Таким образом получаем следующий код:

Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ 100
	|	СообщенияПользователям.ТекстСообщения КАК ТекстСообщения,
	|	СообщенияПользователям.ОбъектЦель КАК ОбъектЦель,
	|	СообщенияПользователям.Период КАК Период,
	|	СообщенияПользователям.Пояснение КАК Пояснение
	|ИЗ
	|	РегистрСведений.СообщенияПользователям КАК СообщенияПользователям
	|ГДЕ
	|	СообщенияПользователям.Пользователь = &Пользователь
	|
	|УПОРЯДОЧИТЬ ПО
	|	СообщенияПользователям.Период УБЫВ";
	
Запрос.УстановитьПараметр("Пользователь", ?(ЗначениеЗаполнено(Пользователь), Пользователь, ПараметрыСеанса.ТекущийПользователь));
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();	
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
	Идентификатор = "Ссылка_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");
	НовыйЭлемент = ЭтаФорма.Элементы.Добавить(Идентификатор, Тип("ДекорацияФормы"), Элементы.ГруппаСообщения);
	НовыйЭлемент.Вид = ВидДекорацииФормы.Надпись;
	НовыйЭлемент.Заголовок = "" + Формат(ВыборкаДетальныеЗаписи.Период, "ДФ='dd.MM HH:mm'; ДЛФ=DT") + "    " + ВыборкаДетальныеЗаписи.ТекстСообщения;	
	НовыйЭлемент.Гиперссылка = Истина;
	НовыйЭлемент.УстановитьДействие("Нажатие", "ПерейтиПоСсылке"); 
	НавигСсылка = "";
	Если ЗначениеЗаполнено(ВыборкаДетальныеЗаписи.ОбъектЦель) Тогда
		Если ТипЗнч(ВыборкаДетальныеЗаписи.ОбъектЦель) = Тип("Строка") Тогда
			НавигСсылка = ВыборкаДетальныеЗаписи.ОбъектЦель;		
		Иначе
			НавигСсылка = ПолучитьНавигационнуюСсылку(ВыборкаДетальныеЗаписи.ОбъектЦель); 
		КонецЕсли;
	КонецЕсли;
	НовыйЭлемент = ЭтаФорма.Элементы.Добавить(Идентификатор + "_Сообщение", Тип("ДекорацияФормы"), Элементы.ГруппаСообщения);
	НовыйЭлемент.Вид = ВидДекорацииФормы.Надпись;
	НовыйЭлемент.Заголовок = "" + ВыборкаДетальныеЗаписи.Пояснение;
	НавигационныеСсылкиСервер.Вставить(Идентификатор, НавигСсылка);
КонецЦикла;

Основные отличия здесь это: 

  • Мы ходим уже по выборке, а не массивам
  • В выборке может быть как ссылка на объект, так и сразу навигационная ссылка
  • Под заголовком темы сообщения выводим некликабельный текст с пояснением

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

 

ОТПРАВКА СООБЩЕНИЙ

Рисуем формочку:

 

 

Где сверху видим список пользователей, обязательно с автосохранением и с галочками, чтобы пользователь накинул туда свои топ 10 адресатов и просто переставлял галочки в нужные моменты. УДОБНО ЖЕ! Ссылочку будем забирать из буфера обмена и при открытии, и при нажатии кнопочки. Как "положить ссылочку в буфер обмена" рисуем инструкцию. Если вашим пользователям она не требуется, то я их заведомо люблю и уважаю. Своих тоже и уважаю, и люблю, но им инструкция нужна.

 

 

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

Осталось только посмотреть на то, как это работает. Сперва следует понять, как получить ссылку из буфера обмена.

Мы получаем данные из буфера обмена, создавая COM объект гипертекстового файла. Я видел, что в новых версиях НАКОНЕЦ-ТО можно будет работать с буфером напрямую из 1С, и даже с файлами и изображениями, но я всё еще живу в средних веках , поэтому делаем так. Если можно оптимальней на версиях 8.3 ниже 20ой, очень прошу подскажите.

&НаКлиенте
Процедура ЗаполнитьИзБО(Команда)
	ОбъектHTML = Новый COMОбъект("htmlfile"); 
	ДанныеБО = ОбъектHTML.ParentWindow.ClipboardData.Getdata("Text");
	ОтправляемаяНавигационнаяСсылка = "";
	Сообщение = "";
	Если ЗначениеЗаполнено(ДанныеБО) Тогда
		Попытка 
			МассивСсылок = Новый Массив;
			МассивСсылок.Добавить(ДанныеБО);
			Представление = ПолучитьПредставленияНавигационныхСсылок(МассивСсылок)[0];
			Если ЗначениеЗаполнено(Представление.Текст) Тогда  
				ОтправляемаяНавигационнаяСсылка = ДанныеБО;
				Сообщение = "Добрый день, посмотрите, пожалуйста 
				 	|" + Представление.Текст;	
			КонецЕсли;
		Исключение
		КонецПопытки			
	КонецЕсли;
КонецПроцедуры

Попытка Исключение здесь на самом деле не нужны. Когда писал код, думал, что при получении представления из невалидной ссылки будет падать ошибка, а она и не падает, просто возвращается пустая строка , поэтому обработчик исключений здесь получается уже какой-то параноидальный. Но убирать всё равно не хочу, потому что ПолучитьПредставленияНавигационныхСсылок — для меня чёрный ящик, а я явно забираю после него из массива нулевой элемент. А вдруг обновим платформу и не будет пустая строка возвращаться, а будет пустой массив. Надо оно мне через пять лет вспоминать, что тут происходило? Вот и вам не надо.

При открытии добавляем:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ЗаполнитьИзБО(Неопределено);
КонецПроцедуры

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

Ну и сама отправка это просто запись в регистр сведений:

&НаСервере
Процедура ОтправитьНаСервере()
	УстановитьПривилегированныйРежим(Истина);
	Для каждого СтрПолучатели Из Получатели Цикл
		Если СтрПолучатели.Пометка Тогда
			МЗ = РегистрыСведений.СообщенияПользователям.СоздатьМенеджерЗаписи();
			МЗ.Период = ТекущаяДата();
			МЗ.Пользователь = СтрПолучатели.Значение; 
			МЗ.ТекстСообщения = "Сообщение от " + ПараметрыСеанса.ТекущийПользователь;
			МЗ.Пояснение = Сообщение;
			МЗ.ОбъектЦель = ОтправляемаяНавигационнаяСсылка;
			МЗ.Записать();
		КонецЕсли;
	КонецЦикла;		
КонецПроцедуры

&НаКлиенте
Процедура Отправить(Команда)
	ОтправитьНаСервере();
	ЭтаФорма.Закрыть();
КонецПроцедуры

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

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

Огромное спасибо всем, кто не пожалел своего времени и дочитал до конца!

has been added to your cart:
Оформление заказа