Migración 0006 v1.6: rol docente, maestros.es_tutor, RPC crear_solicitud con maestro_id
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
-- 0006_docente_maestros_v16.sql
|
||||
-- v1.6:
|
||||
-- A) profiles.rol acepta 'docente' (alumno|docente|admin)
|
||||
-- B) maestros.es_tutor bool (subset de maestros que son elegibles como tutor)
|
||||
-- C) RPC crear_solicitud reescrita: recibe p_maestro_id int; resuelve nombre
|
||||
-- y lo guarda en maestro_responsable (snapshot). Docente no requiere
|
||||
-- maestro (se guarda su propio nombre como responsable). Fallback al
|
||||
-- tutor guardado si p_maestro_id es null y el user es alumno.
|
||||
|
||||
begin;
|
||||
|
||||
-- A) Rol docente ----------------------------------------------------------
|
||||
alter table prestamos.profiles drop constraint if exists profiles_rol_check;
|
||||
alter table prestamos.profiles
|
||||
add constraint profiles_rol_check check (rol in ('alumno','docente','admin'));
|
||||
|
||||
-- B) Flag es_tutor --------------------------------------------------------
|
||||
alter table prestamos.maestros
|
||||
add column if not exists es_tutor boolean not null default false;
|
||||
|
||||
-- 'Sin especificar' queda como fallback de tutor para no romper flows
|
||||
-- previos (no es un maestro real; se puede desmarcar despues desde /admin/maestros).
|
||||
update prestamos.maestros set es_tutor = true where nombre = 'Sin especificar';
|
||||
|
||||
-- Self-heal policy insert amplia el check a los 3 roles no-admin.
|
||||
-- Sigue prohibiendo auto-promocion a admin.
|
||||
drop policy if exists profiles_insert_self on prestamos.profiles;
|
||||
create policy profiles_insert_self on prestamos.profiles
|
||||
for insert to authenticated
|
||||
with check (id = auth.uid() and rol in ('alumno','docente'));
|
||||
|
||||
-- C) RPC crear_solicitud v2: recibe maestro_id ----------------------------
|
||||
-- Cambio de firma: antes (text, text, jsonb) -> ahora (int, text, jsonb).
|
||||
-- Drop de la anterior para no dejar overload muerto.
|
||||
drop function if exists prestamos.crear_solicitud(text, text, jsonb);
|
||||
|
||||
create or replace function prestamos.crear_solicitud(
|
||||
p_maestro_id int,
|
||||
p_notas text,
|
||||
p_items jsonb
|
||||
) returns bigint
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = prestamos, pg_temp
|
||||
as $$
|
||||
declare
|
||||
v_alumno uuid := auth.uid();
|
||||
v_solicitud_id bigint;
|
||||
v_item jsonb;
|
||||
v_material_id int;
|
||||
v_cantidad int;
|
||||
v_descripcion text;
|
||||
v_disponible int;
|
||||
v_estado_mat text;
|
||||
v_trackeado boolean;
|
||||
v_unidad_id bigint;
|
||||
v_asignadas int;
|
||||
v_maestro text;
|
||||
v_rol text;
|
||||
v_nombre_propio text;
|
||||
v_tutor_id int;
|
||||
begin
|
||||
if v_alumno is null then
|
||||
raise exception 'no_autenticado';
|
||||
end if;
|
||||
|
||||
select rol, nombre, tutor_id
|
||||
into v_rol, v_nombre_propio, v_tutor_id
|
||||
from prestamos.profiles where id = v_alumno;
|
||||
|
||||
if v_rol is null then
|
||||
raise exception 'perfil_no_existe';
|
||||
end if;
|
||||
|
||||
if v_rol = 'docente' then
|
||||
-- Docente: no elige maestro, se registra su propio nombre como responsable.
|
||||
v_maestro := coalesce(nullif(trim(v_nombre_propio), ''), '(Docente)');
|
||||
elsif p_maestro_id is not null then
|
||||
select nombre into v_maestro
|
||||
from prestamos.maestros
|
||||
where id = p_maestro_id and activo;
|
||||
if v_maestro is null then
|
||||
raise exception 'maestro_no_valido';
|
||||
end if;
|
||||
else
|
||||
-- Alumno sin selección: cae al tutor guardado.
|
||||
if v_tutor_id is null then
|
||||
raise exception 'maestro_responsable_requerido';
|
||||
end if;
|
||||
select nombre into v_maestro
|
||||
from prestamos.maestros where id = v_tutor_id;
|
||||
if v_maestro is null then
|
||||
raise exception 'tutor_no_valido';
|
||||
end if;
|
||||
end if;
|
||||
|
||||
if p_items is null or jsonb_typeof(p_items) <> 'array' or jsonb_array_length(p_items) = 0 then
|
||||
raise exception 'items_requeridos';
|
||||
end if;
|
||||
|
||||
-- Lock por orden ascendente de material_id, evita deadlocks entre alumnos
|
||||
for v_material_id in
|
||||
select distinct (elem->>'material_id')::int
|
||||
from jsonb_array_elements(p_items) elem
|
||||
order by 1
|
||||
loop
|
||||
perform 1 from prestamos.materiales where id = v_material_id for update;
|
||||
end loop;
|
||||
|
||||
insert into prestamos.solicitudes (alumno_id, estado, maestro_responsable, notas)
|
||||
values (v_alumno, 'pendiente', v_maestro, nullif(trim(coalesce(p_notas, '')), ''))
|
||||
returning id into v_solicitud_id;
|
||||
|
||||
for v_item in select * from jsonb_array_elements(p_items)
|
||||
loop
|
||||
v_material_id := (v_item->>'material_id')::int;
|
||||
v_cantidad := (v_item->>'cantidad')::int;
|
||||
v_descripcion := nullif(trim(coalesce(v_item->>'descripcion', '')), '');
|
||||
|
||||
if v_material_id is null or v_cantidad is null or v_cantidad <= 0 then
|
||||
raise exception 'item_invalido';
|
||||
end if;
|
||||
|
||||
select cantidad_disponible, estado, trackeado_por_unidad
|
||||
into v_disponible, v_estado_mat, v_trackeado
|
||||
from prestamos.materiales where id = v_material_id;
|
||||
|
||||
if v_estado_mat is null then
|
||||
raise exception 'material_no_existe: %', v_material_id;
|
||||
end if;
|
||||
if v_estado_mat <> 'disponible' then
|
||||
raise exception 'material_no_disponible: %', v_material_id;
|
||||
end if;
|
||||
if v_disponible < v_cantidad then
|
||||
raise exception 'stock_insuficiente: %', v_material_id;
|
||||
end if;
|
||||
|
||||
if v_trackeado then
|
||||
v_asignadas := 0;
|
||||
while v_asignadas < v_cantidad loop
|
||||
select id into v_unidad_id
|
||||
from prestamos.material_unidades
|
||||
where material_id = v_material_id and estado = 'disponible'
|
||||
order by id
|
||||
for update skip locked
|
||||
limit 1;
|
||||
|
||||
if v_unidad_id is null then
|
||||
raise exception 'sin_unidad_disponible: %', v_material_id;
|
||||
end if;
|
||||
|
||||
insert into prestamos.solicitud_items
|
||||
(solicitud_id, material_id, cantidad, descripcion, material_unidad_id)
|
||||
values (v_solicitud_id, v_material_id, 1, v_descripcion, v_unidad_id);
|
||||
|
||||
update prestamos.material_unidades
|
||||
set estado = 'prestado'
|
||||
where id = v_unidad_id;
|
||||
|
||||
v_asignadas := v_asignadas + 1;
|
||||
end loop;
|
||||
else
|
||||
insert into prestamos.solicitud_items (solicitud_id, material_id, cantidad, descripcion)
|
||||
values (v_solicitud_id, v_material_id, v_cantidad, v_descripcion);
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return v_solicitud_id;
|
||||
end;
|
||||
$$;
|
||||
|
||||
grant execute on function prestamos.crear_solicitud(int, text, jsonb) to authenticated;
|
||||
|
||||
commit;
|
||||
Reference in New Issue
Block a user