-- Hardware Asset Management Module -- Created: 2026-01-30 -- Description: Complete hardware tracking with ownership, location, and attachment history -- ============================================================================ -- Table 1: hardware_assets -- ============================================================================ CREATE TABLE IF NOT EXISTS hardware_assets ( id SERIAL PRIMARY KEY, asset_type VARCHAR(50) NOT NULL CHECK (asset_type IN ('pc', 'laptop', 'printer', 'skærm', 'telefon', 'server', 'netværk', 'andet')), brand VARCHAR(100), model VARCHAR(100), serial_number VARCHAR(100), customer_asset_id VARCHAR(50), -- Customer's inventory number internal_asset_id VARCHAR(50), -- BMC internal ID notes TEXT, -- Current state (for quick queries) current_owner_type VARCHAR(50) CHECK (current_owner_type IN ('customer', 'bmc', 'third_party')), current_owner_customer_id INT, -- if owner_type = customer current_location_id INT, -- Status status VARCHAR(50) DEFAULT 'active' CHECK (status IN ('active', 'faulty_reported', 'in_repair', 'replaced', 'retired', 'unsupported')), status_reason TEXT, warranty_until DATE, end_of_life DATE, -- Follow-up follow_up_date DATE, follow_up_owner_user_id INT, -- Standard fields created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), deleted_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_hardware_serial ON hardware_assets(serial_number); CREATE INDEX IF NOT EXISTS idx_hardware_customer ON hardware_assets(current_owner_customer_id); CREATE INDEX IF NOT EXISTS idx_hardware_status ON hardware_assets(status) WHERE deleted_at IS NULL; -- ============================================================================ -- Table 2: hardware_ownership_history -- ============================================================================ CREATE TABLE IF NOT EXISTS hardware_ownership_history ( id SERIAL PRIMARY KEY, hardware_id INT NOT NULL REFERENCES hardware_assets(id), owner_type VARCHAR(50) NOT NULL CHECK (owner_type IN ('customer', 'bmc', 'third_party')), owner_customer_id INT, -- if owner_type = customer start_date DATE NOT NULL, end_date DATE, -- NULL = active ownership notes TEXT, created_at TIMESTAMP DEFAULT NOW(), deleted_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_ownership_hardware ON hardware_ownership_history(hardware_id); CREATE INDEX IF NOT EXISTS idx_ownership_active ON hardware_ownership_history(hardware_id, end_date) WHERE end_date IS NULL AND deleted_at IS NULL; -- ============================================================================ -- Table 3: hardware_location_history -- ============================================================================ CREATE TABLE IF NOT EXISTS hardware_location_history ( id SERIAL PRIMARY KEY, hardware_id INT NOT NULL REFERENCES hardware_assets(id), location_id INT, -- Reference to locations table (if exists) location_name VARCHAR(200), -- Free text if no location_id start_date DATE NOT NULL, end_date DATE, -- NULL = current location notes TEXT, created_at TIMESTAMP DEFAULT NOW(), deleted_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_location_hardware ON hardware_location_history(hardware_id); CREATE INDEX IF NOT EXISTS idx_location_active ON hardware_location_history(hardware_id, end_date) WHERE end_date IS NULL AND deleted_at IS NULL; -- ============================================================================ -- Table 4: hardware_attachments -- ============================================================================ CREATE TABLE IF NOT EXISTS hardware_attachments ( id SERIAL PRIMARY KEY, hardware_id INT NOT NULL REFERENCES hardware_assets(id), file_type VARCHAR(50), -- 'image', 'receipt', 'contract', 'manual', 'other' file_name VARCHAR(255) NOT NULL, storage_ref TEXT NOT NULL, -- Path or cloud storage reference file_size_bytes BIGINT, mime_type VARCHAR(100), description TEXT, uploaded_by_user_id INT, uploaded_at TIMESTAMP DEFAULT NOW(), deleted_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_attachments_hardware ON hardware_attachments(hardware_id); -- ============================================================================ -- Table 5: hardware_case_relations -- ============================================================================ CREATE TABLE IF NOT EXISTS hardware_case_relations ( id SERIAL PRIMARY KEY, hardware_id INT NOT NULL REFERENCES hardware_assets(id), case_id INT NOT NULL, -- References sag_sager(id) relation_type VARCHAR(50) DEFAULT 'related', -- 'repair', 'installation', 'support', 'related' created_at TIMESTAMP DEFAULT NOW(), deleted_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_hardware_cases ON hardware_case_relations(hardware_id); CREATE INDEX IF NOT EXISTS idx_case_hardware ON hardware_case_relations(case_id); -- ============================================================================ -- Table 6: hardware_tags -- ============================================================================ CREATE TABLE IF NOT EXISTS hardware_tags ( id SERIAL PRIMARY KEY, hardware_id INT NOT NULL REFERENCES hardware_assets(id), tag_name VARCHAR(100) NOT NULL, tag_type VARCHAR(50) DEFAULT 'manual' CHECK (tag_type IN ('manual', 'system', 'ai_generated')), created_at TIMESTAMP DEFAULT NOW(), deleted_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_hardware_tags ON hardware_tags(hardware_id); CREATE INDEX IF NOT EXISTS idx_tag_name ON hardware_tags(tag_name) WHERE deleted_at IS NULL; -- ============================================================================ -- Success message -- ============================================================================ DO $$ BEGIN RAISE NOTICE '✅ Hardware module tables created successfully'; END $$;