40-clients/ucepsa/edge-oee-demo/sql/reports/090_data_quality_contract.sql
2026-05-30 20:30:27 +02:00

1167 lines
58 KiB
SQL

-- MESAVAULT Edge-OEE UCEPSA
-- Data Quality Contract Odoo-MESAVAULT
-- Schema + seed rules.
-- No secrets.
DROP VIEW IF EXISTS mv_reports_ucepsa_demo.data_quality_contract_owner_summary_v1;
DROP VIEW IF EXISTS mv_reports_ucepsa_demo.data_quality_contract_order_summary_v1;
DROP VIEW IF EXISTS mv_reports_ucepsa_demo.data_quality_contract_order_checks_v1;
DROP TABLE IF EXISTS mv_edge_oee_ucepsa_demo.data_quality_contract_rules;
--
-- PostgreSQL database dump
--
\restrict Jszr4ruwRiCXF4itba8oT9UnTRsL1bJGSsYnj9UgpfOpaiQUSblgKdozAITLZdr
-- Dumped from database version 16.14
-- Dumped by pg_dump version 16.14
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: data_quality_contract_rules; Type: TABLE; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
CREATE TABLE mv_edge_oee_ucepsa_demo.data_quality_contract_rules (
id bigint NOT NULL,
tenant text DEFAULT 'ucepsa'::text NOT NULL,
site text DEFAULT 'ucepsa_test'::text NOT NULL,
rule_code text NOT NULL,
rule_name text NOT NULL,
domain text NOT NULL,
dimension text NOT NULL,
severity text NOT NULL,
owner_area text NOT NULL,
applies_to text NOT NULL,
description text NOT NULL,
expected_condition text NOT NULL,
blocking_for text,
is_active boolean DEFAULT true NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL
);
--
-- Name: data_quality_contract_rules_id_seq; Type: SEQUENCE; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
CREATE SEQUENCE mv_edge_oee_ucepsa_demo.data_quality_contract_rules_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: data_quality_contract_rules_id_seq; Type: SEQUENCE OWNED BY; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
ALTER SEQUENCE mv_edge_oee_ucepsa_demo.data_quality_contract_rules_id_seq OWNED BY mv_edge_oee_ucepsa_demo.data_quality_contract_rules.id;
--
-- Name: data_quality_contract_order_checks_v1; Type: VIEW; Schema: mv_reports_ucepsa_demo; Owner: -
--
CREATE VIEW mv_reports_ucepsa_demo.data_quality_contract_order_checks_v1 AS
WITH raw_stats AS (
SELECT order_raw_material_costs_v1.odoo_instance,
order_raw_material_costs_v1.odoo_production_id,
count(*) AS raw_line_count,
count(*) FILTER (WHERE ((order_raw_material_costs_v1.raw_product_default_code IS NULL) OR (TRIM(BOTH FROM order_raw_material_costs_v1.raw_product_default_code) = ''::text))) AS raw_missing_code_count,
string_agg(DISTINCT concat(COALESCE(order_raw_material_costs_v1.raw_product_default_code, 'sin_codigo'::text), ' - ', COALESCE(order_raw_material_costs_v1.raw_product_name, 'sin_nombre'::text)), '; '::text ORDER BY (concat(COALESCE(order_raw_material_costs_v1.raw_product_default_code, 'sin_codigo'::text), ' - ', COALESCE(order_raw_material_costs_v1.raw_product_name, 'sin_nombre'::text)))) AS raw_materials_detected
FROM mv_reports_ucepsa_demo.order_raw_material_costs_v1
GROUP BY order_raw_material_costs_v1.odoo_instance, order_raw_material_costs_v1.odoo_production_id
), base AS (
SELECT m.order_row_id,
m.tenant,
m.site,
m.odoo_instance,
m.odoo_workorder_id,
m.odoo_production_id,
m.odoo_order_ref,
m.odoo_state,
m.sale_order_ref,
m.customer_name,
m.revenue_net_eur,
m.machine_id,
m.asset,
m.product_default_code,
m.product_name,
m.raw_material_line_count,
m.raw_total_consumed_qty,
m.raw_materials_summary,
m.raw_material_cost_status_summary,
m.missing_or_invalid_raw_material_cost_count,
m.material_cost_estimated_eur,
m.material_cost_source_status,
m.energy_cost_eur_per_kwh,
m.machine_run_cost_eur_per_h,
m.kwh_consumed,
m.run_min,
m.stop_cost_eur,
m.technical_quality_level,
m.final_report_status,
m.margin_status,
r_1.pending_stops,
r_1.production_conversion_status,
COALESCE(rs.raw_line_count, (0)::bigint) AS dqc_raw_line_count,
COALESCE(rs.raw_missing_code_count, (0)::bigint) AS dqc_raw_missing_code_count,
rs.raw_materials_detected
FROM ((mv_reports_ucepsa_demo.order_margin_report_v1 m
LEFT JOIN mv_reports_ucepsa_demo.order_execution_report_full_v1 r_1 ON ((r_1.order_row_id = m.order_row_id)))
LEFT JOIN raw_stats rs ON (((rs.odoo_instance = m.odoo_instance) AND (rs.odoo_production_id = m.odoo_production_id))))
), evaluations AS (
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-ODOO-001'::text AS rule_code,
CASE
WHEN ((b.odoo_order_ref IS NOT NULL) AND (b.odoo_production_id IS NOT NULL)) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN ((b.odoo_order_ref IS NOT NULL) AND (b.odoo_production_id IS NOT NULL)) THEN 'Orden de fabricación correctamente identificada.'::text
ELSE 'Falta referencia de orden o identificador de producción Odoo.'::text
END AS finding,
CASE
WHEN ((b.odoo_order_ref IS NOT NULL) AND (b.odoo_production_id IS NOT NULL)) THEN 'Sin acción.'::text
ELSE 'Revisar sincronización de mrp.production / mrp.workorder desde Odoo.'::text
END AS recommended_action,
jsonb_build_object('odoo_order_ref', b.odoo_order_ref, 'odoo_production_id', b.odoo_production_id) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-ODOO-002'::text AS rule_code,
CASE
WHEN ((b.machine_id IS NOT NULL) AND (TRIM(BOTH FROM b.machine_id) <> ''::text)) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN ((b.machine_id IS NOT NULL) AND (TRIM(BOTH FROM b.machine_id) <> ''::text)) THEN 'Centro de trabajo mapeado a máquina MESAVAULT.'::text
ELSE 'Falta machine_id o mapeo de centro de trabajo.'::text
END AS finding,
CASE
WHEN ((b.machine_id IS NOT NULL) AND (TRIM(BOTH FROM b.machine_id) <> ''::text)) THEN 'Sin acción.'::text
ELSE 'Revisar tabla de mapeo Odoo workcenter → machine_id.'::text
END AS recommended_action,
jsonb_build_object('machine_id', b.machine_id, 'asset', b.asset) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-ODOO-003'::text AS rule_code,
CASE
WHEN (COALESCE(NULLIF(TRIM(BOTH FROM b.product_name), ''::text), NULLIF(TRIM(BOTH FROM b.product_default_code), ''::text)) IS NULL) THEN 'FAIL'::text
WHEN ((b.product_default_code IS NULL) OR (TRIM(BOTH FROM b.product_default_code) = ''::text)) THEN 'WARN'::text
ELSE 'PASS'::text
END AS check_status,
CASE
WHEN (COALESCE(NULLIF(TRIM(BOTH FROM b.product_name), ''::text), NULLIF(TRIM(BOTH FROM b.product_default_code), ''::text)) IS NULL) THEN 'Falta identificación del producto.'::text
WHEN ((b.product_default_code IS NULL) OR (TRIM(BOTH FROM b.product_default_code) = ''::text)) THEN 'Producto identificado por nombre, pero sin código interno.'::text
ELSE 'Producto identificado con nombre y código interno.'::text
END AS finding,
CASE
WHEN (COALESCE(NULLIF(TRIM(BOTH FROM b.product_name), ''::text), NULLIF(TRIM(BOTH FROM b.product_default_code), ''::text)) IS NULL) THEN 'Revisar maestro de producto en Odoo.'::text
WHEN ((b.product_default_code IS NULL) OR (TRIM(BOTH FROM b.product_default_code) = ''::text)) THEN 'Completar código interno/default_code del producto en Odoo.'::text
ELSE 'Sin acción.'::text
END AS recommended_action,
jsonb_build_object('product_default_code', b.product_default_code, 'product_name', b.product_name) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-SALES-001'::text AS rule_code,
CASE
WHEN ((b.sale_order_ref IS NOT NULL) AND (b.revenue_net_eur IS NOT NULL)) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN ((b.sale_order_ref IS NOT NULL) AND (b.revenue_net_eur IS NOT NULL)) THEN 'Ingreso de venta vinculado a la orden.'::text
ELSE 'La orden no tiene ingreso de venta vinculado.'::text
END AS finding,
CASE
WHEN ((b.sale_order_ref IS NOT NULL) AND (b.revenue_net_eur IS NOT NULL)) THEN 'Sin acción.'::text
ELSE 'Revisar sale_line_id, pedido de venta u origen de la orden de fabricación.'::text
END AS recommended_action,
jsonb_build_object('sale_order_ref', b.sale_order_ref, 'revenue_net_eur', b.revenue_net_eur) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-SALES-002'::text AS rule_code,
CASE
WHEN (b.sale_order_ref IS NULL) THEN 'NOT_APPLICABLE'::text
WHEN ((b.customer_name IS NOT NULL) AND (TRIM(BOTH FROM b.customer_name) <> ''::text)) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (b.sale_order_ref IS NULL) THEN 'No aplica porque no hay pedido de venta vinculado.'::text
WHEN ((b.customer_name IS NOT NULL) AND (TRIM(BOTH FROM b.customer_name) <> ''::text)) THEN 'Cliente vinculado correctamente.'::text
ELSE 'Hay pedido de venta, pero falta cliente.'::text
END AS finding,
CASE
WHEN (b.sale_order_ref IS NULL) THEN 'Resolver primero el vínculo de venta.'::text
WHEN ((b.customer_name IS NOT NULL) AND (TRIM(BOTH FROM b.customer_name) <> ''::text)) THEN 'Sin acción.'::text
ELSE 'Revisar partner_id del pedido de venta en Odoo.'::text
END AS recommended_action,
jsonb_build_object('sale_order_ref', b.sale_order_ref, 'customer_name', b.customer_name) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-RAW-001'::text AS rule_code,
CASE
WHEN (COALESCE(b.raw_material_line_count, (0)::bigint) > 0) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (COALESCE(b.raw_material_line_count, (0)::bigint) > 0) THEN 'Materia prima consumida detectada.'::text
ELSE 'No se detecta materia prima consumida para la orden.'::text
END AS finding,
CASE
WHEN (COALESCE(b.raw_material_line_count, (0)::bigint) > 0) THEN 'Sin acción.'::text
ELSE 'Revisar movimientos de stock/MRP asociados a la orden.'::text
END AS recommended_action,
jsonb_build_object('raw_material_line_count', b.raw_material_line_count, 'raw_materials_summary', b.raw_materials_summary) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-RAW-002'::text AS rule_code,
CASE
WHEN (COALESCE(b.dqc_raw_line_count, (0)::bigint) = 0) THEN 'NOT_APPLICABLE'::text
WHEN (COALESCE(b.dqc_raw_missing_code_count, (0)::bigint) = 0) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (COALESCE(b.dqc_raw_line_count, (0)::bigint) = 0) THEN 'No aplica porque no hay materia prima detectada.'::text
WHEN (COALESCE(b.dqc_raw_missing_code_count, (0)::bigint) = 0) THEN 'Todas las materias primas detectadas tienen código identificable.'::text
ELSE 'Hay materias primas sin código identificable.'::text
END AS finding,
CASE
WHEN (COALESCE(b.dqc_raw_line_count, (0)::bigint) = 0) THEN 'Resolver primero la detección de materia prima.'::text
WHEN (COALESCE(b.dqc_raw_missing_code_count, (0)::bigint) = 0) THEN 'Sin acción.'::text
ELSE 'Completar código interno/default_code de materias primas en Odoo.'::text
END AS recommended_action,
jsonb_build_object('raw_line_count', b.dqc_raw_line_count, 'raw_missing_code_count', b.dqc_raw_missing_code_count, 'raw_materials_detected', b.raw_materials_detected) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-COST-001'::text AS rule_code,
CASE
WHEN (b.material_cost_estimated_eur IS NOT NULL) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (b.material_cost_estimated_eur IS NOT NULL) THEN 'Coste de materia prima disponible.'::text
ELSE 'Falta coste de materia prima.'::text
END AS finding,
CASE
WHEN (b.material_cost_estimated_eur IS NOT NULL) THEN 'Sin acción.'::text
ELSE COALESCE(concat('Cargar coste €/kg para materia prima: ', b.raw_materials_summary), 'Cargar coste de materia prima o revisar valoración en Odoo.'::text)
END AS recommended_action,
jsonb_build_object('material_cost_estimated_eur', b.material_cost_estimated_eur, 'material_cost_source_status', b.material_cost_source_status, 'raw_materials_summary', b.raw_materials_summary) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-COST-002'::text AS rule_code,
CASE
WHEN (b.kwh_consumed IS NULL) THEN 'NOT_APPLICABLE'::text
WHEN (b.energy_cost_eur_per_kwh IS NOT NULL) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (b.kwh_consumed IS NULL) THEN 'No aplica porque no hay consumo energético medido.'::text
WHEN (b.energy_cost_eur_per_kwh IS NOT NULL) THEN 'Coste energético parametrizado.'::text
ELSE 'Hay consumo energético, pero falta coste €/kWh.'::text
END AS finding,
CASE
WHEN (b.kwh_consumed IS NULL) THEN 'Sin acción.'::text
WHEN (b.energy_cost_eur_per_kwh IS NOT NULL) THEN 'Sin acción.'::text
ELSE 'Cargar coste €/kWh en margin_cost_parameters.'::text
END AS recommended_action,
jsonb_build_object('kwh_consumed', b.kwh_consumed, 'energy_cost_eur_per_kwh', b.energy_cost_eur_per_kwh) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-COST-003'::text AS rule_code,
CASE
WHEN (b.run_min IS NULL) THEN 'NOT_APPLICABLE'::text
WHEN (b.machine_run_cost_eur_per_h IS NOT NULL) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (b.run_min IS NULL) THEN 'No aplica porque no hay tiempo de máquina utilizable.'::text
WHEN (b.machine_run_cost_eur_per_h IS NOT NULL) THEN 'Coste hora máquina parametrizado.'::text
ELSE 'Hay tiempo de máquina, pero falta coste hora máquina.'::text
END AS finding,
CASE
WHEN (b.run_min IS NULL) THEN 'Sin acción.'::text
WHEN (b.machine_run_cost_eur_per_h IS NOT NULL) THEN 'Sin acción.'::text
ELSE 'Cargar coste hora máquina para la máquina correspondiente.'::text
END AS recommended_action,
jsonb_build_object('machine_id', b.machine_id, 'run_min', b.run_min, 'machine_run_cost_eur_per_h', b.machine_run_cost_eur_per_h) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-EDGE-001'::text AS rule_code,
CASE
WHEN (b.technical_quality_level = ANY (ARRAY['FIABLE'::text, 'ACEPTABLE_CON_OBSERVACIONES'::text])) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (b.technical_quality_level = ANY (ARRAY['FIABLE'::text, 'ACEPTABLE_CON_OBSERVACIONES'::text])) THEN 'Datos técnicos defendibles.'::text
ELSE 'Datos técnicos no defendibles para análisis.'::text
END AS finding,
CASE
WHEN (b.technical_quality_level = ANY (ARRAY['FIABLE'::text, 'ACEPTABLE_CON_OBSERVACIONES'::text])) THEN 'Sin acción.'::text
ELSE 'Revisar telemetría, cobertura Edge-OEE y calidad del informe técnico.'::text
END AS recommended_action,
jsonb_build_object('technical_quality_level', b.technical_quality_level) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-EDGE-002'::text AS rule_code,
CASE
WHEN (b.final_report_status = ANY (ARRAY['LISTO_INFORME_FINAL'::text, 'LISTO_TECNICO_REVISAR_CONVERSION'::text, 'LISTO_CON_OBSERVACIONES'::text])) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (b.final_report_status = ANY (ARRAY['LISTO_INFORME_FINAL'::text, 'LISTO_TECNICO_REVISAR_CONVERSION'::text, 'LISTO_CON_OBSERVACIONES'::text])) THEN 'Informe técnico cerrado o utilizable.'::text
ELSE 'Informe técnico parcial o bloqueado.'::text
END AS finding,
CASE
WHEN (b.final_report_status = ANY (ARRAY['LISTO_INFORME_FINAL'::text, 'LISTO_TECNICO_REVISAR_CONVERSION'::text, 'LISTO_CON_OBSERVACIONES'::text])) THEN 'Sin acción.'::text
ELSE 'Cerrar orden, revisar telemetría o completar clasificación antes de análisis económico final.'::text
END AS recommended_action,
jsonb_build_object('final_report_status', b.final_report_status) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-STOPS-001'::text AS rule_code,
CASE
WHEN (COALESCE(b.pending_stops, (0)::bigint) = 0) THEN 'PASS'::text
ELSE 'FAIL'::text
END AS check_status,
CASE
WHEN (COALESCE(b.pending_stops, (0)::bigint) = 0) THEN 'No hay paros pendientes de clasificación.'::text
ELSE 'Hay paros pendientes de clasificación.'::text
END AS finding,
CASE
WHEN (COALESCE(b.pending_stops, (0)::bigint) = 0) THEN 'Sin acción.'::text
ELSE 'Clasificar paros pendientes desde la consola de paros.'::text
END AS recommended_action,
jsonb_build_object('pending_stops', b.pending_stops) AS evidence_json
FROM base b
UNION ALL
SELECT b.order_row_id,
b.tenant,
b.site,
b.odoo_instance,
b.odoo_workorder_id,
b.odoo_production_id,
b.odoo_order_ref,
b.odoo_state,
b.sale_order_ref,
b.customer_name,
b.revenue_net_eur,
b.machine_id,
b.asset,
b.product_default_code,
b.product_name,
b.raw_material_line_count,
b.raw_total_consumed_qty,
b.raw_materials_summary,
b.raw_material_cost_status_summary,
b.missing_or_invalid_raw_material_cost_count,
b.material_cost_estimated_eur,
b.material_cost_source_status,
b.energy_cost_eur_per_kwh,
b.machine_run_cost_eur_per_h,
b.kwh_consumed,
b.run_min,
b.stop_cost_eur,
b.technical_quality_level,
b.final_report_status,
b.margin_status,
b.pending_stops,
b.production_conversion_status,
b.dqc_raw_line_count,
b.dqc_raw_missing_code_count,
b.raw_materials_detected,
'DQC-CONV-001'::text AS rule_code,
CASE
WHEN (b.production_conversion_status = 'CICLOS_COHERENTES_CON_OBJETIVO'::text) THEN 'PASS'::text
WHEN (b.production_conversion_status = 'CANTIDAD_ODOO_SIN_CICLOS_EDGE'::text) THEN 'FAIL'::text
ELSE 'WARN'::text
END AS check_status,
CASE
WHEN (b.production_conversion_status = 'CICLOS_COHERENTES_CON_OBJETIVO'::text) THEN 'Conversión producción-ciclos coherente.'::text
WHEN (b.production_conversion_status = 'CANTIDAD_ODOO_SIN_CICLOS_EDGE'::text) THEN 'Odoo muestra cantidad producida, pero Edge no detecta ciclos.'::text
ELSE 'Conversión producción-ciclos pendiente de revisión o no concluyente.'::text
END AS finding,
CASE
WHEN (b.production_conversion_status = 'CICLOS_COHERENTES_CON_OBJETIVO'::text) THEN 'Sin acción.'::text
ELSE 'Validar relación entre kg, bolsas, unidad comercial y ciclos/golpes.'::text
END AS recommended_action,
jsonb_build_object('production_conversion_status', b.production_conversion_status) AS evidence_json
FROM base b
)
SELECT e.order_row_id,
e.tenant,
e.site,
e.odoo_instance,
e.odoo_workorder_id,
e.odoo_production_id,
e.odoo_order_ref,
e.odoo_state,
e.sale_order_ref,
e.customer_name,
e.product_default_code,
e.product_name,
e.machine_id,
r.rule_code,
r.rule_name,
r.domain,
r.dimension,
r.severity,
r.owner_area,
r.applies_to,
r.blocking_for,
e.check_status,
CASE
WHEN (e.check_status = 'PASS'::text) THEN true
WHEN (e.check_status = 'NOT_APPLICABLE'::text) THEN NULL::boolean
ELSE false
END AS check_passed,
e.finding,
e.recommended_action,
e.evidence_json,
CASE
WHEN ((e.check_status = 'FAIL'::text) AND (r.severity = 'CRITICAL'::text)) THEN 100
WHEN ((e.check_status = 'FAIL'::text) AND (r.severity = 'HIGH'::text)) THEN 75
WHEN ((e.check_status = 'FAIL'::text) AND (r.severity = 'MEDIUM'::text)) THEN 50
WHEN ((e.check_status = 'WARN'::text) AND (r.severity = 'CRITICAL'::text)) THEN 60
WHEN ((e.check_status = 'WARN'::text) AND (r.severity = 'HIGH'::text)) THEN 45
WHEN ((e.check_status = 'WARN'::text) AND (r.severity = 'MEDIUM'::text)) THEN 30
ELSE 0
END AS issue_score
FROM (evaluations e
JOIN mv_edge_oee_ucepsa_demo.data_quality_contract_rules r ON (((r.tenant = e.tenant) AND (r.site = e.site) AND (r.rule_code = e.rule_code) AND (r.is_active = true))));
--
-- Name: data_quality_contract_order_summary_v1; Type: VIEW; Schema: mv_reports_ucepsa_demo; Owner: -
--
CREATE VIEW mv_reports_ucepsa_demo.data_quality_contract_order_summary_v1 AS
WITH checks AS (
SELECT data_quality_contract_order_checks_v1.order_row_id,
data_quality_contract_order_checks_v1.tenant,
data_quality_contract_order_checks_v1.site,
data_quality_contract_order_checks_v1.odoo_instance,
data_quality_contract_order_checks_v1.odoo_workorder_id,
data_quality_contract_order_checks_v1.odoo_production_id,
data_quality_contract_order_checks_v1.odoo_order_ref,
data_quality_contract_order_checks_v1.odoo_state,
data_quality_contract_order_checks_v1.sale_order_ref,
data_quality_contract_order_checks_v1.customer_name,
data_quality_contract_order_checks_v1.product_default_code,
data_quality_contract_order_checks_v1.product_name,
data_quality_contract_order_checks_v1.machine_id,
data_quality_contract_order_checks_v1.rule_code,
data_quality_contract_order_checks_v1.rule_name,
data_quality_contract_order_checks_v1.domain,
data_quality_contract_order_checks_v1.dimension,
data_quality_contract_order_checks_v1.severity,
data_quality_contract_order_checks_v1.owner_area,
data_quality_contract_order_checks_v1.applies_to,
data_quality_contract_order_checks_v1.blocking_for,
data_quality_contract_order_checks_v1.check_status,
data_quality_contract_order_checks_v1.check_passed,
data_quality_contract_order_checks_v1.finding,
data_quality_contract_order_checks_v1.recommended_action,
data_quality_contract_order_checks_v1.evidence_json,
data_quality_contract_order_checks_v1.issue_score
FROM mv_reports_ucepsa_demo.data_quality_contract_order_checks_v1
), agg AS (
SELECT checks.order_row_id,
checks.tenant,
checks.site,
checks.odoo_instance,
checks.odoo_workorder_id,
checks.odoo_production_id,
checks.odoo_order_ref,
checks.odoo_state,
checks.sale_order_ref,
checks.customer_name,
checks.product_default_code,
checks.product_name,
checks.machine_id,
count(*) AS total_rules,
count(*) FILTER (WHERE (checks.check_status = 'PASS'::text)) AS passed_rules,
count(*) FILTER (WHERE (checks.check_status = 'FAIL'::text)) AS failed_rules,
count(*) FILTER (WHERE (checks.check_status = 'WARN'::text)) AS warn_rules,
count(*) FILTER (WHERE (checks.check_status = 'NOT_APPLICABLE'::text)) AS not_applicable_rules,
count(*) FILTER (WHERE ((checks.check_status = 'FAIL'::text) AND (checks.severity = 'CRITICAL'::text))) AS critical_failures,
count(*) FILTER (WHERE ((checks.check_status = 'FAIL'::text) AND (checks.severity = 'HIGH'::text))) AS high_failures,
count(*) FILTER (WHERE ((checks.check_status = 'FAIL'::text) AND (checks.severity = 'MEDIUM'::text))) AS medium_failures,
count(*) FILTER (WHERE ((checks.check_status = 'WARN'::text) AND (checks.severity = 'CRITICAL'::text))) AS critical_warnings,
count(*) FILTER (WHERE ((checks.check_status = 'WARN'::text) AND (checks.severity = 'HIGH'::text))) AS high_warnings,
count(*) FILTER (WHERE ((checks.check_status = 'WARN'::text) AND (checks.severity = 'MEDIUM'::text))) AS medium_warnings,
COALESCE(sum(checks.issue_score), (0)::bigint) AS total_issue_score,
max(checks.issue_score) AS max_issue_score
FROM checks
GROUP BY checks.order_row_id, checks.tenant, checks.site, checks.odoo_instance, checks.odoo_workorder_id, checks.odoo_production_id, checks.odoo_order_ref, checks.odoo_state, checks.sale_order_ref, checks.customer_name, checks.product_default_code, checks.product_name, checks.machine_id
), main_issue AS (
SELECT DISTINCT ON (checks.order_row_id) checks.order_row_id,
checks.rule_code AS main_blocker_rule_code,
checks.rule_name AS main_blocker_rule_name,
checks.domain AS main_blocker_domain,
checks.severity AS main_blocker_severity,
checks.owner_area AS owner_area_to_act,
checks.blocking_for,
checks.check_status AS main_blocker_status,
checks.finding AS main_blocker_finding,
checks.recommended_action AS main_recommended_action,
checks.issue_score AS main_issue_score
FROM checks
WHERE (checks.check_status = ANY (ARRAY['FAIL'::text, 'WARN'::text]))
ORDER BY checks.order_row_id, checks.issue_score DESC,
CASE checks.severity
WHEN 'CRITICAL'::text THEN 1
WHEN 'HIGH'::text THEN 2
WHEN 'MEDIUM'::text THEN 3
ELSE 4
END, checks.rule_code
)
SELECT a.order_row_id,
a.tenant,
a.site,
a.odoo_instance,
a.odoo_workorder_id,
a.odoo_production_id,
a.odoo_order_ref,
a.odoo_state,
a.sale_order_ref,
a.customer_name,
a.product_default_code,
a.product_name,
a.machine_id,
a.total_rules,
a.passed_rules,
a.failed_rules,
a.warn_rules,
a.not_applicable_rules,
a.critical_failures,
a.high_failures,
a.medium_failures,
a.critical_warnings,
a.high_warnings,
a.medium_warnings,
a.total_issue_score,
a.max_issue_score,
GREATEST((0)::bigint, (100 - LEAST(a.total_issue_score, (100)::bigint))) AS contract_score,
CASE
WHEN (a.critical_failures > 0) THEN 'BLOQUEADO_CRITICO'::text
WHEN (a.high_failures > 0) THEN 'BLOQUEADO_ALTO'::text
WHEN (a.medium_failures > 0) THEN 'BLOQUEADO_MEDIO'::text
WHEN (a.warn_rules > 0) THEN 'VALIDO_CON_OBSERVACIONES'::text
WHEN ((a.failed_rules = 0) AND (a.warn_rules = 0)) THEN 'CONTRATO_CUMPLIDO'::text
ELSE 'REVISAR'::text
END AS contract_status,
mi.main_blocker_rule_code,
mi.main_blocker_rule_name,
mi.main_blocker_domain,
mi.main_blocker_severity,
mi.owner_area_to_act,
mi.blocking_for,
mi.main_blocker_status,
mi.main_blocker_finding,
mi.main_recommended_action,
mi.main_issue_score,
CASE
WHEN (a.critical_failures > 0) THEN concat('Contrato bloqueado por fallo crítico: ', COALESCE(mi.main_blocker_finding, 'sin detalle'::text))
WHEN (a.high_failures > 0) THEN concat('Contrato bloqueado por fallo alto: ', COALESCE(mi.main_blocker_finding, 'sin detalle'::text))
WHEN (a.medium_failures > 0) THEN concat('Contrato bloqueado por fallo medio: ', COALESCE(mi.main_blocker_finding, 'sin detalle'::text))
WHEN (a.warn_rules > 0) THEN concat('Contrato válido con observaciones: ', COALESCE(mi.main_blocker_finding, 'sin detalle'::text))
ELSE 'Contrato de datos cumplido para esta orden.'::text
END AS contract_summary
FROM (agg a
LEFT JOIN main_issue mi ON ((mi.order_row_id = a.order_row_id)));
--
-- Name: data_quality_contract_owner_summary_v1; Type: VIEW; Schema: mv_reports_ucepsa_demo; Owner: -
--
CREATE VIEW mv_reports_ucepsa_demo.data_quality_contract_owner_summary_v1 AS
SELECT tenant,
site,
owner_area,
count(*) FILTER (WHERE (check_status = 'FAIL'::text)) AS failed_checks,
count(*) FILTER (WHERE (check_status = 'WARN'::text)) AS warn_checks,
count(*) FILTER (WHERE ((check_status = 'FAIL'::text) AND (severity = 'CRITICAL'::text))) AS critical_failed_checks,
count(*) FILTER (WHERE ((check_status = 'FAIL'::text) AND (severity = 'HIGH'::text))) AS high_failed_checks,
count(DISTINCT odoo_order_ref) FILTER (WHERE (check_status = ANY (ARRAY['FAIL'::text, 'WARN'::text]))) AS affected_orders,
sum(issue_score) FILTER (WHERE (check_status = ANY (ARRAY['FAIL'::text, 'WARN'::text]))) AS total_issue_score,
string_agg(DISTINCT rule_code, ', '::text ORDER BY rule_code) FILTER (WHERE (check_status = ANY (ARRAY['FAIL'::text, 'WARN'::text]))) AS affected_rules,
string_agg(DISTINCT odoo_order_ref, ', '::text ORDER BY odoo_order_ref) FILTER (WHERE (check_status = ANY (ARRAY['FAIL'::text, 'WARN'::text]))) AS affected_order_refs
FROM mv_reports_ucepsa_demo.data_quality_contract_order_checks_v1
GROUP BY tenant, site, owner_area;
--
-- Name: data_quality_contract_rules id; Type: DEFAULT; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
ALTER TABLE ONLY mv_edge_oee_ucepsa_demo.data_quality_contract_rules ALTER COLUMN id SET DEFAULT nextval('mv_edge_oee_ucepsa_demo.data_quality_contract_rules_id_seq'::regclass);
--
-- Name: data_quality_contract_rules data_quality_contract_rules_pkey; Type: CONSTRAINT; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
ALTER TABLE ONLY mv_edge_oee_ucepsa_demo.data_quality_contract_rules
ADD CONSTRAINT data_quality_contract_rules_pkey PRIMARY KEY (id);
--
-- Name: data_quality_contract_rules ux_data_quality_contract_rules_code; Type: CONSTRAINT; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
ALTER TABLE ONLY mv_edge_oee_ucepsa_demo.data_quality_contract_rules
ADD CONSTRAINT ux_data_quality_contract_rules_code UNIQUE (tenant, site, rule_code);
--
-- Name: idx_data_quality_contract_rules_active; Type: INDEX; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
CREATE INDEX idx_data_quality_contract_rules_active ON mv_edge_oee_ucepsa_demo.data_quality_contract_rules USING btree (tenant, site, is_active, domain, severity);
--
-- PostgreSQL database dump complete
--
\unrestrict Jszr4ruwRiCXF4itba8oT9UnTRsL1bJGSsYnj9UgpfOpaiQUSblgKdozAITLZdr
-- Seed rules
--
-- PostgreSQL database dump
--
\restrict 0GULSAN6PrEX9eK1oI9gTcg4MdETx9P93tGMaONyYc1oC6UFbi5FCS1yjEVkVNx
-- Dumped from database version 16.14
-- Dumped by pg_dump version 16.14
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
--
-- Data for Name: data_quality_contract_rules; Type: TABLE DATA; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (1, 'ucepsa', 'ucepsa_test', 'DQC-ODOO-001', 'Orden de fabricación identificada', 'ODOO', 'COMPLETENESS', 'CRITICAL', 'Odoo / Producción', 'Manufacturing Order', 'La orden debe tener referencia de fabricación e identificador de producción Odoo.', 'odoo_order_ref y odoo_production_id deben existir.', 'order_report', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (2, 'ucepsa', 'ucepsa_test', 'DQC-ODOO-002', 'Centro de trabajo mapeado a máquina MESAVAULT', 'ODOO', 'TRACEABILITY', 'CRITICAL', 'Odoo / MESAVAULT', 'Workcenter / Machine', 'El centro de trabajo de Odoo debe estar asociado a un machine_id MESAVAULT.', 'machine_id debe existir y estar mapeado.', 'edge_oee_report', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (3, 'ucepsa', 'ucepsa_test', 'DQC-ODOO-003', 'Producto identificado', 'ODOO', 'COMPLETENESS', 'HIGH', 'Odoo / Maestro de producto', 'Product', 'El producto debe estar identificado con nombre y, cuando aplique, código interno.', 'product_name o product deben existir; product_default_code recomendable.', 'product_margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (4, 'ucepsa', 'ucepsa_test', 'DQC-SALES-001', 'Ingreso de venta vinculado', 'SALES', 'TRACEABILITY', 'CRITICAL', 'Ventas / Odoo', 'Sale Order', 'La orden debe estar vinculada a una línea de venta para poder calcular margen.', 'sale_order_ref y revenue_net_eur deben existir.', 'margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (5, 'ucepsa', 'ucepsa_test', 'DQC-SALES-002', 'Cliente vinculado', 'SALES', 'COMPLETENESS', 'HIGH', 'Ventas / Odoo', 'Customer', 'La orden debe tener cliente asociado cuando exista pedido de venta.', 'customer_name debe existir si hay sale_order_ref.', 'customer_margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (6, 'ucepsa', 'ucepsa_test', 'DQC-RAW-001', 'Materia prima consumida detectada', 'MATERIAL', 'TRACEABILITY', 'CRITICAL', 'Producción / Almacén / Odoo', 'Raw Material', 'La orden debe tener materia prima consumida o movimiento de material asociado.', 'raw_material_line_count debe ser mayor que cero.', 'material_cost', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (7, 'ucepsa', 'ucepsa_test', 'DQC-RAW-002', 'Materia prima con código identificable', 'MATERIAL', 'COMPLETENESS', 'HIGH', 'Maestro de producto / Odoo', 'Raw Material', 'La materia prima debe tener código interno para poder asignar costes y analizar histórico.', 'raw_product_default_code debe existir.', 'material_cost', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (8, 'ucepsa', 'ucepsa_test', 'DQC-COST-001', 'Coste de materia prima disponible', 'COST', 'COMPLETENESS', 'CRITICAL', 'Compras / Administración / Odoo', 'Raw Material Cost', 'La materia prima debe tener coste €/kg o valoración Odoo para calcular margen.', 'material_cost_estimated_eur debe existir o la materia prima debe tener coste parametrizado.', 'margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (9, 'ucepsa', 'ucepsa_test', 'DQC-COST-002', 'Coste energético parametrizado', 'COST', 'COMPLETENESS', 'MEDIUM', 'Administración / MESAVAULT', 'Energy Cost', 'Si se usa energía en el margen, debe existir coste €/kWh.', 'energy_cost_eur_per_kwh debe existir cuando kwh_consumed exista.', 'full_margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (10, 'ucepsa', 'ucepsa_test', 'DQC-COST-003', 'Coste hora máquina parametrizado', 'COST', 'COMPLETENESS', 'MEDIUM', 'Producción / Administración / MESAVAULT', 'Machine Cost', 'Si se imputa coste de máquina, debe existir coste horario para la máquina.', 'machine_run_cost_eur_per_h debe existir cuando run_min exista.', 'full_margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (11, 'ucepsa', 'ucepsa_test', 'DQC-EDGE-001', 'Datos técnicos defendibles', 'EDGE', 'QUALITY', 'CRITICAL', 'MESAVAULT / Producción', 'Telemetry', 'La orden debe tener datos técnicos suficientes para un informe defendible.', 'technical_quality_level debe ser FIABLE o ACEPTABLE_CON_OBSERVACIONES.', 'technical_report', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (12, 'ucepsa', 'ucepsa_test', 'DQC-EDGE-002', 'Informe técnico cerrado o utilizable', 'EDGE', 'TIMELINESS', 'HIGH', 'MESAVAULT / Producción', 'Order Execution Report', 'La orden debe tener informe técnico cerrado o utilizable antes de análisis económico final.', 'final_report_status debe estar en estado listo o con observaciones.', 'margin', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (13, 'ucepsa', 'ucepsa_test', 'DQC-STOPS-001', 'Paros clasificados', 'LOSSES', 'COMPLETENESS', 'HIGH', 'Producción', 'Stops', 'Los paros imputados a una orden deben estar clasificados para explicar pérdidas.', 'pending_stops debe ser cero.', 'loss_analysis', true, '2026-05-30 20:18:18.616748+02');
INSERT INTO mv_edge_oee_ucepsa_demo.data_quality_contract_rules (id, tenant, site, rule_code, rule_name, domain, dimension, severity, owner_area, applies_to, description, expected_condition, blocking_for, is_active, created_at) VALUES (14, 'ucepsa', 'ucepsa_test', 'DQC-CONV-001', 'Conversión producción-ciclos revisada', 'PRODUCTION', 'CONSISTENCY', 'MEDIUM', 'Odoo / Producción / MESAVAULT', 'Production Conversion', 'La relación entre cantidad Odoo, kg, bolsas y ciclos debe estar validada antes de usar métricas unitarias.', 'production_conversion_status debe ser coherente o marcado como pendiente.', 'unit_margin', true, '2026-05-30 20:18:18.616748+02');
--
-- Name: data_quality_contract_rules_id_seq; Type: SEQUENCE SET; Schema: mv_edge_oee_ucepsa_demo; Owner: -
--
SELECT pg_catalog.setval('mv_edge_oee_ucepsa_demo.data_quality_contract_rules_id_seq', 14, true);
--
-- PostgreSQL database dump complete
--
\unrestrict 0GULSAN6PrEX9eK1oI9gTcg4MdETx9P93tGMaONyYc1oC6UFbi5FCS1yjEVkVNx
-- Read-only grants
GRANT USAGE ON SCHEMA mv_edge_oee_ucepsa_demo TO grafana_ucepsa_ro;
GRANT USAGE ON SCHEMA mv_reports_ucepsa_demo TO grafana_ucepsa_ro;
GRANT SELECT ON mv_edge_oee_ucepsa_demo.data_quality_contract_rules TO grafana_ucepsa_ro;
GRANT SELECT ON mv_reports_ucepsa_demo.data_quality_contract_order_checks_v1 TO grafana_ucepsa_ro;
GRANT SELECT ON mv_reports_ucepsa_demo.data_quality_contract_order_summary_v1 TO grafana_ucepsa_ro;
GRANT SELECT ON mv_reports_ucepsa_demo.data_quality_contract_owner_summary_v1 TO grafana_ucepsa_ro;