feat(api,web): catalogo na area do gerente com todas as pautas e precos
- Menu Catalogo no cockpit do gerente (rota /catalogo existente) - /catalog/pautas: gestor recebe TODAS as pautas ativas com contagem de reps que usam cada uma (rep segue vendo so as suas cod_pauta1..6) - Detalhe do produto: gestor ve o preco em todas as pautas (rep segue restrito as dele) - Select de pauta mostra "N reps" quando disponivel Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -180,13 +180,19 @@ export class CatalogService {
|
|||||||
const idEmpresa = matrizEmpresa(this.cls.get('idEmpresa'));
|
const idEmpresa = matrizEmpresa(this.cls.get('idEmpresa'));
|
||||||
const userId = this.cls.get('userId');
|
const userId = this.cls.get('userId');
|
||||||
const codVendedor = userId ? parseInt(userId, 10) : 0;
|
const codVendedor = userId ? parseInt(userId, 10) : 0;
|
||||||
|
const role = this.cls.get('role') ?? 'rep';
|
||||||
|
|
||||||
interface PautaRow {
|
interface PautaRow {
|
||||||
id_pauta: number;
|
id_pauta: number;
|
||||||
codigo: number;
|
codigo: number;
|
||||||
descricao: string;
|
descricao: string;
|
||||||
|
qtd_reps?: string;
|
||||||
}
|
}
|
||||||
const rows = await prisma.$queryRawUnsafe<PautaRow[]>(`
|
// Rep vê só as pautas atribuídas a ele (cod_pauta1..6); gestor vê TODAS as
|
||||||
|
// pautas ativas, com a contagem de reps que usam cada uma.
|
||||||
|
const rows =
|
||||||
|
role === 'rep'
|
||||||
|
? await prisma.$queryRawUnsafe<PautaRow[]>(`
|
||||||
SELECT DISTINCT pa.id_pauta, pa.codigo, TRIM(pa.descricao) AS descricao
|
SELECT DISTINCT pa.id_pauta, pa.codigo, TRIM(pa.descricao) AS descricao
|
||||||
FROM vw_pautas pa
|
FROM vw_pautas pa
|
||||||
JOIN vw_representantes r ON pa.codigo IN (
|
JOIN vw_representantes r ON pa.codigo IN (
|
||||||
@@ -197,12 +203,26 @@ export class CatalogService {
|
|||||||
AND pa.ativo = 1
|
AND pa.ativo = 1
|
||||||
AND r.codigo = ${codVendedor}
|
AND r.codigo = ${codVendedor}
|
||||||
ORDER BY pa.codigo
|
ORDER BY pa.codigo
|
||||||
|
`)
|
||||||
|
: await prisma.$queryRawUnsafe<PautaRow[]>(`
|
||||||
|
SELECT pa.id_pauta, pa.codigo, TRIM(pa.descricao) AS descricao,
|
||||||
|
COUNT(DISTINCT r.codigo)::text AS qtd_reps
|
||||||
|
FROM vw_pautas pa
|
||||||
|
LEFT JOIN vw_representantes r ON pa.codigo IN (
|
||||||
|
r.cod_pauta1, r.cod_pauta2, r.cod_pauta3,
|
||||||
|
r.cod_pauta4, r.cod_pauta5, r.cod_pauta6
|
||||||
|
)
|
||||||
|
WHERE pa.id_empresa = ${idEmpresa}
|
||||||
|
AND pa.ativo = 1
|
||||||
|
GROUP BY pa.id_pauta, pa.codigo, pa.descricao
|
||||||
|
ORDER BY pa.codigo
|
||||||
`);
|
`);
|
||||||
|
|
||||||
return rows.map((r) => ({
|
return rows.map((r) => ({
|
||||||
idPauta: Number(r.id_pauta),
|
idPauta: Number(r.id_pauta),
|
||||||
codigo: Number(r.codigo),
|
codigo: Number(r.codigo),
|
||||||
descricao: r.descricao,
|
descricao: r.descricao,
|
||||||
|
...(r.qtd_reps != null && { qtdReps: Number(r.qtd_reps) }),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,12 +379,16 @@ export class CatalogService {
|
|||||||
JOIN vw_pautas pa ON pa.id_pauta = pp.id_pauta
|
JOIN vw_pautas pa ON pa.id_pauta = pp.id_pauta
|
||||||
AND pa.id_empresa = ${idEmpresa}
|
AND pa.id_empresa = ${idEmpresa}
|
||||||
AND pa.ativo = 1
|
AND pa.ativo = 1
|
||||||
JOIN vw_representantes r ON pa.codigo IN (
|
${
|
||||||
|
// Rep vê só as pautas dele; gestor vê os preços de TODAS as pautas
|
||||||
|
(this.cls.get('role') ?? 'rep') === 'rep'
|
||||||
|
? `JOIN vw_representantes r ON pa.codigo IN (
|
||||||
r.cod_pauta1, r.cod_pauta2, r.cod_pauta3,
|
r.cod_pauta1, r.cod_pauta2, r.cod_pauta3,
|
||||||
r.cod_pauta4, r.cod_pauta5, r.cod_pauta6
|
r.cod_pauta4, r.cod_pauta5, r.cod_pauta6
|
||||||
)
|
) AND r.codigo = ${codVendedor}`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
WHERE pp.id_produto = ${idErp}
|
WHERE pp.id_produto = ${idErp}
|
||||||
AND r.codigo = ${codVendedor}
|
|
||||||
AND pp.preco1 > 0
|
AND pp.preco1 > 0
|
||||||
ORDER BY pa.codigo
|
ORDER BY pa.codigo
|
||||||
`),
|
`),
|
||||||
|
|||||||
@@ -161,7 +161,10 @@ export function CatalogPage() {
|
|||||||
}}
|
}}
|
||||||
options={pautas?.map((p) => ({
|
options={pautas?.map((p) => ({
|
||||||
value: p.idPauta,
|
value: p.idPauta,
|
||||||
label: `${p.codigo} — ${p.descricao}`,
|
label:
|
||||||
|
p.qtdReps != null
|
||||||
|
? `${p.codigo} — ${p.descricao} · ${p.qtdReps} rep${p.qtdReps !== 1 ? 's' : ''}`
|
||||||
|
: `${p.codigo} — ${p.descricao}`,
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -92,6 +92,11 @@ const GERENTE_ITEMS: ItemType[] = [
|
|||||||
icon: <FontAwesomeIcon icon={faAddressCard} fixedWidth />,
|
icon: <FontAwesomeIcon icon={faAddressCard} fixedWidth />,
|
||||||
label: 'Clientes',
|
label: 'Clientes',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: '/catalogo',
|
||||||
|
icon: <FontAwesomeIcon icon={faBoxesStacked} fixedWidth />,
|
||||||
|
label: 'Catálogo',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: '/relatorios',
|
key: '/relatorios',
|
||||||
icon: <FontAwesomeIcon icon={faFileInvoiceDollar} fixedWidth />,
|
icon: <FontAwesomeIcon icon={faFileInvoiceDollar} fixedWidth />,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export const PautaSchema = z.object({
|
|||||||
idPauta: z.number().int(),
|
idPauta: z.number().int(),
|
||||||
codigo: z.number().int(),
|
codigo: z.number().int(),
|
||||||
descricao: z.string(),
|
descricao: z.string(),
|
||||||
|
// Presente só para gestor: quantos representantes usam esta pauta
|
||||||
|
qtdReps: z.number().int().optional(),
|
||||||
});
|
});
|
||||||
export type Pauta = z.infer<typeof PautaSchema>;
|
export type Pauta = z.infer<typeof PautaSchema>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user