-- ========================================================= -- TABLERO CDC - TARIFARIO FIJO WALMART CONNECT -- Versión segura para producción. -- -- Qué hace: -- 1) Habilita la sección "wmc" en el catálogo dinámico existente. -- 2) Inserta/actualiza las 12 piezas y montos del Excel Tarifario_XCDC_WMC. -- 3) Mantiene intactos proyectos, costos históricos, links, listas, n8n y tarifas CDC existentes. -- 4) Permite administrar el catálogo a usuarios activos con can_manage_internal_pricing = true. -- 5) Protege las tarifas WMC de una futura desactivación masiva del sync del tarifario estándar. -- -- Puede ejecutarse más de una vez: usa CREATE IF NOT EXISTS y UPSERT. -- ========================================================= begin; -- 1) Asegurar la tabla del catálogo dinámico. create table if not exists public.tablero_cdc_tariff_catalog ( id text primary key, catalog_item_id text not null, section text not null, category text not null default '', service text not null default '', notes text not null default '', work_type_id text not null default 'reference', work_type_label text not null default 'Referencia', work_type_short_label text not null default 'Ref.', hour_reference text not null default '', level_id text not null default 'project', level_label text not null default 'Precio único', reference text not null default '', reference_min numeric, reference_max numeric, level_hint text not null default '', sort_order integer not null default 9999, is_active boolean not null default true, created_by uuid references auth.users(id), updated_by uuid references auth.users(id), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Compatibilidad con instalaciones donde alguna columna todavía no exista. alter table public.tablero_cdc_tariff_catalog add column if not exists catalog_item_id text, add column if not exists section text, add column if not exists category text not null default '', add column if not exists service text not null default '', add column if not exists notes text not null default '', add column if not exists work_type_id text not null default 'reference', add column if not exists work_type_label text not null default 'Referencia', add column if not exists work_type_short_label text not null default 'Ref.', add column if not exists hour_reference text not null default '', add column if not exists level_id text not null default 'project', add column if not exists level_label text not null default 'Precio único', add column if not exists reference text not null default '', add column if not exists reference_min numeric, add column if not exists reference_max numeric, add column if not exists level_hint text not null default '', add column if not exists sort_order integer not null default 9999, add column if not exists is_active boolean not null default true, add column if not exists created_by uuid references auth.users(id), add column if not exists updated_by uuid references auth.users(id), add column if not exists created_at timestamptz not null default now(), add column if not exists updated_at timestamptz not null default now(); -- Completar valores obligatorios si la tabla venía de una versión antigua. update public.tablero_cdc_tariff_catalog set catalog_item_id = coalesce(nullif(trim(catalog_item_id), ''), id), section = case when lower(trim(coalesce(section, ''))) in ('grafico', 'estrategia', 'wmc') then lower(trim(section)) else 'grafico' end where catalog_item_id is null or nullif(trim(catalog_item_id), '') is null or section is null or lower(trim(section)) not in ('grafico', 'estrategia', 'wmc'); alter table public.tablero_cdc_tariff_catalog alter column catalog_item_id set not null, alter column section set not null; -- Reemplazar el check antiguo que solo permitía grafico/estrategia. alter table public.tablero_cdc_tariff_catalog drop constraint if exists tablero_cdc_tariff_catalog_section_check; alter table public.tablero_cdc_tariff_catalog add constraint tablero_cdc_tariff_catalog_section_check check (section in ('grafico', 'estrategia', 'wmc')); create index if not exists tablero_cdc_tariff_catalog_section_idx on public.tablero_cdc_tariff_catalog(section, is_active, sort_order); create index if not exists tablero_cdc_tariff_catalog_item_idx on public.tablero_cdc_tariff_catalog(catalog_item_id); -- 2) RLS: lectura para usuarios autenticados y escritura para quienes pueden tarifar. alter table public.tablero_cdc_tariff_catalog enable row level security; create or replace function public.tablero_cdc_current_user_can_manage_internal_pricing() returns boolean language sql stable security definer set search_path = public as $$ select exists ( select 1 from public.tablero_cdc_allowed_users u where lower(trim(u.email)) = lower(trim(coalesce(auth.jwt() ->> 'email', ''))) and u.is_active = true and u.can_manage_internal_pricing = true ); $$; grant execute on function public.tablero_cdc_current_user_can_manage_internal_pricing() to authenticated; drop policy if exists "tablero_cdc_tariff_catalog_select_authenticated" on public.tablero_cdc_tariff_catalog; drop policy if exists "tablero_cdc_tariff_catalog_insert_pricing_admins" on public.tablero_cdc_tariff_catalog; drop policy if exists "tablero_cdc_tariff_catalog_update_pricing_admins" on public.tablero_cdc_tariff_catalog; drop policy if exists "tablero_cdc_tariff_catalog_delete_pricing_admins" on public.tablero_cdc_tariff_catalog; create policy "tablero_cdc_tariff_catalog_select_authenticated" on public.tablero_cdc_tariff_catalog for select to authenticated using (true); create policy "tablero_cdc_tariff_catalog_insert_pricing_admins" on public.tablero_cdc_tariff_catalog for insert to authenticated with check (public.tablero_cdc_current_user_can_manage_internal_pricing()); create policy "tablero_cdc_tariff_catalog_update_pricing_admins" on public.tablero_cdc_tariff_catalog for update to authenticated using (public.tablero_cdc_current_user_can_manage_internal_pricing()) with check (public.tablero_cdc_current_user_can_manage_internal_pricing()); -- No se recomienda borrar tarifas usadas históricamente; se deben desactivar. create policy "tablero_cdc_tariff_catalog_delete_pricing_admins" on public.tablero_cdc_tariff_catalog for delete to authenticated using (public.tablero_cdc_current_user_can_manage_internal_pricing()); grant select, insert, update, delete on public.tablero_cdc_tariff_catalog to authenticated; create or replace function public.set_tablero_cdc_tariff_catalog_updated_at() returns trigger language plpgsql as $$ begin new.updated_at = now(); new.updated_by = auth.uid(); if tg_op = 'INSERT' then new.created_by = coalesce(new.created_by, auth.uid()); end if; return new; end; $$; drop trigger if exists trg_tablero_cdc_tariff_catalog_updated_at on public.tablero_cdc_tariff_catalog; create trigger trg_tablero_cdc_tariff_catalog_updated_at before insert or update on public.tablero_cdc_tariff_catalog for each row execute function public.set_tablero_cdc_tariff_catalog_updated_at(); -- 3) Cargar las 12 piezas del Excel de Walmart Connect. insert into public.tablero_cdc_tariff_catalog ( id, catalog_item_id, section, category, service, notes, work_type_id, work_type_label, work_type_short_label, hour_reference, level_id, level_label, reference, reference_min, reference_max, level_hint, sort_order, is_active ) values ( 'wmc-uniformes__wmc-fixed__fixed', 'wmc-uniformes', 'wmc', 'Walmart Connect', 'Uniformes', 'Uniforme completo para personal de impulso en piso (playera/top, jogger, falda, chaleco), con 4 variantes de diseño exploradas. Equivalencia CDC: Materiales de PDV estándar y otros materiales básicos > Uniformes. Incluye diseño en alta resolución y 3 rondas de cambios.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '80 USD', 80, 80, 'Incluye diseño en alta resolución y 3 rondas de cambios.', 20001, true ), ( 'wmc-uniformes-supervisor__wmc-fixed__fixed', 'wmc-uniformes-supervisor', 'wmc', 'Walmart Connect', 'Uniformes – Supervisor', 'Uniforme para supervisor de piso (polo, chumpa/track jacket, camisa formal), con 3 variantes de diseño exploradas. Equivalencia CDC: Materiales de PDV estándar y otros materiales básicos > Uniformes. Incluye diseño en alta resolución y 3 rondas de cambios.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '100 USD', 100, 100, 'Incluye diseño en alta resolución y 3 rondas de cambios.', 20002, true ), ( 'wmc-glorificador-mano__wmc-fixed__fixed', 'wmc-glorificador-mano', 'wmc', 'Walmart Connect', 'Glorificador de mano', 'Exhibidor portátil pequeño para presentar el producto: bandeja iluminada, aro LED, estuche o base acrílica. Equivalencia CDC: Stands, Muebles y Exhibidores > Estándar (Displays básicos). Incluye diseño 3D, troqueles y 3 rondas de cambios.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '150 USD', 150, 150, 'Incluye diseño 3D, troqueles y 3 rondas de cambios.', 20003, true ), ( 'wmc-carritos-intervenidos__wmc-fixed__fixed', 'wmc-carritos-intervenidos', 'wmc', 'Walmart Connect', 'Carritos intervenidos', 'Vinil o calcomanía de marca aplicada a la canasta y al mango del carrito de compras. Equivalencia CDC: Materiales de PDV estándar y otros materiales básicos (análogo a Backings/Rompetráficos). Pieza análoga a material gráfico estándar aplicado sobre una superficie existente.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '200 USD', 200, 200, 'Pieza análoga a material gráfico estándar aplicado sobre una superficie existente.', 20004, true ), ( 'wmc-dummies-exhibicion__wmc-fixed__fixed', 'wmc-dummies-exhibicion', 'wmc', 'Walmart Connect', 'Dummies de exhibición', 'Réplica escultórica a gran escala de pasta y cepillo dental como punto focal decorativo o táctil. Equivalencia CDC: Proyectos especiales > Diseño de muebles / proyectos con planos y troqueles desde cero. Requiere diseño estructural y visualización 3D; no es solo arte de impresión.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '200 USD', 200, 200, 'Requiere diseño estructural y visualización 3D; no es solo arte de impresión.', 20005, true ), ( 'wmc-photobooth__wmc-fixed__fixed', 'wmc-photobooth', 'wmc', 'Walmart Connect', 'Photobooth', 'Estructura tipo arco con iluminación LED y espacio fotográfico de marca para activaciones. Equivalencia CDC: Proyectos especiales > Diseño de Stands Creativos para ferias o eventos. Estructura autoportante con iluminación, ambientación y 4 rondas de cambios.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '200 USD', 200, 200, 'Estructura autoportante con iluminación, ambientación y 4 rondas de cambios.', 20006, true ), ( 'wmc-play-station__wmc-fixed__fixed', 'wmc-play-station', 'wmc', 'Walmart Connect', 'Play Station', 'Kiosco interactivo con pantalla táctil y mecánica de juego. Equivalencia CDC: Proyectos especiales > Diseño de muebles / proyectos especiales. Incluye diseño de mueble e interfaz gráfica; el desarrollo funcional del juego se cotiza aparte.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '200 USD', 200, 200, 'Incluye diseño de mueble e interfaz gráfica; el desarrollo funcional del juego se cotiza aparte.', 20007, true ), ( 'wmc-isla-wm__wmc-fixed__fixed', 'wmc-isla-wm', 'wmc', 'Walmart Connect', 'Isla WM', 'Mueble isla completo multinivel con iluminación, gráficos y espacio de exhibición de la línea completa de producto. Equivalencia CDC: Proyectos especiales > Diseño de Stands Creativos para ferias o eventos. Mayor escala y complejidad estructural dentro del set de piezas.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '150 USD', 150, 150, 'Mayor escala y complejidad estructural dentro del set de piezas.', 20008, true ), ( 'wmc-exhibicion-especial__wmc-fixed__fixed', 'wmc-exhibicion-especial', 'wmc', 'Walmart Connect', 'Exhibición Especial', 'Propuesta de muebles de exhibición. Equivalencia CDC: Proyectos especiales > Diseño de muebles con planos y troqueles desde cero. Costo de referencia por concepto; las variantes adicionales se cobran como adicionales.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '250 USD', 250, 250, 'Costo de referencia por concepto; las variantes adicionales se cobran como adicionales.', 20009, true ), ( 'wmc-punta-gondola__wmc-fixed__fixed', 'wmc-punta-gondola', 'wmc', 'Walmart Connect', 'Punta de góndola', 'Cabecera de góndola personalizada con iluminación, gráficos y espacio para producto. Equivalencia CDC: Proyectos especiales > Diseño de muebles con planos y troqueles desde cero. Costo de referencia por concepto; las variantes adicionales se cobran como adicionales.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '250 USD', 250, 250, 'Costo de referencia por concepto; las variantes adicionales se cobran como adicionales.', 20010, true ), ( 'wmc-estacion-prueba-producto__wmc-fixed__fixed', 'wmc-estacion-prueba-producto', 'wmc', 'Walmart Connect', 'Estación para prueba de producto', 'Carrito móvil con lavamanos funcional, grifo y desagüe para pruebas de producto en piso. Equivalencia CDC: Proyectos especiales > Diseño de Stands Creativos para ferias o eventos. La complejidad funcional puede requerir una cotización especial fuera del tarifario estándar.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '200 USD', 200, 200, 'La complejidad funcional puede requerir una cotización especial fuera del tarifario estándar.', 20011, true ), ( 'wmc-arco-entrada__wmc-fixed__fixed', 'wmc-arco-entrada', 'wmc', 'Walmart Connect', 'Arco de entrada', 'Estructura de entrada de tienda a gran formato con iluminación, efecto de niebla y gráficos de marca. Equivalencia CDC: Proyectos especiales > Diseño de Stands Creativos para ferias o eventos. La instalación en sitio se cotiza aparte.', 'wmc_fixed', 'Tarifa fija WMC', 'WMC', '35 - 80', 'fixed', 'Precio fijo', '200 USD', 200, 200, 'La instalación en sitio se cotiza aparte.', 20012, true ) on conflict (id) do update set catalog_item_id = excluded.catalog_item_id, section = excluded.section, category = excluded.category, service = excluded.service, notes = excluded.notes, work_type_id = excluded.work_type_id, work_type_label = excluded.work_type_label, work_type_short_label = excluded.work_type_short_label, hour_reference = excluded.hour_reference, level_id = excluded.level_id, level_label = excluded.level_label, reference = excluded.reference, reference_min = excluded.reference_min, reference_max = excluded.reference_max, level_hint = excluded.level_hint, sort_order = excluded.sort_order, is_active = true, updated_at = now(); -- 4) Si el flujo estándar usa esta RPC para desactivar tarifas ausentes, -- solo afectará grafico/estrategia y nunca apagará la sección WMC. create or replace function public.tablero_cdc_deactivate_missing_tariffs(p_active_ids text[]) returns integer language plpgsql security definer set search_path = public as $$ declare v_count integer; begin if p_active_ids is null or array_length(p_active_ids, 1) is null then raise exception 'No se recibieron IDs activos. Se cancela la desactivación.'; end if; if array_length(p_active_ids, 1) < 3 then raise exception 'Se recibieron menos de 3 IDs activos. Se cancela la desactivación por seguridad.'; end if; update public.tablero_cdc_tariff_catalog set is_active = false, updated_at = now() where section in ('grafico', 'estrategia') and is_active = true and not (id = any(p_active_ids)); get diagnostics v_count = row_count; return v_count; end; $$; grant execute on function public.tablero_cdc_deactivate_missing_tariffs(text[]) to authenticated, service_role; commit; -- Verificación final: deben aparecer 12 filas activas y un total de referencia de 2,180 USD. select count(*) as piezas_wmc_activas, coalesce(sum(reference_min), 0)::numeric(12, 2) as suma_de_todas_las_piezas from public.tablero_cdc_tariff_catalog where section = 'wmc' and is_active = true; select service as pieza, reference_min::numeric(12, 2) as monto_fijo, is_active from public.tablero_cdc_tariff_catalog where section = 'wmc' order by sort_order, service;