Полгода назад мы рассказывали о том, как реализовали сервис бронирования переговорных комнат у крупного заказчика с территориально разнесенными офисами и штатом несколько тысяч человек. Мы продолжаем работать с SharePoint-порталом этой компании, в рамках его развития специалисты EastBanc Technologies создали еще один модуль — сервис управления заявками на отпуска в интеграции с автоматизированной системой управления персоналом БОСС-Кадровик.
Назначение этого сервиса, как нам кажется, не нуждается в пространном разъяснении: попросту говоря, сервис автоматизирует учет отпусков в компании. Гораздо интересней, какие практические задачи стояли перед нами в данной конкретной компании, об этом — дальше.
Анализ
В соответствии с ТК РФ, ежегодный основной оплачиваемый отпуск предоставляется работнику по истечении 6 месяцев с первого дня работы в компании, и затем каждый год на основании приказа о предоставлении отпуска, оформленного в соответствии с графиком отпусков. Каждый месяц работы «на счету» сотрудника копится 2,33 дня оплачиваемого отпуска. Оплачиваемый отпуск бывает: основным ежегодным, дополнительным, основным оплачиваемым отпуском авансом, дополнительным отпуском авансом. И отдельным типом идет административный отпуск.
Заявки на эти типы отпусков нам предстояло автоматизировать. В наши задачи входило:
- Предоставить сотрудникам удобный интерфейс на корпоративном портале для оформления и согласования заявок на отпуск. Сотрудник должен видеть, сколько свободных дней есть в его распоряжении, иметь возможность отправить заявку на согласование руководителю.
- Дать возможность руководителю сотрудника согласовывать заявки на отпуска в автоматическом режиме.
- Синхронизовать фактические данные с плановыми данными, которые есть у отдела персонала (хранятся в системе БОСС-Кадровик).
- Минимизировать бумажный документооборот.
С технической точки зрения нужно было учесть следующее:
- Сервис нужно было интегрировать в личный кабинет, сохранив стилистику и логику уже знакомого сервиса бронирования переговорок.
- Решение реализовывалось на MS SharePoint 2010, но все участники процесса разработки уже понимали необходимость перехода на 2013-ый, который позже провели специалисты EastBanc Technologies.
UI
Здесь все было просто: действовать в том же духе, что и с переговорками (картинки кликабельные).
«Мой кабинет»: видим доступное нам количество дней отпуска на заданный период, собственные заявки и их статусы, а в разделе «Архив» — заявки подчиненных, также со статусами. Особняком стоит кнопка «Добавить заявку».
«Мой кабинет» сотрудника отдела кадров: здесь все то же самое, только добавляется раздел «Заявки на согласование», которые прилетают кадровику со всей компании.
Заявка на отпуск — с точки зрения сотрудника. Эту форму он заполняет и отправляет на согласование начальнику своего подразделения, после чего она летит кадровикам.
Заявка на отпуск от сотрудника — в таком виде она приходит непосредственному руководителю сотрудника (начальнику подразделения).
Заявка на отпуск от сотрудника — в таком виде она приходит кадровику после согласования у непосредственного руководителя сотрудника.
Годовой график отпусков по конкретно взятому отделу — как его видит кадровик.
График отпусков по отделу на месяц — как его видит кадровик.
Хранение заявок на портале
Для хранения информации о фактических и запланированных отпусках мы создали список в SharePoint:
Интеграция с БОСС-Кадровик
Информация в SharePoint поступает из системы БОСС-Кадровик по средством обмена файлами с помощью SharePoint Job. Так же информацию о согласованных заявках мы отправляем обратно в БОСС-Кадровик.
Для связывания табельного номера сотрудника с учетной записью в ActiveDirectory было добавлено поле employeeID и «прокинуто» в SharePoint при помощи User Profile Service:
Отдельной проблемой стал график отпусков, т.к. в списке фактических и запланированных отпусков порядка 100 тысяч строк, и определять сотрудника через User Profile Service было трудозатратно. Поэтому мы настроили Search Service Application, и при помощи измененного в SharePoint 2013 механизма KeywordQuery достаем всю необходимую информацию о пользователях:
ResultTableCollection rtc = null;
var kwq = new KeywordQuery(site) {
QueryText = String.Format(querySchema, employeeIdField, nameField, departmentField, department, officeField, office),
ResultTypes = ResultType.RelevantResults,
KeywordInclusion = KeywordInclusion.AllKeywords,
HiddenConstraints = "scope:" + ""People""
};
SearchExecutor se = new SearchExecutor();
rtc = se.ExecuteQuery(kwq);
Для соблюдения ТК РФ мы сделали проверку на заявку пользователя: длительность основного оплачиваемого отпуска кратна 7 дням, и один из отпусков должен длиться не менее двух недель. Помимо данной проверки добавлена дополнительная проверка на «свободные дни»: из БОСС-Кадровик приходит информация о планируемых командировках или отгулах — на эти дни подавать заявку на отпуск запрещено.
Также отпуска можно «складывать»: например, к основному оплачиваемому отпуску из 14 дней «прицепить» административный отпуск из 7 дней.
Форматы данных, которые использовались для обмена:
1. Фактический и запланированный отпуска (из БОСС-Кадровик и в БОСС-Кадровик — формат идентичный)
imployeeId;fromDate;toDate;type
59;2010-08-09 00:00:00;2010-08-22 00:00:00;0
59;2007-06-01 00:00:00;2007-06-15 00:00:00;0
59;2007-08-15 00:00:00;2007-09-04 00:00:00;0
59;2012-06-25 00:00:00;2012-07-15 00:00:00;0
59;2012-09-01 00:00:00;2012-09-07 00:00:00;0
59;2013-04-29 00:00:00;2013-05-06 00:00:00;0
59;2013-07-01 00:00:00;2013-07-21 00:00:00;0
2. Доступно дней для отпуска (из БОСС-Кадровик)
imployeeId;fromDate;toDate;mainVacationDays;additionalVacationDays
59;2013-06-20 00:00:00;2014-06-19 00:00:00;14,0000;3,0000
59;2012-06-20 00:00:00;2013-06-19 00:00:00;,0000;3,0000
59;2011-06-20 00:00:00;2012-06-19 00:00:00;,0000;3,0000
59;2008-06-20 00:00:00;2009-06-19 00:00:00;1,0000;,0000
59;2007-06-20 00:00:00;2008-06-19 00:00:00;3,0000;,0000
3. Информация о прогулах, отгулах, командировках (из БОСС-Кадровик)
imployeeId;fromDate;toDate;type
5236;2007-03-12 00:00:00;2007-03-16 00:00:00; Командировка
5249;2007-03-09 00:00:00;2007-03-18 00:00:00; Командировка
209;2007-03-19 00:00:00;2007-03-19 00:00:00; Выходной день родителей детей инвалидов
Реализация
Для реализации форм создания/редактирования/просмотра заявок мы переопределили стандартные диалоги редактирования элементов SharePoint в schema.xml соответствующего списка при помощи атрибута UseLegacyForm:
<Forms>
<Form Type="DisplayForm" Url="DisplayForm.aspx" WebPartZoneID="Main" UseLegacyForm="TRUE" />
<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main" UseLegacyForm="TRUE" />
<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main" UseLegacyForm="TRUE" />
</Forms>
Как видно из определения, это по сути обычные aspx-страницы, но с уникальными content placeholder’ами.
Например для тэга title:
<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
Заявление на отпуск
</asp:Content>
Более подробно описано здесь office.microsoft.com/en-us/sharepoint-designer-help/working-with-content-placeholder-controls-HA102265026.aspx
Для UI мы использовали jQuery UI.
Также во всплывающем окне просмотра заявки нам нужно было отображать историю согласования данной заявки. Для этого мы создали список «история согласования заявки» с lookup-полем на список заявок. И определили представление с использованием CAML-запроса:
<Query>
<Where>
<Eq>
<FieldRef Name="Request" LookupId="TRUE" />
<Value Type="Lookup">
<GetVar Scope="Request" Name="Id"/>
</Value>
</Eq>
</Where>
<OrderBy>
<FieldRef Name="ID"></FieldRef>
</OrderBy>
</Query>
Параметр Id (это Id из списка заявок) приходит из параметров запроса, что в общем-то понятно, т.к. ссылка, формируемая SharePoint на открытие формы просмотра элемента, выглядит следующим образом:
{$HttpVDir}/_layouts/15/listform.aspx?PageType=6&ListId={$List}&ID={$ID}
Далее для нашего списка истории согласования, его представления для конкретной заявки и aspx-страницы отображения заявки было объявлено xls-представление:
<File Path="RequestList/DisplayForm.aspx" Url="Lists/RequestList/DisplayForm.aspx">
<View WebPartOrder="1" WebPartZoneID="Main" BaseViewID="2" List="$Resources:core,lists_Folder;/RequestHistoryList" >
<![CDATA[
<webParts>
<webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
<data>
<properties>
....
<property name="XslLink" type="string">/_layouts/15/ETR.VacationRequest/xsl/history.xsl</property>
....
</properties>
</data>
</webPart>
</webParts>
]]>
</View>
</File>
Из кода очевидно, что представление будет расположено на странице Lists/RequestList/DisplayForm.aspx в вебпарт зоне WebPartZoneID=«Main». Осталось лишь убедиться, что такая зона у нас присутствует на странице:
<WebPartPages:WebPartZone runat="server" FrameType="None" ID="Main" Title="loc:Main" />
С графиком отпусков мы пошли дальше, т.к. стандартное представление списка в виде календаря не устраивало ни нас, ни заказчика. Поэтому мы дали задание нашим дизайнерам, и они нарисовали этот красивый календарь:
Получив одобрение заказчика, мы принялись за работу: первым делом, по примеру нашего сервиса бронирования переговорок, мы написали WCF-сервис, который возвращает json и при этом работает в контексте SharePoint’a.
Определили DataContract:
[DataContract(Name = "Vacation")]
public class Vacation
{
[DataMember(Name = "fromDate")]
public DateTime FromDate { get; set; }
[DataMember(Name = "toDate")]
public DateTime ToDate { get; set; }
[DataMember(Name = "type")]
public int Type { get; set; }
}
[DataContract(Name = "employeeData")]
public class EmployeeData
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "vacations")]
public List<Vacation> Vacations { get; set; }
}
[DataContract(Name = "departmentData")]
public class DepartmentData
{
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "office")]
public string Office { get; set; }
[DataMember(Name = "employees")]
public List<EmployeeData> Employees { get; set; }
}
Собрали данные о пользователе при помощи KeywordQuery, а данные об отпусках при помощи несложного CAML-запроса. Сделали кастомную верстку и связке jQuery с KnockOut реализовали требуемый интерфейс.
Результат
В итоге нам удалось полностью удовлетворить потребности заказчика в автоматизации бизнес-процесса учета отпусков. Теперь для сотрудников компании-заказчика сборы в отпуск начинаются с легкого, приятного процесса заполнения заявки, а не беготней с бумажками. Также у них всегда в свободном доступе имеется информация о количестве отпускных дней и информация о статусе поданных заявок.
Руководители сотрудников имеют полную, оформленную информацию о графике отпусков своих подчиненных, могут эффективней планировать деятельность подразделения и, что немаловажно, собственные отпуска.
Но больше всех выиграл, конечно, отдел кадров: во-первых, сервис подачи заявок на отпуск — это защита от случайных ошибок в цифрах, во-вторых, он существенно сокращает бумажный документооборот, который обычно неизбежно разрастается вокруг каждого отпускника, в-третьих, интеграция с БОСС-Кадровик позволяет кадровому отделу эффективно вести аналитический учет затрат на персонал и оплату труда.
Автор: eastbanctech