-- Atomic insert that generates a unique task_number and inserts a task in one transaction -- Returns: id (uuid), task_number (text) CREATE OR REPLACE FUNCTION insert_task_with_number( p_title text, p_description text, p_office_id text, p_ticket_id text, p_request_type text, p_request_type_other text, p_request_category text, p_creator_id uuid ) RETURNS TABLE(id uuid, task_number text) LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE -- The numbering trigger on `tasks` will set `task_number` before insert. -- This RPC inserts the task row and returns the resulting id and task_number. BEGIN -- atomically increment (or create) the month counter and use it as the task_number INSERT INTO task_number_counters(year_month, counter) VALUES (to_char(now(), 'YYYY-MM-'), 1) ON CONFLICT (year_month) DO UPDATE SET counter = task_number_counters.counter + 1 RETURNING counter INTO seq; -- build the formatted task number PERFORM seq; -- ensure seq is set new_task_number := to_char(now(), 'YYYY-MM-') || lpad(seq::text, 5, '0'); INSERT INTO tasks( title, description, office_id, ticket_id, request_type, request_type_other, request_category, creator_id, created_at, task_number ) VALUES ( p_title, p_description, CASE WHEN p_office_id IS NULL OR p_office_id = '' THEN NULL ELSE p_office_id::uuid END, CASE WHEN p_ticket_id IS NULL OR p_ticket_id = '' THEN NULL ELSE p_ticket_id::uuid END, CASE WHEN p_request_type IS NULL OR p_request_type = '' THEN NULL ELSE p_request_type::request_type END, p_request_type_other, CASE WHEN p_request_category IS NULL OR p_request_category = '' THEN NULL ELSE p_request_category::request_category END, p_creator_id, now(), new_task_number ) RETURNING tasks.id, tasks.task_number INTO id, task_number; RETURN NEXT; END; $$; -- Grant execute to authenticated so client RPC can call it GRANT EXECUTE ON FUNCTION insert_task_with_number(text,text,text,text,text,text,text,uuid) TO authenticated;