40-clients/ucepsa/edge-oee-demo/sql/004_edge_oee_stop_model.sql

157 lines
5.5 KiB
SQL

CREATE SCHEMA IF NOT EXISTS mv_hot;
CREATE TABLE IF NOT EXISTS mv_hot.edge_oee_stop_causes (
cause_code TEXT PRIMARY KEY,
cause_name TEXT NOT NULL,
description TEXT,
is_planned BOOLEAN NOT NULL DEFAULT false,
is_active BOOLEAN NOT NULL DEFAULT true,
sort_order INT NOT NULL DEFAULT 100
);
INSERT INTO mv_hot.edge_oee_stop_causes
(cause_code, cause_name, description, is_planned, sort_order)
VALUES
('cambio_bobina', 'Cambio de bobina', 'Paro por cambio de bobina o material.', false, 10),
('fallo_empalme', 'Fallo en empalme', 'Paro por fallo o ajuste en empalme de material.', false, 20),
('ajuste_sellado', 'Ajuste de sellado', 'Paro por ajuste de mordazas, temperatura, presión o teflón.', false, 30),
('falta_material', 'Falta de material', 'Paro por ausencia de bobina, material no preparado o espera logística.', false, 40),
('limpieza_fotocelula', 'Limpieza fotocélula', 'Paro por limpieza o ajuste de sensor/fotocélula.', false, 50),
('cambio_formato', 'Cambio de formato', 'Paro por cambio de medidas, producto o configuración.', false, 60),
('averia_mecanica', 'Avería mecánica', 'Paro por fallo mecánico de la máquina.', false, 70),
('espera_instrucciones', 'Espera de instrucciones', 'Paro por falta de definición, autorización o instrucción.', false, 80),
('pausa_cafe', 'Pausa café', 'Pausa del operario o descanso.', true, 90),
('otro', 'Otro', 'Causa no clasificada en el catálogo principal.', false, 999)
ON CONFLICT (cause_code) DO UPDATE SET
cause_name = EXCLUDED.cause_name,
description = EXCLUDED.description,
is_planned = EXCLUDED.is_planned,
sort_order = EXCLUDED.sort_order;
CREATE TABLE IF NOT EXISTS mv_hot.edge_oee_machine_stops (
id BIGSERIAL PRIMARY KEY,
tenant TEXT NOT NULL,
site TEXT NOT NULL,
vertical TEXT NOT NULL,
asset TEXT NOT NULL,
machine_id TEXT,
order_id TEXT,
started_at TIMESTAMPTZ NOT NULL,
ended_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'OPEN',
classification_status TEXT NOT NULL DEFAULT 'PENDING',
cause_code TEXT REFERENCES mv_hot.edge_oee_stop_causes(cause_code),
operator_note TEXT,
value_hour_eur NUMERIC(10,2) NOT NULL DEFAULT 36.00,
duration_s NUMERIC(14,2),
cost_eur NUMERIC(14,2),
start_event_name TEXT,
end_event_name TEXT,
start_payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
end_payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_edge_oee_machine_stops_asset_status
ON mv_hot.edge_oee_machine_stops(asset, status);
CREATE INDEX IF NOT EXISTS idx_edge_oee_machine_stops_started_at
ON mv_hot.edge_oee_machine_stops(started_at DESC);
CREATE INDEX IF NOT EXISTS idx_edge_oee_machine_stops_machine_started
ON mv_hot.edge_oee_machine_stops(machine_id, started_at DESC);
CREATE OR REPLACE VIEW mv_hot.edge_oee_open_stops AS
SELECT
s.id,
s.tenant,
s.site,
s.vertical,
s.asset,
s.machine_id,
s.order_id,
s.started_at,
EXTRACT(EPOCH FROM (now() - s.started_at))::numeric(14,2) AS current_duration_s,
ROUND((EXTRACT(EPOCH FROM (now() - s.started_at)) / 60.0)::numeric, 2) AS current_duration_min,
ROUND(((EXTRACT(EPOCH FROM (now() - s.started_at)) / 3600.0) * s.value_hour_eur)::numeric, 2) AS current_cost_eur,
s.status,
s.classification_status,
s.cause_code,
c.cause_name,
s.operator_note,
s.value_hour_eur
FROM mv_hot.edge_oee_machine_stops s
LEFT JOIN mv_hot.edge_oee_stop_causes c ON c.cause_code = s.cause_code
WHERE s.status = 'OPEN'
ORDER BY s.started_at DESC;
CREATE OR REPLACE VIEW mv_hot.edge_oee_stops_recent AS
SELECT
s.id,
s.tenant,
s.site,
s.vertical,
s.asset,
s.machine_id,
s.order_id,
s.started_at,
s.ended_at,
COALESCE(
s.duration_s,
EXTRACT(EPOCH FROM (now() - s.started_at))::numeric(14,2)
) AS duration_s,
ROUND((
COALESCE(s.duration_s, EXTRACT(EPOCH FROM (now() - s.started_at))::numeric) / 60.0
)::numeric, 2) AS duration_min,
COALESCE(
s.cost_eur,
ROUND(((EXTRACT(EPOCH FROM (now() - s.started_at)) / 3600.0) * s.value_hour_eur)::numeric, 2)
) AS cost_eur,
s.status,
s.classification_status,
s.cause_code,
c.cause_name,
c.is_planned,
s.operator_note,
s.value_hour_eur,
s.start_event_name,
s.end_event_name,
s.created_at,
s.updated_at
FROM mv_hot.edge_oee_machine_stops s
LEFT JOIN mv_hot.edge_oee_stop_causes c ON c.cause_code = s.cause_code
ORDER BY s.started_at DESC;
CREATE OR REPLACE VIEW mv_hot.edge_oee_stop_summary_demo AS
SELECT
COALESCE(c.cause_name, 'Pendiente de clasificar') AS cause_name,
COALESCE(s.cause_code, 'pending') AS cause_code,
COUNT(*) AS stop_count,
ROUND(SUM(COALESCE(s.duration_s, EXTRACT(EPOCH FROM (now() - s.started_at))::numeric)) / 60.0, 2) AS total_min,
ROUND(SUM(COALESCE(s.cost_eur, ((EXTRACT(EPOCH FROM (now() - s.started_at)) / 3600.0) * s.value_hour_eur)))::numeric, 2) AS total_cost_eur
FROM mv_hot.edge_oee_machine_stops s
LEFT JOIN mv_hot.edge_oee_stop_causes c ON c.cause_code = s.cause_code
GROUP BY COALESCE(c.cause_name, 'Pendiente de clasificar'), COALESCE(s.cause_code, 'pending')
ORDER BY total_cost_eur DESC;
CREATE OR REPLACE VIEW mv_hot.edge_oee_stop_cause_options AS
SELECT
cause_code,
cause_name,
description,
is_planned,
sort_order
FROM mv_hot.edge_oee_stop_causes
WHERE is_active = true
ORDER BY sort_order, cause_name;