next-finance-mcp 0.9.76 → 0.9.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +36 -0
- package/dist/client.js +251 -6
- package/dist/http-index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/tools.d.ts +1075 -154
- package/dist/tools.js +141 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1019,6 +1019,42 @@ export interface ContatoDetalhado {
|
|
|
1019
1019
|
valor?: number;
|
|
1020
1020
|
}[];
|
|
1021
1021
|
}
|
|
1022
|
+
/** Lista contratos da carteira (opcionalmente por pessoa). */
|
|
1023
|
+
export declare function listarContratos(o?: {
|
|
1024
|
+
pessoa?: string;
|
|
1025
|
+
pagina?: number;
|
|
1026
|
+
limite?: number;
|
|
1027
|
+
}): Promise<{
|
|
1028
|
+
contratos: Record<string, unknown>[];
|
|
1029
|
+
pagina: number;
|
|
1030
|
+
}>;
|
|
1031
|
+
/** Detalha um contrato (valor, reajuste, parcelas, índice detectado no texto). */
|
|
1032
|
+
export declare function detalharContrato(o: {
|
|
1033
|
+
numero?: number | string;
|
|
1034
|
+
pessoa?: string;
|
|
1035
|
+
}): Promise<Record<string, unknown>>;
|
|
1036
|
+
/** Calcula o reajuste (sem gravar): índice acumulado 12m (BCB) × valor atual. */
|
|
1037
|
+
export declare function calcularReajusteContrato(o: {
|
|
1038
|
+
numero?: number | string;
|
|
1039
|
+
pessoa?: string;
|
|
1040
|
+
indice?: string;
|
|
1041
|
+
meses?: number;
|
|
1042
|
+
}): Promise<Record<string, unknown>>;
|
|
1043
|
+
/** Aplica o reajuste no contrato (PUT), acrescenta a nota no texto e, por padrão,
|
|
1044
|
+
* gera as parcelas (GerarFaturamento). Retorna o antes/depois. */
|
|
1045
|
+
export declare function reajustarContrato(o: {
|
|
1046
|
+
numero?: number | string;
|
|
1047
|
+
pessoa?: string;
|
|
1048
|
+
indice?: string;
|
|
1049
|
+
meses?: number;
|
|
1050
|
+
responsavel?: string;
|
|
1051
|
+
gerarParcelas?: boolean;
|
|
1052
|
+
}): Promise<Record<string, unknown>>;
|
|
1053
|
+
/** Gera (fatura) as parcelas do contrato — GerarFaturamento. */
|
|
1054
|
+
export declare function gerarParcelasContrato(o: {
|
|
1055
|
+
numero?: number | string;
|
|
1056
|
+
pessoa?: string;
|
|
1057
|
+
}): Promise<Record<string, unknown>>;
|
|
1022
1058
|
/** Invalida o cache de pessoas (chamar após criar/editar contato). */
|
|
1023
1059
|
export declare function invalidarCachePessoas(): void;
|
|
1024
1060
|
export type ResultadoBuscaContato = {
|
package/dist/client.js
CHANGED
|
@@ -410,6 +410,45 @@ async function apiPost(path, body, retry = true) {
|
|
|
410
410
|
return text;
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
|
+
/** PUT JSON para a API REST — mesma máquina de auth/refresh do apiPost. */
|
|
414
|
+
async function apiPut(path, body, retry = true) {
|
|
415
|
+
const auth = getAuthHeader();
|
|
416
|
+
const { statusCode, headers, body: resBody } = await reqWithTimeout(`${API_BASE}${path}`, {
|
|
417
|
+
method: "PUT",
|
|
418
|
+
headers: {
|
|
419
|
+
...BASE_HEADERS, ...NEXT_BUSINESS_HEADERS, ...getApiKeyHeaders(),
|
|
420
|
+
Cookie: getCookieHeader("finance.net.br"),
|
|
421
|
+
"Content-Type": "application/json", Accept: "application/json",
|
|
422
|
+
...(auth ? { Authorization: auth } : {}),
|
|
423
|
+
},
|
|
424
|
+
body: JSON.stringify(body),
|
|
425
|
+
dispatcher: agentFollow,
|
|
426
|
+
});
|
|
427
|
+
const setCookies = headers["set-cookie"];
|
|
428
|
+
storeCookies(Array.isArray(setCookies) ? setCookies : setCookies ? [setCookies] : [], "finance.net.br");
|
|
429
|
+
const text = await resBody.text();
|
|
430
|
+
if (isSessionExpired(statusCode, text)) {
|
|
431
|
+
if (retry && await recoverSession())
|
|
432
|
+
return apiPut(path, body, false);
|
|
433
|
+
throwSessionExpired();
|
|
434
|
+
}
|
|
435
|
+
if (retry && isCarteiraTokenExpired(statusCode, text)) {
|
|
436
|
+
if (await recoverSession())
|
|
437
|
+
return apiPut(path, body, false);
|
|
438
|
+
throw new Error("Não foi possível renovar o token da carteira no momento. Tente novamente em instantes.");
|
|
439
|
+
}
|
|
440
|
+
if (statusCode >= 400)
|
|
441
|
+
throw new Error(extrairMensagemApi(text) ?? `API PUT ${path}: ${statusCode}`);
|
|
442
|
+
try {
|
|
443
|
+
return JSON.parse(text);
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
const msg = extrairMensagemApi(text);
|
|
447
|
+
if (msg)
|
|
448
|
+
throw new Error(msg);
|
|
449
|
+
return text;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
413
452
|
function extractCsrf(html) {
|
|
414
453
|
return html.match(/name="__RequestVerificationToken"[^>]+value="([^"]+)"/)?.[1] ?? "";
|
|
415
454
|
}
|
|
@@ -1016,7 +1055,7 @@ async function resolverIdPlanoDeContas(nome, contexto) {
|
|
|
1016
1055
|
// Usa a lista COMPLETA (GET /api/PlanoDeContas, cacheada) — inclui despesa, receita
|
|
1017
1056
|
// E transferência. O /Filtrar retorna só parte (ex: só receitas em algumas carteiras),
|
|
1018
1057
|
// o que escondia planos de despesa como "Semanadas e Mesadas".
|
|
1019
|
-
const termo = nome.trim().
|
|
1058
|
+
const termo = normalizar(nome.trim().replace(/^\[|\]$/g, "")); // sem acento/caixa
|
|
1020
1059
|
const lista = await fetchPlanoDeContasCompleto();
|
|
1021
1060
|
const nomeDe = nomePlano;
|
|
1022
1061
|
const tipoDe = (p) => String(p["NomeTipoPlanoDeContas"] ?? "");
|
|
@@ -1031,13 +1070,13 @@ async function resolverIdPlanoDeContas(nome, contexto) {
|
|
|
1031
1070
|
return t === "Despesa";
|
|
1032
1071
|
return false;
|
|
1033
1072
|
};
|
|
1034
|
-
const matches = lista.filter(p => p["Ativo"] !== false && nomeDe(p)
|
|
1073
|
+
const matches = lista.filter(p => p["Ativo"] !== false && normalizar(nomeDe(p)).includes(termo));
|
|
1035
1074
|
if (matches.length === 0) {
|
|
1036
1075
|
throw new Error(`Plano de contas "${nome}" não encontrado (nem categoria, nem transferência entre contas).`);
|
|
1037
1076
|
}
|
|
1038
1077
|
const compat = matches.filter(p => !incompativel(p));
|
|
1039
1078
|
// Match exato (inclui plano de transferência "[Conta]") de tipo compatível.
|
|
1040
|
-
const exata = compat.find(p => nomeDe(p)
|
|
1079
|
+
const exata = compat.find(p => normalizar(nomeDe(p)) === termo);
|
|
1041
1080
|
if (exata)
|
|
1042
1081
|
return String(exata["Id"] ?? "");
|
|
1043
1082
|
// Entre os compatíveis: categorias têm prioridade sobre transferência.
|
|
@@ -1123,11 +1162,11 @@ async function resolverPlanoTransferencia(nomeContaDestino) {
|
|
|
1123
1162
|
async function resolverIdCentroDeCustos(nome) {
|
|
1124
1163
|
const raw = await apiGet("/api/CentroDeCustos");
|
|
1125
1164
|
const arr = Array.isArray(raw) ? raw : (Object.values(raw).find(v => Array.isArray(v)) ?? []);
|
|
1126
|
-
const termo = nome.trim()
|
|
1127
|
-
const matches = arr.filter(c => String(c["Nome"] ?? c["Descricao"] ?? "")
|
|
1165
|
+
const termo = normalizar(nome.trim()); // sem acento/caixa
|
|
1166
|
+
const matches = arr.filter(c => normalizar(String(c["Nome"] ?? c["Descricao"] ?? "")).includes(termo));
|
|
1128
1167
|
if (matches.length === 0)
|
|
1129
1168
|
throw new Error(`Centro de custo "${nome}" não encontrado.`);
|
|
1130
|
-
const exata = matches.find(c => String(c["Nome"] ?? c["Descricao"] ?? "")
|
|
1169
|
+
const exata = matches.find(c => normalizar(String(c["Nome"] ?? c["Descricao"] ?? "")) === termo);
|
|
1131
1170
|
const escolhido = exata ?? (matches.length === 1 ? matches[0] : null);
|
|
1132
1171
|
if (!escolhido) {
|
|
1133
1172
|
const nomes = matches.slice(0, 10).map(c => String(c["Nome"] ?? c["Descricao"] ?? "")).join(", ");
|
|
@@ -3871,6 +3910,212 @@ async function contratosDaPessoa(idPessoa) {
|
|
|
3871
3910
|
return [];
|
|
3872
3911
|
}
|
|
3873
3912
|
}
|
|
3913
|
+
// ── Contratos (NEXT Business) — reajuste por índice + parcelas ────────────────
|
|
3914
|
+
// Índice de reajuste NÃO é campo do contrato no NEXT: fica no texto de
|
|
3915
|
+
// `InformacaoAdicional` ("Informações sobre o contrato"). O valor do índice vem
|
|
3916
|
+
// da API pública do Banco Central (SGS): IPCA=433, IGP-M=189.
|
|
3917
|
+
const SGS_SERIE = { IPCA: 433, IGPM: 189 };
|
|
3918
|
+
const MESES_PT = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"];
|
|
3919
|
+
const arred2 = (n) => Math.round(n * 100) / 100;
|
|
3920
|
+
const pctBR = (n) => n.toFixed(2).replace(".", ",");
|
|
3921
|
+
const dataBR = (d) => `${String(d.getDate()).padStart(2, "0")}/${String(d.getMonth() + 1).padStart(2, "0")}/${d.getFullYear()}`;
|
|
3922
|
+
/** Normaliza rótulo de índice para IPCA | IGPM. */
|
|
3923
|
+
function normalizarIndice(s) {
|
|
3924
|
+
const t = normalizar(s ?? "");
|
|
3925
|
+
if (/igp[\s-]?m|igpm/.test(t))
|
|
3926
|
+
return "IGPM";
|
|
3927
|
+
if (/ipca/.test(t))
|
|
3928
|
+
return "IPCA";
|
|
3929
|
+
return null;
|
|
3930
|
+
}
|
|
3931
|
+
/** Detecta o índice de reajuste no texto de "Informações sobre o contrato". */
|
|
3932
|
+
function detectarIndiceReajuste(texto) {
|
|
3933
|
+
return normalizarIndice(texto);
|
|
3934
|
+
}
|
|
3935
|
+
/** Índice acumulado dos últimos `meses` (default 12) até o último mês fechado
|
|
3936
|
+
* antes de `ateData` (default hoje). Fonte: BCB SGS (público, sem chave). */
|
|
3937
|
+
async function indiceReajusteAcumulado(indice, meses = 12, ateData) {
|
|
3938
|
+
const serie = SGS_SERIE[indice];
|
|
3939
|
+
if (!serie)
|
|
3940
|
+
throw new Error(`Índice "${indice}" não suportado (use IPCA ou IGPM).`);
|
|
3941
|
+
const ref = ateData ?? new Date();
|
|
3942
|
+
// Último mês fechado = mês anterior ao de referência. Puxa uma janela maior e pega os últimos N disponíveis.
|
|
3943
|
+
const fimRef = new Date(ref.getFullYear(), ref.getMonth() - 1, 1);
|
|
3944
|
+
const ini = new Date(fimRef.getFullYear(), fimRef.getMonth() - (meses + 2), 1);
|
|
3945
|
+
const dd = (d, last = false) => {
|
|
3946
|
+
const dia = last ? new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate() : 1;
|
|
3947
|
+
return `${String(dia).padStart(2, "0")}/${String(d.getMonth() + 1).padStart(2, "0")}/${d.getFullYear()}`;
|
|
3948
|
+
};
|
|
3949
|
+
const url = `https://api.bcb.gov.br/dados/serie/bcdata.sgs.${serie}/dados?formato=json&dataInicial=${dd(ini)}&dataFinal=${dd(fimRef, true)}`;
|
|
3950
|
+
const ctrl = new AbortController();
|
|
3951
|
+
const t = setTimeout(() => ctrl.abort(), 20000);
|
|
3952
|
+
let arr;
|
|
3953
|
+
try {
|
|
3954
|
+
const resp = await fetch(url, { signal: ctrl.signal });
|
|
3955
|
+
if (!resp.ok)
|
|
3956
|
+
throw new Error(`Banco Central respondeu HTTP ${resp.status}`);
|
|
3957
|
+
arr = await resp.json();
|
|
3958
|
+
}
|
|
3959
|
+
catch (e) {
|
|
3960
|
+
throw new Error(`Não consegui consultar o ${indice} no Banco Central agora (${e?.message ?? e}). Tente de novo em instantes.`);
|
|
3961
|
+
}
|
|
3962
|
+
finally {
|
|
3963
|
+
clearTimeout(t);
|
|
3964
|
+
}
|
|
3965
|
+
if (!Array.isArray(arr) || arr.length === 0)
|
|
3966
|
+
throw new Error(`O Banco Central não retornou dados de ${indice} para o período.`);
|
|
3967
|
+
const pts = arr.slice(-meses); // últimos N meses disponíveis (fechados)
|
|
3968
|
+
let fator = 1;
|
|
3969
|
+
const series = pts.map(x => { const v = parseFloat(x.valor); fator *= (1 + v / 100); return { mes: x.data.slice(3), valor: v }; });
|
|
3970
|
+
const [mFim, aFim] = series[series.length - 1].mes.split("/");
|
|
3971
|
+
return {
|
|
3972
|
+
indice, percentual: (fator - 1) * 100, fator,
|
|
3973
|
+
mesInicial: series[0].mes, mesFinal: series[series.length - 1].mes,
|
|
3974
|
+
mesFinalLabel: `${MESES_PT[Number(mFim) - 1]}/${aFim}`,
|
|
3975
|
+
series,
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
/** Localiza um contrato pelo número (e opcionalmente pessoa). Retorna o objeto COMPLETO do GET. */
|
|
3979
|
+
async function resolverContratoCompleto(o) {
|
|
3980
|
+
const numero = o.numero != null && String(o.numero).trim() !== "" ? Number(String(o.numero).replace(/\D/g, "")) : undefined;
|
|
3981
|
+
let idPessoa;
|
|
3982
|
+
if (o.pessoa) {
|
|
3983
|
+
try {
|
|
3984
|
+
idPessoa = String((await resolverPessoa(o.pessoa))["Id"] ?? "");
|
|
3985
|
+
}
|
|
3986
|
+
catch { /* ignora */ }
|
|
3987
|
+
}
|
|
3988
|
+
// Pagina o Filtrar até achar o número (ou lista a 1ª página se só a pessoa foi dada).
|
|
3989
|
+
let achado;
|
|
3990
|
+
for (let pagina = 1; pagina <= 20 && !achado; pagina++) {
|
|
3991
|
+
const raw = await apiPost("/api/Contrato/Filtrar", { NumeroPagina: pagina, TamanhoPagina: 50, Nome: o.pessoa ?? "", PessoaId: idPessoa ?? null });
|
|
3992
|
+
const arr = (Array.isArray(raw) ? raw : Object.values(raw).find(v => Array.isArray(v)) ?? []);
|
|
3993
|
+
if (arr.length === 0)
|
|
3994
|
+
break;
|
|
3995
|
+
if (numero != null)
|
|
3996
|
+
achado = arr.find(c => Number(c["Numero"]) === numero);
|
|
3997
|
+
else if (arr.length === 1)
|
|
3998
|
+
achado = arr[0];
|
|
3999
|
+
if (arr.length < 50)
|
|
4000
|
+
break;
|
|
4001
|
+
}
|
|
4002
|
+
if (!achado)
|
|
4003
|
+
throw new Error(numero != null ? `Contrato número ${numero} não encontrado${o.pessoa ? ` para "${o.pessoa}"` : ""}.` : `Informe o número do contrato (não consegui identificar um único).`);
|
|
4004
|
+
const id = String(achado["Id"] ?? "");
|
|
4005
|
+
return await apiGet(`/api/Contrato/${encodeURIComponent(id)}`);
|
|
4006
|
+
}
|
|
4007
|
+
/** Versão exibível de um contrato (sem IDs internos). */
|
|
4008
|
+
function slimContrato(c) {
|
|
4009
|
+
return {
|
|
4010
|
+
numero: c["Numero"] != null ? Number(c["Numero"]) : undefined,
|
|
4011
|
+
pessoa: c["PessoaNome"] ? String(c["PessoaNome"]).trim() : undefined,
|
|
4012
|
+
valor: c["Valor"] != null ? Number(c["Valor"]) : undefined,
|
|
4013
|
+
situacao: c["Situacao"] ? String(c["Situacao"]).trim() : undefined,
|
|
4014
|
+
inicio: c["DataInicio"] ? String(c["DataInicio"]).slice(0, 10) : undefined,
|
|
4015
|
+
fim: c["DataFim"] ? String(c["DataFim"]).slice(0, 10) : undefined,
|
|
4016
|
+
reajuste_periodo: c["Reajuste"] != null ? `${c["Reajuste"]} ${c["ReajustePeriodo"] ?? ""}`.trim() : undefined,
|
|
4017
|
+
reajuste_status: c["ReajusteStatus"] ? String(c["ReajusteStatus"]).trim() : undefined,
|
|
4018
|
+
parcela_periodo: c["Parcela"] != null ? `${c["Parcela"]} ${c["ParcelaPeriodo"] ?? ""}`.trim() : undefined,
|
|
4019
|
+
indice_detectado: detectarIndiceReajuste(c["InformacaoAdicional"]) ?? undefined,
|
|
4020
|
+
};
|
|
4021
|
+
}
|
|
4022
|
+
/** Lista contratos da carteira (opcionalmente por pessoa). */
|
|
4023
|
+
export async function listarContratos(o = {}) {
|
|
4024
|
+
const pagina = o.pagina ?? 1;
|
|
4025
|
+
let idPessoa;
|
|
4026
|
+
if (o.pessoa) {
|
|
4027
|
+
try {
|
|
4028
|
+
idPessoa = String((await resolverPessoa(o.pessoa))["Id"] ?? "");
|
|
4029
|
+
}
|
|
4030
|
+
catch { /* ignora */ }
|
|
4031
|
+
}
|
|
4032
|
+
const raw = await apiPost("/api/Contrato/Filtrar", { NumeroPagina: pagina, TamanhoPagina: o.limite ?? 50, Nome: o.pessoa ?? "", PessoaId: idPessoa ?? null });
|
|
4033
|
+
const arr = (Array.isArray(raw) ? raw : Object.values(raw).find(v => Array.isArray(v)) ?? []);
|
|
4034
|
+
return { contratos: arr.map(slimContrato), pagina };
|
|
4035
|
+
}
|
|
4036
|
+
/** Detalha um contrato (valor, reajuste, parcelas, índice detectado no texto). */
|
|
4037
|
+
export async function detalharContrato(o) {
|
|
4038
|
+
const c = await resolverContratoCompleto(o);
|
|
4039
|
+
const slim = slimContrato(c);
|
|
4040
|
+
slim["informacoes"] = c["InformacaoAdicional"] ? String(c["InformacaoAdicional"]).trim() : undefined;
|
|
4041
|
+
return slim;
|
|
4042
|
+
}
|
|
4043
|
+
/** Calcula o reajuste (sem gravar): índice acumulado 12m (BCB) × valor atual. */
|
|
4044
|
+
export async function calcularReajusteContrato(o) {
|
|
4045
|
+
const c = await resolverContratoCompleto(o);
|
|
4046
|
+
const indice = normalizarIndice(o.indice) ?? detectarIndiceReajuste(c["InformacaoAdicional"]);
|
|
4047
|
+
if (!indice) {
|
|
4048
|
+
throw new Error(`Não identifiquei o índice (IPCA/IGPM) nas informações do contrato ${c["Numero"]}. Diga qual índice usar (ex.: "reajusta pelo IGPM").`);
|
|
4049
|
+
}
|
|
4050
|
+
const idx = await indiceReajusteAcumulado(indice, o.meses ?? 12);
|
|
4051
|
+
const valorAtual = Number(c["Valor"] ?? 0);
|
|
4052
|
+
const novoValor = arred2(valorAtual * idx.fator);
|
|
4053
|
+
return {
|
|
4054
|
+
contrato: Number(c["Numero"]),
|
|
4055
|
+
pessoa: c["PessoaNome"] ? String(c["PessoaNome"]).trim() : undefined,
|
|
4056
|
+
indice,
|
|
4057
|
+
periodo: `${idx.mesInicial} a ${idx.mesFinal}`,
|
|
4058
|
+
indice_referencia: idx.mesFinalLabel,
|
|
4059
|
+
variacao_percent: arred2(idx.percentual),
|
|
4060
|
+
valor_atual: valorAtual,
|
|
4061
|
+
valor_novo: novoValor,
|
|
4062
|
+
diferenca: arred2(novoValor - valorAtual),
|
|
4063
|
+
observacao: "Cálculo apenas — nada foi gravado. Use reajustar_contrato para aplicar.",
|
|
4064
|
+
};
|
|
4065
|
+
}
|
|
4066
|
+
// Campos SÓ de resposta (não devem voltar no PUT do contrato).
|
|
4067
|
+
const CONTRATO_CAMPOS_RESPOSTA = ["ContaNome", "TipoContratoNome", "PessoaNome", "ReajusteStatus", "PagamentoStatus", "PossuiAnexo"];
|
|
4068
|
+
/** Aplica o reajuste no contrato (PUT), acrescenta a nota no texto e, por padrão,
|
|
4069
|
+
* gera as parcelas (GerarFaturamento). Retorna o antes/depois. */
|
|
4070
|
+
export async function reajustarContrato(o) {
|
|
4071
|
+
const c = await resolverContratoCompleto(o);
|
|
4072
|
+
const indice = normalizarIndice(o.indice) ?? detectarIndiceReajuste(c["InformacaoAdicional"]);
|
|
4073
|
+
if (!indice)
|
|
4074
|
+
throw new Error(`Não identifiquei o índice (IPCA/IGPM) nas informações do contrato ${c["Numero"]}. Diga qual índice usar (ex.: "reajusta pelo IGPM").`);
|
|
4075
|
+
const idx = await indiceReajusteAcumulado(indice, o.meses ?? 12);
|
|
4076
|
+
const valorAtual = Number(c["Valor"] ?? 0);
|
|
4077
|
+
const novoValor = arred2(valorAtual * idx.fator);
|
|
4078
|
+
// Eco do próprio objeto do GET (remove campos de resposta), mutando só valor/serviços/nota.
|
|
4079
|
+
const body = { ...c };
|
|
4080
|
+
for (const k of CONTRATO_CAMPOS_RESPOSTA)
|
|
4081
|
+
delete body[k];
|
|
4082
|
+
body["Valor"] = novoValor;
|
|
4083
|
+
if (Array.isArray(body["ContratoServico"])) {
|
|
4084
|
+
body["ContratoServico"] = body["ContratoServico"].map(s => ({
|
|
4085
|
+
...s,
|
|
4086
|
+
Valor: s["Valor"] != null ? arred2(Number(s["Valor"]) * idx.fator) : s["Valor"],
|
|
4087
|
+
ValorTotal: s["ValorTotal"] != null ? arred2(Number(s["ValorTotal"]) * idx.fator) : s["ValorTotal"],
|
|
4088
|
+
}));
|
|
4089
|
+
}
|
|
4090
|
+
const nota = `Contrato reajustado dia ${dataBR(new Date())}, com Variação ${indice} ${pctBR(idx.percentual)}% (Indice de ${idx.mesFinalLabel}).${o.responsavel ? ` P/${o.responsavel}` : ""}`;
|
|
4091
|
+
const infoAtual = c["InformacaoAdicional"] ? String(c["InformacaoAdicional"]).replace(/\s+$/, "") : "";
|
|
4092
|
+
body["InformacaoAdicional"] = infoAtual ? `${infoAtual}\n\n${nota}` : nota;
|
|
4093
|
+
await apiPut(`/api/Contrato/${encodeURIComponent(String(c["Id"]))}`, body);
|
|
4094
|
+
// Gera as parcelas no valor novo (padrão true).
|
|
4095
|
+
let parcelasGeradas = false;
|
|
4096
|
+
if (o.gerarParcelas !== false) {
|
|
4097
|
+
try {
|
|
4098
|
+
await apiGet(`/api/Contrato/GerarFaturamento/${encodeURIComponent(String(c["Id"]))}`);
|
|
4099
|
+
parcelasGeradas = true;
|
|
4100
|
+
}
|
|
4101
|
+
catch { /* faturamento pode ser feito depois com gerar_parcelas_contrato */ }
|
|
4102
|
+
}
|
|
4103
|
+
return {
|
|
4104
|
+
sucesso: true,
|
|
4105
|
+
contrato: Number(c["Numero"]),
|
|
4106
|
+
pessoa: c["PessoaNome"] ? String(c["PessoaNome"]).trim() : undefined,
|
|
4107
|
+
indice, indice_referencia: idx.mesFinalLabel, variacao_percent: arred2(idx.percentual),
|
|
4108
|
+
valor_anterior: valorAtual, valor_novo: novoValor,
|
|
4109
|
+
parcelas_geradas: parcelasGeradas,
|
|
4110
|
+
nota_adicionada: nota,
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
/** Gera (fatura) as parcelas do contrato — GerarFaturamento. */
|
|
4114
|
+
export async function gerarParcelasContrato(o) {
|
|
4115
|
+
const c = await resolverContratoCompleto(o);
|
|
4116
|
+
await apiGet(`/api/Contrato/GerarFaturamento/${encodeURIComponent(String(c["Id"]))}`);
|
|
4117
|
+
return { sucesso: true, contrato: Number(c["Numero"]), mensagem: "Faturamento gerado — as parcelas 'A RECEBER' foram criadas/atualizadas." };
|
|
4118
|
+
}
|
|
3874
4119
|
/** Monta a ficha completa de um contato a partir do registro slim (precisa do Id). */
|
|
3875
4120
|
async function detalharContato(p) {
|
|
3876
4121
|
const id = String(p["Id"] ?? "");
|
package/dist/http-index.js
CHANGED
|
@@ -27,7 +27,7 @@ import * as client from "./client.js";
|
|
|
27
27
|
import { createCtx, runInCtx, enterDefaultCtx } from "./context.js";
|
|
28
28
|
import { toolDefinitions, handleTool, ok, SOFIA_INSTRUCTIONS } from "./tools.js";
|
|
29
29
|
import { TenantStoreEnv } from "./tenant-store.js";
|
|
30
|
-
const VERSION = "0.9.
|
|
30
|
+
const VERSION = "0.9.78";
|
|
31
31
|
// Contexto default (fallback do ALS). No HTTP cada request roda em runInCtx().
|
|
32
32
|
enterDefaultCtx();
|
|
33
33
|
// Multi-tenant: NUNCA persistir sessão no disco compartilhado do container.
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { openLoginUI } from "./login-server.js";
|
|
|
13
13
|
import { toolDefinitions, handleTool, ok, SOFIA_INSTRUCTIONS } from "./tools.js";
|
|
14
14
|
// Modo stdio: ativa um contexto único global no startup. Multi-tenancy só no modo HTTP.
|
|
15
15
|
enterDefaultCtx();
|
|
16
|
-
const VERSION = "0.9.
|
|
16
|
+
const VERSION = "0.9.78";
|
|
17
17
|
const server = new Server({ name: "SofIA", version: VERSION }, {
|
|
18
18
|
capabilities: { tools: {} },
|
|
19
19
|
instructions: SOFIA_INSTRUCTIONS,
|