diff --git a/Application.cfc b/Application.cfc index 0a8b172..ac4711e 100644 --- a/Application.cfc +++ b/Application.cfc @@ -6,7 +6,7 @@ - + @@ -14,21 +14,34 @@ - + - + + + @@ -76,7 +89,7 @@ - + diff --git a/MemoryDbHelper.cfc b/MemoryDbHelper.cfc new file mode 100644 index 0000000..22b795a --- /dev/null +++ b/MemoryDbHelper.cfc @@ -0,0 +1,98 @@ +component { + + /** + * HSQL 1.8 + * Принимает Lucee Query, создает в HSQL временную таблицу и льет в нее данные + * @name Имя таблицы, которое ты хочешь использовать в SQL + * @data Сам запрос (результат queryExecute или cfhttp) + * @dsn Имя datasource для HSQL (по умолчанию "inmem") + */ + public string function register(required string name, required query data, string dsn = "inmem") { + var cols = arguments.data.columnList; + var meta = getMetaData(arguments.data); + + //writedump(meta); + var colDefs = []; + var colTypes = {}; + var params = []; + + // 1. Генерируем "CREATE TEMP TABLE name (col1 TYPE, col2 TYPE...)" + for (var i = 1; i <= arrayLen(meta); i++) { + var colName = meta[i].name; + // Маппинг типов Lucee -> типы H2 + var dbType = mapType(meta[i].typeName); + arrayAppend(colDefs, "#colName# #dbType#"); + colTypes[colName] = meta[i].typeName; + } + //writedump(colDefs); + //var checkSql = "SELECT * FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE TABLE_NAME = '#ucase(arguments.name)#';" + //var res = queryExecute(checkSql, {}, { datasource: arguments.dsn }); + + //if (res.recordCount EQ 0) { + var createSql = "DROP TABLE IF EXISTS #arguments.name#; + CREATE TEMPORARY TABLE #arguments.name#(#arrayToList(colDefs, ', ')#) ON COMMIT PRESERVE ROWS"; + queryExecute(createSql, {}, { datasource: arguments.dsn }); + //} + + // 2. Цикл вставки + var insertSql = "INSERT INTO #arguments.name# (#cols#) VALUES (#repeatString("?,", listLen(cols)-1)#?)"; + + for (var row in arguments.data) { + var rowData = []; + for (var col in cols) { + + arrayAppend(rowData, formatValue(row[col], colTypes[col])); + } + + queryExecute(insertSql, rowData, { datasource: arguments.dsn }); + } + + return arguments.name; + } + + /** + * Удалить временные таблицы (опционально, база сама почистит при закрытии коннекта) + */ + public void function drop(required string names, string dsn = "inmem") { + var list = arguments.names; + if (!isArray(list)) list = [list]; + for (var name in list) { + queryExecute("DROP TABLE IF EXISTS #name#", {}, { datasource: arguments.dsn }); + } + } + + // --- Вспомогательная функция маппинга типов --- + private string function mapType(required string luceeType) { + switch (arguments.luceeType) { + case "VARCHAR": case "CHAR": case "LONGVARCHAR": return "LONGVARCHAR"; + case "INTEGER": case "BIGINT": case "SMALLINT": case "TINYINT": return "INT"; + case "DOUBLE": case "FLOAT": case "REAL": return "DOUBLE"; + case "BOOLEAN": case "BIT": return "BIT"; + case "DECIMAL": case "NUMERIC": return "DECIMAL(20,6)"; + default: return "LONGVARCHAR"; // И тут тоже + } + } + + private function formatValue(required any val, required string targetType) { + var isTextType = listFindNoCase("VARCHAR,CHAR,LONGVARCHAR", arguments.targetType); + + // 1. Обработка пустых значений + if (isNull(arguments.val) || (isSimpleValue(arguments.val) && len(trim(arguments.val)) == 0)) { + // Если это текстовое поле — сохраняем пустую строку как есть + if (isTextType) { + return ""; + } + // Для чисел, дат и булев — отдаем настоящий SQL NULL + return javaCast("null", ""); + } + + // 2. Форматирование непустых дат + if (listFindNoCase("DATE,TIMESTAMP,TIME", arguments.targetType)) { + return dateFormat(arguments.val, "yyyy-mm-dd") & " " & timeFormat(arguments.val, "HH:mm:ss"); + } + + // 3. Числа и непустой текст отдаем как есть + return arguments.val; + } + +} \ No newline at end of file diff --git a/contragent.cfm b/contragent.cfm index 475b321..2c11812 100644 --- a/contragent.cfm +++ b/contragent.cfm @@ -246,7 +246,7 @@ -m ---> - #type# + style="background:lightgreen">#type# #is_actual# #hash# #version# diff --git a/contragent_rpt.cfm b/contragent_rpt.cfm index 2258513..66f3361 100644 --- a/contragent_rpt.cfm +++ b/contragent_rpt.cfm @@ -62,6 +62,7 @@ #d.__name# [#d.__id#] + #d.id_klienta# @@ -235,7 +236,7 @@ -

Услуги по данным CRM (версии строк спецификации, только актуальные на сегодня, сделки с вероятностью от 50% (начиная с тестирования)

+

Услуги по данным CRM (из DWH, версии строк спецификации, только актуальные на сегодня, сделки с вероятностью от 50% (начиная с тестирования)

@@ -265,7 +266,7 @@ #user_description# - + @@ -283,7 +284,7 @@ -
#cnt# #nFmt(cost*cnt*(100-(isNumeric(discount)?discount:0))/100)# + style="background:lightgreen;color:black;"> install fix @@ -301,6 +302,7 @@
+

Текущее потребление по данным VmReports (VROPS)

select @@ -309,18 +311,19 @@ ,c."VDC" ,c."VDC_CPU_Reserv" ,"Cluster" - , round(sum(c."VM_RAM_Allocated")) as ram - , round(sum(CASE WHEN "VM_is_ON" > 0 THEN c."VM_RAM_Allocated" ELSE 0 END)) as ram_on - , round(sum(c."VM_vCPU_count")) as cpu - , round(sum(CASE WHEN "VM_is_ON" > 0 THEN c."VM_vCPU_count" ELSE 0 END)) as cpu_on - , round(avg(c."VDC_CPU_Speed")::numeric,1) as ghz + ,round(sum(c."VM_RAM_Allocated")) as ram + ,round(sum(CASE WHEN "VM_is_ON" > 0 THEN c."VM_RAM_Allocated" ELSE 0 END)) as ram_on + ,round(sum(c."VM_vCPU_count")) as cpu + ,round(sum(CASE WHEN "VM_is_ON" > 0 THEN c."VM_vCPU_count" ELSE 0 END)) as cpu_on + ,round(avg(c."VDC_CPU_Speed")::numeric,1) as ghz + ,min(c.ts) as ts FROM vmreports.compute c - WHERE ts=(select max(ts) from vmreports.compute) + WHERE c.ts=(select max(ts) from vmreports.compute) AND lower(c."WZ")=lower() GROUP BY c."Organization_UUID", c."Organization", c."VDC", c."VDC_CPU_Reserv", c."Cluster" - + select sum(ram) as ram ,sum(ram_on) as ram_on @@ -329,7 +332,7 @@ FROM qCompute -

Compute факт

+

Compute факт #dateFormat(qCompute.ts,'YYYY-MM-DD')# #timeFormat(qCompute.ts,'HH:MM:SS')#

@@ -379,10 +382,11 @@ ,"VDC" ,"Cluster" ,"Storage_Policy" - , round(sum(c."Disk_Allocated")) as disk_alloc - , round(sum(c."Disk_Used")) as disk_used + ,round(sum(c."Disk_Allocated")) as disk_alloc + ,round(sum(c."Disk_Used")) as disk_used + ,min(c.ts) as ts FROM vmreports.storage c - WHERE ts=(select max(ts) from vmreports.storage) + WHERE c.ts=(select max(ts) from vmreports.storage) AND lower(c."WZ")=lower() GROUP BY c."Organization_UUID",c."Organization",c."VDC",c."Cluster",c."Storage_Policy" @@ -394,7 +398,7 @@ FROM qStorage -

Storage факт

+

Storage факт #dateFormat(qStorage.ts,'YYYY-MM-DD')# #timeFormat(qStorage.ts,'HH:MM:SS')#

@@ -435,18 +439,20 @@ ,"OrgStatus" ,"OrgVDC" ,(select STRING_AGG(DISTINCT "Cluster",',') FROM vmreports.compute c - WHERE ts=(select max(ts) from vmreports.compute) - AND g."Org"=c."Organization") as org_cluster + WHERE ts=(select max(ts) from vmreports.compute) + AND g."Org"=c."Organization" + ) as org_cluster ,"Name" ,"GPU" + ,g.ts as ts FROM vmreports.vm_gpu g - WHERE ts=(select max(ts) from vmreports.vm_gpu) + WHERE g.ts=(select max(ts) from vmreports.vm_gpu) AND lower(g."ClientID")=lower() -

GPU факт

+

GPU факт #dateFormat(qGpu.ts,'YYYY-MM-DD')# #timeFormat(qGpu.ts,'HH:MM:SS')#

diff --git a/payg.cfm b/payg.cfm index 9d3aeda..842a0a5 100644 --- a/payg.cfm +++ b/payg.cfm @@ -7,18 +7,16 @@ - + + + + + - - - - - - + select distinct p.code from elma.deals d join elma.deals_services siv on (d.__id=ANY(siv.deal)) @@ -29,22 +27,26 @@ where p.type=3 AND aa.is_actual order by p.code - - #getTickCount()-request.startTickCount# +qCode:#getTickCount()-request.startTickCount# - + select k.id_klienta as wz, k.__id::text as contragent_uid, k.__name as contragent from elma.companies k where k."__deletedAt" IS NULL - #getTickCount()-request.startTickCount# +qContragent:#getTickCount()-request.startTickCount# + - + + select k.id_klienta as wz, k.__id::text as contragent_uid, p.code, p.price, p.discount, k.__name as contragent, a.__name as contract, a.__id::text as contract_uid, d.__name as deal, d.__id::text as deal_uid, siv.abstractive_service as abstract_service, siv.modifier, siv.user_description, siv.hash as line_key - ,siv.date_nop as dt_from, siv.date_end as dt_to - /*,p.__name as line_key*/, p.ed_izm, p.name as component, aa.__name as additional_agreement, aa.__id::text as additional_agreement_uid + ,siv.date_nop as dt_from, siv.date_end as dt_to, siv.discount as service_discount, siv.cnt as service_quantity + /*,p.__name as line_key*/, p.ed_izm, p.name as component, p.type, aa.__name as additional_agreement, aa.__id::text as additional_agreement_uid --, aa.is_actual from elma.deals d join elma.deals_services siv on (d.__id=ANY(siv.deal)) @@ -52,35 +54,34 @@ join elma.companies k on (d._companies=k.__id) join elma.additional_agreements aa on (aa.__id = ANY(siv.additional_agreement)) join elma.aggrements a on (aa.contract_uid = a.__id) - where p.type=3 AND aa.is_actual + where p.type IN (2,3) AND aa.is_actual AND siv.date_nop < - AND (siv.date_end > OR siv.date_end IS NULL) + AND (siv.date_end > OR siv.date_end IS NULL)/*****/ order by k.id_klienta, p.code - - #getTickCount()-request.startTickCount# - - - - select max(dt_load) as dt_load from elma.deals_services - - - #getTickCount()-request.startTickCount# - +qSpec:#getTickCount()-request.startTickCount# + + + select max(dt_load) as dt_load from elma.deals_services + +qSivAge:#getTickCount()-request.startTickCount# - + + select m.clientid as wz, vdc.name as vdc_name, case - when vdc.name like '%-v1cl1%' then 'iaas.ngc.i29' - when vdc.name like '%-v1cl2%' then 'iaas.ngc.i31' - when vdc.name like '%-v1cl4%' then 'iaas.ngc.a28' - when vdc.name like '%-v1cl6%' then 'iaas.ngc.i28' + WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29' + WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31' + WHEN vdc.name LIKE '%-v1cl4%' THEN 'iaas.ngc.a28' + WHEN vdc.name LIKE '%-v1cl5%' THEN 'iaas.ngc.a45' + WHEN vdc.name LIKE '%-v1cl6%' THEN 'iaas.ngc.i28' + WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40' -- *** смапить все кластера else '['||vdc.name||']' end as code, sum(cpu_used/cpu_speed)/4. as core_h, sum(mem_used)/1024/4. as gb_h @@ -94,44 +95,47 @@ ) tm on (t.id = tm.tenant_id AND tm.rn = 1) left outer join ngcloud_ru.metadata m on (tm.metadata_id=m.id) where length(m.clientid) > 0 - AND capacity_resource.timestamp >= - AND capacity_resource.timestamp < + AND capacity_resource.timestamp::date >= + AND capacity_resource.timestamp::date <= group by m.clientid, vdc.name order by m.clientid, vdc.name; - #getTickCount()-request.startTickCount# +qComputing:#getTickCount()-request.startTickCount# - + select max(capacity_resource.timestamp) as dt_load from ngcloud_ru.capacity_resource - #getTickCount()-request.startTickCount# +qComputingAge:#getTickCount()-request.startTickCount# - + select m.clientid as wz, vdc.name as vdc_name, case WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29' WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31' WHEN vdc.name LIKE '%-v1cl4%' THEN 'iaas.ngc.a28' + WHEN vdc.name LIKE '%-v1cl5%' THEN 'iaas.ngc.a45' WHEN vdc.name LIKE '%-v1cl6%' THEN 'iaas.ngc.i28' + WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40' -- *** смапить все кластера ELSE '['||vdc.name||']' END || CASE - WHEN storage_profile_types.name ILIKE '%-SSD%' THEN '.ssd-m' - WHEN storage_profile_types.name ILIKE '%-SAS' THEN '.sas-m' - WHEN storage_profile_types.name ILIKE '%-SATA' THEN '.sata-m' + WHEN storage_profile_types.name ILIKE '%FASTSSD%' THEN '.fstssd-m' + WHEN storage_profile_types.name ILIKE '%SSD%' THEN '.ssd-m' + WHEN storage_profile_types.name ILIKE '%SAS%' THEN '.sas-m' + WHEN storage_profile_types.name ILIKE '%SATA%' THEN '.sata-m' ELSE '.['|| storage_profile_types.name ||']' END as code, storage_profile_types.name, -- *** это не тип, а профайл - sum("limit")/1024./4 as gb_h_limit, -- 4 потому, что сбор раз в 15 минут - sum(used)/1024./4 as gb_h_used + sum("limit")/1024./4./720. as gb_m_limit, -- 4 потому, что сбор раз в 15 минут + sum(used)/1024./4./720. as gb_m_used -- 720 это количество часов в условном месяце from ngcloud_ru.capacity_storage join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id join ngcloud_ru.tenant t on t.id=vdc.tenant_id @@ -143,31 +147,31 @@ left outer join ngcloud_ru.metadata m on (tm.metadata_id=m.id) join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id where length(m.clientid) > 0 - AND capacity_storage.timestamp >= - AND capacity_storage.timestamp < + AND capacity_storage.timestamp::date >= + AND capacity_storage.timestamp::date < group by m.clientid, vdc.name, storage_profile_types.name order by m.clientid, vdc.name, storage_profile_types.name; - #getTickCount()-request.startTickCount# - - - select max(capacity_storage.timestamp) as dt_load from ngcloud_ru.capacity_storage - +qStorage:#getTickCount()-request.startTickCount# - #getTickCount()-request.startTickCount# + + + select max(capacity_storage.timestamp) as dt_load from ngcloud_ru.capacity_storage + +qStorageAge:#getTickCount()-request.startTickCount# - + select /* *** ниже дублирование кода агрегации - round ceil etc, обратить внимание при правке */ (sum(bucket_stat.usage_rgw_main_size_actual)/) as vol_B ,ceil(sum(bucket_stat.usage_rgw_main_size_actual)//1024/1024/1024) as vol_GB @@ -178,31 +182,31 @@ HOT_FREE_LIMIT 1 10 1 100 when bucket_stat.placement_id = 1 then 'paas.s3.ceph' when bucket_stat.placement_id = 2 then 'paas.s3.cphc' else '['|| TO_CHAR(bucket_stat.placement_id,'FM9') ||']' end as code - --,bucket_stat.placement_id + ,bucket_stat.placement_id ,'WZ'||lpad(bucket_stat.owner, 5, '0') as wz -- *** здесь не число, а строка, поэтому другая формула обратной сборки WZ from s3billing.bucket_stat join s3billing.placement ON s3billing.placement.id=bucket_stat.placement_id join s3billing.bucket_info ON bucket_info.id=bucket_stat.bucket_id where bucket_stat.placement_id in (1,2) - AND bucket_stat.timestamp_addition >= - AND bucket_stat.timestamp_addition < + AND bucket_stat.timestamp_addition::date >= + AND bucket_stat.timestamp_addition::date <= group by bucket_stat.owner, bucket_stat.placement_id order by bucket_stat.owner, bucket_stat.placement_id; +qS3Vol:#getTickCount()-request.startTickCount# + - #getTickCount()-request.startTickCount# - - + select max(timestamp_addition) as dt_load from s3billing.bucket_stat - #getTickCount()-request.startTickCount# +qS3VolAge:#getTickCount()-request.startTickCount# select @@ -273,51 +277,83 @@ select join s3billing.bucket_info on usage_bucket_by_user.bucketid=bucket_info.id join s3billing.placement on bucket_info.placement_id=placement.id where 1=1 - AND usage_bucket_by_user.time >= - AND usage_bucket_by_user.time < + AND usage_bucket_by_user.time::date >= + AND usage_bucket_by_user.time::date <= group by user_info.name, bucket_info.placement_id order by user_info.name, bucket_info.placement_id; +qS3OpsTrf:#getTickCount()-request.startTickCount# + - + select max(time) as dt_load from s3billing.usage_bucket_by_user +qS3OpsTrfAge:#getTickCount()-request.startTickCount# + + + +select wz, code || '.vcpu-m' as code, core_h as raw_metric, core_h as metric, core_h as chargeable_metric, 'ядро-ч' as unit +from qComputing - -select wz, code || '.vcpu-m' as code, core_h as raw_metric, core_h as metric, core_h as chargeable_metric from qComputing union all -select wz, code || '.ram-m' as code, gb_h as raw_metric, gb_h as metric, gb_h as chargeable_metric from qComputing +select wz, code || '.ram-m', gb_h as raw_metric, gb_h as metric, gb_h as chargeable_metric, 'ГБайт-ч' as unit +from qComputing + union all -select wz, code, gb_h_used as raw_metric, gb_h_used as metric, gb_h_used as chargeable_metric from qStorage +select wz, code, gb_m_used as raw_metric, gb_m_used as metric, gb_m_used as chargeable_metric, 'ГБайт-м' as unit +from qStorage + union all -select wz, code || '.vol-m', vol_b as raw_metric, vol_gb as metric, vol_GB_free_tier_subtracted as chargeable_metric from qS3Vol +select wz, code || '.vol-m', vol_b as raw_metric, vol_gb as metric, vol_GB_free_tier_subtracted as chargeable_metric, 'ГБайт-м' as unit +from qS3Vol + union all -select wz, code || '.get-m', get as raw_metric, get_10k as metric, get_10k_free_tier_subtracted as chargeable_metric from qS3OpsTrf +select wz, code || '.get-m', get as raw_metric, get_10k as metric, get_10k_free_tier_subtracted as chargeable_metric, '10тыс.шт.' as unit +from qS3OpsTrf + union all -select wz, code || '.put-m', put as raw_metric, put_10k as metric, put_10k_free_tier_subtracted as chargeable_metric from qS3OpsTrf +select wz, code || '.put-m', put as raw_metric, put_10k as metric, put_10k_free_tier_subtracted as chargeable_metric, '10тыс.шт.' as unit +from qS3OpsTrf + union all -select wz, code || '.trf-m', bytes_sent as raw_metric, bytes_sent_gb as metric, bytes_sent_gb_free_tier_subtracted as chargeable_metric from qS3OpsTrf +select wz, code || '.trf-m', bytes_sent as raw_metric, bytes_sent_gb as metric, bytes_sent_gb_free_tier_subtracted as chargeable_metric, 'ГБайт' as unit +from qS3OpsTrf + +order by wz, code + + +qUnifiedMetric:#getTickCount()-request.startTickCount# + - + + + + + + + + SELECT - c.wz - m.code - m.chargeable_metric + coalesce(coalesce(c.wz, m.wz), s.wz) as wz + coalesce(m.code, s.code) as code + f.code as f_code + m.chargeable_metric + m.unit s.ed_izm - s.price*(100-s.discount)/100 as discounted_price - s.price*(100-s.discount)/100*m.chargeable_metric as charge + s.service_quantity*(100-s.service_discount)/100*s.price*(100-s.discount)/100 as discounted_price + s.service_quantity*(100-s.service_discount)/100*s.price*(100-s.discount)/100*m.chargeable_metric as charge s.dt_from s.dt_to s.line_key @@ -325,8 +361,8 @@ SELECT s.discount m.raw_metric m.metric - c.contragent - c.contragent_uid + coalesce(c.contragent,cs.contragent) as contragent + coalesce(c.contragent_uid,cs.contragent_uid) as contragent_uid s.contract s.contract_uid s.additional_agreement @@ -338,49 +374,30 @@ SELECT s.component s.user_description -FROM qUnifiedMetric m, qContragent c, qSpec s WHERE m.wz=c.wz AND m.wz=s.wz AND m.code=s.code +FROM qUnifiedMetric m +FULL JOIN qSpec s ON (m.wz=s.wz AND m.code=s.code) +LEFT JOIN qSpec f ON (m.wz=f.wz AND m.code=f.code AND f.type=2) +LEFT JOIN qContragent c ON (m.wz=c.wz) +LEFT JOIN qContragent cs ON (s.wz=cs.wz) -- неуклюже, но иначе контрагент теряется +WHERE 1=1 AND s.type=3 + + AND EXISTS(select * from qSpec qs where qs.wz=m.wz AND qs.type=3) + +--AND s.wz='WZ01762' + +ORDER BY wz,code - -SELECT - - c.wz - m.code - m.chargeable_metric - null as ed_izm - null discounted_price - 0 as charge - null as dt_from - null as dt_to - null as line_key - null as price - null as discount - m.raw_metric - m.metric - c.contragent - c.contragent_uid - null as contract - null as contract_uid - null as additional_agreement - null as additional_agreement_uid - null as deal - null as deal_uid - null as abstract_service - null as modifier - null as component - null as user_description - -FROM qUnifiedMetric m, qContragent c WHERE m.wz=c.wz AND m.wz NOT IN (#listQualify(listRemoveDuplicates(ValueList(qChargeInner.wz)),"'")#) - + - -select * from qChargeInner -union -select * from qChargeOuter -order by wz,code - - +qCharge:#getTickCount()-request.startTickCount# + + + + @@ -401,6 +418,8 @@ order by wz,code +Факт потребления по основной базе биллинга, спецификации из DWH. Без детализации по бакетам, VDC и т.д. +Все запросы кэшированы на 25 минут.
Внимание! округление вверх при расчете метрик get, put S3. Расчетные суммы могут содержать погрешности округления, поэтому окончательный расчет стоимости (количество * цену) должен выполняться в бухгалтерской программе. Если у клиента больше 1 действующего допника с услугами PAYG, то данные в отчете могут быть замножены, в этом случае считать вручную
@@ -409,8 +428,10 @@ order by wz,code
Период с по +Только контрагенты с PAYG* checked
/> +* любым PAYG, не только по облаку Часов в периоде: #hours# Строк в отчете: #qCharge.recordCount#
Актуальность данных: @@ -465,15 +486,15 @@ S3 операции и трафик: #dateFormat(qS3OpsTrfAge.dt_load,'YYYY-M
- + - - - - - + + + + + @@ -481,15 +502,15 @@ S3 операции и трафик: #dateFormat(qS3OpsTrfAge.dt_load,'YYYY-M - + - - + + @@ -498,7 +519,7 @@ S3 операции и трафик: #dateFormat(qS3OpsTrfAge.dt_load,'YYYY-M - + @@ -506,6 +527,9 @@ S3 операции и трафик: #dateFormat(qS3OpsTrfAge.dt_load,'YYYY-M
#currentRow##code##chargeable_metric##ed_izm##discounted_price##charge##code# (#f_code#)#request.roundSafe(chargeable_metric,3)##len(unit)?unit:'['& ed_izm &']'##gaussianRoundSafe(discounted_price,2)##gaussianRoundSafe(charge,2)# #component# #user_description##price##gaussianRoundSafe(price,2)# #discount# #dateFormat(dt_from,'YYYY-MM-DD')# #dateFormat(dt_to,'YYYY-MM-DD')# #line_key##raw_metric##metric##request.roundSafe(raw_metric,3)##request.roundSafe(metric,3)# #contract# #additional_agreement# #deal#
Итого #wz# #contragent#:#NumFmt(acc,2)##NumFmt(acc,2)#
+ + + @@ -519,4 +543,31 @@ left outer join 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) ---> \ No newline at end of file +left outer join ngcloud_ru.metadata m on (tm.metadata_id=m.id) ---> + +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; + } +} + \ No newline at end of file diff --git a/test.cfm b/test.cfm new file mode 100644 index 0000000..96660bb --- /dev/null +++ b/test.cfm @@ -0,0 +1,20 @@ + + select svc_id,svc,descr,dt_created,creator_id,DT_UPDATED, UPDATER_ID, IS_PRODUCTION_READY, CODE, MAN SVC_SHORT, RESOURCE_REALM_TYPE_ID, VERSION ,ORCHESTRATOR_NAME, SORT, SVC_GROUP_ID, ICON_SRC, SYNONYMS from svc limit 100; + + + + + + + + + + + select * from eq + + + \ No newline at end of file