Online телефонный справочник из 1С: Зарплата и управление персоналом

10.03.17

Интеграция - WEB-интеграция

В интернете представлено много реализаций online телефонных справочников организаций. Есть справочники, которые использует для хранения информации базу Active Directory (LDAP), есть справочники, которые реализованы с использованием СУБД (например, MySQL). Но я не нашел справочника, который использует информацию из базы 1С. Далее я рассмотрю данную разработку.

Во многих организациях используются телефонные справочники сотрудников. Сначала у нас был красивый бумажный вариант, разработанный в редакторе векторной графики. Но постепенно мне стало надоедать, что приходится его распечатать, затем вырезать и скрепить, а таких справочников варировалось от 10 до 25 штук. В общем, занимало много времени, и особого желания заниматься этим не было. Далее справочник эволюционировал в электронную версию в Excel. Для меня это был идеальный вариант: работает поиск, простота заполнения. Но акктуализировать информацию приходилось мне, возможно из-за сложности формы, а возможно из-за нежелания сотрудника заниматься изучением справочника. В общем со временем мне надоел и этот вариант.  Поспрашивав друзей из других компаний,  я выяснил, что online телефонный справочник у них реализован с использованием LDAP или PHP+MySQL. Мне не понравились данные реализации по ряду причин:

  • при использовании LDAP пришлось бы забивать в базу контакты людей, которые не пользуются корпоративной сетью вообще;
  • при использовании LDAP для поддержания акктуальной информации пришлось кому-то из сотрудников давать доступ к серверу, а точнее к редактированию данных LDAP;
  • при использовании PHP+MySQL необходимо было бы писать и серверную, и клиентскую часть с нуля, либо модифицировать наработки товарищей под свои нужды. Поскольку, я не силен в PHP, то рассматривать данный вариант не стал.

В итоге я решил использовать в качестве хранения и обработки данных 1C, а для удобного вывода HTML и JavaScript. 

Задача разделилась на 3 этапа:

1. Разработка серверной части, т.е.  разработать HTTP-сервис для конфигурации 1С: ЗУП

2.  Разработка клиентской части. Вот тут для меня было сложнее всего, поскольку HTML и JavaScript я знал меньше чем на базовом уровне.

3. Публикация на web-сервере. Об этом я в статье писать не буду, поскольку информации в интернете, да и на infostart.ru много по этому вопросу.

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

Разработка серверной части

Для начала давайте разберемся, как работать с  HTTP-сервисами в 1С. HTTP-сервисы представляют обработчики HTTP-запросов по определенному URL. URL HTTP-сервиса используется специальный, например:

http://<адрес сервера>/<имя базы>/hs/<корневой URL>/<относительный URL>

где

  • адрес сервера - это адрес серевера публикации базы 1С;
  • имя базы - это название базы данных конфигурации 1С;
  • hs - указывает на то, что мы обращаемся к HTTP-сервису;
  • корневой URL - это группа запросов, объеденных общим смыслом. Указывается в свойствах HTTP-запроса;
  • относительный URL - это сам запрос, который может использоваться по шаблону и указывается в объекте Шаблон URL.

Для телефонного справочника используем корневой URL - person, а относительные URL следующие (рисунок 1):

  1. personList - получение списка сотрудников;
  2. personInfo - получение контактной информации по сотруднику;
  3. birthdayList - получение списка дней рождения и дней оставшихся до него.
Для написания HTTP-сервиса используем методы в шаблонах URL, которые связываются в модуле HTTP-сервиса с функциями. В модуле HTTP-сервиса будут 3 функции getPersonList, getPersonInfo, getBirthdayList.
Конфигуратор 1С: ЗУП
Рисунок 1. HTTP-сервис и шаблоны URL
Алгоритмы трех функций очень схожи и сводятся к одному:
  1. Проверяем парметры HTTP-запроса;
  2. Составляем запрос к базе данных
  3. Обрабатываем результаты базы данных в объекты массив и структуры
  4. Формируем из полученных объектов сериализированную строку JSON
  5. Формируем ответ и отправляем обратно клиенту

Поскольку, алгоритмы схожи, то расмотрим только функцию personList, если вы поймете как работает эта функция, то разобраться с другими не составит труда. При формировании HTTP-ответа мы создаем JSON строку, в которой будут содержаться данные для отображения.  

Для начала создадим объект HTTPСервисОтвет с числовым параметром, соответствующий коду HTTP-состояния и укажем тип возвращаемых данных (javascript). Более подробнее про коды HTTP состояния можно почитать тут.

Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-type","application/javascript");

Пока работа с объектом HTTPСервисОтвет закончена. Для того, чтобы получить параметры от клиента мы обрабатываем Запрос методом Получить, аргументом которого идет название параметра:

ПараметрСостояния = ?(Запрос.ПараметрыЗапроса.Получить("fired") = "0", Перечисления.СостоянияСотрудника.Увольнение, Неопределено);

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

Дальше мы производим запрос к базе данных с параметром:

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

Заострять внимание на зпросе не будем, поскольку я писать запросы не умею, тема публикации меньше отностися к запросам и поэтому перейду к обработке результатов запроса:

ВыборкаПодразделений = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам,"КодПодразделения");
СписокПодразделений = Новый Массив;
Пока ВыборкаПодразделений.Следующий() Цикл
	Подразделение = Новый Структура;
	Подразделение.Вставить("id",Строка(ВыборкаПодразделений.КодПодразделения));
	Подразделение.Вставить("open","true");	
	Подразделение.Вставить("value",Строка(ВыборкаПодразделений.Подразделение));
	СписокСотрудников = Новый Массив;
	ВыборкаСотрудники = ВыборкаПодразделений.Выбрать();
	Пока ВыборкаСотрудники.Следующий() Цикл
		Сотрудник = Новый Структура;
		ФИО = ВыборкаСотрудники.Фамилия + " " + Лев( ВыборкаСотрудники.Имя, 1) + ". " + Лев( ВыборкаСотрудники.Отчество, 1) + ".";
		Сотрудник.Вставить("id",Строка(ВыборкаСотрудники.КодСотрудника));
		Сотрудник.Вставить("value",Строка(ФИО));
		СписокСотрудников.Добавить(Сотрудник);		
	КонецЦикла; 	
	Подразделение.Вставить("data",СписокСотрудников);
	СписокПодразделений.Добавить(Подразделение);
КонецЦикла; 

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

Джсон = Новый ЗаписьJSON;
Джсон.УстановитьСтроку();
Настройка = Новый НастройкиСериализацииJSON;
ЗаписатьJSON(Джсон, СписокПодразделений,Настройка);
РезультатСтрока = Джсон.Закрыть(); 	
Ответ.УстановитьТелоИзСтроки(РезультатСтрока,КодировкаТекста.UTF8);
Возврат Ответ;

При формировании JSON использовал пример с its.1c.ru

Код функции GetPersonsList
Функция GetPersonsList(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.Заголовки.Вставить("Content-type","application/javascript");
	ПараметрСостояния = ?(Запрос.ПараметрыЗапроса.Получить("fired") = "0", Перечисления.СостоянияСотрудника.Увольнение, Неопределено); 

	ЗапросКБазе = Новый Запрос;
	ЗапросКБазе.Параметры.Вставить("Состояние",ПараметрСостояния);
	ЗапросКБазе.Текст = "ВЫБРАТЬ
	                    |	ДанныеДляПодбораСотрудников.Сотрудник.Код КАК КодСотрудника,
	                    |	ДанныеДляПодбораСотрудников.Подразделение.Наименование КАК Подразделение,
	                    |	ДанныеДляПодбораСотрудников.Подразделение.Код КАК КодПодразделения,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Фамилия КАК Фамилия,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Имя КАК Имя,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Отчество КАК Отчество
	                    |ИЗ
	                    |	РегистрСведений.ДанныеДляПодбораСотрудников КАК ДанныеДляПодбораСотрудников
	                    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияСотрудников.СрезПоследних КАК СостоянияСотрудниковСрезПоследних
	                    |		ПО ДанныеДляПодбораСотрудников.Сотрудник.Ссылка = СостоянияСотрудниковСрезПоследних.Сотрудник.Ссылка
	                    |ГДЕ
	                    |	ДанныеДляПодбораСотрудников.Подразделение.Наименование <> """"
	                    |	И СостоянияСотрудниковСрезПоследних.Состояние.Ссылка <> &Состояние
	                    |
	                    |СГРУППИРОВАТЬ ПО
	                    |	ДанныеДляПодбораСотрудников.Подразделение.Наименование,
	                    |	ДанныеДляПодбораСотрудников.Сотрудник.Код,
	                    |	ДанныеДляПодбораСотрудников.Подразделение.Код,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Фамилия,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Имя,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Отчество
	                    |
	                    |УПОРЯДОЧИТЬ ПО
	                    |	Подразделение,
	                    |	Фамилия
	                    |ИТОГИ
	                    |	МАКСИМУМ(КодСотрудника),
	                    |	МАКСИМУМ(Подразделение)
	                    |ПО
	                    |	КодПодразделения";
	Результат = ЗапросКБазе.Выполнить();
	ВыборкаПодразделений = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам,"КодПодразделения");
	СписокПодразделений = Новый Массив;
	Пока ВыборкаПодразделений.Следующий() Цикл
		Подразделение = Новый Структура;
		Подразделение.Вставить("id",Строка(ВыборкаПодразделений.КодПодразделения));
		Подразделение.Вставить("open","true");	
		Подразделение.Вставить("value",Строка(ВыборкаПодразделений.Подразделение));
		СписокСотрудников = Новый Массив;
		ВыборкаСотрудники = ВыборкаПодразделений.Выбрать();
		Пока ВыборкаСотрудники.Следующий() Цикл
			Сотрудник = Новый Структура;
			ФИО = ВыборкаСотрудники.Фамилия + " " + Лев( ВыборкаСотрудники.Имя, 1) + ". " + Лев( ВыборкаСотрудники.Отчество, 1) + ".";
			Сотрудник.Вставить("id",Строка(ВыборкаСотрудники.КодСотрудника));
			Сотрудник.Вставить("value",Строка(ФИО));
			СписокСотрудников.Добавить(Сотрудник);		
		КонецЦикла; 	
		Подразделение.Вставить("data",СписокСотрудников);
		СписокПодразделений.Добавить(Подразделение);
	КонецЦикла; 
	Джсон = Новый ЗаписьJSON;
	Джсон.УстановитьСтроку();
	Настройка = Новый НастройкиСериализацииJSON;
	ЗаписатьJSON(Джсон, СписокПодразделений,Настройка);
	РезультатСтрока = Джсон.Закрыть(); 	
	Ответ.УстановитьТелоИзСтроки(РезультатСтрока,КодировкаТекста.UTF8);
	Возврат Ответ;
КонецФункции
Код функции GetPersonInfo
Функция GetPersonInfo(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	Ответ.Заголовки.Вставить("Content-type","application/javascript");
	КодФизлица = Запрос.ПараметрыЗапроса.Получить("id");

	ЗапросКБазе = Новый Запрос;
	ЗапросКБазе.Параметры.Вставить("КодФизлица",КодФизлица);
	ВидыКонтактнойИнформации = Новый Массив;
	ВидыКонтактнойИнформации.Добавить(Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Мобильный телефон").Ссылка);
	ВидыКонтактнойИнформации.Добавить(Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Email").Ссылка);
	ВидыКонтактнойИнформации.Добавить(Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Домашний телефон").Ссылка);
	ВидыКонтактнойИнформации.Добавить(Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Рабочий телефон").Ссылка);
	ВидыКонтактнойИнформации.Добавить(Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Адрес места проживания").Ссылка);
	ЗапросКБазе.УстановитьПараметр("ВидыКонтактов",ВидыКонтактнойИнформации);
	ЗапросКБазе.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
	                    |	ФизическиеЛицаКонтактнаяИнформация.Вид,
	                    |	ФизическиеЛицаКонтактнаяИнформация.Представление,
	                    |	ФизическиеЛицаКонтактнаяИнформация.Ссылка.Ссылка КАК СсылкаФизЛицо
	                    |ПОМЕСТИТЬ Контакты
	                    |ИЗ
	                    |	Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
	                    |ГДЕ
	                    |	ФизическиеЛицаКонтактнаяИнформация.Вид В(&ВидыКонтактов)
	                    |;
	                    |
	                    |////////////////////////////////////////////////////////////////////////////////
	                    |ВЫБРАТЬ
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.ФИО КАК ФИО,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения КАК ДатаРождения,
	                    |	ДанныеДляПодбораСотрудников.Должность.Наименование КАК Должность,
	                    |	Контакты.Вид КАК ТипКонтакта,
	                    |	Контакты.Представление КАК Контакт
	                    |ИЗ
	                    |	Контакты КАК Контакты
	                    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ДанныеДляПодбораСотрудников КАК ДанныеДляПодбораСотрудников
	                    |		ПО Контакты.СсылкаФизЛицо = ДанныеДляПодбораСотрудников.ФизическоеЛицо.Ссылка
	                    |ГДЕ
	                    |	ДанныеДляПодбораСотрудников.Сотрудник.Код = &КодФизлица
	                    |	И ДанныеДляПодбораСотрудников.Должность.Наименование <> """"
	                    |
	                    |СГРУППИРОВАТЬ ПО
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.ФИО,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения,
	                    |	ДанныеДляПодбораСотрудников.Должность.Наименование,
	                    |	Контакты.Вид,
	                    |	Контакты.Представление
	                    |
	                    |УПОРЯДОЧИТЬ ПО
	                    |	ФИО
	                    |ИТОГИ ПО
	                    |	ФИО";
	Результат = ЗапросКБазе.Выполнить();
	ВыборкаСотрудник = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам,"ФИО");
	Пока ВыборкаСотрудник.Следующий() Цикл
		Сотрудник = Новый Структура;
		Сотрудник.Вставить("fio",Строка(ВыборкаСотрудник.ФИО));
		СписокКонтактов = Новый Массив;
		ВыборкаКонтактов = ВыборкаСотрудник.Выбрать();
		Пока ВыборкаКонтактов.Следующий() Цикл
			Контакт = Новый Структура;
			Контакт.Вставить("type",Строка(ВыборкаКонтактов.ТипКонтакта));
			Контакт.Вставить("value",Строка(ВыборкаКонтактов.Контакт));
			Сотрудник.Вставить("birthday",Строка(Лев(ВыборкаКонтактов.ДатаРождения,10)));	
			Сотрудник.Вставить("who",Строка(ВыборкаКонтактов.Должность));
			СписокКонтактов.Добавить(Контакт);		
		КонецЦикла; 	
		Сотрудник.Вставить("contacts",СписокКонтактов);;
	КонецЦикла; 
	Джсон = Новый ЗаписьJSON;
	Джсон.УстановитьСтроку();
	Настройка = Новый НастройкиСериализацииJSON;
	ЗаписатьJSON(Джсон, Сотрудник, Настройка);
	РезультатСтрока = Джсон.Закрыть(); 	
	Ответ.УстановитьТелоИзСтроки(РезультатСтрока,КодировкаТекста.UTF8);
	Возврат Ответ;
	
КонецФункции
Код функции GetBirthdayList
Функция GetBirthdayList(Запрос)
	Ответ = Новый HTTPСервисОтвет(200);
	КоличествоДней = Запрос.ПараметрыЗапроса.Получить("days");
	ЗапросКБазе = Новый Запрос();
	ЗапросКБазе.Параметры.Вставить("Дней", Число(КоличествоДней));
	ЗапросКБазе.Параметры.Вставить("Сегодня", ТекущаяДата());
	ЗапросКБазе.Текст = "ВЫБРАТЬ
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Наименование КАК ФИО,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения КАК ДатаРождения,
	                    |	ДЕНЬГОДА(ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения) - ДЕНЬГОДА(&Сегодня) КАК ДоДняРождения
	                    |ИЗ
	                    |	РегистрСведений.ДанныеДляПодбораСотрудников КАК ДанныеДляПодбораСотрудников
	                    |ГДЕ
	                    |	ДЕНЬГОДА(ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения) - ДЕНЬГОДА(&Сегодня) <= &Дней
	                    |	И ДЕНЬГОДА(ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения) - ДЕНЬГОДА(&Сегодня) > 0
	                    |
	                    |СГРУППИРОВАТЬ ПО
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.ДатаРождения,
	                    |	ДанныеДляПодбораСотрудников.ФизическоеЛицо.Наименование
	                    |
	                    |УПОРЯДОЧИТЬ ПО
	                    |	ДоДняРождения";
	Результат = ЗапросКБазе.Выполнить();
	ВыборкаСотрудник = Результат.Выбрать();
	СписокСотрудников = Новый Массив;
	Пока ВыборкаСотрудник.Следующий() Цикл
		Сотрудник = Новый Структура;
		Сотрудник.Вставить("fio",Строка(ВыборкаСотрудник.ФИО));
		Сотрудник.Вставить("days",Строка(ВыборкаСотрудник.ДоДняРождения));	
		Сотрудник.Вставить("birthday",Строка(Лев(ВыборкаСотрудник.ДатаРождения,10)));	
		СписокСотрудников.Добавить(Сотрудник);
	КонецЦикла; 
	Джсон = Новый ЗаписьJSON;
	Джсон.УстановитьСтроку();
	Настройка = Новый НастройкиСериализацииJSON;
	ЗаписатьJSON(Джсон, СписокСотрудников, Настройка);
	РезультатСтрока = Джсон.Закрыть(); 	
	Ответ.УстановитьТелоИзСтроки(РезультатСтрока,КодировкаТекста.UTF8);
	Возврат Ответ;
КонецФункции

Алгоритм остальных функций аналогичен. Переходим к разработке клиентской части

Разработка клиентской части

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

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

До использования Webix UI

Рисунок 2. Интерфейс до использования Webix UI

Webix UI

Рисунок 3. Исопльзование Webix UI

Помимо данной библиотеки клиентская часть, состоит из  следующих файлов:

  • index.html - точка входа, именно на эту страницу попадает клиент и с нее уже подгружаются logic.js и ui.js
  • logic.js - файл описывающий логику интерфейса: действия при нажатии на элементы, запросы к серверу и т. д.
  • ui.js - файл описывающий сам интерфейс.
Код ui.js
var ui_scheme = {
	id:"phonebook",
	rows: [
	{view: "toolbar", cols: [
	{view: "label", label: "Телефонный справочник", align:"left"},
	{},
	{view: "label", label: "Отображать уволенных: ", align:"right"},
	{view: "toggle", id:"fired", offLabel:"нет", onLabel:"да", width:50},
	{view: "icon", id:"birthday", icon:"birthday-cake"}
	]},
	{cols:[
		{
			width: 300,
			rows:[
			{view: "search",id:"search", placeholder:"Поиск..."},
			{view: "tree", select:"true", id:"list_person"}
			]},
			{   
				id: "info",
				hidden:true ,
				width: "800",
				rows:[
				{view:"template", id:"main_info", template:"<h1>#fio#</h1><h3>Должность: #who#</h3><h5>Дата рождения: #birthday#</h5>",  autoheight:true},
				{
					view:"datatable", 
					id: "contact_info",
					header:false,
					autowidth:true,
					columns:[
					{ id:"type",    header:"", width:200},
					{ id:"value",   header:"", width:600}
					],
					fixedRowHeight:false,  rowLineHeight:25, rowHeight:25
				}
				]            
			}
			]}
			]
		};

		var win_birthday = {
			view:"window",
			id:"wBirthday",
			position: "center",
			modal: "true",
			resize: "true",
			move: "true",
			width: 500,
			head:{
				view: "toolbar", cols: [
				{view: "label", label: "Ближайшие дни рождения"},
				{view: "icon", icon:"close", id:"win_close", align: "right", click:"$$('wBirthday').hide();"}
				]
			},
			body:{
				rows:[
				{view:"counter", id:"days", label:"До дня рождения", labelWidth: 150, step:1, value:14, min:4, max:365},
				{view:"list", id: "birthday_persons",  template:"#fio# - #birthday# (#days#)", hidden:true, width:400}
				]
			}
		};

Интерфейс страницы (ui_scheme) представляет собой рабочую област,ь разделенную на три части:

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

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

Также имеется окно (win_birthday), которое состоит из заголовка окна, кнопки закрыть и списка сотрудников с указанием даты рождения и дней до дня рождения. Размеры и положения окна можно изменять и оно является модальным.

Код logic.js
function update_list()
{
	var url = "/../zup3/hs/person/personList?fired="+$$('fired').getValue();
	var auth = 'Basic ' + btoa(unescape(encodeURIComponent("web:web")))
	var res = null;
	webix.attachEvent("onBeforeAjax", 
		function(mode, url, data, request, headers, files, promise){
			headers["Authorization"]= auth;
		});
	webix.ajax(url,{
		error:function(text, data, XmlHttpRequest){
			alert("Ошибка работы с сервером");
		},
		success:function(text, data, XmlHttpRequest){
			$$("list_person").clearAll();
			$$("phonebook").disable();
			$$("phonebook").showProgress({
				type:"icon",
				delay:100,
				hide:true
			});
			setTimeout(function(){
				$$("list_person").parse(data.json());
				$$("phonebook").enable();
			}, 100);
		}
	});
}

function update_info(id)
{
	var url = "/../zup3/hs/person/personInfo?id="+id;
	var auth = 'Basic ' + btoa(unescape(encodeURIComponent("web:web")))
	var res = null;
	webix.attachEvent("onBeforeAjax", 
		function(mode, url, data, request, headers, files, promise){
			headers["Authorization"]= auth;
		});
	webix.ajax(url,{
		error:function(text, data, XmlHttpRequest){
			alert("Ошибка работы с сервером");
		},
		beforeSend:function(req) {
				req.setRequestHeader('Authorization', auth);
		},
		success:function(text, data, XmlHttpRequest){
		$$("phonebook").disable();
		$$("phonebook").showProgress({
			type:"icon",
			delay:100,
			hide:true
		});
		setTimeout(function(){
			$$("main_info").parse(data.json());
			$$("contact_info").clearAll();
			$$("contact_info").parse(data.json().contacts);
			$$("contact_info").adjustRowHeight("value", true); 
            $$("contact_info").render();
			$$("phonebook").enable();
			$$("info").show();
		}, 100);
	}
	});
}


function update_birthday(days)
{
	var url = "/../zup3/hs/person/birthdayList?days="+days;
	var auth = 'Basic ' + btoa(unescape(encodeURIComponent("web:web")))
	var res = null;
	webix.attachEvent("onBeforeAjax", 
		function(mode, url, data, request, headers, files, promise){
			headers["Authorization"]= auth;
		});
	webix.ajax(url,{
		error:function(text, data, XmlHttpRequest){
			alert("Ошибка работы с сервером");
		},
		beforeSend:function(req) {
				req.setRequestHeader('Authorization', auth);
		},
		success:function(text, data, XmlHttpRequest){
		$$("birthday_persons").disable();
		$$("birthday_persons").showProgress({
			type:"icon",
			delay:100,
			hide:true
		});
		setTimeout(function(){
			$$("birthday_persons").clearAll();
			$$("birthday_persons").parse(data.json());
			$$("birthday_persons").enable();
			$$("birthday_persons").show();
		}, 100);
	}
	});
}

webix.ready(function(){
	webix.ui(ui_scheme);
	webix.ui(win_birthday);
	webix.extend($$("phonebook"), webix.ProgressBar);
	webix.extend($$("birthday_persons"), webix.ProgressBar);
	update_list();
	$$('fired').attachEvent("onItemClick", function(){
		update_list();
	})
	$$('list_person').attachEvent("onItemClick", function(id, e, node){
		if ($$('list_person').isBranch(id) == false)
			update_info(id);
	})
	$$('birthday').attachEvent("onItemClick", function(){
		$$("wBirthday").show();
		update_birthday($$('days').getValue());
	})

	$$("days").attachEvent("onChange",function(){
		update_birthday(this.getValue());
	})

	$$("search").attachEvent("onTimedKeyPress",function(){
		$$("list_person").filter("#value#",this.getValue());
	})

})

Когда выполняется файл logic.js, он загружает интерфейс и подключает индикатор загрузки

webix.ui(ui_scheme);
webix.ui(win_birthday);
webix.extend($$("phonebook"), webix.ProgressBar);
webix.extend($$("birthday_persons"), webix.ProgressBar);

Затем вызывается функция update_list(), которая формирует список сотрудников и подразделений, отправляя запрос на сервер. После чего добавляются события к следующим объектам

$$('fired').attachEvent("onItemClick", function(){
		update_list();
	})

При нажатии на клавишу "Показывать уволенных сотрудников" происходит обновление списка подразделений и сотрудников

$$('list_person').attachEvent("onItemClick", function(id, e, node){
		if ($$('list_person').isBranch(id) == false)
			update_info(id);
	})

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

$$('birthday').attachEvent("onItemClick", function(){
		$$("wBirthday").show();
		update_birthday($$('days').getValue());
	})

При нажатии на кнопку "День рождения" отображается окно и обновляется список сотрудников и дат рождений в данном окне.

$$("search").attachEvent("onTimedKeyPress",function(){
        $$("list_person").filter("#value#",this.getValue());
    })

При изменении значения "До дня рождения" обновляется список сотрудников и дат рождений

$$("search").attachEvent("onTimedKeyPress",function(){
        $$("list_person").filter("#value#",this.getValue());
    })

Если поле search будет изменено то применяется фильтр к дереву подразделений и сотрудников.

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

  1. Формируется URL для запроса
  2. Формируется строка авторизации
  3. До отправки запроса отправляется запрос авторизации
  4. Отправляется запрос на сервер
  5. Обрабатывается результат и парсится в объекты страницы.

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

http-сервис телефонный справочник зарплата и управление персоналом web html javascript

См. также

Интеграция Альфа Авто 5 / Альфа Авто 6 и AUTOCRM / Инфотек

Сайты и интернет-магазины WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме. Без существенных изменений типовой конфигурации. Проверено с брендами: Интеграция 1С и GEELY Интеграция 1С и HAVAL Интеграция 1С и KIA Интеграция 1С и FORD Интеграция 1С и LADA ГАРАНТИЯ 100% ВНЕДРЕНИЯ!

36000 руб.

03.08.2020    15663    9    17    

10

Модуль для обмена "1С:Предприятие 8. УАТ. ПРОФ" с FortMonitor

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    12811    30    8    

10

Интеграция 1С — Битрикс24. Обмен задачами

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс24. Разработка имеет двухстороннюю синхронизацию 1С и Битрикс24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (8.3.18.1289). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    17432    6    15    

13

[Расширение] БОР-Навигатор.Культура

Зарплата Бюджетный учет WEB-интеграция Обмен с ГосИС Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бюджетный учет Платные (руб)

Расширение конфигурации, включающее в себя объекты, необходимые для подготовки и сдачи отчета "Штатная численность" системы "БОР-Навигатор.Культура" в программе "1С:Зарплата и кадры государственного учреждения", редакция 3.1.

8400 руб.

01.02.2019    25689    9    0    

7

Интеграция с сервисом vetmanager

WEB-интеграция Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Внешняя обработка разрабатывалась для загрузки документов из Ветменеджер в 1С: Бухгалтерия 3.0

12000 руб.

02.02.2021    16262    41    49    

22
Вознаграждение за ответ
Показать полностью
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. ruha 56 10.03.17 17:04 Сейчас в теме +3 $m
Исходники стоит прикреплять?
2. andrei.k 15.03.17 09:52 Сейчас в теме
(1) Если не сложно, прикрепите пожалуйста. Реализация интересная.
kraynev-navi; +1 Ответить
6. ruha 56 15.03.17 14:14 Сейчас в теме
(2)
(1) Если не сложно, прикрепите пожалуйста. Реализация интересная.

Не сложно. Прикрепил архив с клиентской частью, и там же файл http.txt с текстом для http-сервиса. Для работы клиентской части, необходимо добавить библиотеку webix.
Прикрепленные файлы:
phone.zip
mkostya; Andr0med; +2 Ответить
12. Andr0med 27.05.19 09:46 Сейчас в теме
(6) Руслан, подскажите, пожалуйста, в чем может быть причина появления окна авторизации при запуске Вашего проекта? Как победить?
14. ruha 56 27.05.19 14:43 Сейчас в теме
(12) Написал ниже. Пост №13
3. DonAlPatino 176 15.03.17 12:13 Сейчас в теме
А что с клиентскими лицензиями на 1С в такой конфигурации происходит? Делал подобную разработку, но у меня получился комбинированный вариант AD + база в MS SQL, куда грузятся данные из 1С. Насчет AD могу сказать еще, что тормозное оно при отдаче данных (у меня там еще фотки сотрудников) до ужаса. Вот думаю может переделать.
4. ruha 56 15.03.17 14:08 Сейчас в теме
(3) А вот это интересный вопрос, даже не думал об этом. Если я не ошибаюсь, то 1С лицензия идет на сеанс, при открытие справочника, сеанс создается только на время обращения к базе. В консоле администрирования посмотрел сеансы только "живых душ", пользователь "web", который используется не висит. Вроде не зависал ни разу.
5. ruha 56 15.03.17 14:10 Сейчас в теме
(3) А сколько пользователей? Просто когда я делал, я все думал как реализовать: каждую запись с отдельным запросом к БД, или просто одним запросом все данные взять. И если вы будете реализовать у себя и будет большой объем данных, хотел узнать, как долго будут лететь данные общий запросом.
7. DonAlPatino 176 15.03.17 14:55 Сейчас в теме
(5) Под 200 человек. Соответственно тянется одним запросом весь список и отдельными запросами фотки.
8. ruha 56 15.03.17 16:00 Сейчас в теме
(7) Может отдельными запросами лучше? По необходимости подгружать информацию по нужному сотруднику
9. DonAlPatino 176 15.03.17 17:21 Сейчас в теме
(8)У меня был проект по замене "старого" списка, который целиком велся в ручную и где карточка сотрудника сразу отображалась. И меня за можай загнали с криком "сделать как было". А потом, я так понимаю, запрос к AD долгий вне зависимости от того просто ты список сотрудников получаешь или список сотрудников с полями. Скорость одна. Ну либо где-то в коде (там .Net и я на этом проектике его изучал) какие-то откровенные глупости. Сейчас рефачу по-немного когда время есть. Может найду.
10. user723770 20.04.17 17:38 Сейчас в теме
У нас крутится нечто подобное для рассылки корреспонденции. 1С на SQL и данные берем из базы на прямую из справочников. Лицензии 1С не задействованы.
11. Andr0med 21.05.19 13:57 Сейчас в теме
Бодрого времени суток! Искал варианты реализации корпоративного сайтика в связке с 1С, нашел эту статью. Помогло сильно, за что нечеловеческое спасибо!
Скачал, установил 32-разрядный Apache (64-разрядный на Win7 64x не взлетел).
В платформе 1С доустановил веб-расширение. В самописной конфигурации создал справочники, заполнил их данными, подправил функции в модуле HTTP-сервиса, чтобы возвращали данные той же структуры, что в примере. Опубликовал. Создался файл default.vrd. В httpd.conf всё, что положено, прописалось.
Захожу в браузере по нужному адресу и получаю форму входа, в которой нужно ввести имя пользователя и пароль.

Я вводил данные локального юзера. Не то.
Создал в конфигурации 1С пользователя, дал ему полные права на все объекты (в учебных целях!), вводил его данные - не то.
Подскажите, какого лешего пользователя нужно ввести ?
Может быть, дело в библиотеках webix'а? Я стандартную скачал/установил, там функционал немного урезан...
Прикрепленные файлы:
13. ruha 56 27.05.19 14:42 Сейчас в теме
Так постараюсь вспомнить) Давно уже это делал, но во-первых посмотрите права на использование сервисом (не помню есть ли они или нет). Во-вторых напишите как вы отправляете строку авторизации в скрипте, точнее как формируете
15. Sergey101081 23.01.21 14:12 Сейчас в теме
На панели инструментов в верхней части не отображается кнопка списка дней рождений;
отвечает это строка {view: "icon", id:"birthday", icon:"birthday-cake"}
Как добавить Иконку? подскажите. В JavaScript полный ноль...
ЕЩЕ
Если в строке браузера ввожу http://localhost/httptest/hs/person/personList, то данные возвращаются
а если запускаю index.html, то выдает "Ошибка работы с сервером"
Сточка в скрипте должна выглядеть так: var url = "http://localhost/httptest/hs/person/personList?fired="+$$('fired').getValue();
или так: var url = "/. ./httptest/hs/person/personList?fired="+$$('fired').getValue();
20. ruha 56 06.04.21 15:18 Сейчас в теме
(15) Вроде и так и так правильно, только во втором варианте между точками не должно быть пробела
16. Sergey101081 23.01.21 15:24 Сейчас в теме
Выдает "Ошибка работы с сервером":
Access to XMLHttpRequest at 'http://localhost/httptest/hs/person/personList?fired=0' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
19. ruha 56 06.04.21 15:16 Сейчас в теме
(16) Ошибка CORSов, (https://developer.mozilla.org/ru/docs/Web/HTTP/CORS). У вас почему-то в headers не отправляется origin, точнее отправляется, но null
17. user1200393 06.04.21 02:14 Сейчас в теме
Не могу разобраться, помогите пожалуйста:
Список сотрудников загружается, но страница остается неактивной и в разделе Info бесконечно отображается прогрессбар.
setTimeout(function(){
				$$("list_person").parse(data.json());
				$$("phonebook").enable();				
}, 100);

Этот код выполняется, но
$$("phonebook").enable();

не срабатывает.

Если закомментировать
$$("phonebook").disable();
$$("phonebook").showProgress({
	type:"icon",
	delay:100,
	hide:true
});

То можно выделять сотрудников и будет вызываться функция function update_info, однако она корректно не выполняется по той же причине (бесконечный прогрессбар). Если и там закомментировать соотвествующий блок и не переводить страницу в disable(), то функция выполняется, но Info и contact_info не заполняются (ничего не отображается - просто пустая страница).

Данные по запросам personList и personInfo из базы приходят.
18. ruha 56 06.04.21 15:15 Сейчас в теме
(17)
То можно выделять сотрудников и будет вызываться функция function update_info, однако она корректно не выполняется по той же причине (бесконечный прогрессбар). Если и там закомментировать соотвествующий блок и не переводить страницу в disable(), то функция выполняется, но Info и contact_info не заполняются (ничего не отображается - просто пустая страница).

Я могу получить удаленнку, чтобы уже на развернутой системе глянуть
21. user1200393 07.04.21 06:16 Сейчас в теме
(17)Разобрался - криво установил Webix.
Повторил сначала и всё заработало.
Спасибо!
Оставьте свое сообщение