-- PRINZIP CRM — заявки и сделки за 8 лет (102K leads / 9K deals / 39.9 млрд ₽). -- Источник: SQLite-дамп analytics.prinzipevent.ru (49 МБ). -- Импорт: data/sql/52_import_prinzip_crm.py --sqlite data/raw/prinzip_crm.sqlite. -- См. план C:\Users\user\.claude\plans\crispy-waddling-swing.md (#14). -- ── Заявки (~102K) ───────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS prinzip_leads ( lead_id BIGINT PRIMARY KEY, created_at TIMESTAMPTZ NOT NULL, source TEXT, -- web/call/cian/avito/... channel TEXT, -- ad campaign / utm name TEXT, phone_hash TEXT, -- SHA256(phone)[:16] для PII compliance phone_last4 TEXT, -- последние 4 цифры для UI email_hash TEXT, -- md5(email) obj_interest BIGINT, -- soft FK → domrf_kn_objects.obj_id rooms INT, budget_min NUMERIC, budget_max NUMERIC, status TEXT, -- new/contacted/qualified/lost/converted converted BOOLEAN DEFAULT FALSE, deal_id BIGINT -- soft FK → prinzip_deals (если converted) ); CREATE INDEX IF NOT EXISTS idx_leads_created ON prinzip_leads (created_at); CREATE INDEX IF NOT EXISTS idx_leads_status ON prinzip_leads (status, created_at); CREATE INDEX IF NOT EXISTS idx_leads_obj ON prinzip_leads (obj_interest); CREATE INDEX IF NOT EXISTS idx_leads_source ON prinzip_leads (source); -- ── Сделки (~9K, sum revenue 39.9 млрд ₽) ────────────────────────────────── CREATE TABLE IF NOT EXISTS prinzip_deals ( deal_id BIGINT PRIMARY KEY, lead_id BIGINT, -- soft FK → prinzip_leads closed_at TIMESTAMPTZ NOT NULL, obj_id BIGINT, -- soft FK → domrf_kn_objects flat_ods_id TEXT, -- match с domrf_kn_flats.ods_id rooms INT, total_area NUMERIC, deal_price NUMERIC, -- ₽ price_per_m2 NUMERIC, payment_type TEXT, -- cash/mortgage/escrow/installment mortgage_bank TEXT, mortgage_rate NUMERIC, deal_stage TEXT -- reservation/dku/contract/done ); CREATE INDEX IF NOT EXISTS idx_deals_closed ON prinzip_deals (closed_at); CREATE INDEX IF NOT EXISTS idx_deals_obj ON prinzip_deals (obj_id); CREATE INDEX IF NOT EXISTS idx_deals_lead ON prinzip_deals (lead_id); -- ── Воронка по месяцам — denormalized aggregate (для UI чартов) ──────────── -- REFRESH MATERIALIZED VIEW prinzip_funnel_monthly после каждого импорта. DROP MATERIALIZED VIEW IF EXISTS prinzip_funnel_monthly; CREATE MATERIALIZED VIEW prinzip_funnel_monthly AS SELECT DATE_TRUNC('month', created_at)::date AS month, COALESCE(source, 'unknown') AS source, COUNT(*) AS leads, COUNT(*) FILTER (WHERE status IN ('qualified', 'contacted', 'converted')) AS engaged, COUNT(*) FILTER (WHERE converted) AS converted, ROUND(100.0 * COUNT(*) FILTER (WHERE converted) / NULLIF(COUNT(*), 0), 2) AS conv_pct FROM prinzip_leads GROUP BY 1, 2 ORDER BY 1 DESC, 2; CREATE UNIQUE INDEX IF NOT EXISTS idx_funnel_monthly_pk ON prinzip_funnel_monthly (month, source); -- ── Воронка по ЖК (агрегат) ──────────────────────────────────────────────── DROP MATERIALIZED VIEW IF EXISTS prinzip_funnel_by_object; CREATE MATERIALIZED VIEW prinzip_funnel_by_object AS WITH leads_by_obj AS ( SELECT obj_interest AS obj_id, COUNT(*) AS leads_count FROM prinzip_leads WHERE obj_interest IS NOT NULL GROUP BY obj_interest ), deals_by_obj AS ( SELECT obj_id, COUNT(*) AS deals_count, SUM(deal_price) AS total_revenue, AVG(deal_price) AS avg_deal_price FROM prinzip_deals WHERE obj_id IS NOT NULL GROUP BY obj_id ) SELECT COALESCE(l.obj_id, d.obj_id) AS obj_id, o.comm_name, COALESCE(l.leads_count, 0) AS leads_count, COALESCE(d.deals_count, 0) AS deals_count, ROUND(100.0 * d.deals_count / NULLIF(l.leads_count, 0), 2) AS conv_pct, d.total_revenue, d.avg_deal_price FROM leads_by_obj l FULL OUTER JOIN deals_by_obj d ON d.obj_id = l.obj_id LEFT JOIN domrf_kn_objects o ON o.obj_id = COALESCE(l.obj_id, d.obj_id) AND o.snapshot_date = (SELECT MAX(snapshot_date) FROM domrf_kn_objects); CREATE UNIQUE INDEX IF NOT EXISTS idx_funnel_by_obj_pk ON prinzip_funnel_by_object (obj_id); COMMENT ON TABLE prinzip_leads IS 'PRINZIP CRM заявки. PII (phone/email) хешированы при импорте.'; COMMENT ON TABLE prinzip_deals IS 'PRINZIP CRM сделки. Sum deal_price ≈ 39.9 млрд ₽ за 8 лет.'; COMMENT ON MATERIALIZED VIEW prinzip_funnel_monthly IS 'Воронка leads → engaged → converted по месяцам и source. REFRESH после import.'; COMMENT ON MATERIALIZED VIEW prinzip_funnel_by_object IS 'Conversion per ЖК: leads / deals / revenue / avg deal_price.';