feat: Añadir columna organization_id a course_instructors y actualizar migraciones para alinear con el modelo compartido
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
-- Hotfix migration: align CMS schema/functions with current application code
|
||||||
|
|
||||||
|
-- 1) Grading categories must support optional tipo_nota_id used by handlers
|
||||||
|
ALTER TABLE IF EXISTS public.grading_categories
|
||||||
|
ADD COLUMN IF NOT EXISTS tipo_nota_id INTEGER;
|
||||||
|
|
||||||
|
-- 2) Course instructors must include organization_id used by shared model
|
||||||
|
ALTER TABLE IF EXISTS public.course_instructors
|
||||||
|
ADD COLUMN IF NOT EXISTS organization_id UUID;
|
||||||
|
|
||||||
|
UPDATE public.course_instructors ci
|
||||||
|
SET organization_id = c.organization_id
|
||||||
|
FROM public.courses c
|
||||||
|
WHERE ci.course_id = c.id
|
||||||
|
AND ci.organization_id IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.course_instructors
|
||||||
|
ALTER COLUMN organization_id SET NOT NULL;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'fk_course_instructors_organization'
|
||||||
|
AND conrelid = 'public.course_instructors'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.course_instructors
|
||||||
|
ADD CONSTRAINT fk_course_instructors_organization
|
||||||
|
FOREIGN KEY (organization_id) REFERENCES public.organizations(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
-- 3) Remove ambiguous fn_update_course overloads and keep one canonical signature
|
||||||
|
DROP FUNCTION IF EXISTS public.fn_update_course(uuid, uuid, character varying, text, integer, character varying, timestamp with time zone, timestamp with time zone, character varying);
|
||||||
|
DROP FUNCTION IF EXISTS public.fn_update_course(uuid, uuid, text, text, integer, text, timestamp with time zone, timestamp with time zone, text, double precision, text);
|
||||||
|
DROP FUNCTION IF EXISTS public.fn_update_course(uuid, uuid, character varying, text, integer, character varying, timestamp with time zone, timestamp with time zone, character varying, double precision, character varying, jsonb, text, character varying);
|
||||||
|
DROP FUNCTION IF EXISTS public.fn_update_course(uuid, uuid, character varying, text, integer, character varying, timestamp with time zone, timestamp with time zone, character varying, double precision, character varying, jsonb, text);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.fn_update_course(
|
||||||
|
p_id UUID,
|
||||||
|
p_organization_id UUID,
|
||||||
|
p_title TEXT,
|
||||||
|
p_description TEXT,
|
||||||
|
p_passing_percentage INTEGER,
|
||||||
|
p_pacing_mode TEXT,
|
||||||
|
p_start_date TIMESTAMPTZ,
|
||||||
|
p_end_date TIMESTAMPTZ,
|
||||||
|
p_certificate_template TEXT,
|
||||||
|
p_price DOUBLE PRECISION,
|
||||||
|
p_currency TEXT,
|
||||||
|
p_marketing_metadata JSONB,
|
||||||
|
p_course_image_url TEXT
|
||||||
|
) RETURNS SETOF public.courses AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN QUERY
|
||||||
|
UPDATE public.courses
|
||||||
|
SET title = COALESCE(p_title, title),
|
||||||
|
description = COALESCE(p_description, description),
|
||||||
|
passing_percentage = COALESCE(p_passing_percentage, passing_percentage),
|
||||||
|
pacing_mode = COALESCE(p_pacing_mode, pacing_mode),
|
||||||
|
start_date = p_start_date,
|
||||||
|
end_date = p_end_date,
|
||||||
|
certificate_template = COALESCE(p_certificate_template, certificate_template),
|
||||||
|
price = COALESCE(p_price, price),
|
||||||
|
currency = COALESCE(p_currency, currency),
|
||||||
|
marketing_metadata = COALESCE(p_marketing_metadata, marketing_metadata),
|
||||||
|
course_image_url = COALESCE(p_course_image_url, course_image_url),
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = p_id AND organization_id = p_organization_id
|
||||||
|
RETURNING *;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
@@ -3966,7 +3966,7 @@ pub struct AddTeamMemberPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_team_member(
|
pub async fn add_team_member(
|
||||||
Org(_org_ctx): Org,
|
Org(org_ctx): Org,
|
||||||
claims: Claims,
|
claims: Claims,
|
||||||
State(pool): State<PgPool>,
|
State(pool): State<PgPool>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
@@ -3997,10 +3997,11 @@ pub async fn add_team_member(
|
|||||||
.map_err(|_| (StatusCode::NOT_FOUND, "User not found".into()))?;
|
.map_err(|_| (StatusCode::NOT_FOUND, "User not found".into()))?;
|
||||||
|
|
||||||
let instructor = sqlx::query_as::<_, CourseInstructor>(
|
let instructor = sqlx::query_as::<_, CourseInstructor>(
|
||||||
"INSERT INTO course_instructors (course_id, user_id, role)
|
"INSERT INTO course_instructors (organization_id, course_id, user_id, role)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3, $4)
|
||||||
RETURNING *, (SELECT email FROM users WHERE id = $2) as email, (SELECT full_name FROM users WHERE id = $2) as full_name"
|
RETURNING *, (SELECT email FROM users WHERE id = $3) as email, (SELECT full_name FROM users WHERE id = $3) as full_name"
|
||||||
)
|
)
|
||||||
|
.bind(org_ctx.id)
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.bind(user.id)
|
.bind(user.id)
|
||||||
.bind(&payload.role)
|
.bind(&payload.role)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
-- Ensure LMS course_instructors schema matches shared model expectations
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.course_instructors
|
||||||
|
ADD COLUMN IF NOT EXISTS organization_id UUID;
|
||||||
|
|
||||||
|
UPDATE public.course_instructors ci
|
||||||
|
SET organization_id = c.organization_id
|
||||||
|
FROM public.courses c
|
||||||
|
WHERE ci.course_id = c.id
|
||||||
|
AND ci.organization_id IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.course_instructors
|
||||||
|
ALTER COLUMN organization_id SET NOT NULL;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'fk_lms_course_instructors_organization'
|
||||||
|
AND conrelid = 'public.course_instructors'::regclass
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE public.course_instructors
|
||||||
|
ADD CONSTRAINT fk_lms_course_instructors_organization
|
||||||
|
FOREIGN KEY (organization_id) REFERENCES public.organizations(id) ON DELETE CASCADE;
|
||||||
|
END IF;
|
||||||
|
END$$;
|
||||||
@@ -764,10 +764,11 @@ pub async fn ingest_course(
|
|||||||
if let Some(instructors) = payload.instructors {
|
if let Some(instructors) = payload.instructors {
|
||||||
for instructor in instructors {
|
for instructor in instructors {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"INSERT INTO course_instructors (id, course_id, user_id, role, created_at)
|
"INSERT INTO course_instructors (id, organization_id, course_id, user_id, role, created_at)
|
||||||
VALUES ($1, $2, $3, $4, $5)"
|
VALUES ($1, $2, $3, $4, $5, $6)"
|
||||||
)
|
)
|
||||||
.bind(instructor.id)
|
.bind(instructor.id)
|
||||||
|
.bind(org_id)
|
||||||
.bind(payload.course.id)
|
.bind(payload.course.id)
|
||||||
.bind(instructor.user_id)
|
.bind(instructor.user_id)
|
||||||
.bind(&instructor.role)
|
.bind(&instructor.role)
|
||||||
|
|||||||
Reference in New Issue
Block a user