Создание отчётов
В ExpertBilling 1.4 внедрена принципиально новая система работы с отчётами. Сейчас вы сами сможете задать их внешний вид, а, также, создавать новые виды отчётов и документов. Из тела отчёта можно обратиться к rpc серверу, выполнить выборки из базы данных и отформатировать их в удобном для вас виде.
Свои отчёты вы можете сделать интерактивными. К примеру, перед его формированием, предложить пользователю указать необходимый период времени или выбрать другие параметры.
Для генерации отчётов необходимо создать шаблоны отчётов. Сделать это можно в меню "Главное меню"->"Шаблоны документов"
В левой части окна расположены категории отчётов. Часть отчётов предзадана и мы не рекомендуем их удалять. Чтобы создать новый отчёт необходимо выбрать первый пункт в дереве типов шаблонов "-- Новый шаблон --" и указать параметры шаблона справа. Выберите тип шаблона, его имя и задайте тело шаблона. С помощью кнопки "Сохранить" на панели инструментов сохраните его. Кнопка "Предпросмотр" служит для предварительного просмотра созданного шаблона
Содержание
- 1 Краткая информация по синтаксису шаблонов
- 2 Доступ к rpc серверу из отчёта
- 3 Пример отчёта
- 4 Отчёт по vpn сессиям абонентов
- 5 Встроенные документы
- 6 Отчёты
- 6.1 Отчёт по текущему списку активированных подключаемых услуг
- 6.2 Отчёт по списку активированных услуг за период
- 6.3 Отчёт по изменениям тарифного плана за период
- 6.4 Отчёт по списку занятых IP адресов
- 6.5 Отчёт по прибыли в день
- 6.6 Отчёт по прибыли с группировкой по месяцам
- 6.7 Занятые порты на коммутаторах
- 6.8 Общий отчёт за предыдущий месяц
- 7 Документация по синтаксису шаблонов
Краткая информация по синтаксису шаблонов
Обозначение переменных:
${value1} ${object.value}
Вызов функций и методов
${int(a)} ${object.method()}
Вычисления
${a+100} ${(1/2)+200**7}
Выполнение кода на языке python в теле отчёта
<% import datetime #получение текущего времени now = datetime.datetime.now() %>
Цикл for в теле отчёта
%for account in accounts: ${account.id}, ${account.username} %endfor
Получение списка доступных переменных и методов в объекте ${dir(object)}
Пример, демонстрирующий работу функции: Код:
<% #получаем учётную запись пользователя с id=100 account=connection.get_model(100, "billservice_account") %> ${dir(account)}
Результат: ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_asdict', '_fromTuple', '_toTuple', 'address', 'allow_expresscards', 'allow_ipn_with_block', 'allow_ipn_with_minus', 'allow_ipn_with_null', 'allow_vpn_block', 'allow_vpn_null', 'allow_webcab', 'assign_dhcp_block', 'assign_dhcp_null', 'assign_ipn_ip_from_dhcp', 'associate_pppoe_mac', 'associate_pptp_ipn_ip', 'balance_blocked', 'ballance', 'city', 'city_id', 'comment', 'contactperson', 'contactperson_phone', 'contract', 'created', 'credit', 'delete', 'disabled_by_limit', 'elevator_direction', 'email', 'entrance', 'entrance_code', 'fullname', 'get', 'hasattr', 'house', 'house_bulk', 'house_id', 'id', 'ipn_added', 'ipn_ip_address', 'ipn_ipinuse_id', 'ipn_mac_address', 'ipn_speed', 'ipn_status', 'isnull', 'last_balance_null', 'nas_id', 'passport', 'passport_date', 'passport_given', 'password', 'phone_h', 'phone_m', 'postcode', 'private_passport_number', 'region', 'room', 'row', 'save', 'status', 'street', 'street_id', 'suspended', 'systemuser_id', 'username', 'vlan', 'vpn_ip_address', 'vpn_ipinuse_id', 'vpn_speed']
Видим все доступные переменные из учётной записи пользователя. Чтобы вставить в тело отчёта телефонный номер абонента, нужно написать ${account.phone_m}, баланс - ${account.ballance} и т.д.
Доступ к rpc серверу из отчёта
В самом начале работы отчёта уже присутствует переменная connection, которая хранит в себе подключение к rpc серверу. Доступ к rpc серверу понадобится вам в том случае, если вы захотите получить доступ к данным, хранящимся в базе данных. Помимо доступа к данным вам будут доступны другие функции rpc сервера. Такие как сброс активной сессии на севрере доступа, активация/деактивация пользователя на сервере доступа и многие другие. Основные функции
- sql() - выполнение запроса. Функция возвращает результат вопроса в виде списка объектов записей
- get_model(id, table_name) - получить конкретную запись из указанной таблицы по её id
- get_models(table_name) - получить список записей из указанной таблицы
Пример:
<% accounts = connection.get_models("billservice_account") %> <table> <tr> <td>id</td><td>username</td> </tr> %for account in accounts: <tr> <td>${account.id}</td><td>${account.username}</td> </tr> %endfor </table>
В примере производится выборка всех аккаунтов и вывод их id и имён пользователей в виде таблицы.
Пример отчёта
Этот отчёт запрашивает период времени и потом показывает суммы, на которые был пополнен баланс у каждого пользователя.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <% import os from CustomForms import PeriodForm child = PeriodForm(realm='ostatok') if child.exec_()!=1: return %> <% accounts = connection.get_models("billservice_account") %> <img src="${os.path.abspath("images/reports/header.png")}"> <center>Пополнения баланса за период <strong>С ${child.start_date}</strong> по <strong>${child.end_date}</strong></center> <table border=1 width=100%> <tr> <td>Имя пользователя</td><td>ФИО</td><td>Сумма</td> </tr> % for account in accounts: <% account_summ = connection.sql(r"SELECT sum(summ) FROM billservice_transaction WHERE account_id=%s and created between '%s' and '%s';" % (account.id, child.start_date, child.end_date))[0].sum or 0 %> <tr> <td>${account.username}</td><td>${account.fullname}</td><td>${account_summ}</td> </tr> % endfor </body> </html>
Отчёт по vpn сессиям абонентов
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <% import os from CustomForms import PeriodForm child = PeriodForm(realm='ostatok') if child.exec_()!=1: return %> <% sessions = connection.sql("""SELECT account_id, count(*) as sess_acount, (SELECT username FROM billservice_account as acc WHERE acc.id=account_id) as username, sum(bytes_in) as bytes_in, sum(bytes_out) as bytes_out FROM radius_activesession WHERE date_start between '%s' and '%s' GROUP BY account_id""" % (child.start_date, child.end_date)) %> <img src="${os.path.abspath("images/reports/header.png")}"> <center>Объём трафика по vpn сессиям<strong>С ${child.start_date}</strong> по <strong>${child.end_date}</strong></center> <table border=1 width=100%> <tr> <td>Имя пользователя</td><td>Принято</td><td>Отправлено</td><td>Количество сессий абонента</td> </tr> % for session in sessions: <tr> <td>${session.username}</td><td>${int((session.bytes_in or 0)/(1024*1024))} МБ</td><td>${int((session.bytes_out or 0)/(1024*1024))} МБ</td><td>${session.sess_acount}</td> </tr> % endfor </body> </html>
Результат:
Встроенные документы
В системе существуют следующие виды шаблонов:
- Договор на подключение физ.лица
- Договор на подключение юр.лица
- Накладная на карты
- Шаблон карты
- Кассовый чек
- Информационное письмо
- Акт выполненных работ
- Счёт-фактура
- Отчёты
Договор на подключение физ.лица
Система позволяет создать несколько шаблонов этого типа. В тело шаблона передаётся список из идентификаторов аккаунтов,если выбрана печать договоров для списка пользователей, или список, состоящий из одного элемента - id аккаунта, если печать производится из карточки аккаунта.
Доступные пременные:
accounts - тип список connection - подключение к rpc серверу
Пример шаблона:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <% import os provider = connection.get_operator()[0] %> <img src="${os.path.abspath("images/reports/header.png")}"> %for account_id in accounts: <% account = connection.get_model(account_id, "billservice_account") %> ID: ${account.id}<br> Договор номер ${account.contract}<br> %endfor </body> </html>
Для печати договора вам потребуется заполнить информацию о провайдере в меню Help->About Operator Получить эту информацию в шаблоне можно с помощью такой конструкции
provider = connection.get_operator()[0]
Список доступных полей у переменной provider:
id organization - название unp okpo contactperson director phone postaddress uraddress email fax bank_id
Получить информацию о банке можно с помощью конструкции
bank = connection.get_model(provide.bank_id, "billservice_bankdata")
Доступные переменные:
id bank bankcode rs
Договор на подключение юр.лица
Печать договора на подключение юр. лица ничем не отличается от физ. лиц за тем исключением, что для получения доступа к расширенному набору полей юрлиц вам необходимо воспользоваться конструкцией:
org = connection.get("SELECT * FROM billservice_organization WHERE account_id=%s" % account.id)
Доступные переменные:
id account_id name uraddress okpo unp bank_id phone fax kpp kor_s
Получить банк вы можете способом, описанным выше в шаблоне для физ. лиц.
Накладная на карты
Этот тип документа описывает факт передачи партии карт доступа/экспресс-оплаты/HotSpot дилеру. В шаблон передаётся список объектов карт в переменной cards, объект с информацией о провайдере в переменной operator, объект дилера в переменной dealer, текущая дата в переменной created, cardcount - количество продаваемых карт, sum_for_pay - сумма к оплате, pay - сколько оплачено, discount - процент скидки, discount_sum - сумма скидки, paydeffer - отсрочка оплаты в днях.
Вид шаблона по-умолчанию:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body> <div style="width:100%; "> <div style="float:right "> <span style="font-weight:bold; ">Дилер</span><br> Организация: ${dealer.organization}<br> Директор: ${dealer.director}<br> Юр адрес: ${dealer.uraddress}<br> р/с: ${dealer.rs}<br> УНН: ${dealer.unp}<br> ОКПО: ${dealer.okpo}<br> Банк: ${dealer.bank}, код ${dealer.bankcode}<br> </div> <div style="float:left "> <span style="font-weight:bold; ">Оператор</span><br> Организация: ${operator.organization}<br> Директор: ${operator.director}<br> Юр адрес: ${operator.uraddress}<br> р/с: ${operator.rs} <br> УНН: ${operator.unp}<br> ОКПО: ${operator.okpo}<br> Банк: ${operator.bank}, Код ${operator.bankcode}<br> </div> </div> <div style="font-weight:bold; float:left; width:100%; text-align:center; margin-bottom:20px; margin-top:20px; "> Накладная от ${created} </div> <div style="clear:both "></div> <table border="1" align="center" style="width:100%"> <tr> <td>ID карты</td> <td>Серия</td> <td>Номинал</td> <td>Активировать С</td> <td>Активировать По</td> </tr> % for card in cards: <tr> <td>${card.id}</td> <td>${card.series}</td> <td>${card.nominal}</td> <td>${card.start_date}</td> <td>${card.end_date}</td> </tr> % endfor </table> Итого ${cardcount} карт на сумму: ${sum_for_pay}<br> Скидка: ${discount} на сумму ${discount_sum}<br> Оплачено: ${pay}<br> Оплатить до:${paydeffer} </body> </html>
Результат работы шаблона:
Шаблон карты
Шаблоны карт предоплаты используется при печати карт в момент передачи их дилеру. Учитывая то, что в одной поставке могут оказаться карты разных типов, номиналов и шаблонов, шаблон карты должен описывать только её внешний вид. Изначально в шаблон передаётся подключение к rpc серверу с переменной connection, переменная operator с объектом, содержащим информацию о провайдере, список карт в переменной cards.
Каждый объект типа card содержит следующие переменные:
id series pin sold nominal start_date end_date disabled created template_id account_id tarif_id nas_id login ip ipinuse_id
Предустановлен следующий шаблон карты по-умолчанию:
<div> <div>Карта пополнения на ${card.nominal} рублей</div> <div>Для пополнения счета зайдите на сайт http://192.168.12.2 и введите свой логин и пароль. Далее нажмите "Активация карточки". В появившемся окне введите: </div> <div>Серия: ${card.series}</div> <div>ID: ${card.id}</div> <div>PIN: ${card.pin}</div> <div>Активировать до ${card.end_date} </div> </div>
При печати к началу документа подставляется стандартный блок
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body>
и
</body></html>
В конце сгенерированного файла. Таким образом не нарушается структура html документа.
Важно!!! Если вы планируете использовать в шаблонах карт картинки - указывайте полный путь к картинке на файловой системе или пользуйтесь конструкцией
<% import os %> - поместить 1 раз в начале файла. <img src="${os.path.abspath("images/reports/header.png")}">
Где images/reports/header.png - путь к файлу с картинкой относительно папки с интерфейсом администратора.
Кассовый чек
Шаблон кассового чека используется при печати чеков из интерфейса кассира и интерфейса администратора. Важно!!! Не создавайте больше 1 шаблона кассового чека. В шаблон чека передаётся переменная connection, содержащая подключение к базе данных, переменная transaction с выполненным платежом, переменная account с пользователем, которому пополняли баланс.
По-умолчанию шаблон чека выглядит следующим образом:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> td{ FONT: 9px Times New Roman; } h1{ FONT: 9px Arial; } </style> </head> <body> <% tr=connection.get("SELECT id FROM billservice_transaction WHERE created='%s'" % transaction.created) tarif_name = connection.get(" SELECT get_tarif_name(%s, now()::timestamp without time zone)" % account.id).get_tarif_name %> <table align=center width="85%"> <tr> <td> <h1 align=center> <b> Квитанция об оплате услуг № ${tr.id} </b> </h1> <strong>Абонент:</strong> ${account.fullname} <br> <strong>Тарифный план:</strong> ${tarif_name} <br> <strong>Логин:</strong> ${account.username}<br> <strong>Сумма:</strong> ${transaction.summ}<br> <strong>Дата приема платежа:</strong> ${transaction.created}<br> </td> </tr> </table> </body> </html>
Доступные переменные в transaction:
bill account_id type_id approved tarif_id summ description created systemuser_id promise end_promise promise_expired accounttarif_id
Обратите внимание, что в базе данных пополнения хранятся с минусом, а списания с плюсом. Поэтому при выводе в чеке суммы, её нужно умножить на -1. ${-1*transaction.summ} Если вы хотите дополнить шаблон чека информацией о кассире, выполнившем платёж - его можно получить следующим выражением: <% user=connection.get_model(transaction.systemuser_id, "billservice_systemuser") %> Доступные переменные системного пользователя:
id username password last_ip last_login description created status host role text_password email job fullname address home_phone mobile_phone passport passport_details passport_number unp im
Информационное письмо
Информационное письмо используется при рассылке уведомлений о приближении баланса к нулю. Рассылку осуществляет скрипт sendmail.py, находящийся в папке с биллингом. Настройка скрипта производится в файле ebs_config.ini
Шаблон письма по-умолчанию:
--------------------------------------------------- Это сообщение сгенерировано биллинговой системой! --------------------------------------------------- Здравствуйте, ${account.username}. Уведомляем, что актуальный баланс Вашего лицевого счета составляет ${"%.2f" % account.ballance} руб. Размер кредита ${account.credit}. Пожалуйста, пополните баланс во избежание блокировки. --- ${operator.organization}
Акт выполненных работ
Счёт-фактура
Отчёты
Отчёт по текущему списку активированных подключаемых услуг
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; font-size:10pt; } table.sample th { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <% import os import datetime from dateutil.relativedelta import relativedelta now = datetime.datetime.now() %> <img src="${os.path.abspath("images/reports/header.png")}"> <% accountaddonservices=connection.sql("""select (select name from billservice_addonservice WHERE id=aas.service_id) as name, (select username from billservice_account WHERE id=aas.account_id) as username, (select username from billservice_subaccount WHERE id=aas.subaccount_id) as subacc_username, activated, temporary_blocked, (select cost FROM billservice_addonservice WHERE id=aas.service_id) as cost from billservice_accountaddonservice as aas WHERE deactivated is not Null ORDER BY activated; """) cost=0 %> <div> <strong>Активные подключаемые услуги:</strong></div> <table class="sample" width="100%"> <tr> <td>Название услуги</td><td>Цена</td><td>Имя аккаунта</td><td>Имя субаккаунта</td><td>Дата активации</td><td>Блокировка</td> </tr> %for aas in accountaddonservices: <% cost+=aas.cost %> <tr> <td>${aas.name}</td><td>${aas.cost}</td><td>${aas.username}</td><td>${aas.subacc_username if aas.subacc_username else ''}</td><td>${aas.activated}</td><td>${aas.temporary_blocked if aas.temporary_blocked else u'Нет'}</td> </tr> %endfor <tr> <td></td><td></td><td>Итого: ${cost}</td><td></td><td></td><td></td> </tr> </table> </body> </html>
Пример работы:
Отчёт по списку активированных услуг за период
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <% import os from CustomForms import PeriodForm child = PeriodForm(realm=u'Период') if child.exec_()!=1: return %> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; font-size:10pt; } table.sample th { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <% import os import datetime from dateutil.relativedelta import relativedelta now = datetime.datetime.now() %> <img src="${os.path.abspath("images/reports/header.png")}"> <% accountaddonservices=connection.sql("""select (select name from billservice_addonservice WHERE id=aas.service_id) as name, (select username from billservice_account WHERE id=aas.account_id) as username, (select username from billservice_subaccount WHERE id=aas.subaccount_id) as subacc_username, activated, deactivated, (select cost FROM billservice_addonservice WHERE id=aas.service_id) as cost from billservice_accountaddonservice as aas WHERE activated between '%s' and '%s' ORDER BY activated; """ % (child.start_date, child.end_date,)) cost=0 %> <div> <strong>Активированные подключаемые услуги:</strong></div> <table class="sample" width="100%"> <tr> <td>Название услуги</td><td>Цена</td><td>Имя аккаунта</td><td>Имя субаккаунта</td><td>Дата активации</td><td>Дата деактивации</td> </tr> %for aas in accountaddonservices: <% cost+=aas.cost %> <tr> <td>${aas.name}</td><td>${aas.cost}</td><td>${aas.username}</td><td>${aas.subacc_username if aas.subacc_username else ''}</td><td>${aas.activated}</td><td>${aas.deactivated if aas.deactivated else ''}</td> </tr> %endfor <tr> <td></td><td></td><td>Итого: ${cost}</td><td></td><td></td><td></td> </tr> </table> </body> </html>
Пример работы:
Отчёт по изменениям тарифного плана за период
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body font-size='10pt'> <% import os from CustomForms import PeriodForm child = PeriodForm(realm=u'Период') if child.exec_()!=1: return %> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; font-size: 10pt; } table.sample th { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <% import os import datetime from dateutil.relativedelta import relativedelta now = datetime.datetime.now() %> <img src="${os.path.abspath("images/reports/header.png")}"> <% accounttarifs=connection.sql("""SELECT acc.username, tarif.name, act.datetime, act.periodical_billed, (SELECT name FROM billservice_tariff WHERE id=(SELECT tarif_id FROM billservice_accounttarif WHERE account_id=acc.id and datetime<act.datetime ORDER BY datetime DESC LIMIT 1)) as prev_tarif FROM billservice_accounttarif as act JOIN billservice_account as acc on acc.id=act.account_id JOIN billservice_tariff as tarif ON tarif.id=act.tarif_id WHERE act.datetime between '%s' and '%s' ORDER BY act.datetime """ % (child.start_date, child.end_date,)) strftimeFormat = "%d.%m.%Y %H:%M:%S" %> <div> <strong>Изменения тарифного плана:</strong></div> <table class="sample" width="100%"> <tr> <td>Имя аккаунта</td><td>Название тарифа</td><td>Дата начала</td><td>Период закрыт</td><td>Предыдущий тариф</td> </tr> %for act in accounttarifs: <tr> <td>${act.username}</td><td>${act.name}</td><td>${act.datetime.strftime(strftimeFormat)}</td><td>${act.periodical_billed}</td><td>${act.prev_tarif if act.prev_tarif else ''}</td> </tr> %endfor </table> </body> </html>
Пример работы
Отчёт по списку занятых IP адресов
Простой отчёт.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <% import os import datetime from dateutil.relativedelta import relativedelta now = datetime.datetime.now() %> <img src="${os.path.abspath("images/reports/header.png")}"> <% pools=connection.sql("""SELECT id, name FROM billservice_ippool""") count=0 %> <div> <strong>Занятые IP адреса в пулах:</strong></div> %for pool in pools: <strong>${pool.name}</strong><br/> <% ips=connection.sql("""SELECT DISTINCT ip FROM billservice_ipinuse WHERE disabled is Null and ip!='0.0.0.0' and pool_id=%s ORDER BY ip ASC""" % pool.id) connection.commit() %> %for ip in ips: ${ip.ip} <br/> <% count+=1 %> %endfor %endfor <tr> <td></td><td></td><td>Итого: ${count}</td><td></td><td></td><td></td> </tr> </table> </body> </html>
Пример работы
Отчёт по прибыли в день
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body font-size='10pt'> <% import os from CustomForms import PeriodForm child = PeriodForm(realm=u'Период') if child.exec_()!=1: return %> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; } table.sample th { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <% import os import datetime from dateutil.relativedelta import relativedelta now = datetime.datetime.now() %> <img src="${os.path.abspath("images/reports/header.png")}"> <% transactions=connection.sql("""SELECT sum(summ*-1), date_part('day',created) as day, date_part('month',created) as month, date_part('year',created) as year FROM billservice_transaction WHERE created between '%s' and '%s' GROUP BY date_part('year',created),date_part('month',created),date_part('day',created) ORDER BY year,month, day ASC; """ % (child.start_date, child.end_date,)) cost=0 %> <div> <strong>Отчёт по прибыли с группировкой по дням:</strong></div> <table class="sample" width="100%"> <tr> <td>Год</td><td>Месяц</td><td>День</td><td>Сумма</td> </tr> %for transaction in transactions: <% cost+=transaction.sum %> <tr> <td>${int(transaction.year)}</td><td>${int(transaction.month)}</td><td>${int(transaction.day)}</td><td>${transaction.sum if transaction.sum else ''}</td> </tr> %endfor <tr> <td></td><td></td><td><strong>Итого</strong></td><td> ${cost}</td> </tr> </table> </body> </html>
Пример работы
Отчёт по прибыли с группировкой по месяцам
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body font-size='10pt'> <% import os from CustomForms import PeriodForm child = PeriodForm(realm=u'Период') if child.exec_()!=1: return %> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; } table.sample th { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <% import os import datetime from dateutil.relativedelta import relativedelta now = datetime.datetime.now() %> <img src="${os.path.abspath("images/reports/header.png")}"> <% transactions=connection.sql("""SELECT sum(summ*-1), date_part('month',created) as month, date_part('year',created) as year FROM billservice_transaction WHERE created between '%s' and '%s' GROUP BY date_part('year',created),date_part('month',created) ORDER BY year,month ASC; """ % (child.start_date, child.end_date,)) cost=0 %> <div> <strong>Отчёт по прибыли с группировкой по дням:</strong></div> <table class="sample" width="100%"> <tr> <td>Год</td><td>Месяц</td><td>Сумма</td> </tr> %for transaction in transactions: <% cost+=transaction.sum %> <tr> <td>${int(transaction.year)}</td><td>${int(transaction.month)}</td><td>${transaction.sum if transaction.sum else ''}</td> </tr> %endfor <tr> <td></td><td><strong>Итого</strong></td><td> ${cost}</td> </tr> </table> </body> </html>
Пример работы
Занятые порты на коммутаторах
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; } table.sample th { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: gray; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 1px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <% import os commutators=connection.get_models("nas_switch") subaccounts_data=connection.sql("""SELECT acc.username, subacc.switch_id, subacc.switch_port FROM billservice_account as acc JOIN billservice_subaccount as subacc ON subacc.account_id=acc.id WHERE acc.id in (SELECT account_id FROM billservice_subaccount WHERE switch_id is not Null and switch_port is not Null)""") subaccounts={} %> %for subacc in subaccounts_data: <% subaccounts[(subacc.switch_id,subacc.switch_port)]=subacc.username %> %endfor <img src="${os.path.abspath("images/reports/header.png")}"> <div> <strong>Отчёт по занятым портам коммутаторов:</strong></div> <table class="sample" width="100%"> <tr> <th>Порт</th><th>Аккаунт</th> </tr> %for commutator in commutators: Коммутатор: ${commutator.manufacturer} ${commutator.model} ${commutator.identify} %for x in xrange(1,commutator.ports_count+1): <tr> <td>${x}</td><td>${subaccounts.get((commutator.id,x), "")}</td> </tr> %endfor </table> %endfor </body>
Общий отчёт за предыдущий месяц
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <style type="text/css"> table.sample { border-width: 1px; border-spacing: 2px; border-style: none; border-color: black; border-collapse: collapse; background-color: white; } table.sample th { border-width: 1px; padding: 3px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } table.sample td { border-width: 1px; padding: 3px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> </head> <body> <% import os import datetime now = datetime.datetime.now()-datetime.timedelta(days=30) accs = connection.get_models("billservice_account") sp_start,sp_end,length=connection.sp_info(2, curdatetime=now) i=0 total_psh=0 total_ash=0 total_tr=0 total_ttr=0 total_total=0 %> <center><img src="${os.path.abspath("images/reports/header.png")}"></center> <center><h3>Месячный отчёт с ${sp_start} по ${sp_end}</h3></center><br /> <center><table class="sample"> <th>№</th><th>Договор</th><th>ФИО</th><th>Тарифицировано трафика</th><th>Абонентская плата</th><th>Подключаемые услуги</th><th>Начислено</th><th>Итого списано</th> % for acc in accs: <% traffictransaction=float("%.2f" % (connection.get("""SELECT sum(summ) as summ FROM billservice_traffictransaction WHERE datetime between '%s' and '%s' and account_id=%s """% (sp_start, sp_end, acc.id)).summ or 0) ); psh=float("%.2f" % (connection.get("""SELECT sum(summ) as summ FROM billservice_periodicalservicehistory AS psh WHERE psh.datetime BETWEEN '%s' AND '%s' and psh.account_id=%s;""" % (sp_start, sp_end, acc.id)).summ or 0)); ash=float(str(connection.get("""SELECT sum(summ) as summ FROM billservice_addonservicetransaction WHERE created BETWEEN '%s' AND '%s' and account_id=%s;""" % (sp_start, sp_end, acc.id)).summ or 0)); tr=-1* (connection.get("""SELECT sum(summ) as summ FROM billservice_transaction WHERE summ<0 and created BETWEEN '%s' AND '%s' and account_id=%s;""" % (sp_start, sp_end, acc.id)).summ or 0) total=traffictransaction+psh+ash total_psh+=psh total_ash+=ash total_tr+=tr total_ttr+=traffictransaction total_total+=total %> <tr> <td>${i}</td><td>${acc.contract}</td><td>${acc.fullname}</td><td>${traffictransaction}</td> <td>${psh}</td><td>${ash}</td><td>${tr}</td><td>${total}</td> </tr> <% i+=1 %> %endfor <td></td><td></td><td><strong>Итого:</strong></td><td><strong>${total_ttr}</strong></td><td><strong>${total_psh}</strong></td><td><strong>${total_ash}</strong></td><td><strong>${total_tr}</strong></td><td><strong>${total_total}</strong></td> </table> </center> </body> </html>