The PAE Inventory System implements a maker-checker approval workflow for all inventory entries. This ensures that no inventory is added to stock without proper authorization and review.
Key Principle: Separation of dutiesThe person who creates an entry guide (maker) cannot approve it themselves. A Director must review and approve it (checker).
Status: Entry guide created but not yet reviewedInventory Impact: None - stock levels remain unchangedWho can create: Madre Procesadora or DirectorDisplayed as:⏱️ PENDIENTE
Status: Director has reviewed and approved the entryInventory Impact: Stock quantities are added immediately upon approvalWho can approve: Director or Desarrollador onlyDisplayed as:✓ APROBADA
Status: Director has reviewed and rejected the entryInventory Impact: None - stock levels remain unchangedWho can reject: Director or Desarrollador onlyRequires: A reason/comment explaining why it was rejectedDisplayed as:✗ RECHAZADA
Click Registrar Guía (Pendiente). The guide will be created with status “Pendiente”.
You will see a confirmation message:“Guía #91 registrada. Estado: Pendiente de aprobación. El inventario se actualizará cuando el Director la apruebe.”
aprobar_guia() Function (supabase_schema.sql:414-477)
CREATE OR REPLACE FUNCTION aprobar_guia( p_id_guia INTEGER, p_comentarios TEXT DEFAULT NULL)RETURNS JSONLANGUAGE plpgsqlSECURITY DEFINERAS $$DECLARE v_input_record RECORD; v_total_productos INTEGER := 0; v_guia_info RECORD; v_user_role INTEGER;BEGIN -- Verify user has Director or Desarrollador role SELECT id_rol INTO v_user_role FROM users WHERE id_user = auth.uid(); IF v_user_role IS NULL OR v_user_role NOT IN (1, 4) THEN RAISE EXCEPTION 'No tiene permisos para aprobar guias.'; END IF; -- Get guide information SELECT * INTO v_guia_info FROM guia_entrada WHERE id_guia = p_id_guia; IF NOT FOUND THEN RAISE EXCEPTION 'Guia con ID % no encontrada', p_id_guia; END IF; -- Ensure guide is still pending IF v_guia_info.estado != 'Pendiente' THEN RAISE EXCEPTION 'La guia ya fue procesada. Estado actual: %', v_guia_info.estado; END IF; -- Update guide status to Aprobada UPDATE guia_entrada SET estado = 'Aprobada', aprobado_por = auth.uid(), fecha_aprobacion = NOW(), comentarios_aprobacion = p_comentarios WHERE id_guia = p_id_guia; -- Add quantities to inventory stock FOR v_input_record IN SELECT i.* FROM input i WHERE i.id_guia = p_id_guia LOOP v_total_productos := v_total_productos + 1; UPDATE product SET stock = stock + v_input_record.amount WHERE id_product = v_input_record.id_product; END LOOP; -- Log the approval in audit trail INSERT INTO audit_log (id_user, action_type, table_affected, record_id, details) VALUES (auth.uid(), 'APPROVE', 'guia_entrada', p_id_guia, jsonb_build_object( 'numero_guia', v_guia_info.numero_guia_sunagro, 'productos_procesados', v_total_productos, 'comentarios', p_comentarios )::text ); RETURN json_build_object( 'success', true, 'id_guia', p_id_guia, 'productos_procesados', v_total_productos, 'mensaje', format('Guia %s aprobada. %s productos actualizados.', v_guia_info.numero_guia_sunagro, v_total_productos) );EXCEPTION WHEN OTHERS THEN RAISE EXCEPTION 'Error al aprobar guia: %', SQLERRM;END;$$;
This function:
Verifies the user has Director/Desarrollador role
Checks the guide exists and is still pending
Updates guide status to “Aprobada”
Adds all product quantities to stock in a single transaction
rechazar_guia() Function (supabase_schema.sql:479-532)
CREATE OR REPLACE FUNCTION rechazar_guia( p_id_guia INTEGER, p_motivo TEXT)RETURNS JSONLANGUAGE plpgsqlSECURITY DEFINERAS $$DECLARE v_guia_info RECORD; v_user_role INTEGER;BEGIN -- Verify permissions SELECT id_rol INTO v_user_role FROM users WHERE id_user = auth.uid(); IF v_user_role IS NULL OR v_user_role NOT IN (1, 4) THEN RAISE EXCEPTION 'No tiene permisos para rechazar guias.'; END IF; SELECT * INTO v_guia_info FROM guia_entrada WHERE id_guia = p_id_guia; IF NOT FOUND THEN RAISE EXCEPTION 'Guia con ID % no encontrada', p_id_guia; END IF; IF v_guia_info.estado != 'Pendiente' THEN RAISE EXCEPTION 'La guia ya fue procesada. Estado actual: %', v_guia_info.estado; END IF; -- Require a rejection reason IF p_motivo IS NULL OR trim(p_motivo) = '' THEN RAISE EXCEPTION 'Debe proporcionar un motivo para rechazar la guia'; END IF; -- Update to Rechazada status UPDATE guia_entrada SET estado = 'Rechazada', aprobado_por = auth.uid(), fecha_aprobacion = NOW(), comentarios_aprobacion = p_motivo WHERE id_guia = p_id_guia; -- Log rejection INSERT INTO audit_log (id_user, action_type, table_affected, record_id, details) VALUES (auth.uid(), 'REJECT', 'guia_entrada', p_id_guia, jsonb_build_object( 'numero_guia', v_guia_info.numero_guia_sunagro, 'motivo', p_motivo )::text ); RETURN json_build_object( 'success', true, 'id_guia', p_id_guia, 'mensaje', format('Guia %s rechazada.', v_guia_info.numero_guia_sunagro) );END;$$;
CREATE POLICY "guia_entrada_select" ON guia_entrada FOR SELECT USING (true);CREATE POLICY "guia_entrada_insert" ON guia_entrada FOR INSERT WITH CHECK (get_user_role() IN (1, 2, 4));CREATE POLICY "guia_entrada_update" ON guia_entrada FOR UPDATE USING (get_user_role() IN (1, 4));