Формирование сводного отчета о фактическом потреблении ресурсов и их стоимости

Формирование сводного отчета о фактическом потреблении ресурсов и их стоимости#

Для формирования сводного отчета необходимо выполнить следующие действия:

  1. Выбрать пункт основного меню слева Провайдер — Компании.

  2. Выбрать компанию и нажать на кнопку Изменить.

  3. В окрывшемся окне Редактирование компании в поле Шаблон акта отображается наименование шаблона, заданного для печати. Если шаблон не задан, выбрать шаблон Русский акт или УПД из списка шаблонов.

  4. Перейти в пункт основного меню слева Настройки — Шаблоны документов.

  5. В открывшемся окне Шаблоны документов выбрать ранее заданный шаблон и нажать на кнопку Изменить.

  6. В открывшемся окне Редактирование шаблона документа очистить содержание блока Содержимое шаблона документа и вставить нижеприведенный фрагмент кода.

    Фрагмент кода
    <%
    /* ------------------------------------------------------------------
    Отчет о фактическом потреблении ресурсов и их стоимости (сводный)
    ------------------------------------------------------------------ */
    
    function findu(data) {
        return (data && data != '') ? data : '';
    }
    
    function obj(v) {
        if (v && v.constructor === Array) {
            for (var i = v.length - 1; i >= 0; i--) {
                if (v[i] && typeof v[i] == 'object') { return v[i]; }
            }
            return {};
        }
        return (v && typeof v == 'object') ? v : {};
    }
    
    function rusdate(date) {
        if (!date || date == '') { return ''; }
        var month = {
            '01': 'января', '02': 'февраля', '03': 'марта',    '04': 'апреля',
            '05': 'мая',    '06': 'июня',    '07': 'июля',     '08': 'августа',
            '09': 'сентября','10': 'октября','11': 'ноября',   '12': 'декабря'
        };
        var c = date.split(' ')[0].split('-');
        return c[2] + ' ' + month[c[1]] + ' ' + c[0] + ' г.';
    }
    
    function rusdatemonth(date) {
        if (!date || date == '') { return ''; }
        var month = {
            '01': 'январь', '02': 'февраль', '03': 'март',    '04': 'апрель',
            '05': 'май',    '06': 'июнь',    '07': 'июль',     '08': 'август',
            '09': 'сентябрь','10': 'октябрь','11': 'ноябрь',   '12': 'декабрь'
        };
        var c = date.split(' ')[0].split('-');
        return month[c[1]];
    }
    
    function rusdatetime(date) {
        if (!date || date == '') { return ''; }
        var parts = date.split(' ');
        var c = parts[0].split('-');
        return c[2] + '.' + c[1] + '.' + c[0] + (parts[1] ? ' ' + parts[1] : '');
    }
    
    function money(n) {
        var v = parseFloat(n);
        if (isNaN(v)) { return '-'; }
        var s = v.toFixed(2).split('.');
        return s[0].replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ',' + s[1];
    }
    
    function num(n) {
        var v = parseFloat(n);
        return isNaN(v) ? '-' : String(v);
    }
    
    function itemList(invoice) {
        if (!invoice.items || !invoice.items.item) { return []; }
        return [].concat(invoice.items.item);
    }
    
    function serviceName(item) {
        var name = String(item.name).replace(/\s+/g, ' ').trim();
        if (name.indexOf(' - ') > -1) { return name.split(' - ')[0].trim(); }
        var m = name.match(/^([^#(,]+)/);
        return m ? m[1].trim() : name;
    }
    
    function resourceName(item) {
        var name = String(item.name).replace(/\s+/g, ' ').trim();
        var tail = name.match(/\(([^()]*#\d+[^()]*)\)\s*$/);
        if (tail) { return tail[1].trim(); }
        var body = name.indexOf(' - ') > -1 ? name.split(' - ').slice(1).join(' - ') : name;
        body = body.replace(/\s*\([^()]*\)/g, ' ');
        body = body.split(',')[0];
        return body.replace(/\s+/g, ' ').trim();
    }
    
    function resourceId(item) {
        var name = String(item.name).replace(/\s+/g, ' ').trim();
        var m = name.match(/#\s*(\d+)/);
        if (m) { return m[1]; }
        m = name.match(/\(([^()]+)\)/);
        return m ? m[1].trim() : '-';
    }
    
    function vdcRef(item) {
        var name = String(item.name).replace(/\s+/g, ' ').trim();
        var ref = '';
        var paren = name.match(/\(([^()]*#\s*\d+[^()]*)\)/);
        if (paren) {
            ref = paren[1].trim();
        } else {
            ref = name.split(',')[0].trim();
        }
        var m = ref.match(/#\s*(\d+)/);
        var id = m ? m[1] : '';
        var title = ref.replace(/\s*#\s*\d+.*$/, '').trim();
        return { id: id, name: title, ref: ref };
    }
    
    function isAddon(item) {
        return /дополнительн/i.test(String(item.name));
    }
    
    function addonMeasure(item) {
        var name = String(item.name).replace(/\s+/g, ' ').trim();
        var idx = name.indexOf(' - ');
        if (idx < 0) { return null; }
        var after = name.slice(idx + 3).split('(')[0].trim();  /* "112 ГиБ" */
        var m = after.match(/^([\d]+(?:[.,][\d]+)?)\s*(.*)$/);
        if (!m) { return null; }
        var qty = parseFloat(m[1].replace(',', '.'));
        return { qty: isNaN(qty) ? null : qty, unit: (m[2] || '').trim() };
    }
    
    function unitName(item) {
        if (isAddon(item)) {
            var a = addonMeasure(item);
            if (a && a.unit) { return a.unit; }
        }
        var name = String(item.name);
        var m = name.match(/,\s*([^,]+)\s*$/);
        if (m && !/#/.test(m[1])) { return m[1].trim(); }
        return 'шт.';
    }
    
    
    function itemCost(item) {
        return parseFloat(item.notaxamount) || 0;
    }
    
    function itemQty(item) {
        /* доп. услуга -> количество из наименования, иначе из <quantity> */
        if (isAddon(item)) {
            var a = addonMeasure(item);
            if (a && a.qty != null && a.qty != 0) { return a.qty; }
        }
        var q = parseFloat(item.quantity);
        return (isNaN(q) || q == 0) ? 1 : q;
    }
    
    function itemPrice(item) {
        return itemCost(item) / itemQty(item);
    }
    
    var VDC_TITLES = {
        'vds':      'Виртуальный дата-центр (VDC)',
        'vdc':      'Виртуальный дата-центр (VDC)'
    };
    
    function buildVdcList(invoice) {
        var items = itemList(invoice);
        var order = [], map = {};
    
        for (var i = 0; i < items.length; i++) {
            var it = items[i];
            var key, title, id;
    
            if (findu(it.vdc_id) != '') {
                key   = 'id:' + it.vdc_id;
                id    = String(it.vdc_id);
                title = findu(it.vdc_name) != '' ? it.vdc_name : id;
            } else {
                var r = vdcRef(it);
                if (r.id != '') {
                    key   = 'num:' + r.id;
                    id    = r.id;
                    title = r.name != '' ? r.name : ('№ ' + r.id);
                } else {
                    var st = findu(it.servicetype) != '' ? String(it.servicetype) : 'other';
                    key   = 'type:' + st;
                    id    = '-';
                    title = VDC_TITLES[st] || st;
                }
            }
    
            if (!map[key]) {
                map[key] = { name: title, id: id, items: [], cost: 0 };
                order.push(key);
            }
            map[key].items.push(it);
            map[key].cost += itemCost(it);
        }
    
        var res = [];
        for (var j = 0; j < order.length; j++) { res.push(map[order[j]]); }
        return res;
    }
    
    /* --- Сводка по услугам ------------------------------------------------ */
    function buildServiceList(invoice) {
        var items = itemList(invoice);
        var order = [], map = {};
    
        for (var i = 0; i < items.length; i++) {
            var it = items[i];
            var key = serviceName(it);
            if (!map[key]) {
                map[key] = { name: key, qty: 0, cost: 0, unit: unitName(it) };
                order.push(key);
            }
            map[key].qty  += itemQty(it);
            map[key].cost += itemCost(it);
        }
    
        var res = [];
        for (var j = 0; j < order.length; j++) {
            var s = map[order[j]];
            s.price = s.qty ? s.cost / s.qty : 0;
            res.push(s);
        }
        return res;
    }
    
    function contractNumber(invoice) {
        if (invoice.contracts && invoice.contracts.contract) {
            var list = [].concat(invoice.contracts.contract), out = [];
            for (var i = 0; i < list.length; i++) {
                out.push(list[i].number + (findu(list[i].signdate) != '' ? ' от ' + rusdate(list[i].signdate) : ''));
            }
            if (out.length) { return out.join(', '); }
        }
        return findu(invoice.contract) != '' ? invoice.contract : '-';
    }
    
    var customer = obj(invoice.customer);
    var company  = obj(invoice.company);
    
    var services = buildServiceList(invoice);
    var vdcs     = buildVdcList(invoice);
    
    var servicesTotal = 0;
    for (var si = 0; si < services.length; si++) { servicesTotal += services[si].cost; }
    
    var vdcTotal = 0;
    for (var vi = 0; vi < vdcs.length; vi++) { vdcTotal += vdcs[vi].cost; }
    
    var grandTotal = (findu(invoice.notaxamount) != '' && invoice.notaxamount != '-')
        ? parseFloat(invoice.notaxamount)
        : servicesTotal;
    %>
    <style media="screen, print" type="text/css">
        @page { margin: 0.5cm 0.5cm 0.5cm 0.5cm; }
    
        #res-report .page-a4-landscape { margin: 0px; width: 29.7cm; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 8pt; color: #000; }
    
        #res-report table.rep { border-collapse: collapse; clear: left; margin: 0px; width: 100%; }
        #res-report table.rep.grid { border: solid 1px #000; }
    
        #res-report td { font-size: 8pt; padding: 2px 3px 2px 4px; vertical-align: top; }
        #res-report th { font-size: 8pt; padding: 2px 3px; text-align: center; border: solid 1px #000; font-weight: bold; }
    
        #res-report td.b   { border: solid 1px #000; }
        #res-report td.bt  { border-top: solid 1px #000; }
        #res-report td.br  { border-right: solid 1px #000; }
        #res-report td.bb  { border-bottom: solid 1px #000; }
        #res-report td.bl  { border-left: solid 1px #000; }
    
        #res-report td.tright  { text-align: right; }
        #res-report td.tcenter { text-align: center; }
        #res-report td.bold    { font-weight: bold; }
        #res-report td.small   { font-size: 6pt; }
    
        #res-report .title    { font-size: 10pt; font-weight: bold; text-align: center; padding: 6px 0px 10px 0px; }
        #res-report .caption  { font-size: 10pt; font-weight: bold; text-align: center; padding-bottom: 8px; }
        #res-report .hdr-lbl  { text-align: right; white-space: nowrap; padding-right: 4px; }
        #res-report .hdr-val  { text-align: left; }
        #res-report .box      { border: solid 1px #000; min-width: 8cm; height: 0.55cm; }
        #res-report .section  { padding: 10px 0px 3px 0px; font-weight: bold; }
        #res-report .spacer   { height: 0.5cm; }
    </style>
    
    <div id="res-report">
        <center>
            <div class="page-a4-landscape">
    
                <div class="caption">Макет отчета (сводный)</div>
    
                <!-- ================= Шапка отчета ================= -->
                <table class="rep grid">
                    <tr>
                        <td colspan="2" class="title">
                            Отчет о фактическом потреблении ресурсов и их стоимости
                            за&nbsp;<%= findu(invoice.cdate) != '' ? rusdatemonth(invoice.cdate) : 'месяц' %>
                            <%= findu(invoice.cdate) != '' ? invoice.cdate.split('-')[0] : '20__' %>&nbsp;(сводный)
                        </td>
                    </tr>
                    <tr>
                        <td class="hdr-lbl" style="width: 50%;">Аккаунт:</td>
                        <td class="hdr-val"><%= findu(customer.account) %></td>
                    </tr>
                    <tr>
                        <td class="hdr-lbl">Отчетный период:</td>
                        <td class="hdr-val"><%= rusdatetime(invoice.fromdate) %> &ndash; <%= rusdatetime(invoice.todate) %></td>
                    </tr>
                    <tr>
                        <td class="hdr-lbl">Компания:</td>
                        <td class="hdr-val"><%= findu(customer.name) %></td>
                    </tr>
                    <tr>
                        <td class="hdr-lbl">Номер договора:</td>
                        <td class="hdr-val"><%= contractNumber(invoice) %></td>
                    </tr>
                    <tr>
                        <td class="hdr-lbl">Дата/время формирования отчета:</td>
                        <td class="hdr-val"><%= rusdatetime(invoice.cdate) %></td>
                    </tr>
                    <tr>
                        <td class="hdr-lbl bold">Итого за начислено за период, руб. без НДС:</td>
                        <td class="hdr-val">
                            <table class="rep"><tr><td class="b bold" style="width: 8cm;"><%= money(grandTotal) %></td><td></td></tr></table>
                        </td>
                    </tr>
                </table>
    
                <div class="spacer"></div>
    
                <!-- ================= Сводка по услугам ================= -->
                <table class="rep grid">
                    <tr>
                        <th style="width: 40%;">Наименование услуги</th>
                        <th style="width: 9%;">Цена</th>
                        <th style="width: 9%;">Количество</th>
                        <th style="width: 7%;">Ед.изм.</th>
                        <th style="width: 13%;">Стоимость без НДС, руб.</th>
                    </tr>
                    <% for (var i = 0; i < services.length; i++) { %>
                        <tr>
                            <td class="b"><%= services[i].name %></td>
                            <td class="b tright"><%= money(services[i].price) %></td>
                            <td class="b tcenter"><%= num(services[i].qty) %></td>
                            <td class="b tcenter"><%= services[i].unit %></td>
                            <td class="b tright"><%= money(services[i].cost) %></td>
                        </tr>
                    <% } %>
                    <tr>
                        <td class="b tright bold" colspan="4">Итого по услугам:</td>
                        <td class="b tright bold"><%= money(servicesTotal) %></td>
                    </tr>
                </table>
    
                <div class="spacer"></div>
    
                <!-- ================= Сводка по дата-центрам (VDC) ================= -->
                <table class="rep grid">
                    <tr>
                        <th style="width: 40%;">Наименование дата-центра (VDC)</th>
                        <th style="width: 32%;">Идентификатор дата-центра (VDC)</th>
                        <th style="width: 14%;">&nbsp;</th>
                        <th style="width: 14%;">Стоимость без НДС, руб.</th>
                    </tr>
                    <% for (var v = 0; v < vdcs.length; v++) { %>
                        <tr>
                            <td class="b"><%= vdcs[v].name %></td>
                            <td class="b tcenter"><%= vdcs[v].id %></td>
                            <td class="b"></td>
                            <td class="b tright"><%= money(vdcs[v].cost) %></td>
                        </tr>
                    <% } %>
                    <tr>
                        <td class="b tright bold" colspan="3">Итого по дата-центрам (VDC):</td>
                        <td class="b tright bold"><%= money(vdcTotal) %></td>
                    </tr>
                </table>
    
                <!-- ================= Детализация по каждому VDC ================= -->
                <% for (var v = 0; v < vdcs.length; v++) { %>
                    <% var vitems = vdcs[v].items; %>
                    <table class="rep">
                        <tr>
                            <td class="section"><%= v + 1 %>. Детализация услуг по дата-центру (VDC): <%= vdcs[v].name %> (<%= vdcs[v].id %>)</td>
                        </tr>
                    </table>
                    <table class="rep grid">
                        <tr>
                            <th style="width: 26%;">Наименование услуги</th>
                            <th style="width: 24%;">Наименование ресурса</th>
                            <th style="width: 14%;">Идентификатор ресурса</th>
                            <th style="width: 9%;">Цена</th>
                            <th style="width: 9%;">Количество</th>
                            <th style="width: 7%;">Ед.изм.</th>
                            <th style="width: 13%;">Стоимость без НДС, руб.</th>
                        </tr>
                        <% for (var i = 0; i < vitems.length; i++) { %>
                            <tr>
                                <td class="b"><%= serviceName(vitems[i]) %></td>
                                <td class="b"><%= resourceName(vitems[i]) %></td>
                                <td class="b tcenter"><%= resourceId(vitems[i]) %></td>
                                <td class="b tright"><%= money(itemPrice(vitems[i])) %></td>
                                <td class="b tcenter"><%= num(itemQty(vitems[i])) %></td>
                                <td class="b tcenter"><%= unitName(vitems[i]) %></td>
                                <td class="b tright"><%= money(itemCost(vitems[i])) %></td>
                            </tr>
                        <% } %>
                        <tr>
                            <td class="b tright bold" colspan="6">Итого по дата-центру (VDC):</td>
                            <td class="b tright bold"><%= money(vdcs[v].cost) %></td>
                        </tr>
                    </table>
                <% } %>
    
            </div>
        </center>
    </div>
    
  7. Нажать на кнопку Сохранить.

  8. Перейти в пункт основного меню слева Финансы — Акт вып. работ.

  9. В открывшемся окне Акты выполненных работ выбрать акт и нажать на кнопку Печать или Скачать.

После выполнения вышеописанных действий откроется отчет следующего вида:

../../../_images/resource_report.png