feat(MMD-011): Add dispatch center schema
- Create dispatch schema with 7 tables - dispatch_boards, unit_statuses, technician_skills, technician_shifts - dispatch_rules, escalation_rules, dispatch_logs - 5 enums for status, shift types, skill levels, channels, actions - 4 views for available units, shifts, expiring skills, activity - RLS policies for multi-tenant isolation - Triggers for updated_at and status change logging Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
daabe5320a
commit
a746fdcd31
503
init/16-dispatch-schema.sql
Normal file
503
init/16-dispatch-schema.sql
Normal file
@ -0,0 +1,503 @@
|
||||
-- ============================================================
|
||||
-- 16-dispatch-schema.sql
|
||||
-- Mecanicas Diesel - ERP Suite
|
||||
--
|
||||
-- Dispatch Center Schema (MMD-011)
|
||||
-- Centro de despacho para asignacion de incidentes
|
||||
-- ============================================================
|
||||
|
||||
-- Create dispatch schema
|
||||
CREATE SCHEMA IF NOT EXISTS dispatch;
|
||||
|
||||
-- ============================================================
|
||||
-- ENUMS
|
||||
-- ============================================================
|
||||
|
||||
-- Unit operational status
|
||||
CREATE TYPE dispatch.unit_status_enum AS ENUM (
|
||||
'available',
|
||||
'assigned',
|
||||
'en_route',
|
||||
'on_site',
|
||||
'returning',
|
||||
'offline',
|
||||
'maintenance'
|
||||
);
|
||||
|
||||
-- Technician shift types
|
||||
CREATE TYPE dispatch.shift_type AS ENUM (
|
||||
'morning',
|
||||
'afternoon',
|
||||
'night',
|
||||
'full_day',
|
||||
'on_call'
|
||||
);
|
||||
|
||||
-- Skill levels
|
||||
CREATE TYPE dispatch.skill_level AS ENUM (
|
||||
'basic',
|
||||
'intermediate',
|
||||
'advanced',
|
||||
'expert'
|
||||
);
|
||||
|
||||
-- Escalation notification channels
|
||||
CREATE TYPE dispatch.notification_channel AS ENUM (
|
||||
'email',
|
||||
'sms',
|
||||
'whatsapp',
|
||||
'push',
|
||||
'call'
|
||||
);
|
||||
|
||||
-- Dispatch log actions
|
||||
CREATE TYPE dispatch.dispatch_action AS ENUM (
|
||||
'created',
|
||||
'assigned',
|
||||
'reassigned',
|
||||
'rejected',
|
||||
'escalated',
|
||||
'cancelled',
|
||||
'acknowledged',
|
||||
'completed'
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- TABLES
|
||||
-- ============================================================
|
||||
|
||||
-- Dispatch Board Configuration
|
||||
CREATE TABLE dispatch.dispatch_boards (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Map defaults
|
||||
default_zoom INTEGER DEFAULT 12,
|
||||
center_lat DECIMAL(10, 7) DEFAULT 19.4326,
|
||||
center_lng DECIMAL(10, 7) DEFAULT -99.1332,
|
||||
|
||||
-- Behavior
|
||||
refresh_interval_seconds INTEGER DEFAULT 30,
|
||||
show_offline_units BOOLEAN DEFAULT true,
|
||||
auto_assign_enabled BOOLEAN DEFAULT false,
|
||||
max_suggestions INTEGER DEFAULT 5,
|
||||
|
||||
-- Filters
|
||||
default_filters JSONB DEFAULT '{}',
|
||||
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID
|
||||
);
|
||||
|
||||
CREATE INDEX idx_dispatch_boards_tenant ON dispatch.dispatch_boards(tenant_id);
|
||||
|
||||
-- Unit Status (real-time unit state)
|
||||
CREATE TABLE dispatch.unit_statuses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Unit reference
|
||||
unit_id UUID NOT NULL,
|
||||
unit_code VARCHAR(50),
|
||||
unit_name VARCHAR(100),
|
||||
|
||||
-- Status
|
||||
status dispatch.unit_status_enum DEFAULT 'offline',
|
||||
|
||||
-- Current assignment
|
||||
current_incident_id UUID,
|
||||
current_technician_ids UUID[] DEFAULT '{}',
|
||||
|
||||
-- Location (cached from GPS)
|
||||
last_position_id UUID,
|
||||
last_known_lat DECIMAL(10, 7),
|
||||
last_known_lng DECIMAL(10, 7),
|
||||
last_location_update TIMESTAMPTZ,
|
||||
|
||||
-- Timing
|
||||
last_status_change TIMESTAMPTZ DEFAULT NOW(),
|
||||
estimated_available_at TIMESTAMPTZ,
|
||||
|
||||
-- Capacity and capabilities
|
||||
unit_capacity VARCHAR(20) DEFAULT 'light', -- light, medium, heavy
|
||||
can_tow BOOLEAN DEFAULT false,
|
||||
max_tow_weight_kg INTEGER,
|
||||
|
||||
notes TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_unit_statuses_tenant_unit ON dispatch.unit_statuses(tenant_id, unit_id);
|
||||
CREATE INDEX idx_unit_statuses_status ON dispatch.unit_statuses(tenant_id, status);
|
||||
CREATE INDEX idx_unit_statuses_current_incident ON dispatch.unit_statuses(current_incident_id) WHERE current_incident_id IS NOT NULL;
|
||||
|
||||
-- Technician Skills
|
||||
CREATE TABLE dispatch.technician_skills (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Technician reference
|
||||
technician_id UUID NOT NULL,
|
||||
|
||||
-- Skill definition
|
||||
skill_code VARCHAR(50) NOT NULL,
|
||||
skill_name VARCHAR(100) NOT NULL,
|
||||
skill_description TEXT,
|
||||
level dispatch.skill_level DEFAULT 'basic',
|
||||
|
||||
-- Certification
|
||||
certification_number VARCHAR(100),
|
||||
certified_at DATE,
|
||||
expires_at DATE,
|
||||
certification_document_url VARCHAR(500),
|
||||
|
||||
-- Status
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
verified_by UUID,
|
||||
verified_at TIMESTAMPTZ,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT uq_technician_skill UNIQUE(tenant_id, technician_id, skill_code)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_technician_skills_tenant ON dispatch.technician_skills(tenant_id);
|
||||
CREATE INDEX idx_technician_skills_technician ON dispatch.technician_skills(tenant_id, technician_id);
|
||||
CREATE INDEX idx_technician_skills_code ON dispatch.technician_skills(tenant_id, skill_code);
|
||||
CREATE INDEX idx_technician_skills_expiry ON dispatch.technician_skills(expires_at) WHERE expires_at IS NOT NULL;
|
||||
|
||||
-- Technician Shifts
|
||||
CREATE TABLE dispatch.technician_shifts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Technician reference
|
||||
technician_id UUID NOT NULL,
|
||||
|
||||
-- Schedule
|
||||
shift_date DATE NOT NULL,
|
||||
shift_type dispatch.shift_type NOT NULL,
|
||||
start_time TIME NOT NULL,
|
||||
end_time TIME NOT NULL,
|
||||
|
||||
-- On-call specifics
|
||||
is_on_call BOOLEAN DEFAULT false,
|
||||
on_call_priority INTEGER DEFAULT 0,
|
||||
|
||||
-- Assignment
|
||||
assigned_unit_id UUID,
|
||||
|
||||
-- Status
|
||||
actual_start_time TIMESTAMPTZ,
|
||||
actual_end_time TIMESTAMPTZ,
|
||||
is_absent BOOLEAN DEFAULT false,
|
||||
absence_reason TEXT,
|
||||
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID
|
||||
);
|
||||
|
||||
CREATE INDEX idx_technician_shifts_tenant ON dispatch.technician_shifts(tenant_id);
|
||||
CREATE INDEX idx_technician_shifts_technician_date ON dispatch.technician_shifts(tenant_id, technician_id, shift_date);
|
||||
CREATE INDEX idx_technician_shifts_date ON dispatch.technician_shifts(tenant_id, shift_date);
|
||||
CREATE INDEX idx_technician_shifts_unit ON dispatch.technician_shifts(assigned_unit_id) WHERE assigned_unit_id IS NOT NULL;
|
||||
|
||||
-- Dispatch Rules (assignment criteria)
|
||||
CREATE TABLE dispatch.dispatch_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
priority INTEGER DEFAULT 0,
|
||||
|
||||
-- Applicability
|
||||
service_type_code VARCHAR(50),
|
||||
incident_category VARCHAR(50),
|
||||
|
||||
-- Conditions (JSON structure for flexibility)
|
||||
conditions JSONB NOT NULL DEFAULT '{}',
|
||||
/*
|
||||
conditions structure:
|
||||
{
|
||||
"requires_skills": ["DIESEL_ENGINE", "ELECTRICAL"],
|
||||
"requires_asset_categories": ["DIAGNOSTIC_TOOL"],
|
||||
"min_unit_capacity": "heavy",
|
||||
"max_distance_km": 50,
|
||||
"zone_codes": ["NORTE", "CENTRO"],
|
||||
"min_skill_level": "intermediate",
|
||||
"exclude_on_call": false
|
||||
}
|
||||
*/
|
||||
|
||||
-- Action
|
||||
auto_assign BOOLEAN DEFAULT false,
|
||||
assignment_weight INTEGER DEFAULT 100,
|
||||
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID
|
||||
);
|
||||
|
||||
CREATE INDEX idx_dispatch_rules_tenant ON dispatch.dispatch_rules(tenant_id);
|
||||
CREATE INDEX idx_dispatch_rules_service_type ON dispatch.dispatch_rules(tenant_id, service_type_code) WHERE service_type_code IS NOT NULL;
|
||||
CREATE INDEX idx_dispatch_rules_priority ON dispatch.dispatch_rules(tenant_id, priority DESC);
|
||||
|
||||
-- Escalation Rules
|
||||
CREATE TABLE dispatch.escalation_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Trigger conditions
|
||||
trigger_after_minutes INTEGER NOT NULL,
|
||||
trigger_status VARCHAR(50), -- incident status that triggers
|
||||
trigger_priority VARCHAR(20), -- only for specific priorities
|
||||
|
||||
-- Escalation target
|
||||
escalate_to_role VARCHAR(50) NOT NULL,
|
||||
escalate_to_user_ids UUID[],
|
||||
|
||||
-- Notification
|
||||
notification_channel dispatch.notification_channel NOT NULL,
|
||||
notification_template TEXT,
|
||||
notification_data JSONB DEFAULT '{}',
|
||||
|
||||
-- Repeat
|
||||
repeat_interval_minutes INTEGER,
|
||||
max_escalations INTEGER DEFAULT 3,
|
||||
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by UUID
|
||||
);
|
||||
|
||||
CREATE INDEX idx_escalation_rules_tenant ON dispatch.escalation_rules(tenant_id);
|
||||
CREATE INDEX idx_escalation_rules_trigger ON dispatch.escalation_rules(tenant_id, trigger_after_minutes);
|
||||
|
||||
-- Dispatch Log (audit trail)
|
||||
CREATE TABLE dispatch.dispatch_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
|
||||
-- Incident reference
|
||||
incident_id UUID NOT NULL,
|
||||
|
||||
-- Action
|
||||
action dispatch.dispatch_action NOT NULL,
|
||||
|
||||
-- Unit/Technician changes
|
||||
from_unit_id UUID,
|
||||
to_unit_id UUID,
|
||||
from_technician_id UUID,
|
||||
to_technician_id UUID,
|
||||
|
||||
-- Context
|
||||
reason TEXT,
|
||||
automated BOOLEAN DEFAULT false,
|
||||
rule_id UUID, -- dispatch rule that caused action
|
||||
escalation_id UUID, -- escalation rule that caused action
|
||||
|
||||
-- Response times
|
||||
response_time_seconds INTEGER,
|
||||
|
||||
-- Actor
|
||||
performed_by UUID,
|
||||
performed_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
-- Extra data
|
||||
metadata JSONB DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE INDEX idx_dispatch_logs_tenant ON dispatch.dispatch_logs(tenant_id);
|
||||
CREATE INDEX idx_dispatch_logs_incident ON dispatch.dispatch_logs(tenant_id, incident_id);
|
||||
CREATE INDEX idx_dispatch_logs_time ON dispatch.dispatch_logs(tenant_id, performed_at DESC);
|
||||
CREATE INDEX idx_dispatch_logs_action ON dispatch.dispatch_logs(tenant_id, action);
|
||||
|
||||
-- ============================================================
|
||||
-- TRIGGERS
|
||||
-- ============================================================
|
||||
|
||||
-- Auto-update updated_at
|
||||
CREATE OR REPLACE FUNCTION dispatch.update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_dispatch_boards_updated_at
|
||||
BEFORE UPDATE ON dispatch.dispatch_boards
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_unit_statuses_updated_at
|
||||
BEFORE UPDATE ON dispatch.unit_statuses
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_technician_skills_updated_at
|
||||
BEFORE UPDATE ON dispatch.technician_skills
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_technician_shifts_updated_at
|
||||
BEFORE UPDATE ON dispatch.technician_shifts
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_dispatch_rules_updated_at
|
||||
BEFORE UPDATE ON dispatch.dispatch_rules
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();
|
||||
|
||||
CREATE TRIGGER trg_escalation_rules_updated_at
|
||||
BEFORE UPDATE ON dispatch.escalation_rules
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.update_updated_at();
|
||||
|
||||
-- Log unit status changes
|
||||
CREATE OR REPLACE FUNCTION dispatch.log_unit_status_change()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF OLD.status IS DISTINCT FROM NEW.status THEN
|
||||
NEW.last_status_change = NOW();
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_unit_status_change
|
||||
BEFORE UPDATE ON dispatch.unit_statuses
|
||||
FOR EACH ROW EXECUTE FUNCTION dispatch.log_unit_status_change();
|
||||
|
||||
-- ============================================================
|
||||
-- VIEWS
|
||||
-- ============================================================
|
||||
|
||||
-- Available units with current position
|
||||
CREATE OR REPLACE VIEW dispatch.vw_available_units AS
|
||||
SELECT
|
||||
us.id,
|
||||
us.tenant_id,
|
||||
us.unit_id,
|
||||
us.unit_code,
|
||||
us.unit_name,
|
||||
us.status,
|
||||
us.last_known_lat,
|
||||
us.last_known_lng,
|
||||
us.last_location_update,
|
||||
us.unit_capacity,
|
||||
us.can_tow,
|
||||
us.max_tow_weight_kg,
|
||||
us.estimated_available_at,
|
||||
EXTRACT(EPOCH FROM (NOW() - us.last_location_update)) / 60 AS minutes_since_update
|
||||
FROM dispatch.unit_statuses us
|
||||
WHERE us.status = 'available';
|
||||
|
||||
-- Technicians on shift today
|
||||
CREATE OR REPLACE VIEW dispatch.vw_technicians_on_shift AS
|
||||
SELECT
|
||||
ts.id,
|
||||
ts.tenant_id,
|
||||
ts.technician_id,
|
||||
ts.shift_date,
|
||||
ts.shift_type,
|
||||
ts.start_time,
|
||||
ts.end_time,
|
||||
ts.is_on_call,
|
||||
ts.assigned_unit_id,
|
||||
ts.is_absent,
|
||||
CASE
|
||||
WHEN ts.is_absent THEN 'absent'
|
||||
WHEN ts.actual_start_time IS NOT NULL AND ts.actual_end_time IS NULL THEN 'active'
|
||||
WHEN ts.actual_start_time IS NULL THEN 'pending'
|
||||
ELSE 'completed'
|
||||
END AS shift_status
|
||||
FROM dispatch.technician_shifts ts
|
||||
WHERE ts.shift_date = CURRENT_DATE
|
||||
AND ts.is_absent = false;
|
||||
|
||||
-- Skills about to expire
|
||||
CREATE OR REPLACE VIEW dispatch.vw_expiring_skills AS
|
||||
SELECT
|
||||
tsk.id,
|
||||
tsk.tenant_id,
|
||||
tsk.technician_id,
|
||||
tsk.skill_code,
|
||||
tsk.skill_name,
|
||||
tsk.level,
|
||||
tsk.expires_at,
|
||||
tsk.expires_at - CURRENT_DATE AS days_until_expiry
|
||||
FROM dispatch.technician_skills tsk
|
||||
WHERE tsk.is_active = true
|
||||
AND tsk.expires_at IS NOT NULL
|
||||
AND tsk.expires_at <= CURRENT_DATE + INTERVAL '30 days';
|
||||
|
||||
-- Dispatch activity summary
|
||||
CREATE OR REPLACE VIEW dispatch.vw_dispatch_activity AS
|
||||
SELECT
|
||||
dl.tenant_id,
|
||||
DATE(dl.performed_at) AS activity_date,
|
||||
dl.action,
|
||||
COUNT(*) AS count,
|
||||
AVG(dl.response_time_seconds) AS avg_response_time_seconds,
|
||||
COUNT(CASE WHEN dl.automated THEN 1 END) AS automated_count
|
||||
FROM dispatch.dispatch_logs dl
|
||||
WHERE dl.performed_at >= CURRENT_DATE - INTERVAL '30 days'
|
||||
GROUP BY dl.tenant_id, DATE(dl.performed_at), dl.action;
|
||||
|
||||
-- ============================================================
|
||||
-- RLS POLICIES
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE dispatch.dispatch_boards ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dispatch.unit_statuses ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dispatch.technician_skills ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dispatch.technician_shifts ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dispatch.dispatch_rules ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dispatch.escalation_rules ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dispatch.dispatch_logs ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- RLS Policies for each table
|
||||
CREATE POLICY tenant_isolation_dispatch_boards ON dispatch.dispatch_boards
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_unit_statuses ON dispatch.unit_statuses
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_technician_skills ON dispatch.technician_skills
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_technician_shifts ON dispatch.technician_shifts
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_dispatch_rules ON dispatch.dispatch_rules
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_escalation_rules ON dispatch.escalation_rules
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
CREATE POLICY tenant_isolation_dispatch_logs ON dispatch.dispatch_logs
|
||||
FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::uuid);
|
||||
|
||||
-- ============================================================
|
||||
-- COMMENTS
|
||||
-- ============================================================
|
||||
|
||||
COMMENT ON SCHEMA dispatch IS 'Dispatch Center schema for incident assignment and unit management (MMD-011)';
|
||||
COMMENT ON TABLE dispatch.dispatch_boards IS 'Dispatch board configurations for map and assignment views';
|
||||
COMMENT ON TABLE dispatch.unit_statuses IS 'Real-time status of fleet units for dispatch';
|
||||
COMMENT ON TABLE dispatch.technician_skills IS 'Skills and certifications of technicians';
|
||||
COMMENT ON TABLE dispatch.technician_shifts IS 'Shift schedules for technicians';
|
||||
COMMENT ON TABLE dispatch.dispatch_rules IS 'Rules for automatic assignment suggestions';
|
||||
COMMENT ON TABLE dispatch.escalation_rules IS 'Rules for escalating unresponded incidents';
|
||||
COMMENT ON TABLE dispatch.dispatch_logs IS 'Audit log of all dispatch actions';
|
||||
Loading…
Reference in New Issue
Block a user