feat(auth,ger): usuarios dev por papel, escopo do supervisor por equipe e positivacao diaria
- DevLogin com 3 usuarios reais: Pavei (rep 29), Sidnei (supervisor 191) e Lucas (gerente 156); guard deixa de forcar DEV_REP_CODE em dev e usa sub/id_empresa do JWT (fallback para os valores do .env); idEmpresa do token dev opcional, default DEV_EMPRESA_ID - getTeamCodes (vw_representantes.cod_supervisor): supervisor ve so a equipe em pedidos SAR/ERP, detalhe, aprovar/recusar, clientes (lista e carteira), dashboard supervisor, chamados SAC e relatorios carteira/curva ABC; gerente/admin seguem vendo a empresa toda - Painel gerencial: card "Positivacao Diaria de Clientes" (clientes distintos por dia via vw_pedidos_erp) com campo de meta/dia persistido em localStorage, linha de meta tracejada e contador de dias na meta Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -64,10 +64,24 @@ export class SacService {
|
||||
};
|
||||
}
|
||||
|
||||
// Códigos da equipe do supervisor (cod_supervisor em vw_representantes),
|
||||
// incluindo o próprio. Retorna null para gerente/admin (sem filtro).
|
||||
private async teamCodes(): Promise<number[] | null> {
|
||||
const role = (this.cls.get('role') as string | undefined) ?? 'rep';
|
||||
if (role !== 'supervisor') return null;
|
||||
const { prisma, codVendedor } = this.ctx();
|
||||
const rows = await prisma.$queryRawUnsafe<{ codigo: number }>(
|
||||
`SELECT DISTINCT codigo FROM vw_representantes WHERE cod_supervisor = ${codVendedor}`,
|
||||
);
|
||||
return [...new Set([codVendedor, ...rows.map((r) => Number(r.codigo))])];
|
||||
}
|
||||
|
||||
async listarEmpresa() {
|
||||
const { prisma, idEmpresa } = this.ctx();
|
||||
// Supervisor vê só os chamados da sua equipe; gerente/admin veem a empresa toda
|
||||
const team = await this.teamCodes();
|
||||
const all = (await prisma.chamado.findMany({
|
||||
where: { idEmpresa },
|
||||
where: { idEmpresa, ...(team ? { codVendedor: { in: team } } : {}) },
|
||||
orderBy: { updatedAt: 'desc' },
|
||||
})) as { idCliente: number; status: string; [k: string]: unknown }[];
|
||||
const nomes = await this.enrichNomes(prisma, idEmpresa, all);
|
||||
@@ -90,6 +104,10 @@ export class SacService {
|
||||
})) as { idCliente: number; codVendedor: number; [k: string]: unknown } | null;
|
||||
if (!chamado) throw new NotFoundException('Chamado não encontrado');
|
||||
if (role === 'rep' && chamado.codVendedor !== codVendedor) throw new ForbiddenException();
|
||||
if (role === 'empresa') {
|
||||
const team = await this.teamCodes();
|
||||
if (team && !team.includes(chamado.codVendedor)) throw new ForbiddenException();
|
||||
}
|
||||
const nomes = await this.enrichNomes(prisma, idEmpresa, [chamado]);
|
||||
return { ...chamado, nomeCliente: nomes.get(chamado.idCliente) ?? null };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user