atualizacoes modulo fechamento
This commit is contained in:
@@ -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();
|
||||
Reference in New Issue
Block a user