801 lines
35 KiB
SQL
801 lines
35 KiB
SQL
CREATE TABLE IF NOT EXISTS analytics_1c.company_forecasts
|
||
(
|
||
generated_at DateTime,
|
||
as_of_date Date,
|
||
infobase LowCardinality(String),
|
||
counterparty String,
|
||
horizon_days UInt16,
|
||
metric LowCardinality(String),
|
||
baseline_daily Float64,
|
||
trend_slope Float64,
|
||
predicted_daily Float64,
|
||
predicted_total Float64,
|
||
confidence Float32,
|
||
model LowCardinality(String),
|
||
source_days UInt16,
|
||
note String
|
||
)
|
||
ENGINE = MergeTree
|
||
ORDER BY (generated_at, infobase, counterparty, metric, horizon_days);
|
||
|
||
CREATE TABLE IF NOT EXISTS analytics_1c.company_health_signals
|
||
(
|
||
generated_at DateTime,
|
||
infobase LowCardinality(String),
|
||
counterparty String,
|
||
signal_id String,
|
||
severity LowCardinality(String),
|
||
score UInt32,
|
||
signal_type LowCardinality(String),
|
||
summary String,
|
||
amount_7d Float64,
|
||
amount_prev_7d Float64,
|
||
docs_7d UInt32,
|
||
docs_prev_7d UInt32,
|
||
days_since_last_activity UInt16,
|
||
open_cases_total UInt32,
|
||
detections_total UInt32
|
||
)
|
||
ENGINE = MergeTree
|
||
ORDER BY (generated_at, severity, infobase, counterparty, signal_id);
|
||
|
||
CREATE TABLE IF NOT EXISTS analytics_1c.company_registry_bindings
|
||
(
|
||
ts DateTime,
|
||
infobase LowCardinality(String),
|
||
company_entity_key String,
|
||
base_id String,
|
||
base_path String,
|
||
base_path_key String,
|
||
registry_company_key String,
|
||
registry_company_name String,
|
||
binding_source LowCardinality(String),
|
||
note String
|
||
)
|
||
ENGINE = MergeTree
|
||
ORDER BY (company_entity_key, ts);
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_companies_current AS
|
||
SELECT
|
||
infobase,
|
||
company_name,
|
||
organization,
|
||
owner_user,
|
||
base_id,
|
||
base_path,
|
||
trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(base_path), '[\\\\/]+', '/'), '[^0-9A-ZА-ЯЁ:/._ -]+', ' '), '\\s+', ' ')) AS base_path_key,
|
||
multiIf(
|
||
base_id != '', concat('baseid:', base_id),
|
||
base_path != '', concat('basepath:', trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(base_path), '[\\\\/]+', '/'), '[^0-9A-ZА-ЯЁ:/._ -]+', ' '), '\\s+', ' '))),
|
||
concat('infobase:', trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(infobase), '(^|\\s)20[0-9]{2}($|\\s)', ' '), '[^0-9A-ZА-ЯЁ]+', ' '), '\\s+', ' ')))
|
||
) AS company_entity_key,
|
||
current_status,
|
||
db_size_bytes,
|
||
reglog_size_bytes,
|
||
active_locks,
|
||
temp_db_present,
|
||
scheduler_touched,
|
||
current_activity_score,
|
||
last_company_snapshot_at
|
||
FROM
|
||
(
|
||
SELECT
|
||
infobase,
|
||
argMax(company_name, ts) AS company_name,
|
||
argMax(organization, ts) AS organization,
|
||
argMax(owner_user, ts) AS owner_user,
|
||
argMax(base_id, ts) AS base_id,
|
||
argMax(base_path, ts) AS base_path,
|
||
argMax(status, ts) AS current_status,
|
||
argMax(db_size_bytes, ts) AS db_size_bytes,
|
||
argMax(reglog_size_bytes, ts) AS reglog_size_bytes,
|
||
argMax(active_locks, ts) AS active_locks,
|
||
argMax(temp_db_present, ts) AS temp_db_present,
|
||
argMax(scheduler_touched, ts) AS scheduler_touched,
|
||
argMax(activity_score, ts) AS current_activity_score,
|
||
max(ts) AS last_company_snapshot_at
|
||
FROM analytics_1c.companies
|
||
GROUP BY infobase
|
||
);
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_activity_daily AS
|
||
SELECT
|
||
toDate(documents.ts) AS d,
|
||
documents.infobase AS infobase,
|
||
ifNull(companies.organization, documents.organization) AS organization,
|
||
ifNull(companies.company_entity_key, concat('infobase:', trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(documents.infobase), '(^|\\s)20[0-9]{2}($|\\s)', ' '), '[^0-9A-ZА-ЯЁ]+', ' '), '\\s+', ' ')))) AS company_entity_key,
|
||
argMax(documents.counterparty, documents.ts) AS source_counterparty,
|
||
count() AS docs_total,
|
||
sum(documents.amount) AS amount_total,
|
||
countIf(documents.posted = 1) AS posted_docs_total,
|
||
countIf(documents.posted = 0) AS unposted_docs_total,
|
||
countIf(documents.status = 'busy') AS busy_docs_total,
|
||
countIf(documents.status = 'online') AS online_docs_total,
|
||
uniqExact(documents.doc_type) AS doc_types_total
|
||
FROM analytics_1c.documents AS documents
|
||
LEFT JOIN analytics_1c.v_companies_current AS companies ON companies.infobase = documents.infobase
|
||
WHERE documents.counterparty != ''
|
||
GROUP BY d, documents.infobase, ifNull(companies.organization, documents.organization), ifNull(companies.company_entity_key, concat('infobase:', trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(documents.infobase), '(^|\\s)20[0-9]{2}($|\\s)', ' '), '[^0-9A-ZА-ЯЁ]+', ' '), '\\s+', ' '))));
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_activity_latest AS
|
||
SELECT
|
||
documents.infobase AS infobase,
|
||
ifNull(companies.organization, documents.organization) AS organization,
|
||
ifNull(companies.company_entity_key, concat('infobase:', trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(documents.infobase), '(^|\\s)20[0-9]{2}($|\\s)', ' '), '[^0-9A-ZА-ЯЁ]+', ' '), '\\s+', ' ')))) AS company_entity_key,
|
||
argMax(documents.counterparty, documents.ts) AS source_counterparty,
|
||
max(documents.ts) AS last_seen_at,
|
||
argMax(documents.doc_type, documents.ts) AS last_doc_type,
|
||
argMax(documents.operation_type, documents.ts) AS last_operation_type,
|
||
argMax(documents.status, documents.ts) AS last_status,
|
||
argMax(documents.amount, documents.ts) AS last_amount,
|
||
count() AS docs_lifetime,
|
||
sum(documents.amount) AS amount_lifetime
|
||
FROM analytics_1c.documents AS documents
|
||
LEFT JOIN analytics_1c.v_companies_current AS companies ON companies.infobase = documents.infobase
|
||
WHERE documents.counterparty != ''
|
||
GROUP BY documents.infobase, ifNull(companies.organization, documents.organization), ifNull(companies.company_entity_key, concat('infobase:', trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(documents.infobase), '(^|\\s)20[0-9]{2}($|\\s)', ' '), '[^0-9A-ZА-ЯЁ]+', ' '), '\\s+', ' '))));
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_forecasts_current AS
|
||
SELECT *
|
||
FROM analytics_1c.company_forecasts
|
||
WHERE generated_at = (SELECT max(generated_at) FROM analytics_1c.company_forecasts);
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_health_current AS
|
||
SELECT *
|
||
FROM analytics_1c.company_health_signals
|
||
WHERE generated_at = (SELECT max(generated_at) FROM analytics_1c.company_health_signals);
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_counterparty_daily AS
|
||
SELECT
|
||
d,
|
||
infobase,
|
||
organization,
|
||
company_entity_key AS counterparty,
|
||
docs_total,
|
||
amount_total,
|
||
posted_docs_total,
|
||
unposted_docs_total,
|
||
busy_docs_total,
|
||
online_docs_total,
|
||
doc_types_total
|
||
FROM analytics_1c.v_company_activity_daily;
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_counterparty_latest_activity AS
|
||
SELECT
|
||
infobase,
|
||
organization,
|
||
company_entity_key AS counterparty,
|
||
source_counterparty,
|
||
last_seen_at,
|
||
last_doc_type,
|
||
last_operation_type,
|
||
last_status,
|
||
last_amount,
|
||
docs_lifetime,
|
||
amount_lifetime
|
||
FROM analytics_1c.v_company_activity_latest;
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_registry_current AS
|
||
SELECT
|
||
company_key,
|
||
argMax(company_name, ts) AS company_name,
|
||
argMax(assignee_name, ts) AS assignee_name,
|
||
argMax(registry_status, ts) AS registry_status,
|
||
argMax(share_text, ts) AS share_text,
|
||
argMax(key_contour, ts) AS key_contour,
|
||
argMax(inn, ts) AS inn,
|
||
argMax(kpp, ts) AS kpp,
|
||
max(ts) AS last_registry_snapshot_at
|
||
FROM analytics_1c.company_registry
|
||
GROUP BY company_key;
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_registry_bindings_current AS
|
||
SELECT
|
||
company_entity_key,
|
||
argMax(infobase, ts) AS infobase,
|
||
argMax(base_id, ts) AS base_id,
|
||
argMax(base_path, ts) AS base_path,
|
||
argMax(base_path_key, ts) AS base_path_key,
|
||
argMax(registry_company_key, ts) AS registry_company_key,
|
||
argMax(registry_company_name, ts) AS registry_company_name,
|
||
argMax(binding_source, ts) AS binding_source,
|
||
argMax(note, ts) AS note,
|
||
max(ts) AS last_binding_at
|
||
FROM analytics_1c.company_registry_bindings
|
||
GROUP BY company_entity_key;
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_registry_alias_map AS
|
||
SELECT
|
||
source_company_key,
|
||
target_company_key,
|
||
target_company_name,
|
||
exclude_from_portfolio,
|
||
note
|
||
FROM
|
||
(
|
||
SELECT
|
||
'ИНФОРМАЦИОННАЯ БАЗА' AS source_company_key,
|
||
'' AS target_company_key,
|
||
'Информационная база' AS target_company_name,
|
||
toUInt8(1) AS exclude_from_portfolio,
|
||
'system_filebase' AS note
|
||
UNION ALL
|
||
SELECT
|
||
'МОСКОТЕЛЬИНКОВ АЛЕНСАНДР',
|
||
'МОСКОТЕЛЬНИКОВ АЛ В',
|
||
'МОСКОТЕЛЬНИКОВ АЛ.В',
|
||
toUInt8(0),
|
||
'manual_alias_typo'
|
||
UNION ALL
|
||
SELECT
|
||
'МОСКОТЕЛЬНИКОВ АЛЕКСАНДР',
|
||
'МОСКОТЕЛЬНИКОВ АЛ В',
|
||
'МОСКОТЕЛЬНИКОВ АЛ.В',
|
||
toUInt8(0),
|
||
'manual_alias_expansion'
|
||
UNION ALL
|
||
SELECT
|
||
'СЕРДИТОВ АНДРЕЙ',
|
||
'СЕРДИТОВ АВ',
|
||
'СЕРДИТОВ АВ',
|
||
toUInt8(0),
|
||
'manual_alias_initials'
|
||
UNION ALL
|
||
SELECT
|
||
'СЕРДИТОВ СЕРГЕЙ',
|
||
'СЕРДИТОВ СВ',
|
||
'СЕРДИТОВ СВ',
|
||
toUInt8(0),
|
||
'manual_alias_initials'
|
||
UNION ALL
|
||
SELECT
|
||
'СОРФИН',
|
||
'СОЛФИН',
|
||
'СОЛФИН',
|
||
toUInt8(0),
|
||
'manual_alias_typo'
|
||
);
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_registry_manual_overrides AS
|
||
SELECT
|
||
company_key,
|
||
company_name,
|
||
assignee_name,
|
||
registry_status,
|
||
share_text,
|
||
key_contour,
|
||
inn,
|
||
kpp,
|
||
note
|
||
FROM
|
||
(
|
||
SELECT
|
||
'МАВЕРИК' AS company_key,
|
||
'МАВЕРИК' AS company_name,
|
||
'' AS assignee_name,
|
||
'manual' AS registry_status,
|
||
'' AS share_text,
|
||
toUInt8(0) AS key_contour,
|
||
'' AS inn,
|
||
'' AS kpp,
|
||
'workbook:list-sheet' AS note
|
||
UNION ALL
|
||
SELECT
|
||
'ТСН КОММУНИСТИЧЕСКАЯ 4',
|
||
'ТСН КОММУНИСТИЧЕСКАЯ 4',
|
||
'',
|
||
'manual',
|
||
'',
|
||
toUInt8(0),
|
||
'',
|
||
'',
|
||
'manual:portfolio-carry'
|
||
UNION ALL
|
||
SELECT
|
||
'ФЕЛИЦТ ГРУПП',
|
||
'ФЕЛИЦТ ГРУПП',
|
||
'',
|
||
'manual',
|
||
'',
|
||
toUInt8(0),
|
||
'',
|
||
'',
|
||
'manual:portfolio-carry'
|
||
);
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_portfolio_overview AS
|
||
WITH
|
||
base AS
|
||
(
|
||
SELECT
|
||
*,
|
||
trimBoth(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(upperUTF8(source_counterparty), '(^|\\s)20[0-9]{2}($|\\s)', ' '), '[^0-9A-ZА-ЯЁ]+', ' '), '\\s+', ' ')) AS source_counterparty_key
|
||
FROM analytics_1c.v_counterparty_latest_activity
|
||
),
|
||
d7 AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
counterparty,
|
||
sum(docs_total) AS docs_7d,
|
||
sum(amount_total) AS amount_7d
|
||
FROM analytics_1c.v_counterparty_daily
|
||
WHERE d >= today() - 7
|
||
GROUP BY infobase, counterparty
|
||
),
|
||
d30 AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
counterparty,
|
||
countDistinct(d) AS active_days_30d,
|
||
sum(docs_total) AS docs_30d,
|
||
sum(amount_total) AS amount_30d,
|
||
sum(busy_docs_total) AS busy_docs_30d
|
||
FROM analytics_1c.v_counterparty_daily
|
||
WHERE d >= today() - 30
|
||
GROUP BY infobase, counterparty
|
||
),
|
||
company_state AS
|
||
(
|
||
SELECT *
|
||
FROM analytics_1c.v_companies_current
|
||
),
|
||
binding_state AS
|
||
(
|
||
SELECT *
|
||
FROM analytics_1c.v_company_registry_bindings_current
|
||
),
|
||
registry_state AS
|
||
(
|
||
SELECT *
|
||
FROM analytics_1c.v_company_registry_current
|
||
),
|
||
alias_state AS
|
||
(
|
||
SELECT *
|
||
FROM analytics_1c.v_company_registry_alias_map
|
||
),
|
||
manual_state AS
|
||
(
|
||
SELECT *
|
||
FROM analytics_1c.v_company_registry_manual_overrides
|
||
),
|
||
signals AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
counterparty AS company_entity_key,
|
||
max(score) AS signal_score,
|
||
argMax(severity, score) AS signal_severity,
|
||
argMax(summary, score) AS top_signal
|
||
FROM analytics_1c.v_company_health_current
|
||
GROUP BY infobase, company_entity_key
|
||
),
|
||
amount_forecast AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
counterparty AS company_entity_key,
|
||
predicted_total AS amount_forecast_30d,
|
||
confidence AS amount_forecast_confidence
|
||
FROM analytics_1c.v_company_forecasts_current
|
||
WHERE metric = 'amount_total'
|
||
AND horizon_days = 30
|
||
),
|
||
docs_forecast AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
counterparty AS company_entity_key,
|
||
predicted_total AS docs_forecast_30d,
|
||
confidence AS docs_forecast_confidence
|
||
FROM analytics_1c.v_company_forecasts_current
|
||
WHERE metric = 'docs_total'
|
||
AND horizon_days = 30
|
||
),
|
||
cases_current AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
entity_id AS company_entity_key,
|
||
countIf(status != 'closed') AS open_cases_total
|
||
FROM analytics_1c.cases
|
||
WHERE entity_type = 'counterparty'
|
||
GROUP BY infobase, company_entity_key
|
||
),
|
||
detections_current AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
entity_id AS company_entity_key,
|
||
count() AS detections_total
|
||
FROM analytics_1c.detections
|
||
WHERE entity_type = 'counterparty'
|
||
AND status != 'closed'
|
||
GROUP BY infobase, company_entity_key
|
||
)
|
||
SELECT
|
||
base.infobase AS infobase,
|
||
base.counterparty AS company_entity_key,
|
||
if(company_state.organization != '', company_state.organization, base.organization) AS organization,
|
||
base.source_counterparty AS source_counterparty,
|
||
if(binding_state.registry_company_name != '', binding_state.registry_company_name, if(alias_state.target_company_name != '', alias_state.target_company_name, if(manual_state.company_name != '', manual_state.company_name, if(company_state.company_name != '', company_state.company_name, base.source_counterparty)))) AS counterparty,
|
||
if(binding_state.registry_company_name != '', binding_state.registry_company_name, if(alias_state.target_company_name != '', alias_state.target_company_name, if(manual_state.company_name != '', manual_state.company_name, if(company_state.company_name != '', company_state.company_name, base.source_counterparty)))) AS company_name,
|
||
if(binding_state.registry_company_name != '', binding_state.registry_company_name, if(alias_state.target_company_name != '', alias_state.target_company_name, if(manual_state.company_name != '', manual_state.company_name, base.source_counterparty))) AS normalized_counterparty,
|
||
multiIf(binding_state.registry_company_key != '', 'technical', ifNull(alias_state.exclude_from_portfolio, 0) = 1, 'excluded', alias_state.target_company_key != '' AND registry_state.company_key != '', 'alias', registry_state.company_key != '', 'direct', manual_state.company_key != '', 'manual', 'none') AS registry_match_mode,
|
||
if(binding_state.registry_company_key != '', binding_state.registry_company_key, if(registry_state.company_key != '', registry_state.company_key, ifNull(manual_state.company_key, ''))) AS registry_company_key,
|
||
ifNull(binding_state.binding_source, '') AS registry_binding_source,
|
||
ifNull(binding_state.note, '') AS registry_binding_note,
|
||
if(registry_state.assignee_name != '', registry_state.assignee_name, ifNull(manual_state.assignee_name, '')) AS registry_assignee_name,
|
||
if(registry_state.registry_status != '', registry_state.registry_status, ifNull(manual_state.registry_status, '')) AS registry_status,
|
||
if(registry_state.share_text != '', registry_state.share_text, ifNull(manual_state.share_text, '')) AS registry_share_text,
|
||
if(registry_state.key_contour != 0, registry_state.key_contour, ifNull(manual_state.key_contour, 0)) AS registry_key_contour,
|
||
if(registry_state.inn != '', registry_state.inn, ifNull(manual_state.inn, '')) AS registry_inn,
|
||
if(registry_state.kpp != '', registry_state.kpp, ifNull(manual_state.kpp, '')) AS registry_kpp,
|
||
ifNull(company_state.owner_user, '') AS owner_user,
|
||
ifNull(company_state.base_id, '') AS base_id,
|
||
ifNull(company_state.base_path, '') AS base_path,
|
||
ifNull(company_state.base_path_key, '') AS base_path_key,
|
||
base.last_seen_at,
|
||
company_state.last_company_snapshot_at,
|
||
base.last_doc_type,
|
||
base.last_operation_type,
|
||
base.last_status,
|
||
ifNull(company_state.current_status, base.last_status) AS current_status,
|
||
ifNull(company_state.db_size_bytes, 0) AS db_size_bytes,
|
||
ifNull(company_state.reglog_size_bytes, 0) AS reglog_size_bytes,
|
||
ifNull(company_state.active_locks, 0) AS active_locks,
|
||
ifNull(company_state.temp_db_present, 0) AS temp_db_present,
|
||
ifNull(company_state.scheduler_touched, 0) AS scheduler_touched,
|
||
ifNull(company_state.current_activity_score, 0) AS current_activity_score,
|
||
dateDiff('day', toDate(base.last_seen_at), today()) AS days_since_last_activity,
|
||
ifNull(d7.docs_7d, 0) AS docs_7d,
|
||
ifNull(d7.amount_7d, 0) AS amount_7d,
|
||
ifNull(d30.active_days_30d, 0) AS active_days_30d,
|
||
ifNull(d30.docs_30d, 0) AS docs_30d,
|
||
ifNull(d30.amount_30d, 0) AS amount_30d,
|
||
ifNull(d30.busy_docs_30d, 0) AS busy_docs_30d,
|
||
ifNull(amount_forecast.amount_forecast_30d, 0) AS amount_forecast_30d,
|
||
ifNull(amount_forecast.amount_forecast_confidence, 0) AS amount_forecast_confidence,
|
||
ifNull(docs_forecast.docs_forecast_30d, 0) AS docs_forecast_30d,
|
||
ifNull(docs_forecast.docs_forecast_confidence, 0) AS docs_forecast_confidence,
|
||
ifNull(cases_current.open_cases_total, 0) AS open_cases_total,
|
||
ifNull(detections_current.detections_total, 0) AS detections_total,
|
||
ifNull(signals.signal_severity, 'none') AS signal_severity,
|
||
ifNull(signals.signal_score, 0) AS signal_score,
|
||
ifNull(signals.top_signal, '') AS top_signal
|
||
FROM base
|
||
LEFT JOIN company_state ON company_state.infobase = base.infobase
|
||
LEFT JOIN binding_state ON binding_state.company_entity_key = base.counterparty
|
||
LEFT JOIN alias_state ON alias_state.source_company_key = base.source_counterparty_key
|
||
LEFT JOIN registry_state ON registry_state.company_key = if(binding_state.registry_company_key != '', binding_state.registry_company_key, if(alias_state.target_company_key != '', alias_state.target_company_key, base.source_counterparty_key))
|
||
LEFT JOIN manual_state ON manual_state.company_key = if(binding_state.registry_company_key != '', binding_state.registry_company_key, if(alias_state.target_company_key != '', alias_state.target_company_key, base.source_counterparty_key))
|
||
LEFT JOIN d7 ON d7.infobase = base.infobase AND d7.counterparty = base.counterparty
|
||
LEFT JOIN d30 ON d30.infobase = base.infobase AND d30.counterparty = base.counterparty
|
||
LEFT JOIN signals ON signals.infobase = base.infobase AND signals.company_entity_key = base.counterparty
|
||
LEFT JOIN amount_forecast ON amount_forecast.infobase = base.infobase AND amount_forecast.company_entity_key = base.counterparty
|
||
LEFT JOIN docs_forecast ON docs_forecast.infobase = base.infobase AND docs_forecast.company_entity_key = base.counterparty
|
||
LEFT JOIN cases_current ON cases_current.infobase = base.infobase AND cases_current.company_entity_key = base.counterparty
|
||
LEFT JOIN detections_current ON detections_current.infobase = base.infobase AND detections_current.company_entity_key = base.counterparty
|
||
WHERE ifNull(alias_state.exclude_from_portfolio, 0) = 0;
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_management_actions_current AS
|
||
WITH
|
||
portfolio AS
|
||
(
|
||
SELECT *
|
||
FROM analytics_1c.v_company_portfolio_overview
|
||
),
|
||
risky_changes_24h AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
company_entity_key,
|
||
count() AS risky_changes_24h,
|
||
max(ts) AS last_risky_change_at,
|
||
arrayStringConcat(groupUniqArray(3)(if(field_name != '', field_name, change_kind)), ', ') AS risky_change_examples
|
||
FROM analytics_1c.document_change_events
|
||
WHERE risk_tag != ''
|
||
AND ts >= now() - INTERVAL 1 DAY
|
||
GROUP BY infobase, company_entity_key
|
||
),
|
||
large_adjustments_24h AS
|
||
(
|
||
SELECT
|
||
infobase,
|
||
company_entity_key,
|
||
count() AS large_adjustments_24h,
|
||
max(ts) AS last_large_adjustment_at,
|
||
max(toFloat64(amount)) AS max_large_adjustment_amount
|
||
FROM analytics_1c.business_events
|
||
WHERE ts >= now() - INTERVAL 1 DAY
|
||
AND event_kind = 'posting'
|
||
AND amount >= 50000
|
||
AND (
|
||
operation_type ILIKE '%adjust%'
|
||
OR document_type ILIKE '%Коррект%'
|
||
)
|
||
GROUP BY infobase, company_entity_key
|
||
),
|
||
actions AS
|
||
(
|
||
SELECT
|
||
now() AS generated_at,
|
||
concat('case_triage:', p.infobase, ':', p.company_entity_key) AS action_id,
|
||
p.infobase AS infobase,
|
||
p.company_entity_key AS company_entity_key,
|
||
p.counterparty AS counterparty,
|
||
p.company_name AS company_name,
|
||
p.organization AS organization,
|
||
if(p.registry_assignee_name != '', p.registry_assignee_name, if(p.owner_user != '', p.owner_user, 'не назначен')) AS owner_name,
|
||
'case_triage' AS action_type,
|
||
multiIf(
|
||
p.signal_severity = 'critical' OR p.open_cases_total >= 10, 'critical',
|
||
p.signal_severity = 'high' OR p.open_cases_total >= 5, 'high',
|
||
'medium'
|
||
) AS priority_tier,
|
||
toUInt32(greatest(toInt32(p.signal_score), 55) + least(toUInt32(p.open_cases_total), toUInt32(20))) AS priority_score,
|
||
multiIf(p.signal_severity = 'critical' OR p.open_cases_total >= 10, 'today', '24h') AS deadline_hint,
|
||
concat(
|
||
'Открытых кейсов ',
|
||
toString(p.open_cases_total),
|
||
', active detections ',
|
||
toString(p.detections_total),
|
||
if(p.top_signal != '', concat('. ', p.top_signal), '.')
|
||
) AS reason,
|
||
'Назначить владельца на разбор кейсов и снять прирост до следующего управленческого цикла.' AS recommended_action,
|
||
concat(
|
||
'severity=', p.signal_severity,
|
||
', cases=', toString(p.open_cases_total),
|
||
', detections=', toString(p.detections_total)
|
||
) AS evidence_summary,
|
||
toUInt32(p.open_cases_total) AS open_cases_total,
|
||
toUInt32(p.detections_total) AS detections_total,
|
||
toUInt32(p.active_locks) AS active_locks,
|
||
toUInt16(p.days_since_last_activity) AS days_since_last_activity,
|
||
toUInt32(ifNull(ch.risky_changes_24h, 0)) AS risky_changes_24h,
|
||
toUInt32(ifNull(adj.large_adjustments_24h, 0)) AS large_adjustments_24h,
|
||
toFloat64(ifNull(adj.max_large_adjustment_amount, 0)) AS max_large_adjustment_amount,
|
||
p.last_seen_at AS last_seen_at
|
||
FROM portfolio AS p
|
||
LEFT JOIN risky_changes_24h AS ch ON ch.infobase = p.infobase AND ch.company_entity_key = p.company_entity_key
|
||
LEFT JOIN large_adjustments_24h AS adj ON adj.infobase = p.infobase AND adj.company_entity_key = p.company_entity_key
|
||
WHERE p.open_cases_total > 0
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
now() AS generated_at,
|
||
concat('lock_recovery:', p.infobase, ':', p.company_entity_key) AS action_id,
|
||
p.infobase AS infobase,
|
||
p.company_entity_key AS company_entity_key,
|
||
p.counterparty AS counterparty,
|
||
p.company_name AS company_name,
|
||
p.organization AS organization,
|
||
if(p.registry_assignee_name != '', p.registry_assignee_name, if(p.owner_user != '', p.owner_user, 'не назначен')) AS owner_name,
|
||
'lock_recovery' AS action_type,
|
||
multiIf(
|
||
p.active_locks >= 5 OR p.current_status = 'busy', 'critical',
|
||
p.active_locks >= 2, 'high',
|
||
'medium'
|
||
) AS priority_tier,
|
||
toUInt32(greatest(toInt32(p.signal_score), 50) + least(toUInt32(p.active_locks * 8), toUInt32(40))) AS priority_score,
|
||
'today' AS deadline_hint,
|
||
concat(
|
||
'Файловая база в состоянии ',
|
||
p.current_status,
|
||
', активных блокировок ',
|
||
toString(p.active_locks),
|
||
'.'
|
||
) AS reason,
|
||
'Проверить busy/lock-контур, снять застрявшие блокировки и подтвердить стабилизацию базы.' AS recommended_action,
|
||
concat(
|
||
'status=', p.current_status,
|
||
', locks=', toString(p.active_locks),
|
||
', activity_score=', toString(p.current_activity_score)
|
||
) AS evidence_summary,
|
||
toUInt32(p.open_cases_total) AS open_cases_total,
|
||
toUInt32(p.detections_total) AS detections_total,
|
||
toUInt32(p.active_locks) AS active_locks,
|
||
toUInt16(p.days_since_last_activity) AS days_since_last_activity,
|
||
toUInt32(ifNull(ch.risky_changes_24h, 0)) AS risky_changes_24h,
|
||
toUInt32(ifNull(adj.large_adjustments_24h, 0)) AS large_adjustments_24h,
|
||
toFloat64(ifNull(adj.max_large_adjustment_amount, 0)) AS max_large_adjustment_amount,
|
||
p.last_seen_at AS last_seen_at
|
||
FROM portfolio AS p
|
||
LEFT JOIN risky_changes_24h AS ch ON ch.infobase = p.infobase AND ch.company_entity_key = p.company_entity_key
|
||
LEFT JOIN large_adjustments_24h AS adj ON adj.infobase = p.infobase AND adj.company_entity_key = p.company_entity_key
|
||
WHERE p.current_status = 'busy' OR p.active_locks > 0
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
now() AS generated_at,
|
||
concat('stale_activity:', p.infobase, ':', p.company_entity_key) AS action_id,
|
||
p.infobase AS infobase,
|
||
p.company_entity_key AS company_entity_key,
|
||
p.counterparty AS counterparty,
|
||
p.company_name AS company_name,
|
||
p.organization AS organization,
|
||
if(p.registry_assignee_name != '', p.registry_assignee_name, if(p.owner_user != '', p.owner_user, 'не назначен')) AS owner_name,
|
||
'stale_activity' AS action_type,
|
||
multiIf(
|
||
p.days_since_last_activity >= 14, 'critical',
|
||
p.days_since_last_activity >= 7, 'high',
|
||
'medium'
|
||
) AS priority_tier,
|
||
toUInt32(greatest(toInt32(p.signal_score), 45) + least(toUInt32(p.days_since_last_activity * 3), toUInt32(40))) AS priority_score,
|
||
multiIf(p.days_since_last_activity >= 14, '24h', '3d') AS deadline_hint,
|
||
concat(
|
||
'Компания молчит ',
|
||
toString(p.days_since_last_activity),
|
||
' дн., активность 30д ',
|
||
toString(p.amount_30d),
|
||
', прогноз 30д ',
|
||
toString(p.amount_forecast_30d),
|
||
'.'
|
||
) AS reason,
|
||
'Подтвердить, это плановая бизнес-пауза или выпадение предприятия из операционного ритма, и назначить владельца на возврат в норму.' AS recommended_action,
|
||
concat(
|
||
'days_since_last_activity=', toString(p.days_since_last_activity),
|
||
', docs_30d=', toString(p.docs_30d),
|
||
', forecast_30d=', toString(p.amount_forecast_30d)
|
||
) AS evidence_summary,
|
||
toUInt32(p.open_cases_total) AS open_cases_total,
|
||
toUInt32(p.detections_total) AS detections_total,
|
||
toUInt32(p.active_locks) AS active_locks,
|
||
toUInt16(p.days_since_last_activity) AS days_since_last_activity,
|
||
toUInt32(ifNull(ch.risky_changes_24h, 0)) AS risky_changes_24h,
|
||
toUInt32(ifNull(adj.large_adjustments_24h, 0)) AS large_adjustments_24h,
|
||
toFloat64(ifNull(adj.max_large_adjustment_amount, 0)) AS max_large_adjustment_amount,
|
||
p.last_seen_at AS last_seen_at
|
||
FROM portfolio AS p
|
||
LEFT JOIN risky_changes_24h AS ch ON ch.infobase = p.infobase AND ch.company_entity_key = p.company_entity_key
|
||
LEFT JOIN large_adjustments_24h AS adj ON adj.infobase = p.infobase AND adj.company_entity_key = p.company_entity_key
|
||
WHERE p.days_since_last_activity >= 7
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
now() AS generated_at,
|
||
concat('risky_change_review:', p.infobase, ':', p.company_entity_key) AS action_id,
|
||
p.infobase AS infobase,
|
||
p.company_entity_key AS company_entity_key,
|
||
p.counterparty AS counterparty,
|
||
p.company_name AS company_name,
|
||
p.organization AS organization,
|
||
if(p.registry_assignee_name != '', p.registry_assignee_name, if(p.owner_user != '', p.owner_user, 'не назначен')) AS owner_name,
|
||
'risky_change_review' AS action_type,
|
||
multiIf(ch.risky_changes_24h >= 3, 'critical', 'high') AS priority_tier,
|
||
toUInt32(greatest(toInt32(p.signal_score), 60) + least(toUInt32(ch.risky_changes_24h * 12), toUInt32(40))) AS priority_score,
|
||
'today' AS deadline_hint,
|
||
concat(
|
||
'За 24ч зафиксировано рискованных изменений документов: ',
|
||
toString(ch.risky_changes_24h),
|
||
if(ch.risky_change_examples != '', concat(' (', ch.risky_change_examples, ')'), '.')
|
||
) AS reason,
|
||
'Проверить изменённые реквизиты и связать их с документами, проводками и ответственным пользователем.' AS recommended_action,
|
||
concat(
|
||
'risky_changes_24h=', toString(ch.risky_changes_24h),
|
||
if(ch.risky_change_examples != '', concat(', fields=', ch.risky_change_examples), '')
|
||
) AS evidence_summary,
|
||
toUInt32(p.open_cases_total) AS open_cases_total,
|
||
toUInt32(p.detections_total) AS detections_total,
|
||
toUInt32(p.active_locks) AS active_locks,
|
||
toUInt16(p.days_since_last_activity) AS days_since_last_activity,
|
||
toUInt32(ch.risky_changes_24h) AS risky_changes_24h,
|
||
toUInt32(ifNull(adj.large_adjustments_24h, 0)) AS large_adjustments_24h,
|
||
toFloat64(ifNull(adj.max_large_adjustment_amount, 0)) AS max_large_adjustment_amount,
|
||
p.last_seen_at AS last_seen_at
|
||
FROM portfolio AS p
|
||
INNER JOIN risky_changes_24h AS ch ON ch.infobase = p.infobase AND ch.company_entity_key = p.company_entity_key
|
||
LEFT JOIN large_adjustments_24h AS adj ON adj.infobase = p.infobase AND adj.company_entity_key = p.company_entity_key
|
||
|
||
UNION ALL
|
||
|
||
SELECT
|
||
now() AS generated_at,
|
||
concat('large_adjustment_review:', p.infobase, ':', p.company_entity_key) AS action_id,
|
||
p.infobase AS infobase,
|
||
p.company_entity_key AS company_entity_key,
|
||
p.counterparty AS counterparty,
|
||
p.company_name AS company_name,
|
||
p.organization AS organization,
|
||
if(p.registry_assignee_name != '', p.registry_assignee_name, if(p.owner_user != '', p.owner_user, 'не назначен')) AS owner_name,
|
||
'large_adjustment_review' AS action_type,
|
||
multiIf(adj.max_large_adjustment_amount >= 250000, 'critical', 'high') AS priority_tier,
|
||
toUInt32(greatest(toInt32(p.signal_score), 65) + least(toUInt32(adj.large_adjustments_24h * 10), toUInt32(30))) AS priority_score,
|
||
'today' AS deadline_hint,
|
||
concat(
|
||
'За 24ч крупных корректировок/проводок: ',
|
||
toString(adj.large_adjustments_24h),
|
||
', максимальная сумма ',
|
||
toString(adj.max_large_adjustment_amount),
|
||
'.'
|
||
) AS reason,
|
||
'Подтвердить основание крупной корректировки, автора и влияние на управленческий результат предприятия.' AS recommended_action,
|
||
concat(
|
||
'large_adjustments_24h=', toString(adj.large_adjustments_24h),
|
||
', max_amount=', toString(adj.max_large_adjustment_amount)
|
||
) AS evidence_summary,
|
||
toUInt32(p.open_cases_total) AS open_cases_total,
|
||
toUInt32(p.detections_total) AS detections_total,
|
||
toUInt32(p.active_locks) AS active_locks,
|
||
toUInt16(p.days_since_last_activity) AS days_since_last_activity,
|
||
toUInt32(ifNull(ch.risky_changes_24h, 0)) AS risky_changes_24h,
|
||
toUInt32(adj.large_adjustments_24h) AS large_adjustments_24h,
|
||
toFloat64(adj.max_large_adjustment_amount) AS max_large_adjustment_amount,
|
||
p.last_seen_at AS last_seen_at
|
||
FROM portfolio AS p
|
||
INNER JOIN large_adjustments_24h AS adj ON adj.infobase = p.infobase AND adj.company_entity_key = p.company_entity_key
|
||
LEFT JOIN risky_changes_24h AS ch ON ch.infobase = p.infobase AND ch.company_entity_key = p.company_entity_key
|
||
)
|
||
SELECT
|
||
*,
|
||
multiIf(priority_tier = 'critical', toUInt8(4), priority_tier = 'high', toUInt8(3), priority_tier = 'medium', toUInt8(2), toUInt8(1)) AS priority_rank
|
||
FROM actions;
|
||
|
||
CREATE OR REPLACE VIEW analytics_1c.v_company_management_companies_current AS
|
||
SELECT
|
||
generated_at,
|
||
infobase,
|
||
company_entity_key,
|
||
counterparty,
|
||
company_name,
|
||
organization,
|
||
owner_names,
|
||
priority_rank_max AS priority_rank,
|
||
priority_tier,
|
||
priority_score,
|
||
actions_total,
|
||
critical_actions_total,
|
||
high_actions_total,
|
||
medium_actions_total,
|
||
low_actions_total,
|
||
action_types_summary,
|
||
recommended_action_summary,
|
||
reason_summary,
|
||
evidence_summary,
|
||
open_cases_total,
|
||
detections_total,
|
||
active_locks,
|
||
days_since_last_activity,
|
||
risky_changes_24h,
|
||
large_adjustments_24h,
|
||
max_large_adjustment_amount,
|
||
last_seen_at
|
||
FROM
|
||
(
|
||
SELECT
|
||
max(a.generated_at) AS generated_at,
|
||
infobase,
|
||
company_entity_key,
|
||
argMax(a.counterparty, tuple(a.priority_rank, a.priority_score, a.generated_at)) AS counterparty,
|
||
argMax(a.company_name, tuple(a.priority_rank, a.priority_score, a.generated_at)) AS company_name,
|
||
argMax(a.organization, tuple(a.priority_rank, a.priority_score, a.generated_at)) AS organization,
|
||
arrayStringConcat(arraySort(groupUniqArray(a.owner_name)), ', ') AS owner_names,
|
||
max(a.priority_rank) AS priority_rank_max,
|
||
argMax(a.priority_tier, tuple(a.priority_rank, a.priority_score, a.generated_at)) AS priority_tier,
|
||
max(a.priority_score) AS priority_score,
|
||
count() AS actions_total,
|
||
countIf(a.priority_tier = 'critical') AS critical_actions_total,
|
||
countIf(a.priority_tier = 'high') AS high_actions_total,
|
||
countIf(a.priority_tier = 'medium') AS medium_actions_total,
|
||
countIf(a.priority_tier = 'low') AS low_actions_total,
|
||
arrayStringConcat(arraySort(groupUniqArray(a.action_type)), ', ') AS action_types_summary,
|
||
arrayStringConcat(groupUniqArray(3)(a.recommended_action), ' | ') AS recommended_action_summary,
|
||
arrayStringConcat(groupUniqArray(3)(a.reason), ' | ') AS reason_summary,
|
||
arrayStringConcat(groupUniqArray(3)(a.evidence_summary), ' | ') AS evidence_summary,
|
||
max(a.open_cases_total) AS open_cases_total,
|
||
max(a.detections_total) AS detections_total,
|
||
max(a.active_locks) AS active_locks,
|
||
max(a.days_since_last_activity) AS days_since_last_activity,
|
||
max(a.risky_changes_24h) AS risky_changes_24h,
|
||
max(a.large_adjustments_24h) AS large_adjustments_24h,
|
||
max(a.max_large_adjustment_amount) AS max_large_adjustment_amount,
|
||
max(a.last_seen_at) AS last_seen_at
|
||
FROM analytics_1c.v_company_management_actions_current AS a
|
||
GROUP BY infobase, company_entity_key
|
||
);
|