@startuml uml-activity-aprobacion-reembolso-sod !include _style.iuml title 6. Aprobación de reembolso con Segregation of Duties start partition "Cliente" { :Iniciar solicitud de reembolso\ndesde detalle de orden; :Adjuntar evidencia y motivo; } partition "Soporte" { :Recibir solicitud; :Crear ticket interno categoría\n'cobro_incorrecto'; :Trigger auto-grupo asigna 'finanzas_l2'; :Notificar a Finanzas A; } partition "Finanzas A (Aprobador)" { :Cargar caso y validar evidencia; if (¿procede el reembolso?) then (sí) :Marcar reembolso 'aprobado'\n(aprobador_id = Finanzas A); note right Regla SoD: el aprobador no puede ser el mismo usuario que ejecuta el ACH. end note else (no) partition "Soporte" { :Notificar al cliente con\nmotivo de rechazo; :Cerrar ticket\nresolución = 'rechazado'; stop } endif } partition "Finanzas B (Procesador)" { repeat if (¿hay Finanzas B disponible\ny distinto a Finanzas A?) then (sí) :Tomar caso aprobado; :Validar cuenta CLABE del cliente; else (no) :Esperar siguiente turno; endif repeat while (¿asignado un procesador válido?) is (no) not (sí) :Confirmar monto y método; } partition "Banco" { :Procesar ACH refund; if (¿confirmación recibida?) then (sí) :Devolver clave de rastreo; else (no) partition "Finanzas B (Procesador)" { :Reintentar al siguiente día hábil; :Si persiste, escalar a tesorería; stop } endif } partition "Sistema" { :UPDATE reembolsos\nSET estatus = 'procesado',\nprocesador_id = Finanzas B,\nclave_rastreo = ...; :Registrar evento en logAccion\n(actor, recurso, antes, despues); } partition "Soporte" { :Cerrar ticket con\nresolución = 'reembolsado'; } partition "Cliente" { :Recibir notificación\ncon clave de rastreo; } stop footer Fuente: 00.sql (reembolsos, pagos), apps/web/src/lib/dashboard/actions/reembolsos.ts, tickets-cerrar.ts; SoD: aprobador != procesador @enduml