-- 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([REDACTED], 'sin_codigo'::text), ' - ', COALESCE(order_raw_material_costs_v1.raw_product_name, 'sin_nombre'::text)), '; '::text ORDER BY (concat(COALESCE([REDACTED], '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;
