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

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

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

    Итак, представим средних размеров организацию, которая разбросана по территории нашей большой страны, а именно такие компании выбирают 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-портал действительно эффективным рабочим ресурсом.
    True Engineering
    Лаборатория технологических инноваций

    Комментарии 6

      0
      Ребята это уже ни для кого не новость. Называется процессный подход к организации деятельности в организации. Кроме SharePoint уже есть разный софт для реализации таких возможностей. Например elma-bpm.ru
        0
        Да такие системы прекрасно работают, но лишь до того момента когда нужно что-то дописать свое, вот тогда начинается АД с лицензиями, разработчиками и сроками (а в некоторых случаях еще и API не дают). А тут все свое.
          0
          Мы сразу очертили ситуацию — заказчик выбрал для внутрикорпоративной коммуникации портал на SharePoint. Согласитесь, множить ИТ-продукты в компании не сильно правильное решение. Поэтому они и не рассматривали коробки и обратились к нам.
            0
            Вы отлично справились со своей задачей. А что делать этому заказчику, когда понадобятся другие процессы или изменить существующие? Правильно, опять обращаться к вам. Накладные расходы, как мне кажется, возрастают по мере их хотелок. Но это их путь...
              0
              Хорошо, допустим есть система (Elma, lement.pro, bitrix24 и прочие), и есть система доступа АСКД (допустим rusGuard,msscom) и нужно на портале показывать отчеты по проходам уходам, как это реализовать стандартными средствами?
              Ответ: никак.
              Все равно придется связываться с програмистами системы и просить их сделать эту связку. По как правило конторы редко идут на доработки или идут но ты по сути оплачиваешь им модуль в их портфолио.
          0
          Обычно, такие системы как я привел в пример имеют готовый функционал из коробки и не требуют серьезной доработки. Хватает вполне конструктора или дизайнера, чтобы справляться с необходимыми задачами для настройки. Тут все естественно зависит от требований заказчика. Да, в кодинге таких систем действительно не все так гладко.

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

          Самое читаемое