feat(api,web): meta de positivacao diaria salva no servidor com botao Salvar
Antes a meta vivia no localStorage do navegador (perdia em outro dispositivo). Agora: coluna meta_positivacao_dia em config_empresa, PUT /dashboard/manager/meta-positivacao-dia (gerente/admin), meta vem no dashboard e o card tem botao Salvar (desabilitado sem mudanca). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
-- Meta diaria de positivacao do painel gerencial - salva por empresa para
|
||||
-- nao depender do navegador do gerente.
|
||||
-- Idempotente (provision roda o schema espelho antes do migrate deploy).
|
||||
-- ATENCAO: bancos ERP usam LATIN1 - nao usar em-dash nem box-drawing aqui.
|
||||
ALTER TABLE sar.config_empresa ADD COLUMN IF NOT EXISTS meta_positivacao_dia INTEGER;
|
||||
@@ -201,9 +201,10 @@ model RegraDesconto {
|
||||
// impressão do pedido de todos os representantes.
|
||||
|
||||
model ConfigEmpresa {
|
||||
idEmpresa Int @id @map("id_empresa")
|
||||
logoBase64 String? @map("logo_base64")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
idEmpresa Int @id @map("id_empresa")
|
||||
logoBase64 String? @map("logo_base64")
|
||||
metaPositivacaoDia Int? @map("meta_positivacao_dia")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("config_empresa")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { Controller, ForbiddenException, Get, Query } from '@nestjs/common';
|
||||
import { Body, Controller, ForbiddenException, Get, HttpCode, Put, Query } from '@nestjs/common';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import type { RepDashboard, SupervisorDashboard, ManagerDashboard } from '@sar/api-interface';
|
||||
import { createZodDto } from 'nestjs-zod';
|
||||
import {
|
||||
SaveMetaPositivacaoBodySchema,
|
||||
type RepDashboard,
|
||||
type SupervisorDashboard,
|
||||
type ManagerDashboard,
|
||||
type SaveMetaPositivacaoBody,
|
||||
} from '@sar/api-interface';
|
||||
import type { WorkspaceClsStore } from '../workspace/workspace.types';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
|
||||
class SaveMetaPositivacaoDto extends createZodDto(SaveMetaPositivacaoBodySchema) {}
|
||||
|
||||
@Controller({ path: 'dashboard' })
|
||||
export class DashboardController {
|
||||
constructor(
|
||||
@@ -31,6 +40,14 @@ export class DashboardController {
|
||||
return this.dashboard.supervisorDashboard();
|
||||
}
|
||||
|
||||
@Put('manager/meta-positivacao-dia')
|
||||
@HttpCode(204)
|
||||
saveMetaPositivacaoDia(@Body() body: SaveMetaPositivacaoDto): Promise<void> {
|
||||
this.assertGestor();
|
||||
const parsed = SaveMetaPositivacaoBodySchema.parse(body) as SaveMetaPositivacaoBody;
|
||||
return this.dashboard.saveMetaPositivacaoDia(parsed.meta);
|
||||
}
|
||||
|
||||
@Get('manager')
|
||||
managerDashboard(
|
||||
@Query('mes') mes?: string,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import type {
|
||||
RepDashboard,
|
||||
@@ -605,6 +605,11 @@ export class DashboardService {
|
||||
const totalReps = new Set(rankingRows.map((r) => Number(r.cod_vendedor))).size;
|
||||
const metaTotal = Number(metaTotalRows[0]?.meta_total ?? 0);
|
||||
|
||||
// Meta diária de positivação salva pelo gerente (config da empresa matriz)
|
||||
const config = await prisma.configEmpresa.findUnique({
|
||||
where: { idEmpresa: idEmpresaMatriz },
|
||||
});
|
||||
|
||||
const novosMap = new Map(
|
||||
novosClientesRows.map((n) => [Number(n.cod_vendedor), Number(n.novos)]),
|
||||
);
|
||||
@@ -635,10 +640,29 @@ export class DashboardService {
|
||||
dia: r.dia,
|
||||
clientes: Number(r.clientes),
|
||||
})),
|
||||
metaPositivacaoDia: config?.metaPositivacaoDia ?? null,
|
||||
syncedAt: now.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// Salva a meta diária de positivação (config_empresa) — só gerente/admin.
|
||||
async saveMetaPositivacaoDia(meta: number): Promise<void> {
|
||||
const role = this.cls.get('role') ?? 'rep';
|
||||
if (role !== 'manager' && role !== 'admin') {
|
||||
throw new ForbiddenException('Apenas gerentes podem salvar a meta de positivação');
|
||||
}
|
||||
const prisma = this.cls.get('prisma');
|
||||
if (!prisma) throw new Error('prisma não disponível no CLS');
|
||||
const idEmpresa = matrizEmpresa(this.cls.get('idEmpresa'));
|
||||
|
||||
const value = meta > 0 ? meta : null;
|
||||
await prisma.configEmpresa.upsert({
|
||||
where: { idEmpresa },
|
||||
create: { idEmpresa, metaPositivacaoDia: value },
|
||||
update: { metaPositivacaoDia: value },
|
||||
});
|
||||
}
|
||||
|
||||
async supervisorDashboard(): Promise<SupervisorDashboard> {
|
||||
const prisma = this.cls.get('prisma');
|
||||
if (!prisma) throw new Error('prisma não disponível no CLS');
|
||||
|
||||
Reference in New Issue
Block a user