Как стать автором
Обновить
0
True Engineering
Лаборатория технологических инноваций

Портал на службе бухгалтерии или автоматизация авансовых отчетов

Время на прочтение8 мин
Количество просмотров12K
Всем привет! Сегодня мы решили поделиться результатами небольшого, но важного проекта, как это часто бывает очень простые с первого взгляда вещи решают сложные задачи, которые дают существенную эффективность в повседневной жизни.

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

Итак, представим средних размеров организацию, которая разбросана по территории нашей большой страны, а именно такие компании выбирают SharePoint в качестве корпоративного портала. Самый обычный сотрудник собирается поехать в командировку и вот тут начинается самое интересное: заполни заявку, согласуй у руководителя, передай в службу персонала, получи аванс на расходы, съезди в командировку, заполни отчет, сдай его в бухгалтерию.

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



Цифры из реальной жизни – сотрудники нашего заказчика совершают 20 000 командировок в год, вы скажете не может быть такого? это не реально? Но посмотрим на эту цифру по-другому – эти 20 000 командировок совершают 4 000 человек. То есть в среднем по 5 командировок на человека за год.

Вполне реальная цифра.

НО если посчитать время, которое траться на поддержку данного процесса, то выходит 200 000 часов… Двести тысяч часов, КАРЛ!

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

И как это можно автоматизировать?


Мы придумали модуль на корпоративный портал заказчика, интегрированный с используемыми в компании системами 1С и БОСС-кадровик, который позволяет сотрудникам осуществлять всю процедуру согласования командировок и отчетности по ним в электронном виде.

И процедура согласования командировок стала выглядеть вот так:



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

Звучит неплохо, правда? Сейчас расскажем, как мы этого добились:

1) UI/UX-концепция


Первым шагом мы проработали UI/UX – прорисовали простые, последовательные, логичные и удобные экраны решения, а затем перешли к реализации.

Вот так выглядят пользовательские экраны:
Список командировок и добавление новой заявки


Заявка на командировку


Форма согласования заявки руководителем


Форма авансового отчета о командировке


А в таком виде авансовый отчет видит руководитель и бухгалтерия


2) Прикрутка к SharePoint


Далее начали прикручивать все к порталу на SharePoint. Портал на шарике всем хорош, кроме того, что в нем нет транзакционности. Но на то мы и спецы, чтобы решать нестандартные задачи.

Мы научились обходить ее так:
1. Например, для хранения списка городов, целей и дат добавили в список SharePoint поле типа Note:
<Field Type="Note" DisplayName="Destinations" ID="{0aa6522b-ce74-4148-9b54-b9b7cd218098}" Name="Destinations" NumLines="6" RichText="FALSE" />


2. При сохранении айтема в это поле сохранили сериализованный JSON:
requestListItem[Constants.Lists.Requests.Destinations] = JsonHelper.JsonSerializer(destinations); 


3. Для получения данных о городах, целях и датах при просмотре десериализовываем обратно:
Destinations = JsonHelper.JsonDeserialize<RequestDestination[]>((string)item[Constants.Lists.Requests.Destinations])

Сим-салабим и порчу с Шарепоинта сняли.

3) Интеграция с БОСС-кадровик и 1С


С системами БОСС-кадровик и «1С Бухгалтерия» проинтегрировались через базы SQL с помощью EntityFramework. Но это задача более-менее стандартная и зависит от конкретных конфигураций. Уверены, вы и без нас знаете, что делать.

4) Мобильная версия модуля


И, наконец, сделали мобильную версию для оформления заявок на командировки прямо на лету.

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


Итак, как мы обеспечили мобильность.

JavaScript решили использовать единый, а переключение между мобильным представлением и основным сделали с помощью средств SharePoint:

1. Создали канал устройств:


2. Сделали переключение MasterPage в зависимости от канала.

3. На aspx странице сделали отображение и подключение скриптов и css в зависимости от канала:
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
    <PublishingWebControls:DeviceChannelPanel ID="DefaultPanel" runat="server" IncludedChannels="Default">
        <defaultMain:Header runat="server"></defaultMain:Header>
    </PublishingWebControls:DeviceChannelPanel>
        
    <PublishingWebControls:DeviceChannelPanel runat="server" IncludedChannels="Mobile">
        <mobileMain:Header runat="server"></mobileMain:Header>
    </PublishingWebControls:DeviceChannelPanel>    
    <meta name="CollaborationServer" content="SharePoint Team Web Site" />
</asp:Content>

<asp:Content ID="Content8" ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <PublishingWebControls:DeviceChannelPanel ID="DeviceChannelPanel1" runat="server" IncludedChannels="Default">
        <defaultMain:Page runat="server"></defaultMain:Page>
    </PublishingWebControls:DeviceChannelPanel>
        
    <PublishingWebControls:DeviceChannelPanel runat="server" IncludedChannels="Mobile">
        <mobileMain:Page runat="server"></mobileMain:Page>
    </PublishingWebControls:DeviceChannelPanel>      
</asp:Content>


Для мобильного и основного отображения сделали View, которые похожи структурой, но отличаются разметкой. Ниже примеры списков заявок.

1. Основная:
            <div class="list-block">
                <div class="list-header">
                    <h2 class="text-uppercase color-blue inline-block">Твои командировки</h2>
                    <button type="button" class="new-trip-button" data-bind="click: editRequest.newRequest">Новая командировка</button>
                    <!-- ko if: $root.user().hasDebt -->
                    <div class="alert alert-danger text-center">
                        <span class="glyphicon glyphicon-alert pull-left"></span>
                        <span>У тебя есть незакрытый аванс в размере <span data-bind="text: $root.user().debt"></span> RUB. Ты не можешь взять новый аванс, пока не закроешь старый.</span>
                    </div>
                    <!-- /ko -->
                    <!-- ko if: $root.user().debt < 0 -->
                    <div class="alert alert-danger text-center cred">
                        <span class="glyphicon glyphicon-alert pull-left"></span>
                        <span>У тебя есть сумма к возмещению в размере <span data-bind="text: -$root.user().debt"></span> RUB.</span>
                    </div>
                    <!-- /ko -->
                </div>
                <div class="list-body" data-bind="with: requests">
                    <table class="table">
                        <thead>
                            <tr class="text-muted">
                                <th></th>
                                <th></th>
                                <th></th>
                                <th></th>
                                <th>Город</th>
                                <th>Статус</th>
                                <th>Цель</th>
                                <th class="text-right">Сумма</th>
                            </tr>
                        </thead>
                        <tbody>
                            <!-- ko foreach: items -->
                            <tr data-bind="css: { 'travel-request': needAnswer }">
                                <td class="status-block">
                                    <small class="status travel-request">заявка на командировку</small>
                                    <strong class="link-blue-lg" data-bind="text: period, click: viewTripRequest"></strong>
                                </td>
                                <td>
                                    <!-- ko if: questions -->
                                    <i class="travel-request-icon" data-bind="click: $parent.conversation"></i>
                                    <!-- /ko -->
                                    <!-- ko if: statusID == 14 -->
                                    <i class="glyphicon icon-print" data-bind="click: $parent.print"></i>
                                    <!-- /ko -->
                                </td>
                                <td>
                                    <!-- ko if: forManager -->
                                    <i class="for-manager-icon"></i>
                                    <!-- /ko -->
                                </td>
                                <td>
                                    <!-- ko if: advanceReportStatus -->
                                    <i class="pink-doc-icon" data-bind="click: viewAdvanceReport"></i>
                                    <!-- /ko -->
                                </td>
                                <td>
                                    <span data-bind="text: city"></span>
                                </td>
                                <td>
                                    <div data-bind="text: statusDescription"></div>
                                </td>
                                <td>
                                    <div data-bind="text: goal"></div>
                                </td>
                                <td class="text-right">
                                    <!-- ko if: advance.length > 0 -->
                                    <strong class="color-red" data-bind="text: advance, css: { 'color-red': statusID != 15 }"></strong>
                                    <!-- /ko -->
                                    <!-- ko if: advance.length == 0 -->
                                    <span class="text-muted">Без аванса</span>
                                    <!-- /ko -->
                                </td>
                            </tr>
                            <!-- /ko -->
                        </tbody>
                    </table>
                </div>
            </div>


2. Мобильная:
<div class="list-block last" data-bind="with: requests">
                <div class="list-header">
                    <h1 class="color-blue text-uppercase inline-block">Твои командировки</h1>
                    <button type="button" class="new-trip-button" data-bind="click: $parent.editRequest.newRequest"></button>
                    <!-- ko if: $root.user().hasDebt -->
                    <div class="alert alert-danger">
                        <span class="glyphicon glyphicon-alert"></span>
                        <span>У тебя есть незакрытый аванс в размере <span data-bind="text: $root.user().debt"></span> RUB. Ты не можешь взять новый аванс, пока не закроешь старый.</span>
                    </div>
                    <!-- /ko -->
                    <!-- ko if: $root.user().debt < 0 -->
                    <div class="alert alert-danger cred">
                        <span class="glyphicon glyphicon-alert"></span>
                        <span>У тебя есть сумма к возмещению в размере <span data-bind="text: -$root.user().debt"></span> RUB.</span>
                    </div>
                    <!-- /ko -->
                </div>
                <!-- ko foreach: items -->
                <div class="list-content">
                    <div class="row">
                        <div class="state-block">
                            <div class="form-group">
                                <small class="state bg-green color-white text-lowercase">Заявка на командировку</small>
                            </div>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col-xs-8">
                            <div class="form-group">
                                <span class="text-large" data-bind="text: statusDescription"></span>
                            </div>
                        </div>
                        <div class="col-xs-4">
                            <div class="form-group text-right">
                                <!-- ko if: advance.length > 0 -->
                                <span class="color-red text-large" data-bind="text: advance"></span>
                                <!-- /ko -->
                                <!-- ko if: advance.length == 0 -->
                                <span class="text-muted text-large">Без аванса</span>
                                <!-- /ko -->
                            </div>
                        </div>
                    </div>
                    <div class="row list-foot">
                        <div class="col-xs-4">
                            <h4><a href="javascript://" class="link link-blue" data-bind="text: period, click: viewTripRequest"></a></h4>
                        </div>
                        <div class="col-xs-4">
                            <h4 class="icon" data-bind="text: city, css: { 'icon-rus': tripType[0] == 'По России', 'icon-non-rus': tripType[0] == 'Заграничная' }"></h4>
                        </div>
                        <div class="col-xs-4">
                            <!-- ko if: advanceReportStatus -->
                            <span class="icon-attach" data-bind="click: viewAdvanceReport"></span>
                            <!-- /ko -->
                            <!-- ko if: questions -->
                            <i class="travel-request-icon" data-bind="click: $parent.conversation"></i>
                            <!-- /ko -->
                            <!-- ko if: advanceReportStatus == 'Авансовый отчет утвержден' -->
                            <i class="icon-print" data-bind="click: $parent.print"></i>
                            <!-- /ko -->

                        </div>
                    </div>
                </div>
                <!-- /ko -->
            </div>


Вуаля!

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

При этом мы не изобретали велосипед, а мягко встроились в существующую ИТ-экосистему заказчика. Компания давно и надолго выбрала стратегию работы с корпоративным порталом как основным инструментом коммуникации с сотрудниками. И мы с радостью помогаем им делать SharePoint-портал действительно эффективным рабочим ресурсом.
Теги:
Хабы:
+8
Комментарии6

Публикации

Изменить настройки темы

Информация

Сайт
www.trueengineering.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия

Истории