From 91b6687220e1a08a4e38b3b3bf1fb61a5cc3ee8e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 14 Aug 2026 18:07:24 +0300 Subject: [PATCH] 071 deep debug --- Application.cfc | 26 +++- etc/db/20260812.sql | 3 + etc/db/billing_schema_vc.sql | 4 +- etc/db/billing_schema_vmreports.sql | 16 ++- mod/MemoryDbHelper.cfc | 99 ++++++++++++++ mod/compute_payg.cfm | 196 ++++++++++++++-------------- mod/crm_contract.cfm | 8 +- payg.cfm | 52 ++++++-- 8 files changed, 277 insertions(+), 127 deletions(-) create mode 100644 etc/db/20260812.sql create mode 100644 mod/MemoryDbHelper.cfc diff --git a/Application.cfc b/Application.cfc index d0193e8..eb0baa1 100644 --- a/Application.cfc +++ b/Application.cfc @@ -38,6 +38,20 @@ + + + @@ -94,7 +108,7 @@ - + @@ -155,8 +169,8 @@ ---> - - + + @@ -196,14 +210,17 @@ var requestData = GetHttpRequestData(); var headers = requestData.headers; + var local_token = false; //для отладки мы можем использовать токен из окружения, но когда он истекает, это легко упустить и потом искать проблему //var authorizationHeader = server.system.environment?.AUTHORIZATION; if (structKeyExists(headers,"Authorization")) { request.auth_header = headers.Authorization; + //writedump(headers.Authorization); } else { //request.auth_header = createObject("java", "java.lang.System").getEnv("AUTHORIZATION"); request.auth_header = server.system.environment?.AUTHORIZATION; //так короче + local_token = true; if (isNull(request.auth_header)){ /*for local debug we can inject long-living token into environment*/ structDelete(request,"auth_header"); } else { @@ -236,8 +253,9 @@ if (prefix.status_code NEQ 200) { //writedump(this.iamServiceUrl); //writedump(resp); + //abort; var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500; - throw(message="IAM error", detail="URL: #this.iamServiceUrl# Status Code: #prefix.status_code#"); + throw(message="IAM error", detail="local_token: #local_token# URL: #this.iamServiceUrl# Status Code: #prefix.status_code#"); //abort; //throw("IDP response not OK"); } diff --git a/etc/db/20260812.sql b/etc/db/20260812.sql new file mode 100644 index 0000000..90bc77a --- /dev/null +++ b/etc/db/20260812.sql @@ -0,0 +1,3 @@ +truncate table ngcloud_ru.tenant; +alter table ngcloud_ru.tenant ADD clientid varchar(30) NULL; +alter table ngcloud_ru.tenant drop column wzcode; \ No newline at end of file diff --git a/etc/db/billing_schema_vc.sql b/etc/db/billing_schema_vc.sql index 2e66787..b549b6a 100644 --- a/etc/db/billing_schema_vc.sql +++ b/etc/db/billing_schema_vc.sql @@ -107,10 +107,12 @@ ALTER SEQUENCE ngcloud_ru.storage_profile_types_id_seq OWNED BY ngcloud_ru.stora CREATE TABLE ngcloud_ru.tenant ( id uuid NOT NULL, name character varying(255) NOT NULL, - wzcode bigint + --wzcode bigint + clientid varchar(30) NULL ); + ALTER TABLE ngcloud_ru.tenant OWNER TO vc_billing; -- diff --git a/etc/db/billing_schema_vmreports.sql b/etc/db/billing_schema_vmreports.sql index b6726ee..3f5fd3a 100644 --- a/etc/db/billing_schema_vmreports.sql +++ b/etc/db/billing_schema_vmreports.sql @@ -88,18 +88,26 @@ create table vmreports.storage ( "Organization" varchar(255) NULL, "WZ" varchar(255) NULL, "Cluster" varchar(255) NULL, - "Disk" varchar(255) NULL, - "Storage_Policy" varchar(255) NULL, - + "Disk" varchar(255) NOT NULL, --part of the key + "Storage_Policy" varchar(255) NULL, "Disk_Allocated" float NULL, "Disk_Used" float NULL, "VM_is_ON" float NULL, "Object_UUID" uuid NOT NULL, ts timestamptz NOT NULL - ,CONSTRAINT PK_storage PRIMARY KEY (ts, "Object_UUID") + ,CONSTRAINT PK_storage PRIMARY KEY (ts, "Object_UUID", "Disk") ); +--Disk мог содержать NULL, но должен быть частью ключа, как оказалось --grant all on vmreports.storage to nifi; +--UPDATE vmreports.storage SET "Disk"='' WHERE "Disk" IS NULL; +--ALTER TABLE vmreports.storage ALTER COLUMN "Disk" SET NOT NULL; +--ALTER TABLE vmreports.storage DROP CONSTRAINT PK_storage; +--ALTER TABLE vmreports.storage ADD CONSTRAINT PK_storage PRIMARY KEY (ts, "Object_UUID", "Disk"); + + UpdateRecord[id=3d3d3faa-fabd-1de9-d3d8-8f24953a9832] Failed to process FlowFile[filename=9cd148f7-bd09-43da-9ccd-82c8607ee98d]; will route to failure: java.lang.NullPointerException: Cannot invoke "org.apache.nifi.serialization.record.RecordField.getDataType()" because the return value of "org.apache.nifi.record.path.FieldValue.getField()" is null + + drop table if exists vmreports.vm_gpu; create table vmreports.vm_gpu ( "Name" varchar(255) NULL, diff --git a/mod/MemoryDbHelper.cfc b/mod/MemoryDbHelper.cfc new file mode 100644 index 0000000..3c95c3a --- /dev/null +++ b/mod/MemoryDbHelper.cfc @@ -0,0 +1,99 @@ +component { + /*0.02 timestamp support*/ + /** + * 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)"; + case "TIMESTAMP": case "DATE": case "TIME": return "TIMESTAMP"; + 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/mod/compute_payg.cfm b/mod/compute_payg.cfm index ae431b7..db28e17 100644 --- a/mod/compute_payg.cfm +++ b/mod/compute_payg.cfm @@ -86,8 +86,10 @@ - select s.catalogName as svc, s.component, s.serviceCode as service_code, s.fullCode as code, s.measureShort as unit - , s.priceWVat as price, (s.discount + s.serviceDiscount - s.discount*s.serviceDiscount/100) as discount + select s.catalogName as svc, s.component, s.serviceCode as service_code, NULL as sku_code, s.fullCode as code, s.measureShort as unit + , s.priceWVat as price + ,s.componentDiscount as component_discount, s.serviceDiscount as service_discount, s.serviceQuantity as service_quantity, s.componentQuantity as component_quantity + ,coalesce(s.serviceQuantity*(100-s.serviceDiscount)/100*s.priceWVat*(100-s.componentDiscount)/100,0) as discounted_price ,s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, f.free_tier from qSpecification s, qFreeTier f WHERE s.fullCode=f.code @@ -95,13 +97,18 @@ UNION - select s.catalogName as svc, s.component, s.serviceCode as service_code, s.fullCode as code, s.measureShort as unit - , s.priceWVat as price, (s.discount + s.serviceDiscount - s.discount*s.serviceDiscount/100) as discount + select s.catalogName as svc, s.component, s.serviceCode as service_code, NULL as sku_code, s.fullCode as code, s.measureShort as unit + , s.priceWVat as price + ,s.componentDiscount as component_discount, s.serviceDiscount as service_discount, s.serviceQuantity as service_quantity, s.componentQuantity as component_quantity + ,coalesce(s.serviceQuantity*(100-s.serviceDiscount)/100*s.priceWVat*(100-s.componentDiscount)/100,0) as discounted_price ,s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, 0 from qSpecification s WHERE s.fullCode NOT IN () AND s.serviceType = 3 + + + qSpec (Left Outer Join with Free Tier) @@ -124,7 +131,7 @@ CREATE INDEX idx_tenant_wzcode ON ngcloud_ru.tenant (wzcode); ---> - select 'WZ'||to_char(tenant.wzcode,'FM00000') as wz, vdc.name as vdc_name, + select upper(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' @@ -141,11 +148,11 @@ CREATE INDEX idx_tenant_wzcode ON ngcloud_ru.tenant (wzcode); ---> from ngcloud_ru.capacity_resource join ngcloud_ru.vdc on capacity_resource.vdc_id=vdc.id join ngcloud_ru.tenant on tenant.id=vdc.tenant_id - where tenant.wzcode = + where tenant.clientid = AND capacity_resource.timestamp >= AND capacity_resource.timestamp < - group by tenant.wzcode, vdc.name, capacity_resource.timestamp::date - order by tenant.wzcode, vdc.name, capacity_resource.timestamp::date; + group by tenant.clientid, vdc.name, capacity_resource.timestamp::date + order by tenant.clientid, vdc.name, capacity_resource.timestamp::date; @@ -172,7 +179,7 @@ from a - select 'WZ'||to_char(tenant.wzcode,'FM00000') as wz, vdc.name as vdc_name, + select upper(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' @@ -182,15 +189,15 @@ from a WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40' ELSE '['||vdc.name||']' END || CASE - 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' + WHEN storage_profile_types.name ILIKE '%FASTSSD%' THEN '.fstssd' + WHEN storage_profile_types.name ILIKE '%SSD%' THEN '.ssd' + WHEN storage_profile_types.name ILIKE '%SAS%' THEN '.sas' + WHEN storage_profile_types.name ILIKE '%SATA%' THEN '.sata' ELSE '.['|| storage_profile_types.name ||']' END as code, storage_profile_types.name, -- *** это не тип, а профайл - sum("limit")/1024./4./720 as gb_m_limit, - sum(used)/1024./4./720 as gb_m_used, + sum("limit")/1024./4./ as gb_m_limit, + sum(used)/1024./4./ as gb_m_used, capacity_storage.timestamp::date as dt, vdc.name as dimension_1, storage_profile_types.name as dimension_2 @@ -198,11 +205,11 @@ from a join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id join ngcloud_ru.tenant on tenant.id=vdc.tenant_id join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id - where tenant.wzcode = --AND 1=0 + where tenant.clientid = AND capacity_storage.timestamp >= AND capacity_storage.timestamp < - group by tenant.wzcode, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date - order by tenant.wzcode, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date + group by tenant.clientid, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date + order by tenant.clientid, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date ; @@ -221,11 +228,8 @@ from a select /* *** ниже дублирование кода агрегации - round ceil etc, обратить внимание при правке */ /* *** переделать на avg - сначала агрегировать по бакетам*/ - (sum(bucket_stat.usage_rgw_main_size_actual)/720) as vol_B - ,ceil(sum(bucket_stat.usage_rgw_main_size_actual)/720/1024/1024/1024) as vol_GB - + (sum(bucket_stat.usage_rgw_main_size_actual)/) as vol_B + ,sum(bucket_stat.usage_rgw_main_size_actual)//1024/1024/1024 as vol_GB ,case when bucket_stat.placement_id = 1 then 'paas.s3.ceph' when bucket_stat.placement_id = 2 then 'paas.s3.cphc' @@ -233,10 +237,10 @@ from a --,bucket_stat.placement_id ,'WZ'||lpad(bucket_stat.owner, 5, '0') as wz -- *** здесь не число, а строка, поэтому другая формула обратной сборки WZ ,bucket_stat.timestamp_addition::date as dt - ,bucket_info.name || ' - ' || case + ,bucket_info.name /*|| ' - ' || case 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 + else '['|| TO_CHAR(bucket_stat.placement_id,'FM9') ||']' end */ as dimension_1 ,null as dimension_2 from s3billing.bucket_stat @@ -249,7 +253,6 @@ from a group by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name, bucket_stat.timestamp_addition::date order by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name, bucket_stat.timestamp_addition::date; - qS3Vol #getTickCount() - request.startTickCount# @@ -267,21 +270,14 @@ from a OR category.name LIKE 'options%' ) THEN ops ELSE 0 END ) as get - ,ceil(sum( + ,(sum( CASE WHEN (category.name LIKE 'get%' OR category.name LIKE 'head%' OR category.name LIKE 'options%' ) THEN ops ELSE 0 END )/10000) as get_10k - /*,greatest(ceil(sum( - CASE WHEN - (category.name LIKE 'get%' - OR category.name LIKE 'head%' - OR category.name LIKE 'options%' - ) THEN ops ELSE 0 END - )/10000) - CASE WHEN bucket_info.placement_id = 1 THEN 10 ELSE 0 END, 0 - ) as get_10k_free_tier_subtracted*/ + ,sum( CASE WHEN @@ -291,7 +287,7 @@ from a OR category.name LIKE 'list%' ) THEN ops ELSE 0 END ) as put - ,ceil(sum( + ,(sum( CASE WHEN (category.name LIKE 'put%' OR category.name LIKE 'post%' @@ -299,20 +295,10 @@ from a OR category.name LIKE 'list%' ) THEN ops ELSE 0 END )/10000) as put_10k - /*,greatest(ceil(sum( - CASE WHEN - (category.name LIKE 'put%' - OR category.name LIKE 'post%' - OR category.name LIKE 'patch%' - OR category.name LIKE 'list%' - ) THEN ops ELSE 0 END - )/10000) - CASE WHEN bucket_info.placement_id = 1 THEN 1 ELSE 0 END, 0 - ) as put_10k_free_tier_subtracted*/ ,(sum(bytes_sent)) as bytes_sent - ,ceil(sum(bytes_sent)/1024/1024/1024) as bytes_sent_GB - /*,greatest(ceil(sum(bytes_sent)/1024/1024/1024) - CASE WHEN bucket_info.placement_id = 1 THEN 100 ELSE 0 END, 0 - ) as bytes_sent_GB_free_tier_subtracted */ + ,(sum(bytes_sent)/1024/1024/1024) as bytes_sent_GB + ,case when bucket_info.placement_id = 1 then 'paas.s3.ceph' when bucket_info.placement_id = 2 then 'paas.s3.cphc' @@ -321,10 +307,10 @@ from a --category.name, bucket_info.name ,'WZ'||lpad(user_info.name, 5, '0') as wz ,usage_bucket_by_user.time::date as dt - ,bucket_info.name || ' - ' || case + ,bucket_info.name as dimension_1 ,null as dimension_2 from s3billing.usage_bucket_by_user @@ -352,12 +338,12 @@ from a - select dt, wz, code + '.vcpu-m' as code, core_h as raw_metric, core_h as metric, + select dt, wz, code + '.vcpu' as code, core_h as raw_metric, core_h as metric, dimension_1, 'vdc' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ядро-ч' as unit from qComputing union all - select dt, wz, code + '.ram-m' as code, gb_h as raw_metric, gb_h as metric, + select dt, wz, code + '.ram' as code, gb_h as raw_metric, gb_h as metric, dimension_1, 'vdc' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт-ч' as unit from qComputing @@ -367,22 +353,22 @@ from a from qStorage union all - select dt, wz, code + '.vol-m', vol_b as raw_metric, vol_gb as metric, + select dt, wz, code + '.vol', vol_b as raw_metric, vol_gb as metric, dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт-м' as unit from qS3Vol union all - select dt, wz, code + '.get-m', get as raw_metric, get_10k as metric, + select dt, wz, code + '.get', get as raw_metric, get_10k as metric, dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, '10тыс.шт.' as unit from qS3OpsTrf union all - select dt, wz, code + '.put-m', put as raw_metric, put_10k as metric, + select dt, wz, code + '.put', put as raw_metric, put_10k as metric, dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, '10тыс.шт.' as unit from qS3OpsTrf union all - select dt, wz, code + '.trf-m', bytes_sent as raw_metric, bytes_sent_gb as metric, + select dt, wz, code + '.trf', bytes_sent as raw_metric, bytes_sent_gb as metric, dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт' as unit from qS3OpsTrf @@ -398,7 +384,6 @@ from a - qUnifiedMetric #getTickCount() - request.startTickCount# @@ -421,20 +406,30 @@ from a - + + + + + + SELECT s.code as s_code m.code as m_code - s.service_code as service_code + s.service_code + s.sku_code s.svc s.component m.unit - + + s.discounted_price s.price - s.discount + s.component_quantity + s.component_discount + s.service_quantity + s.service_discount m.raw_metric m.metric s.serviceStart @@ -448,33 +443,35 @@ from a m.dimension_2 m.dimension_2_name - FROM qSpec as s, qUnifiedMetric as m - WHERE s.code=m.code - AND s.serviceStart <= m.dt - AND (m.dt <= s.serviceEnd OR s.serviceEnd IS NULL) + FROM qSpec s + FULL OUTER JOIN qUnifiedMetric m ON ( + s.sku_code=m.code + AND s.serviceStart <= m.dt + AND (m.dt <= s.serviceEnd OR s.serviceEnd IS NULL) + ) + WHERE 1=1 AND m.wz= - + + + UNION ALL - SELECT NULL, m.code, NULL, NULL, NULL, NULL, 0, - NULL, NULL, m.raw_metric, m.metric, NULL, NULL, NULL, NULL, m.dt, 0, m.dimension_1, m.dimension_1_name, m.dimension_2, m.dimension_2_name + SELECT NULL, m.code, NULL, NULL, NULL, NULL, NULL, 0, + 0, 0, 0, 0, 0, m.raw_metric, m.metric, NULL, NULL, NULL, NULL, m.dt, 0, m.dimension_1, m.dimension_1_name, m.dimension_2, m.dimension_2_name FROM qUnifiedMetric as m - WHERE m.code NOT IN () + WHERE m.code NOT IN () UNION ALL - SELECT s.code, NULL, s.service_code, s.svc, s.component, s.unit, s.price*(100-s.discount)/100 as discounted_price, - s.price, s.discount, NULL, NULL, s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, NULL, 0, NULL, NULL, NULL, NULL + SELECT s.code, NULL, s.service_code, s.sku_code, s.svc, s.component, s.unit, s.discounted_price, + s.price, s.component_quantity, s.component_discount, s.service_quantity, s.service_discount, NULL, NULL, s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, NULL, 0, NULL, NULL, NULL, NULL FROM qSpec as s - WHERE s.code NOT IN () - - ORDER by dt, s_code, m_code, dimension_1, dimension_2 + WHERE s.code NOT IN () ---> - - + qChargeDaily #getTickCount() - request.startTickCount# @@ -491,11 +488,18 @@ from a m_code s_code as code service_code + sku_code svc - serviceCustomName + serviceCustomName component unit discounted_price + price + component_quantity + component_discount + 0 as discounted_price2 + service_quantity + service_discount sum(raw_metric) as raw_metric sum(metric) as metric serviceStart @@ -508,35 +512,27 @@ from a dimension_1_name dimension_2 dimension_2_name - year(dt) as year - month(dt) as month + year(dt) as year + month(dt) as month from qChargeDaily - group by s_code, m_code, svc, service_code, serviceCustomName, component, unit, discounted_price, serviceStart, ServiceEnd, serviceHash, free_tier, year(dt), month(dt), + group by s_code, m_code, svc, service_code, sku_code, serviceCustomName, component, unit, discounted_price, price, component_quantity, component_discount, service_quantity, service_discount, serviceStart, ServiceEnd, serviceHash, free_tier, year(dt), month(dt), dimension_1_name, dimension_1, dimension_2_name, dimension_2 - order by year(dt), month(dt), m_code, code, serviceStart, dimension_1, dimension_2 + order by year(dt), month(dt), sku_code, m_code, serviceStart, dimension_1, dimension_2 - + + + + + + + + - - - - - - - + ---> + qChargeMonthly #getTickCount() - request.startTickCount# diff --git a/mod/crm_contract.cfm b/mod/crm_contract.cfm index dd36352..e32d40a 100644 --- a/mod/crm_contract.cfm +++ b/mod/crm_contract.cfm @@ -55,9 +55,9 @@ ,componentCode ,component ,measureShort - ,quantity + ,componentQuantity ,priceWVat - ,discount + ,componentDiscount ,atomicCode ,fullCode ", @@ -93,9 +93,9 @@ componentCode=local.component.componentCode, component=local.component.component, measureShort=local.component.measureShort, - quantity=local.component.quantity, + componentQuantity=local.component.quantity, priceWVat=local.component.priceWVat, - discount=local.component.discount, + componentDiscount=local.component.discount, atomicCode=local.component.atomicCode, fullCode="#local.component.componentCode#-#listGetAt('i,f,m',local.service.serviceType)#" }])/> diff --git a/payg.cfm b/payg.cfm index 2fe2bdc..847324b 100644 --- a/payg.cfm +++ b/payg.cfm @@ -130,6 +130,14 @@ S3 операции и трафик: Услуга Компонент + Цена ₽ до ск. с НДС + Цена ₽ co ск. с НДС без окр. + + Service Quantity + Service Discount + Component Quantity + Component Discount + + #year#-#numberFormat(month,"00")# @@ -164,7 +173,7 @@ S3 операции и трафик: - + #svc# #service_code# (#serviceCustomName#) (не найдено в договоре) @@ -179,7 +188,7 @@ S3 операции и трафик: #code# (#m_code#) #svc# - #component# + #component# @@ -196,33 +205,43 @@ S3 операции и трафик: #unit# - #gaussianRound(discounted_price,2)# + #gaussianRoundSafe(discounted_price,2)# - + + + #price# + #discounted_price# + + #service_quantity# + #service_discount# + #component_quantity# + #component_discount# - + + Итого #code# (#m_code#): - #round(accum_metric_dimension_1,3)# + #round(final_metric_dimension_1,3)# #free_tier# - #max(ceiling(accum_metric_dimension_1) - free_tier, 0)# + #max(final_metric_dimension_1 - free_tier, 0)# #unit# - #gaussianRound(discounted_price,2)# + #gaussianRoundSafe(discounted_price,2)# #cost_dimension_1# #svc# - #component# + #component# + @@ -241,6 +260,7 @@ S3 операции и трафик:   + Total: #accum_cost_total# @@ -268,7 +288,7 @@ S3 операции и трафик: #contractOutsideId# #additionalAgreementName# - #serviceType# + style="background:lightgreen">#serviceType# #serviceCustomName# #serviceQuantity# #serviceDiscount# @@ -276,11 +296,11 @@ S3 операции и трафик: #dateFormat(serviceEnd, "YYYY-MM-DD")# #component# #componentCode# - #quantity# + #componentQuantity# #measureShort# - #gaussianRound(priceWVat,2)# - #discount# - #gaussianRound((100-serviceDiscount)/100*(100-discount)/100*priceWVat,4)# + #gaussianRoundSafe(priceWVat,2)# | #priceWVat# + #componentDiscount# + #gaussianRoundSafe((100-serviceDiscount)/100*(100-componentDiscount)/100*priceWVat,7)#/td> @@ -293,6 +313,10 @@ S3 операции и трафик: +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;