Создание отчётов

Материал из ExpertBilling
Перейти к: навигация, поиск

В ExpertBilling 1.4 внедрена принципиально новая система работы с отчётами. Сейчас вы сами сможете задать их внешний вид, а, также, создавать новые виды отчётов и документов. Из тела отчёта можно обратиться к rpc серверу, выполнить выборки из базы данных и отформатировать их в удобном для вас виде.

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

Reports.png

Для генерации отчётов необходимо создать шаблоны отчётов. Сделать это можно в меню "Главное меню"->"Шаблоны документов"

В левой части окна расположены категории отчётов. Часть отчётов предзадана и мы не рекомендуем их удалять. Чтобы создать новый отчёт необходимо выбрать первый пункт в дереве типов шаблонов "-- Новый шаблон --" и указать параметры шаблона справа. Выберите тип шаблона, его имя и задайте тело шаблона. С помощью кнопки "Сохранить" на панели инструментов сохраните его. Кнопка "Предпросмотр" служит для предварительного просмотра созданного шаблона

Краткая информация по синтаксису шаблонов

Обозначение переменных:

${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>
Period1.png

Отчёт по 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>

Результат:

Report sessions.png

Встроенные документы

В системе существуют следующие виды шаблонов:

  • Договор на подключение физ.лица
  • Договор на подключение юр.лица
  • Накладная на карты
  • Шаблон карты
  • Кассовый чек
  • Информационное письмо
  • Акт выполненных работ
  • Счёт-фактура
  • Отчёты

Договор на подключение физ.лица

Система позволяет создать несколько шаблонов этого типа. В тело шаблона передаётся список из идентификаторов аккаунтов,если выбрана печать договоров для списка пользователей, или список, состоящий из одного элемента - 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>

Результат работы шаблона:

Sale card.png

Шаблон карты

Шаблоны карт предоплаты используется при печати карт в момент передачи их дилеру. Учитывая то, что в одной поставке могут оказаться карты разных типов, номиналов и шаблонов, шаблон карты должен описывать только её внешний вид. Изначально в шаблон передаётся подключение к 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>

Пример работы:

Activated addonservices.png

Отчёт по списку активированных услуг за период

<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>

Пример работы:

Accauntaddonservices period.png

Отчёт по изменениям тарифного плана за период

<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>

Пример работы

Accounttarifs.png

Отчёт по списку занятых 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>

Пример работы

Freeips.png

Отчёт по прибыли в день

<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>

Пример работы

Money days.png

Отчёт по прибыли с группировкой по месяцам

<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>

Пример работы

Money months.png

Занятые порты на коммутаторах

<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>

Документация по синтаксису шаблонов