256 lines
13 KiB
Plaintext
256 lines
13 KiB
Plaintext
<cfsilent>
|
||
<cfimport prefix="m" taglib="lib"/>
|
||
<cfimport prefix="c" taglib="lib/controls"/>
|
||
<cfimport prefix="d" taglib="lib/data"/>
|
||
<cfimport prefix="layout" taglib="layout"/>
|
||
</cfsilent>
|
||
|
||
<m:prepare_ls entity="payg" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
|
||
<!--- <cfdump var=#form#/> --->
|
||
|
||
<cfparam name="dt_finish" type="date" default=#dateAdd('d',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
|
||
<cfparam name="dt_start" type="date" default=#dateAdd('m',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
|
||
|
||
<!--- <cfparam name="dt_finish" default=#dateAdd('d',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
|
||
<cfparam name="dt_start" default=#dateAdd('m',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/> --->
|
||
|
||
|
||
<cfset hours=dateDiff('h', dt_start, dt_finish)/>
|
||
<!--- <cfdump var=#variables#/> --->
|
||
|
||
<cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ --->
|
||
|
||
|
||
<cfif isDefined("output_xls")><cfmodule template="mod/compute_payg.cfm"
|
||
dt_finish=#dt_finish#
|
||
dt_start=#dt_start#
|
||
tenant_wz_index=#tenant_wz_index#
|
||
output="report"
|
||
debug=#isDefined("DEBUG")#
|
||
/><layout:xml qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.xml"/>
|
||
<cfabort/>
|
||
</cfif><cfif isDefined("output_json")><cfmodule template="mod/compute_payg.cfm"
|
||
dt_finish=#dt_finish#
|
||
dt_start=#dt_start#
|
||
tenant_wz_index=#tenant_wz_index#
|
||
output="report"
|
||
debug=#isDefined("DEBUG")#
|
||
/><!--- <cfdump var=#report#/><cfabort/> ---><layout:json qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.json"/>
|
||
<cfabort/>
|
||
</cfif><!---
|
||
---><layout:page section="header" pageInfo=#pageInfo#>
|
||
|
||
<layout:attribute name="title">
|
||
<cfoutput><b>Отчет по PAYG</b> #request.auth.wz#</cfoutput>
|
||
</layout:attribute>
|
||
<layout:attribute name="controls">
|
||
<!---skip filter link, filter is not implemented--->
|
||
<!---<layout:language_switch/>--->
|
||
</layout:attribute>
|
||
</layout:page>
|
||
|
||
<!--- Внимание! округление вверх при расчете метрик get, put S3. Расчетные суммы могут содержать погрешности округления, поэтому окончательный расчет стоимости (количество * цену) выполняeтся в бухгалтерской программе.
|
||
<b>Если у клиента больше 1 действующего допника с услугами PAYG, то данные в отчете могут быть замножены, в этом случае считать вручную</b> --->
|
||
|
||
<cfoutput>
|
||
<form method="post" action="#request.thisPage#" style="display:inline"><!--- не надо делать action="" - параметр, затесавшийся в URL будет затирать параметр формы, и заметить это трудно --->
|
||
Период с <input type="text" name="dt_start" value="#dateFormat(dt_start,'YYYY-MM-DD')#"/>
|
||
по <input type="text" name="dt_finish" value="#dateFormat(dt_finish,'YYYY-MM-DD')#"/>
|
||
<input type="submit" style="cursor:pointer;"/>
|
||
<input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/><!--- ********* --->
|
||
</form>
|
||
<cfset month_start = createDate(year(now()), month(now()), 1)/>
|
||
<a href="?dt_start=#dateFormat(dateAdd('m', -1, month_start),'YYYY-MM-DD')#&dt_finish=#dateFormat(dateAdd('d', -1, month_start),'YYYY-MM-DD')#">прошлый месяц</a>
|
||
<br/>
|
||
|
||
<!--- <cfflush/> --->
|
||
|
||
<cfmodule template="mod/compute_payg.cfm"
|
||
dt_finish=#dt_finish#
|
||
dt_start=#dt_start#
|
||
tenant_wz_index=#tenant_wz_index#
|
||
output="report"
|
||
debug=#structKeyExists(Form,"DEBUG")#
|
||
/>
|
||
|
||
Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#report.qCharge.recordCount#</b><br/>
|
||
Актуальность данных:
|
||
VCD Computing: <b<cfif datediff("h",report.qComputingAge.dt_load, Now()) GT 24> class="err"</cfif>>
|
||
#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qComputingAge.dt_load,'HH:MM:SS')#
|
||
</b>
|
||
VCD Storage: <b<cfif datediff("h",report.qStorageAge.dt_load, Now()) GT 24> class="err"</cfif>>
|
||
#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#</b>
|
||
S3 хранение: <b<cfif datediff("h",report.qS3VolAge.dt_load, Now()) GT 24> class="err"</cfif>>
|
||
#dateFormat(report.qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#
|
||
</b>
|
||
S3 операции и трафик: <b<cfif datediff("h",report.qS3OpsTrfAge.dt_load, Now()) GT 24> class="err"</cfif>>
|
||
#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#
|
||
</b>
|
||
<br/>
|
||
|
||
<a href="#request.thisPage#?output_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a>
|
||
|
||
<a href="#request.thisPage#?output_json" title="экспорт в json" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/json.svg" style="vertical-align:text-bottom;" width="13" height="13"/></a>
|
||
</cfoutput>
|
||
<table class="worktable">
|
||
<thead>
|
||
<td width="1%"></td>
|
||
<th width="7%" colspan="2">Артикул</th>
|
||
<th width="3%">С</td>
|
||
<th width="3%">По</td>
|
||
<th width="3%">Кол-во (метрика)</td>
|
||
<th width="3%">Free Tier</td>
|
||
<th width="7%">Оплачиваемый объем</th>
|
||
|
||
<th width="3%">Ед.изм.</th>
|
||
|
||
<th width="3%">Цена ₽ с НДС</th>
|
||
<th width="3%">Стоимость ₽ с НДС</th>
|
||
|
||
<th width="10%">Услуга</th>
|
||
<th width="10%">Компонент</th>
|
||
|
||
<!--- <td width="10%">Клиентс. назв.</td> --->
|
||
|
||
<!--- <td width="3%">GPL с НДС</td>
|
||
<td width="3%">Скидка%</td> --->
|
||
|
||
<!--- <td width="6%">Дата НОУ</td>
|
||
<td width="6%">Дата оконч.</td>
|
||
|
||
<td width="3%">Ключ строки</td> --->
|
||
<!--- <td width="3%">Сырая метрика</td> --->
|
||
|
||
|
||
<!--- <td width="5%">Договор</td>
|
||
<td width="10%">Допник</td>
|
||
<td width="10%">Сделка</td> --->
|
||
|
||
</thead>
|
||
|
||
<!--- *** Обнаружена засада: возможно, группировка (сортировка) отображаемого вывода нужна по размерности детализации, а не по коду
|
||
Но: агрегация скорее нужна по коду
|
||
Видимо, придется на фронте перетасовывать?
|
||
--->
|
||
<!--- Еще засада: если я делегирую агрегацию на фронт, мне нужно и вычет free_tier делегировать на фронт (помня еще, что он на месяц, а не на произвольный период - наверно, можно его вычитать всегда, а больше месяца не агрегировать... или умножать на число полных месяцев) --->
|
||
<!--- видимо, Free Tier и единицу измерения нужно брать и для строк, которые не найдены в договоре
|
||
(еше можно было бы туда для полноты добавлять GPL цену, но для этого нужен каталог)
|
||
Добавить реквизиты договора? --->
|
||
<cfoutput query="report.qCharge" group="year">
|
||
<cfoutput group="month">
|
||
<tr><td colspan="99"><cfif month GT 0><b>#year#-#numberFormat(month,"00")#</b></cfif></td></tr>
|
||
<cfoutput group="svc">
|
||
<cfset var accum_cost_svc = 0/>
|
||
<cfset var accum_metric_svc = 0/>
|
||
<tr>
|
||
<td bgcolor="##ccc"></td>
|
||
<td bgcolor="##ccc" colspan="12">
|
||
#svc# <b>#service_code#</b>
|
||
<cfif len(serviceCustomName)>(#serviceCustomName#)</cfif>
|
||
<cfif serviceHash EQ "">(не найдено в договоре)</cfif>
|
||
</td>
|
||
</tr>
|
||
|
||
|
||
<cfoutput group="code">
|
||
<cfoutput group="serviceStart"> <!--- может быть, для ясности надо в каждой строке писать точный период --->
|
||
<cfset accum_metric_svc += isNumeric(metric) ? metric : 0/><!--- агрегация по метрике возможна только на уровне компонента --->
|
||
<tr>
|
||
<td></td>
|
||
<td colspan="10">#code# <cfif code EQ "">(#m_code#)</cfif></td>
|
||
<td>#svc#</td>
|
||
<td>#component# </td>
|
||
</tr>
|
||
|
||
<cfset accum_metric_dimension_1 = 0/>
|
||
|
||
<cfoutput group="dimension_1">
|
||
<cfset accum_metric_dimension_1 += isNumeric(metric) ? metric : 0/>
|
||
<tr class="i">
|
||
<td>#currentRow#</td>
|
||
<td>#dimension_1_name#</td>
|
||
<td>#dimension_1#</td>
|
||
<td class="c">#dateFormat(dt_billed_from,"YYYY-MM-DD")#</td>
|
||
<td class="c">#dateFormat(dt_billed_to,"YYYY-MM-DD")#</td>
|
||
<td class="r"><cfif metric NEQ 0 AND isNumeric(metric)>#round(metric,3)#</cfif></td>
|
||
<td><!--- free tier ---></td>
|
||
<td class="r" style="font-size:120%; padding:0 1em;"></td>
|
||
<td class="c">#unit#</td>
|
||
<td class="r">#gaussianRound(discounted_price,2)#</td>
|
||
<td class="r"><!--- здесь не вычисляется стоимость, потому что Free Tier вычитается после агрегации (скидочные политики реализовывать будет непросто) ---></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
</cfoutput>
|
||
|
||
<cfset cost_dimension_1 = precisionEvaluate("gaussianRound(max(accum_metric_dimension_1 - free_tier, 0)*gaussianRound(discounted_price, 2), 2)")/>
|
||
|
||
<tr class="b">
|
||
<td></td>
|
||
<td colspan="4">Итого #code# <cfif code EQ "">(#m_code#)</cfif>:</td>
|
||
<td class="r"><cfif accum_metric_dimension_1 NEQ 0 AND isNumeric(accum_metric_dimension_1)>#round(accum_metric_dimension_1,3)#</cfif></td>
|
||
<td class="r">#free_tier#</td>
|
||
<td class="r" style="font-size:120%; padding:0 1em;"><cfif accum_metric_dimension_1 NEQ 0 AND isNumeric(accum_metric_dimension_1)>#round(max(accum_metric_dimension_1 - free_tier, 0),3)#</cfif></td>
|
||
<td class="c">#unit#</td>
|
||
<td class="r">#gaussianRound(discounted_price,2)#</td><!--- если цена менялась в периоде, неправильно показыввать последнюю - но мы не тестировали с изменением в периоде --->
|
||
<!--- непонятно, как вычитать Free Tier для неполных месяцев. Если с 15 по 15 - вычитать 2 раза, вычитать (каким-то сложным образом) 1 раз или ни разу --->
|
||
<!--- видимо, нужно сформировать тест кейсы на неполные месяцы. Где взять биллинговые данные для этого ВПР --->
|
||
<td class="r">#cost_dimension_1#</td>
|
||
<cfset accum_cost_svc = precisionEvaluate("accum_cost_svc + cost_dimension_1")/><!--- простое сложение дает ошибку округления и лишние знаки, но и precisionEvaluate не выглядит слишком надежным --->
|
||
<td>#svc# <!--- #service_code# ---></td>
|
||
<td>#component# </td>
|
||
</tr>
|
||
|
||
</cfoutput><!--- serviceStart --->
|
||
</cfoutput><!--- code --->
|
||
<tr>
|
||
<td bgcolor="##eee" style="border-bottom:2px solid gray;"></td>
|
||
<td bgcolor="##eee" colspan="9" class="r" style="border-bottom:2px solid gray;">
|
||
<b>Итого </b>
|
||
<cfif len(service_code)>
|
||
#svc# <b>#service_code#</b>
|
||
<cfelse>
|
||
(#m_code#)
|
||
</cfif>
|
||
<cfif month GT 0>за #year#-#numberFormat(month,"00")#</cfif>:
|
||
</td>
|
||
<td bgcolor="##eee" class="r b" style="border-bottom:2px solid gray;">#accum_cost_svc#</td>
|
||
<td bgcolor="##eee" colspan="99" style="border-bottom:2px solid gray;"></td>
|
||
</tr >
|
||
</cfoutput>
|
||
<tr><td colspan="99"> </td></tr>
|
||
</cfoutput>
|
||
</cfoutput>
|
||
|
||
</table>
|
||
<!---<cfdump var=#report.qCharge#/> --->
|
||
<!--- <cfdump var=#this.datasources[request.ds].connectionString#/> --->
|
||
|
||
<layout:page section="footer"/>
|
||
|
||
<!--- select * from s3billing.billing_per_user_fix_pl('2025-05-01 00:00:00','2025-06-01 00:00:00','1395'); --->
|
||
<cfscript>
|
||
function gaussianRound(numeric number, numeric precision = 0) {
|
||
var factor = 10 ^ precision;
|
||
var scaledNumber = number * factor;
|
||
var absScaled = abs(scaledNumber);
|
||
var sign = sgn(scaledNumber);
|
||
var floored = int(absScaled);
|
||
|
||
// If it's not ending in exactly .5, use standard rounding or precision scaling
|
||
if (absScaled - floored != 0.5) {
|
||
return round(number * factor) / factor;
|
||
}
|
||
|
||
// If it is exactly .5, round towards the nearest even number
|
||
if (floored % 2 != 0) {
|
||
// Nearest even is UP
|
||
return (floored + 1) * sign / factor;
|
||
} else {
|
||
// Nearest even is DOWN
|
||
return floored * sign / factor;
|
||
}
|
||
}
|
||
</cfscript>
|
||
|