auto-claude: subtask-1-1 - Create SQL migration for IT audit tables

Add migration script for it_audits and it_collaboration_matches tables:

- it_audits: 9-section form storage (IT Contact, Cloud & Identity,
  Server Infrastructure, Endpoints, Security, Backup & DR, Monitoring,
  Business Apps, Collaboration flags)
- it_collaboration_matches: Cross-company matching with 6 types
  (shared licensing, backup replication, Teams federation,
  shared monitoring, collective purchasing, knowledge sharing)
- Indexes for company_id, audit_date, scores, maturity level
- Update triggers for updated_at timestamps
- Views: company overview, audit history, collaboration overview,
  technology statistics
- Grants for nordabiz_app user

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-09 08:14:51 +01:00
parent 86db0a98d4
commit 8edcda52e3

View File

@ -0,0 +1,446 @@
-- ============================================================
-- NordaBiz - Migration: IT Infrastructure Audit Tables
-- ============================================================
-- Created: 2026-01-09
-- Description:
-- - Creates it_audits table for storing IT infrastructure audit results
-- - Creates it_collaboration_matches table for company cooperation matching
-- - Tracks security, collaboration, and completeness scores
-- - Stores form data and AI-generated recommendations as JSONB
-- - Includes indexes and helpful views
--
-- Usage:
-- PostgreSQL: psql -h localhost -U nordabiz_app -d nordabiz -f it_audit_migration.sql
-- SQLite: Not fully supported (JSONB columns, TEXT[] arrays)
-- ============================================================
-- ============================================================
-- 1. MAIN IT_AUDITS TABLE
-- ============================================================
CREATE TABLE IF NOT EXISTS it_audits (
id SERIAL PRIMARY KEY,
-- Company reference
company_id INTEGER NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
-- Audit timestamp and metadata
audit_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
audit_source VARCHAR(50) DEFAULT 'form', -- form, api_sync
audited_by INTEGER REFERENCES users(id),
-- Scores (0-100)
overall_score INTEGER,
completeness_score INTEGER,
security_score INTEGER,
collaboration_score INTEGER,
maturity_level VARCHAR(20), -- basic, developing, established, advanced
-- ========================================
-- Cloud & Identity (Section 2)
-- ========================================
has_azure_ad BOOLEAN DEFAULT FALSE,
azure_tenant_name VARCHAR(255),
azure_user_count VARCHAR(20), -- 1-10, 11-50, 51-100, 100+
has_m365 BOOLEAN DEFAULT FALSE,
m365_plans TEXT[], -- business_basic, business_standard, business_premium, e3, e5
teams_usage TEXT[], -- chat, meetings, channels, phone_system
has_google_workspace BOOLEAN DEFAULT FALSE,
-- ========================================
-- Server Infrastructure (Section 3)
-- ========================================
server_count VARCHAR(20), -- 0, 1-5, 6-10, 11-20, 20+
server_types TEXT[], -- physical, virtual, cloud_vm
virtualization_platform VARCHAR(50), -- proxmox, vmware, hyperv, none
server_os TEXT[], -- windows_server, ubuntu, debian, centos, other_linux
network_firewall_brand VARCHAR(100),
-- ========================================
-- Endpoints (Section 4)
-- ========================================
employee_count VARCHAR(20), -- 1-10, 11-25, 26-50, 51-100, 100+
computer_count VARCHAR(20), -- same ranges
desktop_os TEXT[], -- windows_10, windows_11, macos, linux
has_mdm BOOLEAN DEFAULT FALSE,
mdm_solution VARCHAR(50), -- intune, jamf, other
-- ========================================
-- Security (Section 5) - Highest weight
-- ========================================
antivirus_solution VARCHAR(50), -- windows_defender, eset, kaspersky, bitdefender, other
has_edr BOOLEAN DEFAULT FALSE,
edr_solution VARCHAR(100), -- crowdstrike, sentinel_one, defender_atp, other
has_vpn BOOLEAN DEFAULT FALSE,
vpn_solution VARCHAR(50), -- openvpn, wireguard, fortinet, cisco_anyconnect, other
has_mfa BOOLEAN DEFAULT FALSE,
mfa_scope TEXT[], -- email, vpn, admin_panels, all_apps
-- ========================================
-- Backup & Disaster Recovery (Section 6)
-- ========================================
backup_solution VARCHAR(50), -- veeam, acronis, proxmox_pbs, bacula, other
backup_targets TEXT[], -- local, nas, cloud, offsite
backup_frequency VARCHAR(20), -- daily, weekly, continuous, none
has_proxmox_pbs BOOLEAN DEFAULT FALSE,
has_dr_plan BOOLEAN DEFAULT FALSE,
-- ========================================
-- Monitoring (Section 7)
-- ========================================
monitoring_solution VARCHAR(50), -- zabbix, prometheus, prtg, datadog, none
zabbix_integration JSONB, -- {hostname: "...", api_enabled: true}
-- ========================================
-- Business Applications (Section 8)
-- ========================================
ticketing_system VARCHAR(50), -- jira, freshdesk, zendesk, none
erp_system VARCHAR(50), -- sap, optima, enova, wfmag, other, none
crm_system VARCHAR(50), -- salesforce, pipedrive, hubspot, other, none
-- ========================================
-- Active Directory (Section 3 extension)
-- ========================================
has_local_ad BOOLEAN DEFAULT FALSE,
ad_domain_name VARCHAR(255),
has_ad_azure_sync BOOLEAN DEFAULT FALSE,
-- ========================================
-- Collaboration Flags (Section 9)
-- ========================================
open_to_shared_licensing BOOLEAN DEFAULT FALSE,
open_to_backup_replication BOOLEAN DEFAULT FALSE,
open_to_teams_federation BOOLEAN DEFAULT FALSE,
open_to_shared_monitoring BOOLEAN DEFAULT FALSE,
open_to_collective_purchasing BOOLEAN DEFAULT FALSE,
open_to_knowledge_sharing BOOLEAN DEFAULT FALSE,
-- ========================================
-- IT Contact (Section 1)
-- ========================================
it_contact_name VARCHAR(255),
it_contact_email VARCHAR(255),
has_it_manager BOOLEAN DEFAULT FALSE,
it_outsourced BOOLEAN DEFAULT FALSE,
it_provider_name VARCHAR(255),
-- ========================================
-- Raw Data and AI Recommendations
-- ========================================
form_data JSONB, -- Complete form data for backup/debugging
recommendations JSONB, -- AI-generated improvement recommendations
audit_errors TEXT,
-- Timestamps
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE it_audits IS 'IT infrastructure audit results for companies';
COMMENT ON COLUMN it_audits.overall_score IS 'Weighted overall score 0-100 (Security 50%, Collaboration 30%, Completeness 20%)';
COMMENT ON COLUMN it_audits.security_score IS 'Security posture score 0-100 (EDR, MFA, firewall, backup, DR, VPN, monitoring)';
COMMENT ON COLUMN it_audits.collaboration_score IS 'Collaboration readiness score 0-100 (flags + Azure AD + M365 + PBS + Zabbix)';
COMMENT ON COLUMN it_audits.completeness_score IS 'Form completeness score 0-100 (percentage of filled fields)';
COMMENT ON COLUMN it_audits.maturity_level IS 'IT maturity: basic (0-39), developing (40-59), established (60-79), advanced (80-100)';
COMMENT ON COLUMN it_audits.audit_source IS 'How audit was triggered: form (user input), api_sync (future Zabbix integration)';
COMMENT ON COLUMN it_audits.form_data IS 'Complete form data as JSONB for backup and debugging';
COMMENT ON COLUMN it_audits.recommendations IS 'AI-generated improvement recommendations as JSON array';
COMMENT ON COLUMN it_audits.zabbix_integration IS 'Zabbix integration settings (hostname, API key placeholder)';
-- ============================================================
-- 2. IT_COLLABORATION_MATCHES TABLE
-- ============================================================
CREATE TABLE IF NOT EXISTS it_collaboration_matches (
id SERIAL PRIMARY KEY,
-- Company pair (company_a_id < company_b_id to avoid duplicates)
company_a_id INTEGER NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
company_b_id INTEGER NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
-- Match details
match_type VARCHAR(50) NOT NULL, -- shared_licensing, backup_replication, teams_federation, shared_monitoring, collective_purchasing, knowledge_sharing
match_reason TEXT, -- Human-readable explanation
match_score INTEGER, -- 0-100 compatibility score
-- Status management
status VARCHAR(20) DEFAULT 'suggested', -- suggested, contacted, in_progress, completed, rejected
-- Shared attributes for this match
shared_attributes JSONB, -- {"common_platform": "M365", "both_have_pbs": true, ...}
-- Timestamps
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- Ensure unique match per type between company pairs
UNIQUE(company_a_id, company_b_id, match_type),
-- Prevent self-matching
CONSTRAINT no_self_match CHECK (company_a_id <> company_b_id)
);
COMMENT ON TABLE it_collaboration_matches IS 'Cross-company collaboration opportunities based on IT audit data';
COMMENT ON COLUMN it_collaboration_matches.match_type IS 'Type of collaboration: shared_licensing, backup_replication, teams_federation, shared_monitoring, collective_purchasing, knowledge_sharing';
COMMENT ON COLUMN it_collaboration_matches.match_reason IS 'Human-readable explanation of why these companies match';
COMMENT ON COLUMN it_collaboration_matches.match_score IS 'Compatibility score 0-100 based on matching criteria';
COMMENT ON COLUMN it_collaboration_matches.status IS 'Match status: suggested, contacted, in_progress, completed, rejected';
COMMENT ON COLUMN it_collaboration_matches.shared_attributes IS 'JSON object with common attributes that triggered the match';
-- ============================================================
-- 3. INDEXES FOR PERFORMANCE
-- ============================================================
-- IT Audits indexes
CREATE INDEX IF NOT EXISTS idx_it_audits_company ON it_audits(company_id);
CREATE INDEX IF NOT EXISTS idx_it_audits_date ON it_audits(audit_date DESC);
CREATE INDEX IF NOT EXISTS idx_it_audits_overall_score ON it_audits(overall_score);
CREATE INDEX IF NOT EXISTS idx_it_audits_company_date ON it_audits(company_id, audit_date DESC);
CREATE INDEX IF NOT EXISTS idx_it_audits_maturity ON it_audits(maturity_level);
-- Collaboration matches indexes
CREATE INDEX IF NOT EXISTS idx_it_collab_company_a ON it_collaboration_matches(company_a_id);
CREATE INDEX IF NOT EXISTS idx_it_collab_company_b ON it_collaboration_matches(company_b_id);
CREATE INDEX IF NOT EXISTS idx_it_collab_match_type ON it_collaboration_matches(match_type);
CREATE INDEX IF NOT EXISTS idx_it_collab_status ON it_collaboration_matches(status);
-- ============================================================
-- 4. UPDATE TRIGGERS FOR updated_at
-- ============================================================
-- IT Audits trigger
CREATE OR REPLACE FUNCTION it_audits_update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trigger_it_audits_update ON it_audits;
CREATE TRIGGER trigger_it_audits_update
BEFORE UPDATE ON it_audits
FOR EACH ROW
EXECUTE FUNCTION it_audits_update_timestamp();
-- Collaboration matches trigger
CREATE OR REPLACE FUNCTION it_collab_matches_update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trigger_it_collab_matches_update ON it_collaboration_matches;
CREATE TRIGGER trigger_it_collab_matches_update
BEFORE UPDATE ON it_collaboration_matches
FOR EACH ROW
EXECUTE FUNCTION it_collab_matches_update_timestamp();
-- ============================================================
-- 5. IT AUDIT OVERVIEW VIEW
-- ============================================================
CREATE OR REPLACE VIEW v_company_it_overview AS
SELECT
c.id,
c.name,
c.slug,
c.website,
cat.name as category_name,
ia.overall_score,
ia.security_score,
ia.collaboration_score,
ia.completeness_score,
ia.maturity_level,
ia.has_azure_ad,
ia.has_m365,
ia.has_edr,
ia.has_mfa,
ia.has_proxmox_pbs,
ia.monitoring_solution,
ia.audit_date,
ia.audit_source,
-- Score category (same as maturity_level but for display)
CASE
WHEN ia.overall_score >= 80 THEN 'advanced'
WHEN ia.overall_score >= 60 THEN 'established'
WHEN ia.overall_score >= 40 THEN 'developing'
WHEN ia.overall_score IS NOT NULL THEN 'basic'
ELSE 'not_audited'
END as score_category,
-- Count collaboration flags
(COALESCE(ia.open_to_shared_licensing::int, 0) +
COALESCE(ia.open_to_backup_replication::int, 0) +
COALESCE(ia.open_to_teams_federation::int, 0) +
COALESCE(ia.open_to_shared_monitoring::int, 0) +
COALESCE(ia.open_to_collective_purchasing::int, 0) +
COALESCE(ia.open_to_knowledge_sharing::int, 0)) as collaboration_flags_count
FROM companies c
LEFT JOIN categories cat ON c.category_id = cat.id
LEFT JOIN LATERAL (
SELECT * FROM it_audits
WHERE company_id = c.id
ORDER BY audit_date DESC
LIMIT 1
) ia ON TRUE
ORDER BY ia.overall_score DESC NULLS LAST;
COMMENT ON VIEW v_company_it_overview IS 'Latest IT audit results per company for admin dashboard';
-- ============================================================
-- 6. IT AUDIT HISTORY VIEW
-- ============================================================
CREATE OR REPLACE VIEW v_it_audit_history AS
SELECT
ia.id as audit_id,
c.id as company_id,
c.name as company_name,
c.slug as company_slug,
ia.overall_score,
ia.security_score,
ia.collaboration_score,
ia.completeness_score,
ia.maturity_level,
ia.audit_date,
ia.audit_source,
-- Previous score for comparison
LAG(ia.overall_score) OVER (
PARTITION BY ia.company_id
ORDER BY ia.audit_date
) as previous_score,
-- Score change
ia.overall_score - LAG(ia.overall_score) OVER (
PARTITION BY ia.company_id
ORDER BY ia.audit_date
) as score_change
FROM it_audits ia
JOIN companies c ON ia.company_id = c.id
ORDER BY ia.audit_date DESC;
COMMENT ON VIEW v_it_audit_history IS 'IT audit history with score trend tracking';
-- ============================================================
-- 7. COLLABORATION MATCHES VIEW
-- ============================================================
CREATE OR REPLACE VIEW v_it_collaboration_overview AS
SELECT
m.id as match_id,
m.match_type,
m.match_score,
m.match_reason,
m.status,
m.created_at,
ca.id as company_a_id,
ca.name as company_a_name,
ca.slug as company_a_slug,
cb.id as company_b_id,
cb.name as company_b_name,
cb.slug as company_b_slug,
m.shared_attributes,
-- Match type display name
CASE m.match_type
WHEN 'shared_licensing' THEN 'Wspólne licencje M365'
WHEN 'backup_replication' THEN 'Replikacja backup (PBS)'
WHEN 'teams_federation' THEN 'Federacja Teams'
WHEN 'shared_monitoring' THEN 'Wspólny monitoring (Zabbix)'
WHEN 'collective_purchasing' THEN 'Zakupy grupowe'
WHEN 'knowledge_sharing' THEN 'Wymiana wiedzy'
ELSE m.match_type
END as match_type_display
FROM it_collaboration_matches m
JOIN companies ca ON m.company_a_id = ca.id
JOIN companies cb ON m.company_b_id = cb.id
ORDER BY m.match_score DESC NULLS LAST, m.created_at DESC;
COMMENT ON VIEW v_it_collaboration_overview IS 'Collaboration matches with company names for admin dashboard';
-- ============================================================
-- 8. TECHNOLOGY STATISTICS VIEW
-- ============================================================
CREATE OR REPLACE VIEW v_it_technology_stats AS
SELECT
'azure_ad' as technology,
COUNT(*) FILTER (WHERE ia.has_azure_ad = TRUE) as company_count,
ROUND(100.0 * COUNT(*) FILTER (WHERE ia.has_azure_ad = TRUE) / NULLIF(COUNT(*), 0), 1) as percentage
FROM it_audits ia
WHERE ia.id IN (
SELECT DISTINCT ON (company_id) id FROM it_audits ORDER BY company_id, audit_date DESC
)
UNION ALL
SELECT
'm365' as technology,
COUNT(*) FILTER (WHERE ia.has_m365 = TRUE),
ROUND(100.0 * COUNT(*) FILTER (WHERE ia.has_m365 = TRUE) / NULLIF(COUNT(*), 0), 1)
FROM it_audits ia
WHERE ia.id IN (
SELECT DISTINCT ON (company_id) id FROM it_audits ORDER BY company_id, audit_date DESC
)
UNION ALL
SELECT
'edr' as technology,
COUNT(*) FILTER (WHERE ia.has_edr = TRUE),
ROUND(100.0 * COUNT(*) FILTER (WHERE ia.has_edr = TRUE) / NULLIF(COUNT(*), 0), 1)
FROM it_audits ia
WHERE ia.id IN (
SELECT DISTINCT ON (company_id) id FROM it_audits ORDER BY company_id, audit_date DESC
)
UNION ALL
SELECT
'mfa' as technology,
COUNT(*) FILTER (WHERE ia.has_mfa = TRUE),
ROUND(100.0 * COUNT(*) FILTER (WHERE ia.has_mfa = TRUE) / NULLIF(COUNT(*), 0), 1)
FROM it_audits ia
WHERE ia.id IN (
SELECT DISTINCT ON (company_id) id FROM it_audits ORDER BY company_id, audit_date DESC
)
UNION ALL
SELECT
'proxmox_pbs' as technology,
COUNT(*) FILTER (WHERE ia.has_proxmox_pbs = TRUE),
ROUND(100.0 * COUNT(*) FILTER (WHERE ia.has_proxmox_pbs = TRUE) / NULLIF(COUNT(*), 0), 1)
FROM it_audits ia
WHERE ia.id IN (
SELECT DISTINCT ON (company_id) id FROM it_audits ORDER BY company_id, audit_date DESC
);
COMMENT ON VIEW v_it_technology_stats IS 'Technology adoption statistics across audited companies';
-- ============================================================
-- 9. GRANTS FOR APPLICATION USER
-- ============================================================
-- Grant permissions on tables
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE it_audits TO nordabiz_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE it_collaboration_matches TO nordabiz_app;
-- Grant permissions on sequences
GRANT USAGE, SELECT ON SEQUENCE it_audits_id_seq TO nordabiz_app;
GRANT USAGE, SELECT ON SEQUENCE it_collaboration_matches_id_seq TO nordabiz_app;
-- Grant permissions on views
GRANT SELECT ON v_company_it_overview TO nordabiz_app;
GRANT SELECT ON v_it_audit_history TO nordabiz_app;
GRANT SELECT ON v_it_collaboration_overview TO nordabiz_app;
GRANT SELECT ON v_it_technology_stats TO nordabiz_app;
-- ============================================================
-- MIGRATION COMPLETE
-- ============================================================
-- Verify migration (PostgreSQL only)
DO $$
BEGIN
RAISE NOTICE 'IT Audit migration completed successfully!';
RAISE NOTICE 'Created:';
RAISE NOTICE ' - Table: it_audits (IT infrastructure audit storage)';
RAISE NOTICE ' - Table: it_collaboration_matches (cross-company matching)';
RAISE NOTICE ' - Indexes: company_id, audit_date, scores, maturity level, match types';
RAISE NOTICE ' - Triggers: updated_at auto-update for both tables';
RAISE NOTICE ' - Views: v_company_it_overview, v_it_audit_history, v_it_collaboration_overview, v_it_technology_stats';
RAISE NOTICE ' - Grants: nordabiz_app permissions on all objects';
END $$;