-- ============================================================= -- TABLERO CDC — APROBAR BRIEFS DESDE "RECHAZADOS" -- Ejecutar una sola vez si ya instalaste la bandeja Nuevos/Rechazados. -- -- Este parche NO crea tablas ni modifica proyectos existentes. -- Solo reemplaza la RPC de aprobación para permitir: -- new -> approved -- rejected -> approved -- Mantiene idempotencia para approved y crea el proyecto como Activo. -- ============================================================= create or replace function public.tablero_cdc_approve_brief(p_inbox_id uuid) returns jsonb language plpgsql security definer set search_path = public, auth as $$ declare v_item public.tablero_cdc_brief_inbox%rowtype; v_project_id uuid; v_email text; v_name text; v_country_manager text := ''; v_title text; v_description text; v_activity_description text; begin if auth.uid() is null or not public.tablero_cdc_is_active_allowed_user() then raise exception 'No tienes permiso para revisar briefs del Tablero CDC.'; end if; select * into v_item from public.tablero_cdc_brief_inbox where id = p_inbox_id for update; if not found then raise exception 'No se encontró el brief solicitado.'; end if; -- Idempotencia: si el usuario repite el clic o se reintenta la petición -- después de haber completado la transacción, devolvemos el mismo proyecto. if v_item.review_status = 'approved' and v_item.approved_project_id is not null then return jsonb_build_object( 'ok', true, 'already_approved', true, 'project_id', v_item.approved_project_id, 'brief_id', v_item.brief_id ); end if; if v_item.review_status not in ('new', 'rejected') then raise exception 'Este brief ya fue revisado y no puede aprobarse desde esta bandeja.'; end if; -- Seguridad adicional: un brief rechazado no debe conservar un proyecto asociado. -- Si existiera una inconsistencia manual, detenemos la operación para evitar duplicados. if v_item.review_status = 'rejected' and v_item.approved_project_id is not null then raise exception 'Este brief rechazado ya tiene un proyecto asociado. Revisa los datos antes de aprobarlo.'; end if; v_activity_description := case when v_item.review_status = 'rejected' then 'El brief fue recuperado de Rechazados y aprobado como proyecto Activo.' else 'El brief fue revisado y aprobado desde la bandeja Nuevos.' end; v_email := lower(trim(coalesce(auth.jwt() ->> 'email', ''))); select coalesce(nullif(trim(u.full_name), ''), nullif(trim(u.email), ''), 'Usuario CDC') into v_name from public.tablero_cdc_allowed_users u where lower(trim(u.email)) = v_email and u.is_active = true limit 1; v_name := coalesce(v_name, nullif(v_email, ''), 'Usuario CDC'); -- Si existe el mapeo BU -> CM en las listas dinámicas, lo reutilizamos. -- Si no existe, se deja vacío y el proyecto continúa siendo válido/editable. begin select coalesce(nullif(trim(l.label), ''), '') into v_country_manager from public.tablero_cdc_app_lists l where l.is_active = true and lower(trim(coalesce(l.value, ''))) = lower(trim(coalesce(v_item.country, ''))) and lower(trim(coalesce(l.category, ''))) in ( 'country_manager', 'country manager', 'cm', 'bu_cm', 'bu cm', 'manager', 'managers' ) order by coalesce(l.sort_order, 999999), l.label limit 1; exception when undefined_table then v_country_manager := ''; end; v_title := coalesce( nullif(trim(v_item.title), ''), nullif(trim(v_item.client), ''), 'Brief CDC ' || v_item.brief_id ); v_description := coalesce(nullif(trim(v_item.summary), ''), ''); insert into public.tablero_cdc_projects ( title, client, brand, country, requested_by, country_manager, description, status, internal_amount, brief_link, extra_data, created_by, updated_by ) values ( v_title, coalesce(v_item.client, ''), coalesce(v_item.brand, ''), coalesce(v_item.country, ''), coalesce(v_item.requested_by, ''), coalesce(v_country_manager, ''), v_description, 'Activo', null, coalesce(nullif(trim(v_item.brief_link), ''), nullif(trim(v_item.document_link), ''), ''), jsonb_strip_nulls( jsonb_build_object( 'source', 'cdc_brief', 'source_brief_id', v_item.brief_id, 'source_brief_inbox_id', v_item.id, 'source_brief_created_at', v_item.source_created_at, 'cdc_brief_document_link', nullif(trim(v_item.document_link), ''), 'delivery_date', nullif(trim(v_item.delivery_date), ''), 'request_type', nullif(trim(v_item.request_type), ''), 'deliverable_type', nullif(trim(v_item.deliverable_type), ''), 'cdc_brief_fields', v_item.raw_fields, 'color', 'blue' ) ), auth.uid(), auth.uid() ) returning id into v_project_id; update public.tablero_cdc_brief_inbox set review_status = 'approved', approved_project_id = v_project_id, reviewed_by = auth.uid(), reviewed_by_name = v_name, reviewed_by_email = v_email, reviewed_at = now(), rejection_reason = null where id = v_item.id; -- Mantiene el historial existente cuando el módulo de actividad ya está instalado, -- sin convertirlo en una dependencia obligatoria de esta migración. if to_regclass('public.tablero_cdc_project_activity') is not null then execute $activity$ insert into public.tablero_cdc_project_activity ( project_id, activity_type, title, description, actor_id, actor_name, actor_email ) values ($1, 'created', 'Proyecto creado desde CDC Brief', $5, $2, $3, $4) $activity$ using v_project_id, auth.uid(), v_name, v_email, v_activity_description; end if; return jsonb_build_object( 'ok', true, 'already_approved', false, 'project_id', v_project_id, 'brief_id', v_item.brief_id ); end; $$; revoke all on function public.tablero_cdc_approve_brief(uuid) from public; grant execute on function public.tablero_cdc_approve_brief(uuid) to authenticated;