182 lines
6.7 KiB
Plaintext
182 lines
6.7 KiB
Plaintext
<cfset msStartAt=getTickCount()/>
|
||
<cfparam name="dt_finish" type="date" default=#createDateTime(year(Now()),month(Now()),1,0,0,0)#/>
|
||
<cfparam name="dt_start" type="date" default=#dateAdd('m',-1,dt_finish)#/>
|
||
<cfset hours=dateDiff('h',dt_start,dt_finish)/>
|
||
<cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ --->
|
||
|
||
<!--- <cffunction name="snake2camel"
|
||
returntype="string"
|
||
output="false"
|
||
hint="convert snake style name to camel style name">
|
||
<cfargument name="snake" type="string" required="true" />
|
||
<cfreturn #reReplace(ARGUMENTS.snake,"_([a-z])","\u\1","ALL")#/>
|
||
</cffunction>
|
||
|
||
<cffunction name="camel2snake"
|
||
returntype="string"
|
||
output="false"
|
||
hint="convert camel style name to snake style name">
|
||
<cfargument name="camel" type="string" required="true" />
|
||
<cfreturn #reReplace(ARGUMENTS.camel,"([A-Z])","_\l\1","ALL")#/>
|
||
</cffunction>
|
||
|
||
|
||
<cffunction name="iso8601dtFormat"
|
||
returntype="string"
|
||
output="false"
|
||
hint="formates date and time like this: 2023-12-18T15:41:42.516+0300">
|
||
<cfargument name="input" required="true" />
|
||
<cfreturn "#dateFormat( input, "yyyy-mm-dd" )#T#timeFormat( input, "HH:mm:ss.lXX" )#"/>
|
||
</cffunction>
|
||
|
||
|
||
<cffunction name="query2array"
|
||
output="false"
|
||
hint="print query in json format as array of structures">
|
||
<cfargument name="qry" type="query" required="true"/><!--- column names should not contain commas --->
|
||
<cfargument name="convertSnakeToCamel" type="boolean" default=true/>
|
||
|
||
<cfset var columnsIn=#arguments.qry.columnList()#/>
|
||
<cfset var columnsOut=""/>
|
||
|
||
<cfif arguments.convertSnakeToCamel>
|
||
<cfloop list=#columnsIn# item="col">
|
||
<cfset columnsOut=listAppend(columnsOut,snake2camel(lcase(col)))/>
|
||
</cfloop>
|
||
|
||
<cfset var outArray=arrayNew(1)/>
|
||
<!--- //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)#>
|
||
<cfset structInsert(structRow,"#listGetAt(#columnsOut#,i)#",arguments.qry["#listGetAt(#columnsIn#,i)#"])/>
|
||
</cfloop>
|
||
<cfset arrayAppend(outArray,structRow)/>
|
||
</cfloop>
|
||
<!--- <cfdump var=#outArray#/> --->
|
||
<cfreturn outArray/>
|
||
<cfelse>
|
||
<cfreturn #arguments.qry.reduce((result, row) => {
|
||
result.append(row);
|
||
return result;
|
||
}, [])#/>
|
||
</cfif>
|
||
</cffunction> --->
|
||
|
||
<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>
|
||
|
||
|
||
<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")#
|
||
/>
|
||
|
||
<cfset temp.out=["dtStart"="#dateFormat(dt_start, "yyyy-mm-dd")#"
|
||
,"dtFinish"="#dateFormat(dt_finish, "yyyy-mm-dd")#"
|
||
,"hours"=#hours#
|
||
,"tenantWzIndex"="#tenant_wz_index#"
|
||
,"periods"=[]
|
||
]/><!--- using [] instead of {} for struct literals to obtain linked (ordered) structures: keys order preserved --->
|
||
<!--- beware nulls --->
|
||
<cfoutput query="report.qCharge" group="year">
|
||
<cfoutput group="month">
|
||
<cfset temp.period=[
|
||
"year"=#year#
|
||
,"month"=#month#
|
||
,"services"=[]
|
||
]/>
|
||
<cfset arrayAppend(temp.out.periods,temp.period)/>
|
||
|
||
<cfoutput group="svc">
|
||
<cfset var accum_cost_svc = 0/>
|
||
<!--- <cfset var accum_metric_component = 0/> --->
|
||
|
||
<cfset "temp.service"=["svc"="#svc#"
|
||
,"serviceСode"="#service_code#"
|
||
,"serviceCustomName"="#serviceCustomName#"
|
||
<!--- ,"note"="#(serviceHash EQ '')?'не найдено в договоре':''#" --->
|
||
,"hash"="#serviceHash#"
|
||
<!--- ,"metric"=#accum_metric_component# --->
|
||
,"components"=[]
|
||
]/>
|
||
|
||
<cfoutput group="code">
|
||
<cfoutput group="serviceStart">
|
||
<!--- <cfset accum_metric_component += isNumeric(metric) ? metric : 0/> --->
|
||
<cfset accum_metric_dimension_1 = 0/>
|
||
|
||
<cfset "temp.component"=["code"="#code#"
|
||
,"codeMetered"="#m_code#"
|
||
,"component"="#component#"
|
||
,"unit"="#unit#"
|
||
,"freeTier"="#free_tier#"
|
||
,"price"="#gaussianRound(discounted_price,2)#"
|
||
,"details"=[]
|
||
]/>
|
||
|
||
<cfoutput group="dimension_1">
|
||
<cfset accum_metric_dimension_1 += isNumeric(metric) ? metric : 0/>
|
||
|
||
<cfset temp.dimension=["dimension1name"="#dimension_1_name#"
|
||
,"dimension1value"="#dimension_1#"
|
||
,"dtBilledFrom"="#dateFormat(dt_billed_from,'YYYY-MM-DD')#"
|
||
,"dtBilledTo"="#dateFormat(dt_billed_to,'YYYY-MM-DD')#"
|
||
,"metric"=#(metric NEQ 0 AND isNumeric(metric))?round(metric,3):javaCast("null",0)#
|
||
,"unit"="#unit#"
|
||
<!--- ,"price"="#gaussianRound(discounted_price,2)#" --->
|
||
]/>
|
||
|
||
<cfset arrayAppend(temp.component.details, temp.dimension)/>
|
||
</cfoutput><!--- dimension_1 --->
|
||
|
||
<!--- считаем, что free_tier NOT NULL (вместо NULL 0) --->
|
||
<cfset var payable_metric_dimension_1 = (isNumeric(accum_metric_dimension_1))? round(max(accum_metric_dimension_1 - free_tier, 0),3):javaCast("null",0)/>
|
||
<cfset var cost_dimension_1 = precisionEvaluate("gaussianRound(payable_metric_dimension_1*gaussianRound(discounted_price, 2), 2)")/>
|
||
<cfset accum_cost_svc = precisionEvaluate("accum_cost_svc + cost_dimension_1")/>
|
||
|
||
<cfset "temp.component.metric"=#(accum_metric_dimension_1 NEQ 0 AND isNumeric(accum_metric_dimension_1))? round(accum_metric_dimension_1,3):javaCast("null",0)#/>
|
||
<cfset "temp.component.metricPayable"=#payable_metric_dimension_1#/>
|
||
<cfset "temp.component.cost"=#cost_dimension_1#/>
|
||
|
||
<cfset arrayAppend(temp.service.components, temp.component)/>
|
||
</cfoutput><!--- serviceStart (component version in specification, that is svc version joined down to the component) --->
|
||
|
||
<cfset "temp.service.cost"=#accum_cost_svc#/>
|
||
</cfoutput><!--- code (component) --->
|
||
|
||
<cfset arrayAppend(temp.period.services, temp.service)/>
|
||
</cfoutput><!--- svc --->
|
||
</cfoutput><!--- month --->
|
||
</cfoutput><!--- year --->
|
||
<cfset "temp.out.durationMs"=(getTickCount()-request.startTickCount)/>
|
||
<cfcontent
|
||
type="application/json"
|
||
/><cfoutput>#serializeJSON(temp.out)#</cfoutput>
|
||
|
||
|