Compare commits

...
10 Commits
Author SHA1 Message Date
smishchuk 669eaec34f 089 xl json dates, gpu fixes 2026-09-28 18:22:17 +03:00
smishchuk d6193481e1 087 tests and storage rpt improved 2026-09-27 09:41:28 +03:00
smishchuk 2f260a3b63 086 deals for tests 2026-09-24 13:55:37 +03:00
smishchuk 189dd90fcb 086 MemoryDbHepler 0.03 precision fix 2026-09-22 16:31:38 +03:00
smishchuk b1ca6d27bf 085 s3 report 2026-09-17 18:37:15 +03:00
smishchuk 96a283dd08 084 airplane 2026-09-14 11:56:11 +03:00
smishchuk 26451cbd1d 83 intermediate metric sum in payg and performance improvement 2026-09-08 19:37:33 +03:00
smishchuk 3db978003f 81 vm_ls fix 2026-09-01 15:28:17 +03:00
smishchuk a527dd66e7 080 pricing model 2026-08-25 20:29:49 +03:00
smishchuk 1875c3a3d8 079 bug fixes 2026-08-25 19:22:12 +03:00
16 changed files with 613 additions and 157 deletions
+5 -4
View File
@@ -13,10 +13,11 @@
<cfset this.setclientcookies="No"/> <cfset this.setclientcookies="No"/>
<cfset this.mappings = structNew() /> <cfset this.mappings = structNew() />
<cfset this.mappings["/mod"] = getDirectoryFromPath(getCurrentTemplatePath()) & "mod/" /> <cfset this.mappings["/mod"] = getDirectoryFromPath(getCurrentTemplatePath()) & "mod/" />
<cfset this.customTagPaths = expandPath(getDirectoryFromPath(getCurrentTemplatePath())&'mod')/> <cfset this.customTagPaths = expandPath(getDirectoryFromPath(getCurrentTemplatePath())&'mod')/>
<cfset this.datasource = "dwh"/>
<cfset this.timeZone = "Europe/Moscow"/><!--- для биллинга очень важен часовой пояс, поэтому ставим вручную в коде ---> <cfset this.timeZone = "Europe/Moscow"/><!--- для биллинга очень важен часовой пояс, поэтому ставим вручную в коде --->
<!--- <cfset this.serialization.json.date = "iso8601"/> --->
<cfset this.datasource = "dwh"/>
<cfset this.defaultdatasource = this.datasource/> <cfset this.defaultdatasource = this.datasource/>
<cfset request.DS = "#this.datasource#"> <cfset request.DS = "#this.datasource#">
<!--- кажется, нужно инициализировать датасорцы в псевдоконструкторе - onRequest не получается ---> <!--- кажется, нужно инициализировать датасорцы в псевдоконструкторе - onRequest не получается --->
@@ -87,7 +88,7 @@
<!--- global settings ---> <!--- global settings --->
<!--- *************************************************************************************************** ---> <!--- *************************************************************************************************** --->
<cfset request.APP_VERSION="0.00.078"/> <!--- *********************************************************** ---> <cfset request.APP_VERSION="0.00.089"/> <!--- *********************************************************** --->
<!--- *************************************************************************************************** ---> <!--- *************************************************************************************************** --->
<cfset request.STAND=getStand()/> <cfset request.STAND=getStand()/>
<cfset request.RECORDS_PER_PAGE=500/> <cfset request.RECORDS_PER_PAGE=500/>
+5 -2
View File
@@ -1,4 +1,5 @@
component { component {
/*0.03 precision fix*/
/*0.02 timestamp support*/ /*0.02 timestamp support*/
/** /**
* HSQL 1.8 * HSQL 1.8
@@ -33,6 +34,7 @@ component {
CREATE TEMPORARY TABLE #arguments.name#(#arrayToList(colDefs, ', ')#) ON COMMIT PRESERVE ROWS"; CREATE TEMPORARY TABLE #arguments.name#(#arrayToList(colDefs, ', ')#) ON COMMIT PRESERVE ROWS";
queryExecute(createSql, {}, { datasource: arguments.dsn }); queryExecute(createSql, {}, { datasource: arguments.dsn });
//} //}
//writedump(meta); writedump(createSql);abort;
// 2. Цикл вставки // 2. Цикл вставки
var insertSql = "INSERT INTO #arguments.name# (#cols#) VALUES (#repeatString("?,", listLen(cols)-1)#?)"; var insertSql = "INSERT INTO #arguments.name# (#cols#) VALUES (#repeatString("?,", listLen(cols)-1)#?)";
@@ -65,10 +67,11 @@ component {
private string function mapType(required string luceeType) { private string function mapType(required string luceeType) {
switch (arguments.luceeType) { switch (arguments.luceeType) {
case "VARCHAR": case "CHAR": case "LONGVARCHAR": return "LONGVARCHAR"; case "VARCHAR": case "CHAR": case "LONGVARCHAR": return "LONGVARCHAR";
case "INTEGER": case "BIGINT": case "SMALLINT": case "TINYINT": return "INT"; case "INTEGER": case "SMALLINT": case "TINYINT": return "INT";
case "BIGINT": return "BIGINT";
case "DOUBLE": case "FLOAT": case "REAL": return "DOUBLE"; case "DOUBLE": case "FLOAT": case "REAL": return "DOUBLE";
case "BOOLEAN": case "BIT": return "BIT"; case "BOOLEAN": case "BIT": return "BIT";
case "DECIMAL": case "NUMERIC": return "DECIMAL(20,6)"; case "DECIMAL": case "NUMERIC": return "DECIMAL";
case "TIMESTAMP": case "DATE": case "TIME": return "TIMESTAMP"; case "TIMESTAMP": case "DATE": case "TIME": return "TIMESTAMP";
default: return "LONGVARCHAR"; // И тут тоже default: return "LONGVARCHAR"; // И тут тоже
} }
+13 -5
View File
@@ -21,10 +21,10 @@
<cfquery name="qRead" datasource="#request.DS#"> <cfquery name="qRead" datasource="#request.DS#">
select select
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount"> <d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="ID" cfSqlType="CF_SQL_OTHER">d.__id</d:field> <d:field title="ID" cfSqlType="CF_SQL_OTHER">d.__id::text as __id</d:field>
<d:field title="Внут.номер">d.agreement_id</d:field> <d:field title="Внут.номер">d.agreement_id</d:field>
<d:field title="Договор">d.__name</d:field> <d:field title="Договор">d.__name</d:field>
<d:field title="company_uid">d.company_uid</d:field> <d:field title="company_uid">d.company_uid::text as company_uid</d:field>
<d:field title="Контрагент">k.__name as contragent</d:field> <d:field title="Контрагент">k.__name as contragent</d:field>
<d:field title="WZ">k.id_klienta as wz</d:field> <d:field title="WZ">k.id_klienta as wz</d:field>
<d:field title="Дата договора">d.date_of_aggrement</d:field> <d:field title="Дата договора">d.date_of_aggrement</d:field>
@@ -33,15 +33,23 @@
<d:field title="Ответственный за к/а">c.__name as responsible</d:field> <d:field title="Ответственный за к/а">c.__name as responsible</d:field>
<d:field title="Email отв-го">c.email as responsible_email</d:field> <d:field title="Email отв-го">c.email as responsible_email</d:field>
<d:field title="Статус">c.__status_status as responsible_status</d:field> <d:field title="Статус">c.__status_status as responsible_status</d:field>
<d:field title="Доп. соглашений">(select count(*) from elma.additional_agreements a where a.contract_uid=d.__id AND a."__deletedAt" IS NULL) as aa_cnt</d:field> <!--- <d:field title="Доп. соглашений">COALESCE(a.aa_cnt, 0) AS aa_cnt</d:field> --->
<d:field title="Доп. соглашений">a.aa_cnt</d:field>нас null вместо 0 совершенно устраивает
</d:field_set> </d:field_set>
from elma.aggrements d from elma.aggrements d
LEFT OUTER JOIN elma.companies k ON (d.company_uid=k.__id) LEFT OUTER JOIN elma.companies k ON (d.company_uid=k.__id)
LEFT OUTER JOIN elma.comp_users_ext c ON (k.responsible=c.__id) LEFT OUTER JOIN elma.comp_users_ext c ON (k.responsible=c.__id)
LEFT OUTER JOIN (
SELECT contract_uid, count(*) AS aa_cnt
FROM elma.additional_agreements
WHERE "__deletedAt" IS NULL
GROUP BY contract_uid
) a ON (a.contract_uid = d.__id)
where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/> where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/>
order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/> order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/>
</cfquery> </cfquery>
<!--- <cfdump var=#qRead#/> --->
<cfcatch type="database"> <cfcatch type="database">
<m:ls_catch catch=#cfcatch# status=#pageInfo.status#/> <m:ls_catch catch=#cfcatch# status=#pageInfo.status#/>
@@ -116,10 +124,10 @@ select count(*) as cnt from elma.aggrements d where 1=1
<c:column width="1%" sortable="false"><!---*** class="c" не пробрасывается ---> <c:column width="1%" sortable="false"><!---*** class="c" не пробрасывается --->
<c:td field="f_link_view_edit" class="c"/> <c:td field="f_link_view_edit" class="c"/>
</c:column> </c:column>
<c:column width="10%" field="__id"><c:td class="l"/></c:column> <c:column width="10%" field="__id" sortable=false><c:td class="l"/></c:column>
<c:column width="10%" field="f_contract"/> <c:column width="10%" field="f_contract"/>
<c:column width="10%" field="agreement_id"/> <c:column width="10%" field="agreement_id"/>
<c:column width="10%" field="company_uid"/> <c:column width="10%" field="company_uid" sortable=false/>
<c:column width="10%" field="f_contragent"/> <c:column width="10%" field="f_contragent"/>
<c:column width="10%" field="wz"/> <c:column width="10%" field="wz"/>
<c:column width="10%" field="responsible"/> <c:column width="10%" field="responsible"/>
+22 -4
View File
@@ -31,12 +31,30 @@
<d:field title="Создано">k."__createdAt" as __createdAt</d:field> <d:field title="Создано">k."__createdAt" as __createdAt</d:field>
<d:field title="Обновлено">k."__updatedAt" as __updatedAt</d:field> <d:field title="Обновлено">k."__updatedAt" as __updatedAt</d:field>
<d:field title="Удалено">k."__deletedAt" as __deletedAt</d:field> <d:field title="Удалено">k."__deletedAt" as __deletedAt</d:field>
<d:field title="Сделок">(select count(*) from elma.deals d where d._companies=k.__id) as deal_cnt</d:field> <d:field title="Сделок">COALESCE(dc.deal_cnt, 0) AS deal_cnt</d:field>
<d:field title="Договоров">(select count(*) from elma.aggrements d where d.company_uid=k.__id) as contract_cnt</d:field> <d:field title="Договоров">COALESCE(cc.contract_cnt, 0) AS contract_cnt</d:field>
<d:field title="Допников">(select count(*) from elma.additional_agreements g join elma.aggrements d on (g.contract_uid=d.__id) where d.company_uid=k.__id) as aa_cnt</d:field> <d:field title="Допников">COALESCE(aac.aa_cnt, 0) AS aa_cnt</d:field>
</d:field_set> </d:field_set>
from elma.companies k from elma.companies k
LEFT OUTER JOIN elma.comp_users_ext c on (k.responsible=c.__id) LEFT OUTER JOIN elma.comp_users_ext c on (k.responsible=c.__id)
LEFT JOIN (
SELECT _companies AS company_id, count(*) AS deal_cnt
FROM elma.deals
GROUP BY _companies
) dc ON dc.company_id = k.__id
-- Считаем договоры
LEFT JOIN (
SELECT company_uid, count(*) AS contract_cnt
FROM elma.aggrements
GROUP BY company_uid
) cc ON cc.company_uid = k.__id
-- Считаем доп. соглашения
LEFT JOIN (
SELECT d.company_uid, count(*) AS aa_cnt
FROM elma.additional_agreements g
JOIN elma.aggrements d ON g.contract_uid = d.__id
GROUP BY d.company_uid
) aac ON aac.company_uid = k.__id
where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/> where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/>
--AND exists (select * from elma.deals d where d._companies=k.__id) --AND exists (select * from elma.deals d where d._companies=k.__id)
order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/> order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/>
@@ -109,7 +127,7 @@ select count(*) as cnt from elma.companies where 1=1
<c:column width="1%" sortable="false"> <c:column width="1%" sortable="false">
<c:td field="f_link_view_edit" class="c"/> <c:td field="f_link_view_edit" class="c"/>
</c:column> </c:column>
<c:column width="10%" field="__id"/> <c:column width="10%" field="__id" sortable="false"/>
<c:column width="3%" field="__index"><c:td class="c"/></c:column> <c:column width="3%" field="__index"><c:td class="c"/></c:column>
<c:column width="20%" field="f_contragent"/> <c:column width="20%" field="f_contragent"/>
<c:column width="5%" field="wz"/> <c:column width="5%" field="wz"/>
+3 -3
View File
@@ -54,7 +54,7 @@
k.__name as contragent k.__name as contragent
,k.id_klienta as wz ,k.id_klienta as wz
,k.__index as contragent_index ,k.__index as contragent_index
,d."_plannedDueDate" as _plannedDueDate <!--- ,d."_plannedDueDate" as _plannedDueDate --->
,d."__createdAt" ,d."__createdAt"
,d."__index" as deal_index ,d."__index" as deal_index
,s.status ,s.status
@@ -131,12 +131,12 @@
</div> </div>
</div> </div>
<div class="tr"> <!--- <div class="tr">
<div class="th">_plannedDueDate</div> <div class="th">_plannedDueDate</div>
<div class="td">#dateFormat(qDecoration._plannedDueDate,'YYYY-MM-DD')# <div class="td">#dateFormat(qDecoration._plannedDueDate,'YYYY-MM-DD')#
Паразитное поле, не использовать в расчетах Паразитное поле, не использовать в расчетах
</div> </div>
</div> </div> --->
<div class="tr"> <div class="tr">
<div class="th">Статус</div> <div class="th">Статус</div>
+1
View File
@@ -0,0 +1 @@
Предлагаю показывать в отчете статус верхней сделки контрагента, на сейчас. Может быть, с датой последней активности? Может быть, надо фильтровать по типу ресурса и матчить с сервисом, но тогда будет теряться Other и все такое
+2 -80
View File
@@ -44,91 +44,13 @@
select select
count(distinct obj_id) as unique_vm_cnt, count(distinct obj)
sum(p.value)/12. as vm_h, -- 5 minute sampling sum(p.value)/12. as vm_h -- 5 minute sampling
sum(p.value)/12.*32.18 as cost,
sum(p.value)/12./744 as avg_month_vm -- в августе 744 часа
from gpu.vm from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid) JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
where 1=1 where 1=1
AND p.ts >= '2026-08-01' AND p.ts >= '2026-08-01'
AND p.ts < '2026-08-31' AND p.ts < '2026-08-31'
AND vm.client_id='WZ01516' AND vm.client_id='WZ01516'
-- упражнение: посчитать, сколько средних ВМ, включенных круглые сутки, создаст такой же движ за время оказания сервиса
select
p.ts::date,
count(distinct obj_id) as unique_vm_cnt,
round((sum(p.value)/12.)::numeric,1) as vm_h, -- 5 minute sampling
round((sum(p.value)/12.*32.18)::numeric,2) as cost,
round((sum(p.value)/12./24)::numeric, 1) as avg_roundclock_vm
from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
where 1=1
AND p.ts >= '2026-08-01'
AND p.ts < '2026-08-31'
AND vm.client_id='WZ01516'
GROUP BY p.ts::date
ORDER BY 1
select
p.ts::date,
count(distinct obj_id) as unique_vm_cnt,
round((sum(p.value)/12.)::numeric,1) as vm_h, -- 5 minute sampling
round((sum(p.value)/12.*32.18)::numeric,2) as cost,
round((sum(p.value)/12.*32.18*(1-0.02*400/32.18))::numeric,2) as cost_wo_disk,
round((count(distinct obj_id)*0.02*400*24)::numeric,2) as disk_cost_simplified, -- считаем все ВМ, включавшиеся за данные сутки
round((sum(p.value)/12./24)::numeric, 1) as avg_roundclock_vm
from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
where 1=1
AND p.ts >= '2026-08-01'
AND p.ts < '2026-08-31'
AND vm.client_id='WZ01516'
GROUP BY p.ts::date
ORDER BY 1
select
p.ts::date,
count(distinct obj_id) as unique_vm_cnt,
(select count(distinct obj_id) from gpu.vm vm1
JOIN gpu.vm_power_state p1 on (vm1.obj_uuid=p1.obj_uuid)
WHERE vm1.client_id='WZ01516' AND p1.ts <= p.ts::date) as unique_vm_acc,
round((sum(p.value)/12.)::numeric,1) as vm_h, -- 5 minute sampling
round((sum(p.value)/12.*32.18)::numeric,2) as cost,
round((sum(p.value)/12.*32.18*(1-0.02*400/32.18))::numeric,2) as cost_wo_disk,
round((count(distinct obj_id)*0.02*400*24)::numeric,2) as disk_cost_simplified, -- считаем все ВМ, включавшиеся за данные сутки
round((sum(p.value)/12./24)::numeric, 1) as avg_roundclock_vm
from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
where 1=1
AND p.ts >= '2026-08-01'
AND p.ts < '2026-08-31'
AND vm.client_id='WZ01516'
GROUP BY p.ts::date
ORDER BY 1
with daily as (
select
p.ts::date as dt,
count(distinct obj_id) as unique_vm_cnt,
round((sum(p.value)/12.)::numeric,1) as vm_h, -- 5 minute sampling
round((sum(p.value)/12.*32.18)::numeric,2) as cost,
round((sum(p.value)/12.*32.18*(1-0.02*400/32.18))::numeric,2) as cost_wo_disk,
round((count(distinct obj_id)*0.02*400*24)::numeric,2) as disk_cost_simplified, -- считаем все ВМ, включавшиеся за данные сутки
round((sum(p.value)/12./24)::numeric, 1) as avg_roundclock_vm
from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
where 1=1
AND p.ts >= '2026-08-01'
AND p.ts < '2026-08-31'
AND vm.client_id='WZ01516'
GROUP BY p.ts::date
ORDER BY 1
),
acc as (select p.ts::date as dt, count(distinct obj_id) as unique_vm_accum from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
WHERE vm.client_id='WZ01516'
group by p.ts::date)
select * from daily join acc on daily.dt<=acc.dt
+26 -8
View File
@@ -8,21 +8,39 @@
<m:prepare_ls entity="crm2cloud" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/> <m:prepare_ls entity="crm2cloud" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<cfset this.nullSupport = true/>
<cfquery name="qRead" datasource="billing-vc" cachedWithin=#createTimeSpan(0, 0, 25, 0)#>
<cfquery name="qGPU" datasource="billing-vc" cachedWithin=#createTimeSpan(0, 0, 25, 0)#> select
select obj_uuid, obj_id, client_id as WZ, vcd, org, org_vdc, vcenter, gpu, dt_load, dt_load_ts, org_status, cluster as clus, is_template <d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field>obj_uuid::text as obj_uuid</d:field>
<d:field>obj_id</d:field>
<d:field>client_id as WZ</d:field>
<d:field>vcd</d:field>
<d:field>org</d:field>
<d:field>org_vdc</d:field>
<d:field>vcenter</d:field>
<d:field>gpu::text as gpu</d:field>
<d:field cfSqlType="CF_SQL_TIMESTAMP">dt_load</d:field>
<d:field cfSqlType="CF_SQL_TIMESTAMP">dt_load_ts</d:field>
<d:field>org_status</d:field>
<d:field>cluster as clus</d:field>
<d:field cfSqlType="CF_SQL_BOOLEAN" formatter=#request.castToBoolSimple#>is_template</d:field>
<d:field cfSqlType="CF_SQL_BOOLEAN" formatter=#request.castToBoolSimple#>(1=1) as t1</d:field>
<d:field cfSqlType="CF_SQL_BOOLEAN" formatter=#request.castToBoolSimple#>(1=0) as t2</d:field>
<d:field cfSqlType="CF_SQL_BOOLEAN" formatter=#request.castToBoolSimple#>(1=null) as t3</d:field>
</d:field_set>
from gpu.vm from gpu.vm
order by client_id order by client_id
</cfquery> </cfquery>
<!--- <cfdump var=#qGPU# abort=true/> ---> <!--- <cfdump var=#qRead# abort=true/> --->
<cfif isDefined("output_xls")> <cfif isDefined("output_xls")>
<layout:xml qRead=#qCharge# titleMap=#titleMap# filename="#pageInfo.entity#.xml"/> <layout:xml qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/> <cfabort/>
</cfif><cfif isDefined("output_json")> </cfif><cfif isDefined("output_json")>
<layout:json qRead=#qCharge# titleMap=#titleMap# filename="#pageInfo.entity#.json"/> <layout:json qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.json"/>
<cfabort/> <cfabort/>
</cfif><!--- </cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#> ---><layout:page section="header" pageInfo=#pageInfo#>
@@ -45,7 +63,7 @@ order by client_id
<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> <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> </cfoutput>
<cfoutput><b>#qGPU.recordCount#</b></cfoutput> <cfoutput><b>#qRead.recordCount#</b></cfoutput>
<table class="worktable"> <table class="worktable">
<thead> <thead>
<tr> <tr>
@@ -62,7 +80,7 @@ order by client_id
</tr> </tr>
</thead> </thead>
<cfoutput query="qGPU"> <cfoutput query="qRead">
<tr> <tr>
<td><a href="contragent.cfm?wz=#WZ#&#tr.fwx#">#WZ#</a></td> <td><a href="contragent.cfm?wz=#WZ#&#tr.fwx#">#WZ#</a></td>
<td>#obj_uuid#</td> <td>#obj_uuid#</td>
+26 -1
View File
@@ -159,7 +159,7 @@ function skuCode(area_code="", abstract_service_code="") {
} }
request.skuCode = skuCode; request.skuCode = skuCode;
*/ */
function roundSafe(a, precision) { function roundSafe(a, precision=0) {
return (isNumeric(a) AND isNumeric(precision)) ? round(a,precision) : a; return (isNumeric(a) AND isNumeric(precision)) ? round(a,precision) : a;
} }
request.roundSafe = roundSafe; request.roundSafe = roundSafe;
@@ -311,6 +311,29 @@ public string function formatJsonString(str) {
} }
request.formatJsonString = formatJsonString; request.formatJsonString = formatJsonString;
//нужна еще функция, которая сериализует структуру как pretty printed json. См. https://github.com/bennadel/JsonSerializer.cfc (все не совсем просто, например, не заранее не определен формат даты) //нужна еще функция, которая сериализует структуру как pretty printed json. См. https://github.com/bennadel/JsonSerializer.cfc (все не совсем просто, например, не заранее не определен формат даты)
/**
* @hint Приводит поле резалтсета к булевому типу, пригодному для сериализации в json, вместо null возвращает false.
*/
boolean function castToBoolSimple(any x) {
if ( !structKeyExists( arguments, "x" ) || isNull( arguments.x ) ) {
return false;
}
// 2. Handle empty strings, empty arrays, or empty structs
if ( isSimpleValue( arguments.x ) && trim( arguments.x ) == "" ) {
return false;
}
if ( isArray( arguments.x ) && arrayIsEmpty( arguments.x ) ) {
return false;
}
if ( isStruct( arguments.x ) && structIsEmpty( arguments.x ) ) {
return false;
}
// 3. Fall back to standard CFML truthy evaluation
return isBoolean( arguments.x ) && arguments.x ? true : false;
}
request.castToBoolSimple=castToBoolSimple;
</cfscript> </cfscript>
<cffunction name=structGetKey> <cffunction name=structGetKey>
@@ -361,3 +384,5 @@ request.formatJsonString = formatJsonString;
</cffunction> </cffunction>
<cfset request.camel2snake = camel2snake/> <cfset request.camel2snake = camel2snake/>
+2
View File
@@ -19,12 +19,14 @@
<c:menu_item acl="" page="vm_ls.cfm" label="VM"/> <c:menu_item acl="" page="vm_ls.cfm" label="VM"/>
<c:menu_item acl="" page="gpu_ls.cfm" label="GPU"/> <c:menu_item acl="" page="gpu_ls.cfm" label="GPU"/>
<c:menu_item acl="" page="s3_ls.cfm" label="S3"/>
<li class="menu-title">Отчеты</li> <li class="menu-title">Отчеты</li>
<c:menu_item acl="" page="crm2cloud.cfm" label="CRM - Cloud"/> <c:menu_item acl="" page="crm2cloud.cfm" label="CRM - Cloud"/>
<c:menu_item acl="" page="cloud_capacity.cfm" label="Cloud Capacity"/> <c:menu_item acl="" page="cloud_capacity.cfm" label="Cloud Capacity"/>
<c:menu_item acl="" page="payg.cfm" label="PAYG"/> <c:menu_item acl="" page="payg.cfm" label="PAYG"/>
<c:menu_item acl="" page="test_storage_rpt.cfm" label="Тесты и хранение"/> <c:menu_item acl="" page="test_storage_rpt.cfm" label="Тесты и хранение"/>
<c:menu_item acl="" page="vm_gpu_rpt.cfm" label="Аэроплан"/>
<li class="menu-title">Справочники</li> <li class="menu-title">Справочники</li>
+59 -4
View File
@@ -67,7 +67,7 @@
</cfloop> </cfloop>
<cfset var outArray=arrayNew(1)/> <cfset var outArray=arrayNew(1)/>
//copy fields to structure, renaming fields, this preserves data type <!--- //copy fields to structure, renaming fields, this preserves data type --->
<cfloop query=#arguments.qry#> <cfloop query=#arguments.qry#>
<cfset var structRow=structNew()/> <cfset var structRow=structNew()/>
<cfloop index="i" from="1" to=#listLen(columnsOut)#> <cfloop index="i" from="1" to=#listLen(columnsOut)#>
@@ -76,9 +76,9 @@
<cfset arrayAppend(outArray,structRow)/> <cfset arrayAppend(outArray,structRow)/>
</cfloop> </cfloop>
<cfreturn serializeJSON(outArray)/> <cfreturn customJson(outArray)/>
<cfelse> <cfelse>
<cfreturn serializeJSON(arguments.qry,"struct")/> <cfreturn customJson(arguments.qry,"struct")/>
</cfif> </cfif>
</cffunction> </cffunction>
@@ -96,4 +96,59 @@
type="application/json" type="application/json"
/><cfoutput>#query2json(ATTRIBUTES.qRead,ATTRIBUTES.convertSnakeToCamel)#</cfoutput><!--- /><cfoutput>#query2json(ATTRIBUTES.qRead,ATTRIBUTES.convertSnakeToCamel)#</cfoutput><!---
,<cfoutput>"duration":"#(getTickCount()- msStartAt)#"</cfoutput>} ---> ,<cfoutput>"duration":"#(getTickCount()- msStartAt)#"</cfoutput>} --->
<cfexit method="exittag"/> <cfexit method="exittag"/>
<!--- *** еще добиться форматирования null, true, false
https://www.bennadel.com/blog/2165-returning-null-values-in-json-using-serializejson-and-coldfusion.htm
--->
<cfscript>
public string function customJson(required any data) {
return serializeJson(formatDatesToIso(arguments.data));
}
private any function formatDatesToIso(required any input) {
// 1. Handle Dates (Strict verification using isValid and checking underlying type)
// Lucee dates are typically objects (java.util.Date / java.time).
// This prevents strings like "12345" or "Yes" from being treated as dates.
if (isDate(arguments.input) && (isObject(arguments.input) || !isValid("integer", arguments.input))) {
return dateTimeFormat(arguments.input, "iso8601");
}
// 2. Handle Structs (Preserve case-sensitivity using [:] syntax)
if (isStruct(arguments.input)) {
var newStruct = [Ordered: true]; // Lucee/CF literal for preserving key case
for (var key in arguments.input) {
newStruct[key] = formatDatesToIso(arguments.input[key]);
}
return newStruct;
}
// 3. Handle Arrays
if (isArray(arguments.input)) {
var newArray = [];
for (var item in arguments.input) {
arrayAppend(newArray, formatDatesToIso(item));
}
return newArray;
}
// 4. Handle Queries
if (isQuery(arguments.input)) {
var queryArray = [];
var columns = arguments.input.getColumnNames(); // Returns correct case array
loop query="arguments.input" {
var rowStruct = [Ordered: true];
loop array="#columns#" index="col" {
// Safely grab cell using row coordinates
rowStruct[col] = formatDatesToIso(arguments.input[col][arguments.input.currentRow]);
}
arrayAppend(queryArray, rowStruct);
}
return queryArray;
}
// 5. Return everything else untouched (Strings, Numbers, Booleans, etc.)
return arguments.input;
}
</cfscript>
+45 -30
View File
@@ -13,8 +13,12 @@
<cfparam name="dt_finish" type="date" default=#dateAdd('d',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/> <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_start" type="date" default=#dateAdd('m',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
<cfset hours=dateDiff('h', dt_start, dateAdd('d', 1, dt_finish))/> <cfset hours=dateDiff('h', dt_start, dateAdd('d', 1, dt_finish))/>
<cfparam name="only_articles_in_spec" type="boolean" default=false/>
<cfparam name="wz" default=""/> <cfparam name="wz" default=""/>
<cfparam name="local.only_articles_in_spec" type="boolean" default=#structIsEmpty(form)? true : (form?.only_articles_in_spec GT 0)# /> <!--- не вполне корректный способ определить наличие формы, если там все чекбоксы и ни один не выбран... --->
<cfparam name="local.inner_join" type="boolean" default=#structIsEmpty(form)? true : (form?.inner_join GT 0)# />
<cfparam name="code" default=""/> <cfparam name="code" default=""/>
<!--- *** коды лучше взять из каталога ---> <!--- *** коды лучше взять из каталога --->
@@ -65,7 +69,7 @@
,siv.end_customer as end_customer_uid ,siv.end_customer as end_customer_uid
,z.id_klienta as end_customer_wz ,z.id_klienta as end_customer_wz
,z.__name as end_customer_name ,z.__name as end_customer_name
,ft.free_tier --,ft.free_tier
,s.probability_pc ,s.probability_pc
,siv.__id::text as siv_uid ,siv.__id::text as siv_uid
from elma.deals d from elma.deals d
@@ -78,7 +82,7 @@
join elma.additional_agreements aa on (aa.__id = ANY(siv.additional_agreement)) join elma.additional_agreements aa on (aa.__id = ANY(siv.additional_agreement))
join elma.aggrements a on (aa.contract_uid = a.__id) join elma.aggrements a on (aa.contract_uid = a.__id)
left outer join elma.companies z on (siv.end_customer=z.__id) left outer join elma.companies z on (siv.end_customer=z.__id)
left outer join (values ('paas.s3.ceph.vol-m',1),('paas.s3.ceph.get-m',10),('paas.s3.ceph.put-m',1),('paas.s3.ceph.trf-m',100)) as ft(code,free_tier) ON (p.code=ft.code)
where p.type IN (2,3) AND aa.is_actual where p.type IN (2,3) AND aa.is_actual
AND siv.date_nop < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dt_finish#/> AND siv.date_nop < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dt_finish#/>
AND (siv.date_end > <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dt_start#/> OR siv.date_end IS NULL)/*****/ AND (siv.date_end > <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dt_start#/> OR siv.date_end IS NULL)/*****/
@@ -334,9 +338,6 @@ select
<!--- выданы права на схему и таблицы, чтобы без толку не плодить источники данных ---> <!--- выданы права на схему и таблицы, чтобы без толку не плодить источники данных --->
<!--- считаются часы работы ВМ с GPU ---> <!--- считаются часы работы ВМ с GPU --->
<cfquery name="qGpu" datasource="billing-vc" cachedWithin=#createTimeSpan(0, 0, 31, 0)#> <cfquery name="qGpu" datasource="billing-vc" cachedWithin=#createTimeSpan(0, 0, 31, 0)#>
@@ -413,10 +414,10 @@ from qS3Vol
</cfif> </cfif>
union all union all
select wz, code || '.vol', get as raw_metric, get_10k as metric, '10k' as unit, bucket as dimension1 select wz, code || '.get', get as raw_metric, get_10k as metric, '10k' as unit, bucket as dimension1
from qS3OpsTrf from qS3OpsTrf
<cfif len(code)> <cfif len(code)>
where code || '.vol' like <cfqueryparam cfsqltype="cf_sql_varchar" value="%#code#%"/> where code || '.get' like <cfqueryparam cfsqltype="cf_sql_varchar" value="%#code#%"/>
</cfif> </cfif>
union all union all
@@ -487,10 +488,10 @@ SELECT
<d:field title="Артикул по специф.">s.code as s_code</d:field><!--- содержит суффикс типа тарификации ---> <d:field title="Артикул по специф.">s.code as s_code</d:field><!--- содержит суффикс типа тарификации --->
<d:field title="SKU code по специф.">s.sku_code as sku_code</d:field><!--- без суффикса ---> <d:field title="SKU code по специф.">s.sku_code as sku_code</d:field><!--- без суффикса --->
<d:field title="Артикул по метрикам">m.code as m_code</d:field><!--- без суффикса ---> <d:field title="Артикул по метрикам">m.code as m_code</d:field><!--- без суффикса --->
<d:field title="Кол-во компонента по спец.">s.component_quantity</d:field> <d:field title="Кол-во компонента по спец." cfSqlType="CF_SQL_NUMERIC">s.component_quantity</d:field>
<d:field title="Кол-во сервиса по спец.">s.service_quantity</d:field> <d:field title="Кол-во сервиса по спец." cfSqlType="CF_SQL_NUMERIC">s.service_quantity</d:field>
<d:field title="Скидка на сервис">s.service_discount</d:field> <d:field title="Скидка на сервис" cfSqlType="CF_SQL_NUMERIC">s.service_discount</d:field>
<d:field title="Free Tier">coalesce(s.free_tier,0) as free_tier</d:field> <d:field title="Free Tier" cfSqlType="CF_SQL_NUMERIC">coalesce(ft.free_tier,0) as free_tier</d:field>
<d:field>m.dimension1</d:field> <d:field>m.dimension1</d:field>
<d:field title="Тип по спец.">s.type</d:field> <d:field title="Тип по спец.">s.type</d:field>
<d:field title="Ед.изм.">m.unit</d:field> <d:field title="Ед.изм.">m.unit</d:field>
@@ -519,9 +520,16 @@ SELECT
<d:field title="Модификатор">s.modifier</d:field> <d:field title="Модификатор">s.modifier</d:field>
<d:field title="Компонент">s.component</d:field> <d:field title="Компонент">s.component</d:field>
<d:field title="Клиентс. назв.">s.user_description</d:field> <d:field title="Клиентс. назв.">s.user_description</d:field>
<d:field title="Pricing Model">CASE s.type WHEN 1 THEN 'install' WHEN 2 THEN 'fix' WHEN 3 THEN 'payg' ELSE NULL END as pricing_model</d:field>
</d:field_set> </d:field_set>
FROM qUnifiedMetric m FROM qUnifiedMetric m
FULL JOIN qSpec s ON (m.wz=s.wz AND m.code=s.sku_code) -- сервис или компонент, но не цена с типом тарификации, суффикс типа тарификации отрезан <cfif local.inner_join>
<cfelse>
FULL
</cfif>
JOIN qSpec s ON (m.wz=s.wz AND m.code=s.sku_code) -- сервис или компонент, но не цена с типом тарификации, суффикс типа тарификации отрезан
--left outer join (values ('paas.s3.ceph.vol-m',1),('paas.s3.ceph.get-m',10),('paas.s3.ceph.put-m',1),('paas.s3.ceph.trf-m',100)) as ft(code,free_tier) ON (m.code=ft.code)
left outer join (values ('paas.s3.ceph.vol',1),('paas.s3.ceph.get',10),('paas.s3.ceph.put',1),('paas.s3.ceph.trf',100)) as ft(code,free_tier) ON (m.code=ft.code)
LEFT JOIN qContragent c ON ((coalesce(s.wz,m.wz))=(c.wz)) -- пробелы в составе WZ стоили нескольких часов работы LEFT JOIN qContragent c ON ((coalesce(s.wz,m.wz))=(c.wz)) -- пробелы в составе WZ стоили нескольких часов работы
--(поскольку trim слева в джойне катастрофически замедляет работу, делаем чистку выше при формировании исходных резалтсетов) --(поскольку trim слева в джойне катастрофически замедляет работу, делаем чистку выше при формировании исходных резалтсетов)
WHERE 1=1 WHERE 1=1
@@ -581,18 +589,22 @@ ORDER BY coalesce(coalesce(c.wz, m.wz), s.wz), coalesce(s.code, m.code)
<form method="post" action=""> <form method="post" action="">
Период с <input type="text" name="dt_start" value="#dateFormat(dt_start,'YYYY-MM-DD')#"/> Период с <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="text" name="dt_finish" value="#dateFormat(dt_finish,'YYYY-MM-DD')#"/>
Только PAYG <input type="checkbox" name="only_articles_in_spec" value="1"<cfif only_articles_in_spec> checked</cfif>/> <input type="checkbox" name="only_articles_in_spec" id="only_articles_in_spec" value="1"<cfif local.only_articles_in_spec> checked</cfif>/><label for="only_articles_in_spec">Только PAYG </label>
<input type="checkbox" name="inner_join" id="inner_join" value="1"<cfif local.inner_join> checked</cfif>/><label for="inner_join">Только найденные в договоре</label>
&nbsp;&nbsp;&nbsp;
WZ <input type="text" name="wz" value="#wz#"/>по вхождению, с регистром WZ <input type="text" name="wz" value="#wz#"/>по вхождению, с регистром
Код <input type="text" name="code" value="#code#"/>>по вхождению &nbsp;&nbsp;&nbsp;
Код <input type="text" name="code" value="#code#"/>по вхождению
&nbsp;&nbsp;&nbsp;
<input type="submit" style="cursor:pointer;"/> <input type="submit" style="cursor:pointer;"/>
<input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/><!--- #dateFormat( input, "yyyy-mm-dd" )#T#timeFormat( input, "HH:mm:ss.lXX" )# ---> <input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/><!--- #dateFormat( input, "yyyy-mm-dd" )#T#timeFormat( input, "HH:mm:ss.lXX" )# --->
</form> </form>
Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#qCharge.recordCount#</b><br/> Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#qCharge.recordCount#</b><br/>
Актуальность данных: Актуальность данных:
VCD Computing: <b<cfif datediff("h",qComputingAge.dt_load, Now()) GT 24> class="err"</cfif>>>#dateFormat(qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(qComputingAge.dt_load,'HH:mm:ss.lXX')#</b> VCD Computing: <b<cfif datediff("h",qComputingAge.dt_load, Now()) GT 24> class="err"</cfif>>#dateFormat(qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(qComputingAge.dt_load,'HH:mm:ss.lXX')#</b>
VCD Storage: <b<cfif datediff("h",qStorageAge.dt_load, Now()) GT 24> class="err"</cfif>>>#dateFormat(qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(qStorageAge.dt_load,'HH:mm:ss.lXX')#</b> VCD Storage: <b<cfif datediff("h",qStorageAge.dt_load, Now()) GT 24> class="err"</cfif>>#dateFormat(qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(qStorageAge.dt_load,'HH:mm:ss.lXX')#</b>
S3 хранение: <b<cfif datediff("h",qS3VolAge.dt_load, Now()) GT 24> class="err"</cfif>>>#dateFormat(qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(qS3VolAge.dt_load,'HH:mm:ss.lXX')#</b> S3 хранение: <b<cfif datediff("h",qS3VolAge.dt_load, Now()) GT 24> class="err"</cfif>>#dateFormat(qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(qS3VolAge.dt_load,'HH:mm:ss.lXX')#</b>
S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load, Now()) GT 24> class="err"</cfif>>>#dateFormat(qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(qS3OpsTrfAge.dt_load,'HH:mm:ss.lXX')#</b> S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load, Now()) GT 24> class="err"</cfif>>#dateFormat(qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(qS3OpsTrfAge.dt_load,'HH:mm:ss.lXX')#</b>
<br/> <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_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a>
@@ -603,6 +615,7 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
<thead> <thead>
<td width="1%"></td> <td width="1%"></td>
<!--- <th width="3%">WZ</th> ---> <!--- <th width="3%">WZ</th> --->
<th width="7%">Услуга</th>
<th width="7%">Артикул специф.</th> <th width="7%">Артикул специф.</th>
<th width="5%">Кол-во специф.</th> <th width="5%">Кол-во специф.</th>
<th width="5%">Артикул метр.</th> <th width="5%">Артикул метр.</th>
@@ -616,9 +629,8 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
<td width="3%">&nbsp;&nbsp;&nbsp;</td> <td width="3%">&nbsp;&nbsp;&nbsp;</td>
<td width="10%">Услуга</td>
<td width="10%">Компонент</td> <td width="10%">Компонент</td>
<td width="15%">Клиентс. назв.</td> <td width="15%">Польз. наим.</td>
<td width="3%">GPL с НДС</td> <td width="3%">GPL с НДС</td>
<td width="3%">Скидка%</td> <td width="3%">Скидка%</td>
@@ -667,18 +679,19 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
<cfset accum_metric_svc += isNumeric(metric) ? metric : 0/> <cfset accum_metric_svc += isNumeric(metric) ? metric : 0/>
<cfset accum_metric_dimension_1 = 0/> <cfset accum_metric_dimension_1 = 0/>
<cfset accum_metric_dimension_1_all = 0/><!--- не различая PAYG/FIX --->
<cfset current_price=0/> <cfset current_price=0/>
<cfoutput><!--- dimensions ---> <cfoutput><!--- dimensions --->
<cfif type EQ 3> <cfif type EQ 3>
<cfset accum_metric_dimension_1 += isNumeric(metric) ? metric : 0/> <cfset accum_metric_dimension_1 += isNumeric(metric) ? metric : 0/>
<cfset current_price=gaussianRound(discounted_price, 2)/> <cfset current_price=gaussianRound(discounted_price, 2)/>
</cfif> </cfif>
<cfset accum_metric_dimension_1_all += isNumeric(metric) ? metric : 0/>
<!--- детализация ---> <!--- детализация --->
<tr<cfif !len(unit)> class="gray"</cfif>> <tr<cfif !len(unit)> class="gray"</cfif>>
<td>#currentRow#</a></td> <td>#currentRow#</a></td>
<td>#abstract_service#<cfif len(modifier)>: #modifier#</cfif></a></td>
<td><div<cfif type EQ 3> style="background:lightgreen;color:black;"</cfif>>#s_code#</div></td> <td><div<cfif type EQ 3> style="background:lightgreen;color:black;"</cfif>>#s_code#</div></td>
<td class=<cfif type EQ 3 AND component_quantity NEQ 1>"err r"<cfelse>"r"</cfif>>#component_quantity#</td> <td class=<cfif type EQ 3 AND component_quantity NEQ 1>"err r"<cfelse>"r"</cfif>>#component_quantity#</td>
<td>#m_code#</td> <td>#m_code#</td>
@@ -692,7 +705,6 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
<td></td> <td></td>
<td>#abstract_service# #modifier#</td>
<td>#component#</td> <td>#component#</td>
<td>#user_description#</td> <td>#user_description#</td>
@@ -708,7 +720,7 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
<!--- сырые исходные данные без округлений ---> <!--- сырые исходные данные без округлений --->
<td class="r">#price#</td> <td class="r">#price#</td>
<td class="r">#discounted_price#</td> <td class="r">#discounted_price#</td><!--- style="word-wrap:break-word" --->
<td class="r">#service_quantity#</td> <td class="r">#service_quantity#</td>
<td class="r">#service_discount#</td> <td class="r">#service_discount#</td>
<td class="r">#component_quantity#</td> <td class="r">#component_quantity#</td>
@@ -721,18 +733,21 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
</tr> </tr>
</cfoutput> </cfoutput>
<cfset final_metric_dimension_1 = ceiling(accum_metric_dimension_1)/> <cfset final_metric_dimension_1 = ceiling(accum_metric_dimension_1)/>
<cfset final_metric_dimension_1_all = ceiling(accum_metric_dimension_1_all)/>
<cfset cost_dimension_1 = precisionEvaluate("gaussianRound(max(final_metric_dimension_1 - free_tier, 0)*current_price, 2)")/> <cfset cost_dimension_1 = precisionEvaluate("gaussianRound(max(final_metric_dimension_1 - free_tier, 0)*current_price, 2)")/>
<cfset accum_cost_svc = precisionEvaluate("accum_cost_svc + cost_dimension_1")/><!--- простое сложение дает ошибку округления и лишние знаки, но и precisionEvaluate не выглядит слишком надежным ---> <cfset accum_cost_svc = precisionEvaluate("accum_cost_svc + cost_dimension_1")/><!--- простое сложение дает ошибку округления и лишние знаки, но и precisionEvaluate не выглядит слишком надежным --->
<tr> <tr>
<td colspan="3"></td> <td colspan="4"></td>
<td colspan="2" class="b r">Итого #code#:</td> <td colspan="2" class="b r">Итого #code#:</td>
<td class="b r" style="font-size:110%; padding:0 1em;"><cfif final_metric_dimension_1 NEQ 0 AND isNumeric(final_metric_dimension_1)>#round(final_metric_dimension_1,3)#</cfif></td> <td class="b r" style="font-size:110%; padding:0 1em;">#accum_metric_dimension_1#<cfif final_metric_dimension_1 NEQ 0 AND isNumeric(final_metric_dimension_1)>#round(final_metric_dimension_1,3)#</cfif></td>
<td class="b r">#free_tier#</td> <td class="b r">#free_tier#</td>
<td class="b r" style="font-size:120%; padding:0 1em;"><cfif final_metric_dimension_1 NEQ 0 AND isNumeric(final_metric_dimension_1)>#max(ceiling(final_metric_dimension_1) - free_tier, 0)#</cfif></td> <td class="b r" style="font-size:120%; padding:0 1em;"><cfif final_metric_dimension_1 NEQ 0 AND isNumeric(final_metric_dimension_1)>#max(ceiling(final_metric_dimension_1) - free_tier, 0)#</cfif></td>
<td class="b c">#unit# <cfif len(ed_izm)>[#ed_izm#]</cfif></td> <td class="b c">#unit# <cfif len(ed_izm)>[#ed_izm#]</cfif></td>
<td class="b r">#current_price#</td> <td class="b r">#current_price#</td>
<td class="b r">#gaussianRoundSafe(cost_dimension_1,2)#</td> <td class="b r">#gaussianRoundSafe(cost_dimension_1,2)#</td>
<td></td> <td></td>
<td colspan="8"></td>
<td class="b r" title="сумма корректна, если нет замножения строк из-за наличия PAYG и FIX по одному артикулу">#request.roundSafe(accum_metric_dimension_1_all,3)#<!--- #final_metric_dimension_1_all# ---></td>
<td colspan="100"></td> <td colspan="100"></td>
</tr> </tr>
</cfoutput><!--- serviceStart ---> </cfoutput><!--- serviceStart --->
@@ -740,7 +755,7 @@ S3 операции и трафик: <b<cfif datediff("h",qS3OpsTrfAge.dt_load,
<tr> <tr>
<td bgcolor="##eee" style="border-bottom:2px solid gray;"></td> <td bgcolor="##eee" style="border-bottom:2px solid gray;"></td>
<td bgcolor="##eee" colspan="9" class="r b" style="border-bottom:2px solid gray;">Итого <a href="contragent.cfm?__id=#contragent_uid#&#tr.fwx#">#wz#</a> #contragent#:</td> <td bgcolor="##eee" colspan="10" class="r b" style="border-bottom:2px solid gray;">Итого <a href="contragent.cfm?__id=#contragent_uid#&#tr.fwx#">#wz#</a> #contragent#:</td>
<td bgcolor="##eee" class="r b nw" style="border-bottom:2px solid gray; font-size:130%;">#accum_cost_svc#</td> <td bgcolor="##eee" class="r b nw" style="border-bottom:2px solid gray; font-size:130%;">#accum_cost_svc#</td>
<td bgcolor="##eee" style="border-bottom:2px solid gray;"></td> <td bgcolor="##eee" style="border-bottom:2px solid gray;"></td>
<td bgcolor="##eee" colspan="99" style="border-bottom:2px solid gray;"></td> <td bgcolor="##eee" colspan="99" style="border-bottom:2px solid gray;"></td>
+156
View File
@@ -0,0 +1,156 @@
<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:silent silent="No">
<cffunction name="hideNonPositive">
<cfargument name="a"/>
<cfreturn (a GT 0)? a : ""/>
</cffunction>
<m:prepare_ls entity="s3_user" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<!--- <m:filter_settings target="#pageInfo.entity#_ls">
<m:filterparam filter=#filter# param="quickfilter" ftype="string" prefix="%" suffix="%" expression="((p.project like ?) OR (p.customer like ?) OR (p.customer_alias like ?) OR (p.contragent_class_type like ?) OR (p.division like ?) OR (p.performer_short like ?))" default=""/>
</m:filter_settings> --->
<!--- <cfset pageInfo.settings.filter=#filter#/> --->
<!--- <cftry> --->
<cfquery name="qS3" datasource="billing-s3">
select
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
u.id
,u.owner
,(select count(*) from s3billing.bucket_stat b where b.timestamp_addition=t.timestamp_addition AND b.owner=u.owner) as bucket_cnt
,(select sum(b.usage_rgw_main_size_actual) from s3billing.bucket_stat b where b.timestamp_addition=t.timestamp_addition AND b.owner=u.owner) as size_byte
,(select STRING_AGG(i.name ||'['||
case
when ( p.name = 'default-placement') then 'HOT'
else p.name
end || ']', ', ' order by i.name)
from s3billing.bucket_stat b
join s3billing.bucket_info i on (b.bucket_id= i.id)
join s3billing.placement p on (i.placement_id = p.id)
where b.timestamp_addition=t.timestamp_addition AND b.owner=u.owner
) as buckets
,'WZ' || lpad(u.name, 5, '0') as wz
from s3billing.user_info u
cross join (select max(timestamp_addition) as timestamp_addition from s3billing.bucket_stat) t
</cfquery>
<!--- <cfdump var=#qRead#/> --->
<!--- <cfcatch type="database">
<m:ls_catch catch=#cfcatch# status=#pageInfo.status#/>
</cfcatch>
</cftry> --->
<cfquery name="qCountTotal" datasource="billing-s3">
select count(*) as cnt from s3billing.user_info where 1=1
</cfquery>
<cfquery name="qContragent" datasource="dwh">
select upper(trim(k.id_klienta)) as wz, k.__id::text as contragent_uid, k.__name as contragent,
c.__name as responsible, c.email as responsible_email, c.__status_status as responsible_status
from elma.companies k
LEFT OUTER JOIN elma.comp_users_ext c on (k.responsible=c.__id)
where k."__deletedAt" IS NULL
</cfquery>
<cfset db = new MemoryDbHelper()/>
<cfset db.register("qS3", qS3, "inmem")/>
<cfset db.register("qContragent", qContragent, "inmem")/>
<cfquery name="qRead" datasource="inmem">
select
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="id" cfSqlType="CF_SQL_INTEGER">s.id</d:field>
<d:field title="owner">s.owner</d:field>
<d:field title="Бакетов" cfSqlType="CF_SQL_INTEGER">s.bucket_cnt</d:field>
<d:field title="Объем байт" cfSqlType="CF_SQL_INTEGER">s.size_byte</d:field>
<d:field title="Бакеты">s.buckets</d:field>
<d:field title="WZ">c.wz</d:field>
<d:field title="Контрагент">c.contragent</d:field>
<d:field title="contragent_uid">c.contragent_uid</d:field>
<d:field title="Ответственный">c.responsible</d:field>
<d:field title="Email ответственного">c.responsible_email</d:field>
</d:field_set>
from qS3 s
left outer join qContragent c on (s.wz=c.wz)
where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/>
order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/>
</cfquery>
</m:silent><!---
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
---><cfif isDefined("output_xls")>
<layout:xml qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/>
</cfif><cfif isDefined("output_json")>
<layout:json qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.json"/>
<cfabort/>
</cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput><b>Учетки S3</b></cfoutput>
</layout:attribute>
<!--- <layout:attribute name="controls"></layout:attribute> --->
</layout:page>
<cfif pageInfo.readPermitted() AND !pageInfo.status.errorState>
<layout:grid_summary
recordCount=#qRead.recordCount#
totalCount=#qCountTotal.cnt#
footerOut="gridFooter"
excelLink="Yes"
jsonLink="Yes"
/>
<table class="worktable">
<thead>
<layout:grid_head titleMap=#titleMap# sortArray=#pageInfo.settings.sort.sortArray#>
<!--- <tr> --->
<th width="1%"><layout:column_head name="id"/></th>
<th width="10%"><layout:column_head name="owner"/></th>
<th width="2%"><layout:column_head name="wz"/></th>
<th width="10%"><layout:column_head name="contragent"/></th>
<!--- <th width="3%"><layout:column_head name="contragent_uid"/></th> --->
<th width="5%"><layout:column_head name="responsible"/></th>
<th width="5%"><layout:column_head name="responsible_email"/></th>
<th width="1%"><layout:column_head name="bucket_cnt"/></th>
<th width="3%"><layout:column_head name="size_byte"/></th>
<th width="60%"><layout:column_head name="buckets"/></th>
<!--- </tr> --->
</layout:grid_head>
</thead>
<cfoutput query="qRead">
<tr>
<td class="r">#id#</td>
<td<cfif !(isNumeric(owner) AND owner LT 10000)> class="err"</cfif>>#owner#</td>
<td><a href="contragent.cfm?__id=#contragent_uid#&#tr.fwx#">#wz#</a></td>
<td><a href="contragent.cfm?__id=#contragent_uid#&#tr.fwx#">#contragent#</a></td>
<td>#responsible#</td>
<td><a href="mailto:#responsible_email#">#responsible_email#</a></td>
<td class="r">#bucket_cnt#</td>
<td class="r">#size_byte#</td>
<td>#buckets#</td>
</tr>
</cfoutput>
</table>
<cfoutput>#gridFooter#</cfoutput>
</cfif>
<layout:page section="footer"/>
+73 -6
View File
@@ -59,7 +59,42 @@ SELECT upper(k.id_klienta) as wz, k.__id as id, c.__name as responsible, c.email
LEFT OUTER JOIN elma.comp_users_ext c on (k.responsible=c.__id) LEFT OUTER JOIN elma.comp_users_ext c on (k.responsible=c.__id)
</cfquery> </cfquery>
<!--- patch date ---> <cfquery name="qCurrentDeal">
WITH
status(status, status_id, probability_pc)
AS (VALUES
('Закрыта неуспешно',7,0),('Закрыта успешно',6,100),('Договор подписан',5,100),('Договор на подписании',15,90),('Договор на согласовании',12,70),
('Тестирование',8,50),('Отправлено ТКП',10,20),('Внутреннее согласование',37,10),('Проработка решения',4,10),('Сбор потребностей',1,0)
),
current_deal
AS (select
d.__id, d._companies, d.__status_status, d.__index, d.__name, d.kommentarii_k_sdelke, d."__statusComment", d."__deletedAt", d.is_corrective_transaction,
a.__id as additional_agreement_uid, a.__name as additional_agreement,
g.__id as contract_uid, g.__name as contract, g.date_of_aggrement as dt_contract, g.agreement_id as contract_id /*сохранена авторская орфография*/
,ROW_NUMBER() OVER (PARTITION BY _companies ORDER BY d.__index DESC) rn
from elma.deals d
join status s on (d.__status_status=s.status_id)
left outer join elma.additional_agreements a on (d.__id=a.deal_uid)
left outer join elma.aggrements g on (a.contract_uid = g.__id)
where d."__deletedAt" IS NULL AND s.probability_pc > 0
AND NOT coalesce(d.is_corrective_transaction, false)
AND NOT d.__status_status=7
)
SELECT z.id_klienta as wz, z.__name as company,
d.__id::text as deal_uid, d.__status_status as status_id, s.status, d.__index as deal_index, d.__name as deal,
d.additional_agreement_uid::text as additional_agreement_uid, d.additional_agreement,
d.contract_uid::text as contract_uid, d.contract, d.dt_contract, d.contract_id
FROM elma.companies z
LEFT JOIN current_deal d ON (z.__id=d._companies AND d.rn=1)
LEFT JOIN status s ON (d.__status_status=s.status_id)
where z."__deletedAt" IS NULL
ORDER BY z.id_klienta, d.__index DESC
</cfquery>
<!--- <cfdump var=#qCurrentDeal#/>
--->
<!--- patch date to make id processable--->
<cfloop query="qStorage"> <cfloop query="qStorage">
<cftry> <cftry>
<cfif len(qStorage.end_test_date)> <cfif len(qStorage.end_test_date)>
@@ -75,13 +110,14 @@ SELECT upper(k.id_klienta) as wz, k.__id as id, c.__name as responsible, c.email
<cfset db = new MemoryDbHelper()/> <cfset db = new MemoryDbHelper()/>
<cfset db.register("qStorage", qStorage, "inmem")/> <cfset db.register("qStorage", qStorage, "inmem")/>
<cfset db.register("qResponsible", qResponsible, "inmem")/><!--- имя колонки __id мы не принимаем почему-то ---> <cfset db.register("qResponsible", qResponsible, "inmem")/><!--- имя колонки __id мы не принимаем почему-то --->
<cfset db.register("qCurrentDeal", qCurrentDeal, "inmem")/><!--- имя колонки __id мы не принимаем почему-то --->
<cfquery name="qRead" datasource="inmem"> <cfquery name="qRead" datasource="inmem">
select select
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount"> <d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="Тенант">s.tenant</d:field> <d:field title="Тенант">s.tenant</d:field>
<d:field title="WZ">s.wz</d:field> <d:field title="WZ">s.wz</d:field>
<d:field title="status">s.status</d:field> <d:field title="status">s.status</d:field><!--- статус ресурса, а не сделки --->
<d:field title="End of Test">s.end_test_date</d:field> <d:field title="End of Test">s.end_test_date</d:field>
<d:field title="End of Test">s.dt_end_of_test</d:field> <d:field title="End of Test">s.dt_end_of_test</d:field>
<d:field title="VDC">s.vdc</d:field> <d:field title="VDC">s.vdc</d:field>
@@ -94,9 +130,23 @@ SELECT upper(k.id_klienta) as wz, k.__id as id, c.__name as responsible, c.email
<d:field title="Ответственный за к/а">r.responsible</d:field> <d:field title="Ответственный за к/а">r.responsible</d:field>
<d:field title="Email отв-го">r.responsible_email</d:field> <d:field title="Email отв-го">r.responsible_email</d:field>
<d:field title="Статус отв-го">r.responsible_status</d:field> <d:field title="Статус отв-го">r.responsible_status</d:field>
<d:field>d.deal_uid</d:field>
<d:field title="Номер сделки">d.deal_index</d:field>
<d:field title="Сделка">d.deal</d:field>
<d:field title="Статус сделки">d.status_id</d:field>
<d:field title="Статус сделки">d.status as deal_status</d:field>
<d:field>d.additional_agreement_uid</d:field>
<d:field title="Допник">d.additional_agreement</d:field>
<d:field>d.contract_uid</d:field>
<d:field title="Договор">d.contract</d:field>
<d:field title="Дата договора">d.dt_contract</d:field>
<d:field title="Номер договора">d.contract_id</d:field>
</d:field_set> </d:field_set>
from qStorage s from qStorage s
LEFT OUTER JOIN qResponsible r ON (s.wz=r.wz) LEFT OUTER JOIN qResponsible r ON (s.wz=r.wz)
LEFT OUTER JOIN qCurrentDeal d ON (s.wz=d.wz)
ORDER BY ORDER BY
<m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/> <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/>
</cfquery> </cfquery>
@@ -133,12 +183,20 @@ ORDER BY
<table class="worktable"> <table class="worktable">
<layout:grid_head titleMap=#titleMap# sortArray=#pageInfo.settings.sort.sortArray#> <layout:grid_head titleMap=#titleMap# sortArray=#pageInfo.settings.sort.sortArray#>
<th><layout:column_head name="wz"/></th>
<th><layout:column_head name="tenant"/></th> <th><layout:column_head name="tenant"/></th>
<th><layout:column_head name="wz"/></th>
<th><layout:column_head name="status"/></th> <th><layout:column_head name="status"/></th>
<!--- <th><layout:column_head name="end_test_date"/></th> ---> <!--- <th><layout:column_head name="end_test_date"/></th> --->
<th><layout:column_head name="dt_end_of_test"/></th> <th><layout:column_head name="dt_end_of_test"/></th>
<th><layout:column_head name="deal"/></th>
<th><layout:column_head name="deal_status"/></th>
<th><layout:column_head name="additional_agreement"/></th>
<th><layout:column_head name="contract"/></th>
<th><layout:column_head name="dt_contract"/></th>
<th><layout:column_head name="vdc"/></th> <th><layout:column_head name="vdc"/></th>
<th><layout:column_head name="provider_vdc"/></th> <th><layout:column_head name="provider_vdc"/></th>
<th><layout:column_head name="code"/></th> <th><layout:column_head name="code"/></th>
@@ -150,14 +208,23 @@ ORDER BY
<th><layout:column_head name="responsible_email"/></th> <th><layout:column_head name="responsible_email"/></th>
<th><layout:column_head name="responsible_status"/></th> <th><layout:column_head name="responsible_status"/></th>
</layout:grid_head> </layout:grid_head>
<cfoutput query="qRead"> <cfoutput query="qRead">
<tr> <tr>
<!--- <td><a href="contragent.cfm?__id=#company_uid#&#tr.fwx#">#WZ#</a><a href="contragent_rpt.cfm?__id=#company_uid#&#tr.fwx#" style="float:right"><img src="img/view.gif" title="сводка по контрагенту"/></a></td> ---> <!--- <td><a href="contragent.cfm?__id=#company_uid#&#tr.fwx#">#WZ#</a><a href="contragent_rpt.cfm?__id=#company_uid#&#tr.fwx#" style="float:right"><img src="img/view.gif" title="сводка по контрагенту"/></a></td> --->
<td>#tenant#</td>
<td class="c"><a href="contragent.cfm?id_klienta=#UCASE(wz)#&#tr.fwx#">#wz#</td> <td class="c"><a href="contragent.cfm?id_klienta=#UCASE(wz)#&#tr.fwx#">#wz#</td>
<td>#tenant#</td>
<td<cfif NOT (status EQ "prod" OR status EQ "internal-test")> class="b"</cfif>>#status#</td> <td<cfif NOT (status EQ "prod" OR status EQ "internal-test")> class="b"</cfif>>#status#</td>
<!--- <td>#end_test_date#</td> ---> <!--- <td>#end_test_date#</td> --->
<td class="c<cfif NOT (status EQ "prod" OR status EQ "internal-test") AND isDate(dt_end_of_test) AND dt_end_of_test LE Now()> b err</cfif>">#dateFormat(dt_end_of_test,"YYYY-MM-DD")#</td> <td class="c<cfif NOT (status EQ "prod" OR status EQ "internal-test") AND isDate(dt_end_of_test) AND dt_end_of_test LE Now()> b err</cfif>">#dateFormat(dt_end_of_test,"YYYY-MM-DD")#</td>
<td><a href="deal.cfm?__id=#deal_uid#&#tr.fwx#">#deal#</a></td>
<td>#deal_status#<cfif len(status_id)> [#status_id#]</cfif></td>
<td><a href="additional_agreement.cfm?__id=#additional_agreement_uid#&#tr.fwx#">#additional_agreement#</a></td>
<td><a href="contract.cfm?__id=#contract_uid#&#tr.fwx#">#contract#</a> <cfif contract_id NEQ contract>(#contract_id#)</cfif> </td>
<td>#dateFormat(dt_contract,"DD.MM.YYYY")#</td>
<td>#VDC#</td> <td>#VDC#</td>
<td>#provider_vdc#</td> <td>#provider_vdc#</td>
<td>#code#</td> <td>#code#</td>
+165
View File
@@ -0,0 +1,165 @@
<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="vm_gpu_rpt" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<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))#/>
<cfset hours=dateDiff('h', dt_start, dateAdd('d', 1, dt_finish))/>
<!--- <cfparam name="only_articles_in_spec" type="boolean" default=false/> --->
<cfparam name="wz" default="WZ01516"/>
<cfparam name="code" default=""/>
<cfquery name="qRead" datasource="billing-s3">
select
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="Час" cfSqlType="CF_SQL_TIMESTAMP">DATE_TRUNC('hour',p.ts) as ts_hour</d:field>
<d:field title="Уникальных ВМ" cfSqlType="CF_SQL_INTEGER">count(distinct p.obj_uuid) as unique_vm_cnt</d:field> <!--- -- все ВМ, существовавшие в данный час --->
<d:field title="ВМ вкл." cfSqlType="CF_SQL_INTEGER">count(distinct p.obj_uuid) FILTER (WHERE p.value=1) as unique_vm_cnt_on</d:field> <!--- -- все ВМ, включавшиеся за данный час --->
<d:field title="ВМ-часов" cfSqlType="CF_SQL_NUMERIC">round((sum(p.value)/12.)::numeric,1) as vm_h</d:field> <!--- -- 5 minute sampling, so /12 --->
<d:field title="Стоимость без диска" cfSqlType="CF_SQL_NUMERIC">round((sum(p.value)/12.*24.19)::numeric,2) as cost_wo_disk</d:field>
<d:field title="Стоимость диска" cfSqlType="CF_SQL_NUMERIC">round((count(distinct obj_id)*8.00)::numeric,2) as disk_cost</d:field> <!--- -- считаем все ВМ, существовавшие в данный час --->
<d:field title="Круглосуточных ВМ" cfSqlType="CF_SQL_NUMERIC">round((sum(p.value)/12.)::numeric, 1) as avg_roundclock_vm</d:field>
</d:field_set>
from gpu.vm
JOIN gpu.vm_power_state p on (vm.obj_uuid=p.obj_uuid)
where 1=1
/*watch time zone, accounting by MSK*/
AND p.ts >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dt_start#/>
AND p.ts < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,dt_finish)#/>
AND vm.client_id='WZ01516'
GROUP BY DATE_TRUNC('hour',p.ts)
ORDER BY 1;
</cfquery>
<cfif isDefined("DEBUG")>
<cfdump var=#qRead#/>
<cfif !isDefined("output_xls") AND !isDefined("output_json")><cfflush/></cfif>
</cfif>
<cfquery name="qGpuAge" datasource="billing-s3"><!--- cachedWithin=#createTimeSpan(0, 0, 25, 0)# --->
select max(ts) as dt_load from gpu.vm_power_state
</cfquery>
<cfoutput>qGpuAge:#getTickCount()-request.startTickCount#</cfoutput><cfif !isDefined("output_xls") AND !isDefined("output_json")><cfflush/></cfif>
<cfif isDefined("output_xls")>
<layout:xml qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/>
</cfif><cfif isDefined("output_json")>
<layout:json qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.json"/>
<cfabort/>
</cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput><b>Отчет по включенным ВМ с GPU</b></cfoutput>
</layout:attribute>
<layout:attribute name="controls">
<!---skip filter link, filter is not implemented--->
<!---<layout:language_switch/>--->
</layout:attribute>
</layout:page>
<cfoutput>
<form method="post" action="">
Период с <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')#"/>
<!--- WZ <input type="text" name="wz" value="#wz#"/>по вхождению, с регистром
Код <input type="text" name="code" value="#code#"/>по вхождению --->
<input type="submit" style="cursor:pointer;"/>
<input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/><!--- #dateFormat( input, "yyyy-mm-dd" )#T#timeFormat( input, "HH:mm:ss.lXX" )# --->
</form>
Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#qRead.recordCount#</b><br/>
Актуальность данных:
GPU-VM Power State: <b<cfif datediff("h",qGpuAge.dt_load, Now()) GT 24> class="err"</cfif>>#dateFormat(qGpuAge.dt_load,'YYYY-MM-DD')# #timeFormat(qGpuAge.dt_load,'HH:mm:ss.lXX')#</b>
</b>
<br/>
<a href="#request.thisPage#?output_xls&dt_start=#dateFormat(dt_start,'YYYY-MM-DD')#&dt_finish=#dateFormat(dt_finish,'YYYY-MM-DD')#" 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&dt_start=#dateFormat(dt_start,'YYYY-MM-DD')#&dt_finish=#dateFormat(dt_finish,'YYYY-MM-DD')#" 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>
<i>(перед выгрузкой отчета на произвольную дату просмотрите его в браузере)</i>
</cfoutput>
<table class="worktable">
<thead>
<th width="10%">Час</th>
<th width="10%">Уникальных ВМ</th>
<th width="10%">ВМ вкл</th>
<th width="10%">ВМ-часов</th>
<th width="10%">Стоимость без диска</th>
<th width="10%">Стоимость диска</th>
<th width="10%">Круглосуточных ВМ</th>
</thead>
<cfoutput query="qRead">
<tr>
<td class="c">#dateFormat(ts_hour,"YYYY-MM-DD")# #timeFormat(ts_hour,"HH:MM")#</td>
<td class="r">#unique_vm_cnt#</td>
<td class="r">#unique_vm_cnt_on#</td>
<td class="r">#vm_h#</td>
<td class="r">#cost_wo_disk#</td>
<td class="r">#disk_cost#</td>
<td class="r">#avg_roundclock_vm#</td>
</tr>
</cfoutput>
</table>
<!--- <cfdump var=#qCharge#/> --->
<!---
<cfdump var=#this.datasources[request.ds].connectionString#/>
<cfdump var=#this.datasources["billing-vc"].connectionString#/> --->
<cfdump var=#this.datasources["billing-s3"].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'); --->
<!--- select t.id as tenant_id,
from ngcloud_ru.tenant t
left outer join
(select tenant_id, ts, metadata_id,
ROW_NUMBER() OVER (PARTITION BY tenant_id ORDER BY ts desc, metadata_id desc)
as rn
from ngcloud_ru.tenant_metadata) tm
on (t.id = tm.tenant_id AND tm.rn = 1)
left outer join ngcloud_ru.metadata m on (tm.metadata_id=m.id) --->
<cfscript>
string function gaussianRoundSafe(any num, numeric precision = 0, string default="") {
return (isNumeric(arguments.num)) ? gaussianRound(arguments.num, arguments.precision) : arguments.default;
}
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>
+10 -10
View File
@@ -119,8 +119,8 @@ S3 операции и трафик: <b>#dateFormat(qS3OpsTrfAge.dt_load,'YYYY-M
<td>#Organization#</td> <td>#Organization#</td>
<td>#VDC#</td> <td>#VDC#</td>
<td><cftry>#round(VDC_CPU_Reserv,3)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(VDC_CPU_Reserv,3)#<cfcatch></cfcatch></cftry></td>
<td><cftry>#round(VDC_CPU_Speed,3)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(VDC_CPU_Speed,3)#<cfcatch></cfcatch></cftry></td>
<td>#qVM.Cluster#</td> <td>#qVM.Cluster#</td>
@@ -129,19 +129,19 @@ S3 операции и трафик: <b>#dateFormat(qS3OpsTrfAge.dt_load,'YYYY-M
<td>#CPU_Model#</td> <td>#CPU_Model#</td>
<td>#Host_CPU_Cores#</td> <td>#Host_CPU_Cores#</td>
<td>#Host_CPU_Sockets#</td> <td>#Host_CPU_Sockets#</td>
<td>#round(Host_CPU_Speed,3)#</td> <td>#request.roundSafe(Host_CPU_Speed,3)#</td>
<td>#round(Host_RAM)#</td> <td>#request.roundSafe(Host_RAM)#</td>
<td><cftry>#round(Host_CPU_Used,1)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(Host_CPU_Used,1)#<cfcatch></cfcatch></cftry></td>
<td><cftry>#round(Host_CPU_Used_Avg,1)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(Host_CPU_Used_Avg,1)#<cfcatch></cfcatch></cftry></td>
<td>#VM_vCPU_count#</td> <td>#VM_vCPU_count#</td>
<td><cftry>#round(VM_CPU_Used,1)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(VM_CPU_Used,1)#<cfcatch></cfcatch></cftry></td>
<td><cftry>#round(VM_CPU_Used_AVG,1)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(VM_CPU_Used_AVG,1)#<cfcatch></cfcatch></cftry></td>
<td><cftry>#round(VM_CPU_Reserv_GHz,1)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(VM_CPU_Reserv_GHz,1)#<cfcatch></cfcatch></cftry></td>
<td>#VM_CPU_Reserv_perc#</td> <td>#VM_CPU_Reserv_perc#</td>
<td>#VM_RAM_Allocated#</td> <td>#VM_RAM_Allocated#</td>
<td><cftry>#round(VM_RAM_Used)#<cfcatch></cfcatch></cftry></td> <td><cftry>#request.roundSafe(VM_RAM_Used)#<cfcatch></cfcatch></cftry></td>
<td>#VM_is_ON#</td> <td>#VM_is_ON#</td>