089 xl json dates, gpu fixes

This commit is contained in:
2026-09-28 18:22:17 +03:00
parent d6193481e1
commit 669eaec34f
7 changed files with 119 additions and 19 deletions
+2 -1
View File
@@ -15,6 +15,7 @@
<cfset this.mappings["/mod"] = getDirectoryFromPath(getCurrentTemplatePath()) & "mod/" />
<cfset this.customTagPaths = expandPath(getDirectoryFromPath(getCurrentTemplatePath())&'mod')/>
<cfset this.timeZone = "Europe/Moscow"/><!--- для биллинга очень важен часовой пояс, поэтому ставим вручную в коде --->
<!--- <cfset this.serialization.json.date = "iso8601"/> --->
<cfset this.datasource = "dwh"/>
<cfset this.defaultdatasource = this.datasource/>
@@ -87,7 +88,7 @@
<!--- global settings --->
<!--- *************************************************************************************************** --->
<cfset request.APP_VERSION="0.00.087"/> <!--- *********************************************************** --->
<cfset request.APP_VERSION="0.00.089"/> <!--- *********************************************************** --->
<!--- *************************************************************************************************** --->
<cfset request.STAND=getStand()/>
<cfset request.RECORDS_PER_PAGE=500/>
+3 -3
View File
@@ -54,7 +54,7 @@
k.__name as contragent
,k.id_klienta as wz
,k.__index as contragent_index
,d."_plannedDueDate" as _plannedDueDate
<!--- ,d."_plannedDueDate" as _plannedDueDate --->
,d."__createdAt"
,d."__index" as deal_index
,s.status
@@ -131,12 +131,12 @@
</div>
</div>
<div class="tr">
<!--- <div class="tr">
<div class="th">_plannedDueDate</div>
<div class="td">#dateFormat(qDecoration._plannedDueDate,'YYYY-MM-DD')#
Паразитное поле, не использовать в расчетах
</div>
</div>
</div> --->
<div class="tr">
<div class="th">Статус</div>
+26 -8
View File
@@ -8,21 +8,39 @@
<m:prepare_ls entity="crm2cloud" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<cfset this.nullSupport = true/>
<cfquery name="qGPU" datasource="billing-vc" cachedWithin=#createTimeSpan(0, 0, 25, 0)#>
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
<cfquery name="qRead" datasource="billing-vc" cachedWithin=#createTimeSpan(0, 0, 25, 0)#>
select
<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
order by client_id
</cfquery>
<!--- <cfdump var=#qGPU# abort=true/> --->
<!--- <cfdump var=#qRead# abort=true/> --->
<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/>
</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/>
</cfif><!---
---><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>
</cfoutput>
<cfoutput><b>#qGPU.recordCount#</b></cfoutput>
<cfoutput><b>#qRead.recordCount#</b></cfoutput>
<table class="worktable">
<thead>
<tr>
@@ -62,7 +80,7 @@ order by client_id
</tr>
</thead>
<cfoutput query="qGPU">
<cfoutput query="qRead">
<tr>
<td><a href="contragent.cfm?wz=#WZ#&#tr.fwx#">#WZ#</a></td>
<td>#obj_uuid#</td>
+25
View File
@@ -311,6 +311,29 @@ public string function formatJsonString(str) {
}
request.formatJsonString = formatJsonString;
//нужна еще функция, которая сериализует структуру как 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>
<cffunction name=structGetKey>
@@ -361,3 +384,5 @@ request.formatJsonString = formatJsonString;
</cffunction>
<cfset request.camel2snake = camel2snake/>
+59 -4
View File
@@ -67,7 +67,7 @@
</cfloop>
<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#>
<cfset var structRow=structNew()/>
<cfloop index="i" from="1" to=#listLen(columnsOut)#>
@@ -76,9 +76,9 @@
<cfset arrayAppend(outArray,structRow)/>
</cfloop>
<cfreturn serializeJSON(outArray)/>
<cfreturn customJson(outArray)/>
<cfelse>
<cfreturn serializeJSON(arguments.qry,"struct")/>
<cfreturn customJson(arguments.qry,"struct")/>
</cfif>
</cffunction>
@@ -96,4 +96,59 @@
type="application/json"
/><cfoutput>#query2json(ATTRIBUTES.qRead,ATTRIBUTES.convertSnakeToCamel)#</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>
+1 -1
View File
@@ -219,7 +219,7 @@ ORDER BY
<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# [#status_id#]</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>
+3 -2
View File
@@ -89,9 +89,10 @@ GPU-VM Power State: <b<cfif datediff("h",qGpuAge.dt_load, Now()) GT 24> class="e
</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_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" 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&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>