atualizacoes modulo fechamento

This commit is contained in:
Vitex Tecnologia
2026-04-26 22:54:56 -03:00
parent 7a01c6f97b
commit 6da067c641
29 changed files with 5973 additions and 3 deletions
+288
View File
@@ -0,0 +1,288 @@
import axios from "axios";
import { buildCommanderHeaders, resolveCommanderBaseUrl } from "./commanderHttp";
export type FechamentoTarefaItem = {
id: string;
fechamentoId: string;
tipo: string;
numeroTicket: string | null;
descricao: string;
cliente: string | null;
linkAsana: string | null;
tempoMinutos: number | null;
pontuacao: number;
pontuacaoOriginal: number;
estaRevisada: boolean;
dataConclusao: string | null;
editadoEm: string | null;
editadoPorId: string | null;
criadoEm: string;
};
type ListarTarefasResponse = {
data: FechamentoTarefaItem[];
};
type PatchTarefaResponse = {
data: FechamentoTarefaItem;
};
type CriarLancamentoResponse = {
data: FechamentoTarefaItem;
};
export type FechamentoFechadoResponse = {
id: string;
competenciaId: string;
parceiroId: string;
status: "fechado";
versao: number;
pontuacaoTotalEntregue: number;
pontuacaoMeta: number;
pontuacaoPaga: number;
pontuacaoBanco: number;
exportadoFinanceiro: boolean;
fechadoPorId: string | null;
fechadoEm: string | null;
criadoEm: string;
atualizadoEm: string;
};
export type FechamentoReabertoResponse = {
id: string;
competenciaId: string;
parceiroId: string;
status: "em_aberto";
versao: number;
pontuacaoTotalEntregue: number;
pontuacaoMeta: number;
pontuacaoPaga: number;
pontuacaoBanco: number;
exportadoFinanceiro: boolean;
fechadoPorId: string | null;
fechadoEm: string | null;
criadoEm: string;
atualizadoEm: string;
};
type ConcluirFechamentoResponse = {
data: FechamentoFechadoResponse;
};
type ReabrirFechamentoResponse = {
data: FechamentoReabertoResponse;
};
type ReprocessarAsanaResponse = {
data: {
fechamentoId: string;
tarefasRecebidas: number;
tarefasImportadas: number;
tarefasIgnoradasSemParceiro: number;
chamadasSubtarefasAsana: number;
};
};
export type ExportarPlanilhaResponse = {
buffer: ArrayBuffer;
filename: string | null;
};
type ApiErrorShape = {
error?: {
message?: string;
};
};
class FechamentoFechamentosService {
private extractFilenameFromContentDisposition(value: string | undefined): string | null {
if (!value) return null;
const utf8Match = value.match(/filename\*=UTF-8''([^;]+)/i);
if (utf8Match?.[1]) {
try {
return decodeURIComponent(utf8Match[1]);
} catch {
return utf8Match[1];
}
}
const regularMatch = value.match(/filename="?([^";]+)"?/i);
return regularMatch?.[1] ?? null;
}
private handleAxiosError(error: unknown, fallback: string): never {
if (axios.isAxiosError(error)) {
if (!error.response) {
throw new Error(
"Não foi possível conectar na API do Commander. Verifique VITE_API_BASE_URL_COMMANDER e se o backend está acessível.",
);
}
const message = ((error.response.data as ApiErrorShape | undefined)?.error?.message ??
error.message ??
fallback) as string;
throw new Error(message);
}
throw new Error(fallback);
}
async listarTarefas(fechamentoId: string): Promise<FechamentoTarefaItem[]> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.get<ListarTarefasResponse>(`${baseUrl}/fechamentos/${fechamentoId}/tarefas`, {
headers,
});
return response.data.data ?? [];
} catch (error) {
this.handleAxiosError(error, "Erro ao listar tarefas do fechamento.");
}
}
async patchTarefa(
fechamentoId: string,
tarefaId: string,
input: {
estaRevisada?: boolean;
pontuacao?: number;
numeroTicket?: string | null;
descricao?: string;
cliente?: string | null;
tempoMinutos?: number | null;
editadoPorId: string;
},
): Promise<FechamentoTarefaItem> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.patch<PatchTarefaResponse>(
`${baseUrl}/fechamentos/${fechamentoId}/tarefas/${tarefaId}`,
{
...(input.estaRevisada !== undefined ? { esta_revisada: input.estaRevisada } : {}),
...(input.pontuacao !== undefined ? { pontuacao: input.pontuacao } : {}),
...(input.numeroTicket !== undefined ? { numero_ticket: input.numeroTicket } : {}),
...(input.descricao !== undefined ? { descricao: input.descricao } : {}),
...(input.cliente !== undefined ? { cliente: input.cliente } : {}),
...(input.tempoMinutos !== undefined ? { tempo_minutos: input.tempoMinutos } : {}),
editado_por_id: input.editadoPorId,
},
{ headers },
);
return response.data.data;
} catch (error) {
this.handleAxiosError(error, "Erro ao atualizar aprovação da tarefa.");
}
}
async criarLancamento(
fechamentoId: string,
input: { tipo: "bonus" | "desconto"; descricao: string; pontuacao: number },
): Promise<FechamentoTarefaItem> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.post<CriarLancamentoResponse>(
`${baseUrl}/fechamentos/${fechamentoId}/tarefas`,
{
tipo: input.tipo,
descricao: input.descricao,
pontuacao: input.pontuacao,
},
{ headers },
);
return response.data.data;
} catch (error) {
this.handleAxiosError(error, "Erro ao criar lançamento no fechamento.");
}
}
async excluirLancamento(fechamentoId: string, tarefaId: string): Promise<FechamentoTarefaItem> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.delete<PatchTarefaResponse>(
`${baseUrl}/fechamentos/${fechamentoId}/tarefas/${tarefaId}`,
{ headers },
);
return response.data.data;
} catch (error) {
this.handleAxiosError(error, "Erro ao excluir lançamento.");
}
}
async concluirFechamento(
fechamentoId: string,
input: { pontuacaoPaga: number; fechadoPorId?: string; motivoAjuste?: string },
): Promise<FechamentoFechadoResponse> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.post<ConcluirFechamentoResponse>(
`${baseUrl}/fechamentos/${fechamentoId}/concluir`,
{
pontuacao_paga: input.pontuacaoPaga,
...(input.fechadoPorId ? { fechado_por_id: input.fechadoPorId } : {}),
...(input.motivoAjuste?.trim() ? { motivo_ajuste: input.motivoAjuste.trim() } : {}),
},
{ headers },
);
return response.data.data;
} catch (error) {
this.handleAxiosError(error, "Erro ao concluir fechamento.");
}
}
async exportarPlanilha(fechamentoId: string): Promise<ExportarPlanilhaResponse> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.get<ArrayBuffer>(`${baseUrl}/fechamentos/${fechamentoId}/exportar`, {
headers,
responseType: "arraybuffer",
});
return {
buffer: response.data,
filename: this.extractFilenameFromContentDisposition(response.headers["content-disposition"]),
};
} catch (error) {
this.handleAxiosError(error, "Erro ao exportar planilha do fechamento.");
}
}
async reabrirFechamento(
fechamentoId: string,
input: { reabertoPorId: string; motivo?: string },
): Promise<FechamentoReabertoResponse> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.post<ReabrirFechamentoResponse>(
`${baseUrl}/fechamentos/${fechamentoId}/reabrir`,
{
reaberto_por_id: input.reabertoPorId,
...(input.motivo?.trim() ? { motivo: input.motivo.trim() } : {}),
},
{ headers },
);
return response.data.data;
} catch (error) {
this.handleAxiosError(error, "Erro ao reabrir fechamento.");
}
}
async reprocessarAsana(fechamentoId: string): Promise<ReprocessarAsanaResponse["data"]> {
try {
const headers = await buildCommanderHeaders();
const baseUrl = resolveCommanderBaseUrl();
const response = await axios.post<ReprocessarAsanaResponse>(
`${baseUrl}/fechamentos/${fechamentoId}/reprocessar-asana`,
{},
{ headers },
);
return response.data.data;
} catch (error) {
this.handleAxiosError(error, "Erro ao reprocessar tarefas do Asana.");
}
}
}
export const fechamentoFechamentosService = new FechamentoFechamentosService();