feat(usuarios): supervisor puede invitar usuarios cliente
- Backend inviteUsuario: permite owner, cfo y supervisor - Backend valida que supervisor solo pueda invitar rol cliente - Backend addClienteAcceso: supervisor solo puede asignar contribuyentes que tenga visibles (getEntidadesVisibles) - Frontend: supervisor ve botón Invitar Usuario y solo puede seleccionar rol Cliente en el dropdown
This commit is contained in:
@@ -170,6 +170,15 @@ export async function addClienteAcceso(req: Request, res: Response, next: NextFu
|
|||||||
const { userId } = req.body;
|
const { userId } = req.body;
|
||||||
if (!userId || typeof userId !== 'string') return next(new AppError(400, 'userId requerido'));
|
if (!userId || typeof userId !== 'string') return next(new AppError(400, 'userId requerido'));
|
||||||
const entidadId = String(req.params.id);
|
const entidadId = String(req.params.id);
|
||||||
|
|
||||||
|
// Seguridad: supervisor solo puede asignar contribuyentes que supervise
|
||||||
|
if (req.user!.role === 'supervisor') {
|
||||||
|
const visibleIds = await getEntidadesVisibles(req.tenantPool!, req.user!.userId, req.user!.role);
|
||||||
|
if (!visibleIds.includes(entidadId)) {
|
||||||
|
return next(new AppError(403, 'No tienes acceso a este contribuyente'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await req.tenantPool!.query(
|
await req.tenantPool!.query(
|
||||||
'INSERT INTO cliente_accesos (user_id, entidad_id) VALUES ($1, $2) ON CONFLICT DO NOTHING',
|
'INSERT INTO cliente_accesos (user_id, entidad_id) VALUES ($1, $2) ON CONFLICT DO NOTHING',
|
||||||
[userId, entidadId],
|
[userId, entidadId],
|
||||||
|
|||||||
@@ -65,11 +65,16 @@ export async function getAllUsuarios(req: Request, res: Response, next: NextFunc
|
|||||||
|
|
||||||
export async function inviteUsuario(req: Request, res: Response, next: NextFunction) {
|
export async function inviteUsuario(req: Request, res: Response, next: NextFunction) {
|
||||||
try {
|
try {
|
||||||
if (req.user!.role !== 'owner') {
|
if (!['owner', 'cfo', 'supervisor'].includes(req.user!.role)) {
|
||||||
throw new AppError(403, 'Solo los dueños pueden invitar usuarios');
|
throw new AppError(403, 'No autorizado para invitar usuarios');
|
||||||
}
|
}
|
||||||
const data = inviteSchema.parse(req.body);
|
const data = inviteSchema.parse(req.body);
|
||||||
|
|
||||||
|
// Los supervisores solo pueden invitar clientes
|
||||||
|
if (req.user!.role === 'supervisor' && data.role !== 'cliente') {
|
||||||
|
throw new AppError(403, 'Los supervisores solo pueden invitar clientes');
|
||||||
|
}
|
||||||
|
|
||||||
// Validate: auxiliar requires a supervisor
|
// Validate: auxiliar requires a supervisor
|
||||||
if (data.role === 'auxiliar' && !data.supervisorUserId) {
|
if (data.role === 'auxiliar' && !data.supervisorUserId) {
|
||||||
throw new AppError(400, 'Debes asignar un supervisor al auxiliar');
|
throw new AppError(400, 'Debes asignar un supervisor al auxiliar');
|
||||||
|
|||||||
@@ -77,10 +77,14 @@ export default function UsuariosPage() {
|
|||||||
const deleteUsuario = useDeleteUsuario();
|
const deleteUsuario = useDeleteUsuario();
|
||||||
|
|
||||||
const isDespacho = isDespachoTenant(currentUser?.tenantRfc);
|
const isDespacho = isDespachoTenant(currentUser?.tenantRfc);
|
||||||
const inviteRoles = isDespacho ? despachoInviteRoles : legacyInviteRoles;
|
const inviteRoles = isDespacho
|
||||||
|
? (currentUser?.role === 'supervisor'
|
||||||
|
? despachoInviteRoles.filter(r => r.value === 'cliente')
|
||||||
|
: despachoInviteRoles)
|
||||||
|
: legacyInviteRoles;
|
||||||
const defaultInviteRole = isDespacho ? 'auxiliar' : 'visor';
|
const defaultInviteRole = isDespacho ? 'auxiliar' : 'visor';
|
||||||
|
|
||||||
const isAdmin = currentUser?.role === 'owner' || currentUser?.role === 'cfo';
|
const isAdmin = currentUser?.role === 'owner' || currentUser?.role === 'cfo' || currentUser?.role === 'supervisor';
|
||||||
|
|
||||||
const [showInvite, setShowInvite] = useState(false);
|
const [showInvite, setShowInvite] = useState(false);
|
||||||
const [inviteForm, setInviteForm] = useState<{ email: string; nombre: string; role: UserInvite['role']; supervisorUserId?: string }>({
|
const [inviteForm, setInviteForm] = useState<{ email: string; nombre: string; role: UserInvite['role']; supervisorUserId?: string }>({
|
||||||
|
|||||||
Reference in New Issue
Block a user