system-prompts-and-models-o.../salesflow-saas/backend/app/core/database.py
Intelligence OS b56a2f388b feat(intelligence): Revenue Intelligence OS — Lead Machine complete
ADDED MODULES:
- intelligence/icp.py: ICP Builder — 34 Saudi industries, Arabic+English queries
- intelligence/discovery.py: Multi-source discovery — web search + 34 seed companies
- intelligence/enrichment.py: Company/person enrichment — website data + news
- intelligence/scoring.py: 5-dimension scoring — Fit/Intent/Access/Value/Urgency
- intelligence/entity_resolution.py: Arabic/English dedup + fuzzy matching
- intelligence/outreach.py: Arabic-first outreach — WhatsApp/Email/LinkedIn
- intelligence/triggers.py: Trigger alerts — funding/hiring/expansion/IPO
- intelligence/pipeline.py: End-to-end orchestrator — ICP→Discovery→Score→Brief
- routes/intelligence.py: 15 REST endpoints + audit chain integration
- DB: 5 new tables — intelligence_leads/runs/watchlist/triggers/entities

ARCHITECTURE:
- Layer 1: Signal collection (web + curated Saudi B2B DB of 34 companies)
- Layer 2: Enrichment (website data, news, tech stack detection)
- Layer 3: 5D scoring — Master = 0.30 Fit + 0.25 Intent + 0.15 Access + 0.20 Value + 0.10 Urgency
- Layer 4: Entity resolution — Arabic/English fuzzy dedup
- Layer 5: Outreach brief — signal-driven WhatsApp/Email/LinkedIn in Arabic

MOTION SUPPORT: B2B sales | partnership | channel | tender
2026-04-20 06:35:59 +00:00

562 lines
22 KiB
Python

"""Dealix Database Core — SQLite with full schema for 9 OS modules"""
import sqlite3
import hashlib
import json
import time
from contextlib import contextmanager
from pathlib import Path
DB_PATH = Path(__file__).parent.parent.parent / "dealix.db"
def get_connection():
conn = sqlite3.connect(str(DB_PATH), check_same_thread=False)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=DELETE")
conn.execute("PRAGMA foreign_keys=ON")
return conn
@contextmanager
def db():
conn = get_connection()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
def init_db():
with db() as conn:
conn.executescript("""
-- Users & Auth
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'sales',
org_id TEXT NOT NULL DEFAULT 'dealix',
password_hash TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 1. REVENUE OS
-- ============================================================
CREATE TABLE IF NOT EXISTS leads (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
company_name TEXT NOT NULL,
contact_name TEXT,
contact_email TEXT,
contact_phone TEXT,
source TEXT DEFAULT 'website',
industry TEXT,
company_size TEXT,
annual_revenue TEXT,
region TEXT,
status TEXT DEFAULT 'new',
score INTEGER DEFAULT 0,
stage TEXT DEFAULT 'intake',
assigned_to TEXT,
notes TEXT,
enriched_data TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS deals (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
lead_id TEXT,
title TEXT NOT NULL,
value REAL DEFAULT 0,
currency TEXT DEFAULT 'SAR',
stage TEXT DEFAULT 'discovery',
probability INTEGER DEFAULT 0,
close_date TEXT,
owner_id TEXT,
account_id TEXT,
notes TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS accounts (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
company_name TEXT NOT NULL,
industry TEXT,
tier TEXT DEFAULT 'standard',
arr REAL DEFAULT 0,
health_score INTEGER DEFAULT 75,
csm_id TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 2. PRICING & MARGIN CONTROL OS
-- ============================================================
CREATE TABLE IF NOT EXISTS quotes (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
deal_id TEXT,
account_id TEXT,
line_items TEXT,
subtotal REAL DEFAULT 0,
discount_pct REAL DEFAULT 0,
discount_reason TEXT,
final_price REAL DEFAULT 0,
margin_pct REAL DEFAULT 0,
approval_status TEXT DEFAULT 'pending',
approved_by TEXT,
approved_at TEXT,
valid_until TEXT,
created_by TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS discount_policies (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
max_discount_pct REAL NOT NULL,
approver_role TEXT NOT NULL,
deal_value_min REAL DEFAULT 0,
deal_value_max REAL,
active INTEGER DEFAULT 1
);
-- ============================================================
-- 3. PARTNERSHIP & ALLIANCE OS
-- ============================================================
CREATE TABLE IF NOT EXISTS partners (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
company_name TEXT NOT NULL,
partner_type TEXT DEFAULT 'reseller',
status TEXT DEFAULT 'prospect',
fit_score INTEGER DEFAULT 0,
revenue_contribution REAL DEFAULT 0,
health_score INTEGER DEFAULT 75,
contact_name TEXT,
contact_email TEXT,
notes TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS alliance_workflows (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
partner_id TEXT NOT NULL,
stage TEXT DEFAULT 'scouting',
economics_model TEXT,
term_sheet TEXT,
approval_status TEXT DEFAULT 'pending',
approved_by TEXT,
activation_date TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 4. PROCUREMENT / VENDOR OS
-- ============================================================
CREATE TABLE IF NOT EXISTS vendors (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
vendor_name TEXT NOT NULL,
category TEXT,
risk_level TEXT DEFAULT 'medium',
spend REAL DEFAULT 0,
health_score INTEGER DEFAULT 75,
contract_expiry TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS procurement_requests (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
vendor_id TEXT,
title TEXT NOT NULL,
amount REAL NOT NULL,
justification TEXT,
status TEXT DEFAULT 'draft',
approval_status TEXT DEFAULT 'pending',
approved_by TEXT,
approved_at TEXT,
created_by TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 5. RENEWAL & EXPANSION OS
-- ============================================================
CREATE TABLE IF NOT EXISTS renewals (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
account_id TEXT NOT NULL,
current_arr REAL DEFAULT 0,
renewal_date TEXT,
churn_risk_score INTEGER DEFAULT 0,
expansion_score INTEGER DEFAULT 0,
status TEXT DEFAULT 'upcoming',
rescue_play_active INTEGER DEFAULT 0,
assigned_to TEXT,
notes TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 6. EXPANSION / MARKET ENTRY OS
-- ============================================================
CREATE TABLE IF NOT EXISTS market_entries (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
market_name TEXT NOT NULL,
segment TEXT,
readiness_score INTEGER DEFAULT 0,
status TEXT DEFAULT 'scanning',
gtm_plan TEXT,
launch_date TEXT,
stop_loss_triggered INTEGER DEFAULT 0,
actual_vs_forecast TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 7. M&A / CORPORATE DEVELOPMENT OS
-- ============================================================
CREATE TABLE IF NOT EXISTS ma_targets (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
target_name TEXT NOT NULL,
industry TEXT,
estimated_value REAL,
fit_score INTEGER DEFAULT 0,
stage TEXT DEFAULT 'screening',
dd_findings TEXT,
valuation_memo TEXT,
synergy_model TEXT,
ic_pack_status TEXT DEFAULT 'pending',
board_pack_ready INTEGER DEFAULT 0,
close_date TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 8. PMI / STRATEGIC PMO OS
-- ============================================================
CREATE TABLE IF NOT EXISTS pmo_projects (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
title TEXT NOT NULL,
type TEXT DEFAULT 'pmi',
status TEXT DEFAULT 'active',
day1_readiness INTEGER DEFAULT 0,
plan_30_60_90 TEXT,
synergy_target REAL DEFAULT 0,
synergy_realized REAL DEFAULT 0,
blockers TEXT,
health TEXT DEFAULT 'green',
created_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- 9. EXECUTIVE / BOARD OS
-- ============================================================
CREATE TABLE IF NOT EXISTS approvals (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
module TEXT NOT NULL,
reference_id TEXT NOT NULL,
title TEXT NOT NULL,
amount REAL,
risk_level TEXT DEFAULT 'medium',
status TEXT DEFAULT 'pending',
requested_by TEXT,
approved_by TEXT,
decision_at TEXT,
evidence_pack TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS executive_packs (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
week_label TEXT,
actual_revenue REAL DEFAULT 0,
forecast_revenue REAL DEFAULT 0,
open_approvals INTEGER DEFAULT 0,
blockers TEXT,
next_best_actions TEXT,
risk_heatmap TEXT,
generated_at TEXT DEFAULT (datetime('now'))
);
-- ============================================================
-- AUDIT CHAIN (cross-module)
-- ============================================================
-- =============================================
-- Revenue Intelligence OS — Lead Machine Tables
-- =============================================
-- ICP configs per org
CREATE TABLE IF NOT EXISTS icp_configs (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
name TEXT NOT NULL,
config TEXT NOT NULL, -- JSON ICPConfig
is_active INTEGER DEFAULT 1,
created_by TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- Discovered leads (raw, before enrichment)
CREATE TABLE IF NOT EXISTS intelligence_leads (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
company_name TEXT NOT NULL,
domain TEXT,
industry TEXT,
region TEXT,
company_size TEXT,
description TEXT,
website TEXT,
tech_stack TEXT, -- JSON list
signals TEXT, -- JSON list
recent_news TEXT, -- JSON list
contact_name TEXT,
contact_title TEXT,
contact_email TEXT,
contact_phone TEXT,
contact_linkedin TEXT,
decision_maker_score INTEGER DEFAULT 0,
enrichment_source TEXT DEFAULT 'web',
enrichment_confidence REAL DEFAULT 0.5,
source TEXT,
source_url TEXT,
raw_snippet TEXT,
trigger TEXT,
-- Scores
score_fit INTEGER DEFAULT 0,
score_intent INTEGER DEFAULT 0,
score_access INTEGER DEFAULT 0,
score_value INTEGER DEFAULT 0,
score_urgency INTEGER DEFAULT 0,
score_master INTEGER DEFAULT 0,
priority_tier TEXT DEFAULT 'P4',
score_reasons TEXT, -- JSON list
next_action TEXT,
next_action_ar TEXT,
-- Outreach
outreach_whatsapp_ar TEXT,
outreach_email_subject_ar TEXT,
outreach_email_body_ar TEXT,
outreach_linkedin_ar TEXT,
outreach_angle TEXT,
-- Pipeline tracking
pipeline_run_id TEXT,
crm_lead_id TEXT, -- linked to leads table
status TEXT DEFAULT 'discovered', -- discovered | contacted | qualified | archived
reviewed_by TEXT,
reviewed_at TEXT,
enriched_at TEXT,
discovered_at TEXT DEFAULT (datetime('now')),
created_at TEXT DEFAULT (datetime('now'))
);
-- Pipeline run history
CREATE TABLE IF NOT EXISTS intelligence_runs (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
icp_id TEXT,
run_mode TEXT DEFAULT 'auto', -- auto | manual | triggered
motion TEXT DEFAULT 'sales',
total_discovered INTEGER DEFAULT 0,
total_deduped INTEGER DEFAULT 0,
total_enriched INTEGER DEFAULT 0,
tier_p1 INTEGER DEFAULT 0,
tier_p2 INTEGER DEFAULT 0,
tier_p3 INTEGER DEFAULT 0,
tier_p4 INTEGER DEFAULT 0,
duration_sec REAL DEFAULT 0,
status TEXT DEFAULT 'running', -- running | complete | error
error_message TEXT,
created_by TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- Watchlist for trigger alerts
CREATE TABLE IF NOT EXISTS intelligence_watchlist (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
company_name TEXT NOT NULL,
domain TEXT,
priority INTEGER DEFAULT 0,
last_scanned TEXT,
active INTEGER DEFAULT 1,
added_by TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- Trigger events detected
CREATE TABLE IF NOT EXISTS intelligence_triggers (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL,
company_name TEXT NOT NULL,
trigger_type TEXT NOT NULL,
trigger_label_ar TEXT,
signal_strength INTEGER DEFAULT 0,
evidence TEXT,
source_url TEXT,
recommended_action_ar TEXT,
recommended_action_en TEXT,
is_actioned INTEGER DEFAULT 0,
actioned_by TEXT,
detected_at TEXT DEFAULT (datetime('now'))
);
-- Entity registry (deduplication)
CREATE TABLE IF NOT EXISTS intelligence_entities (
id TEXT PRIMARY KEY,
canonical_name TEXT NOT NULL,
normalized_name TEXT,
domain TEXT,
aliases TEXT, -- JSON list
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
org_id TEXT NOT NULL,
module TEXT NOT NULL,
action TEXT NOT NULL,
actor_id TEXT,
resource_id TEXT,
payload TEXT,
prev_hash TEXT,
entry_hash TEXT,
ts TEXT DEFAULT (datetime('now'))
);
""")
# Seed admin users
import hashlib, uuid
users = [
("admin-001", "admin@dealix.io", "Admin", "admin"),
("mgr-001", "manager@dealix.io", "Manager", "manager"),
("sales-001", "sales@dealix.io", "Sales Rep", "sales"),
]
passwords = {"admin": "Admin1234!", "manager": "Manager1234!", "sales": "Sales1234!"}
for uid, email, name, role in users:
pw = hashlib.sha256(passwords[role].encode()).hexdigest()
conn.execute("""
INSERT OR IGNORE INTO users (id, email, name, role, password_hash)
VALUES (?, ?, ?, ?, ?)
""", (uid, email, name, role, pw))
# Seed discount policies
conn.execute("""
INSERT OR IGNORE INTO discount_policies (id, org_id, max_discount_pct, approver_role, deal_value_min, deal_value_max)
VALUES ('dp-1','dealix',10,'sales',0,50000),
('dp-2','dealix',20,'manager',50000,200000),
('dp-3','dealix',35,'admin',200000,NULL)
""")
# Seed sample data for dashboard
_seed_sample_data(conn)
def _seed_sample_data(conn):
import uuid
# Sample leads
leads = [
("lead-001","dealix","البنك الأهلي","محمد الغامدي","m@anb.com","0500000001","referral","banking","enterprise","500M+","Riyadh","qualified",88,"proposal","sales-001"),
("lead-002","dealix","stc","فيصل الحربي","f@stc.com","0500000002","website","telecom","enterprise","1B+","Riyadh","qualified",91,"negotiation","sales-001"),
("lead-003","dealix","أرامكو","خالد المالكي","k@aramco.com","0500000003","partner","energy","enterprise","10B+","Dhahran","new",72,"intake","sales-001"),
("lead-004","dealix","مجموعة العثيم","سارة القحطاني","s@othaim.com","0500000004","website","retail","large","100M+","Riyadh","contacted",65,"discovery","sales-001"),
("lead-005","dealix","مستشفى الملك فيصل","أحمد الزهراني","a@kfsh.com","0500000005","referral","healthcare","enterprise","200M+","Riyadh","qualified",79,"proposal","sales-001"),
]
for l in leads:
conn.execute("""INSERT OR IGNORE INTO leads
(id,org_id,company_name,contact_name,contact_email,contact_phone,source,industry,company_size,annual_revenue,region,status,score,stage,assigned_to)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", l)
# Sample deals
deals = [
("deal-001","dealix","lead-001","صفقة البنك الأهلي — Revenue OS",850000,"SAR","proposal",75,"2026-06-30","sales-001","acc-001"),
("deal-002","dealix","lead-002","stc — Enterprise Suite",1200000,"SAR","negotiation",85,"2026-05-31","sales-001","acc-002"),
("deal-003","dealix","lead-003","أرامكو — Executive OS",2500000,"SAR","discovery",40,"2026-09-30","sales-001","acc-003"),
("deal-004","dealix","lead-005","KFSH — Procurement OS",420000,"SAR","proposal",65,"2026-07-15","sales-001","acc-004"),
]
for d in deals:
conn.execute("""INSERT OR IGNORE INTO deals
(id,org_id,lead_id,title,value,currency,stage,probability,close_date,owner_id,account_id)
VALUES (?,?,?,?,?,?,?,?,?,?,?)""", d)
# Sample accounts
accounts = [
("acc-001","dealix","البنك الأهلي","banking","enterprise",850000,82,"sales-001"),
("acc-002","dealix","stc","telecom","strategic",1200000,91,"sales-001"),
("acc-003","dealix","أرامكو","energy","strategic",0,88,"sales-001"),
("acc-004","dealix","KFSH","healthcare","enterprise",420000,74,"sales-001"),
]
for a in accounts:
conn.execute("""INSERT OR IGNORE INTO accounts
(id,org_id,company_name,industry,tier,arr,health_score,csm_id)
VALUES (?,?,?,?,?,?,?,?)""", a)
# Sample partners
partners = [
("part-001","dealix","Oracle Arabia","technology","active",87,320000,82,"علي الدوسري","a@oracle.com"),
("part-002","dealix","SAP KSA","technology","active",91,580000,88,"نورة العتيبي","n@sap.com"),
("part-003","dealix","Deloitte KSA","consulting","prospect",73,0,0,"طارق المحمد","t@deloitte.com"),
]
for p in partners:
conn.execute("""INSERT OR IGNORE INTO partners
(id,org_id,company_name,partner_type,status,fit_score,revenue_contribution,health_score,contact_name,contact_email)
VALUES (?,?,?,?,?,?,?,?,?,?)""", p)
# Sample M&A targets
conn.execute("""INSERT OR IGNORE INTO ma_targets
(id,org_id,target_name,industry,estimated_value,fit_score,stage)
VALUES ('ma-001','dealix','Salesbook KSA','SaaS',8500000,84,'due_diligence')""")
# Sample renewals
conn.execute("""INSERT OR IGNORE INTO renewals
(id,org_id,account_id,current_arr,renewal_date,churn_risk_score,expansion_score,status)
VALUES ('ren-001','dealix','acc-001',850000,'2026-12-31',22,67,'upcoming'),
('ren-002','dealix','acc-002',1200000,'2026-10-31',8,88,'upcoming')""")
# Sample approvals
conn.execute("""INSERT OR IGNORE INTO approvals
(id,org_id,module,reference_id,title,amount,risk_level,status,requested_by)
VALUES
('appr-001','dealix','pricing','deal-001','خصم 25% — البنك الأهلي',212500,'high','pending','sales-001'),
('appr-002','dealix','procurement','pr-001','تجديد عقد Oracle',145000,'medium','pending','mgr-001'),
('appr-003','dealix','partnership','part-003','تفعيل شراكة Deloitte',0,'low','pending','mgr-001')""")
# Executive pack
conn.execute("""INSERT OR IGNORE INTO executive_packs
(id,org_id,week_label,actual_revenue,forecast_revenue,open_approvals,blockers,next_best_actions)
VALUES ('ep-001','dealix','الأسبوع 16 — 2026',3850000,4200000,3,
'["صفقة أرامكو: تأخر RFP","تجديد Oracle: انتهاء العقد في 30 يوم"]',
'["أغلق خصم البنك الأهلي — 25%","ادفع تجديد Oracle قبل الانتهاء","جدول kickoff مع Deloitte"]')""")
# Audit chain seed
prev = "GENESIS"
entries = [
("dealix","revenue","lead_created","admin-001","lead-001","{}"),
("dealix","pricing","quote_created","sales-001","deal-001","{}"),
("dealix","partnership","partner_added","mgr-001","part-001","{}"),
("dealix","executive","pack_generated","admin-001","ep-001","{}"),
]
for org, module, action, actor, resource, payload in entries:
import hashlib, time
content = f"{org}:{module}:{action}:{actor}:{resource}:{time.time()}"
entry_hash = hashlib.sha256(f"{prev}:{content}".encode()).hexdigest()
conn.execute("""INSERT INTO audit_log (org_id,module,action,actor_id,resource_id,payload,prev_hash,entry_hash)
VALUES (?,?,?,?,?,?,?,?)""", (org, module, action, actor, resource, payload, prev, entry_hash))
prev = entry_hash