ДОМЕНЫ - ВОПРОСЫ - Этап 4: точный маршрут confirmed payables на дату без эвристического фолбэка
This commit is contained in:
@@ -508,11 +508,11 @@ function classifyPayablesLiabilityCategory(row, counterparty) {
|
||||
scores.supplier_or_contractor += 1;
|
||||
reasons.add("участие счета 76");
|
||||
}
|
||||
if (/(?:банк|сбер|втб|альфа|газпромбанк|кредит|loan|overdraft)/iu.test(text)) {
|
||||
if (/(?:банк|сбер|втб|альфа|газпромбанк|кредит|депозит|loan|overdraft|deposit)/iu.test(text)) {
|
||||
scores.bank_or_credit += 3;
|
||||
reasons.add("банк/кредит в аналитике");
|
||||
}
|
||||
if (/(?:уфк|ифнс|фнс|налог|пфр|фсс|сфр|казнач|бюджет|гос)/iu.test(text)) {
|
||||
if (/(?:уфк|ифнс|фнс|налог|пфр|фсс|сфр|казнач|бюджет|гос|департамент|министер|муницип|город москвы|федерал)/iu.test(text)) {
|
||||
scores.tax_or_state += 3;
|
||||
reasons.add("налог/госорган в аналитике");
|
||||
}
|
||||
@@ -525,6 +525,42 @@ function classifyPayablesLiabilityCategory(row, counterparty) {
|
||||
reasons: Array.from(reasons)
|
||||
};
|
||||
}
|
||||
const PAYABLES_CATEGORY_KEYS = ["supplier_or_contractor", "bank_or_credit", "tax_or_state", "other"];
|
||||
function resolvePayablesLiabilityCategory(scores) {
|
||||
let winner = "other";
|
||||
let best = Number.NEGATIVE_INFINITY;
|
||||
for (const key of PAYABLES_CATEGORY_KEYS) {
|
||||
const score = scores[key];
|
||||
if (score > best) {
|
||||
best = score;
|
||||
winner = key;
|
||||
}
|
||||
}
|
||||
if (best <= 0) {
|
||||
return "other";
|
||||
}
|
||||
return winner;
|
||||
}
|
||||
function hasPayablesSectionPrefix(account) {
|
||||
const section = extractAccountSectionCode(account);
|
||||
return section === "60" || section === "76";
|
||||
}
|
||||
function resolvePayablesAsOfDate(options) {
|
||||
const explicit = normalizeIsoDateOnly(options.asOfDate);
|
||||
if (explicit) {
|
||||
return explicit;
|
||||
}
|
||||
const periodTo = normalizeIsoDateOnly(options.periodTo);
|
||||
if (periodTo) {
|
||||
return periodTo;
|
||||
}
|
||||
const periodFrom = normalizeIsoDateOnly(options.periodFrom);
|
||||
if (periodFrom) {
|
||||
return periodFrom;
|
||||
}
|
||||
const now = new Date();
|
||||
return toIsoDate(now.getUTCFullYear(), now.getUTCMonth() + 1, now.getUTCDate());
|
||||
}
|
||||
function buildPayablesCounterpartyRiskAggregate(rows) {
|
||||
const byCounterparty = new Map();
|
||||
for (const row of rows) {
|
||||
@@ -574,26 +610,10 @@ function buildPayablesCounterpartyRiskAggregate(rows) {
|
||||
current.reasons.add(reason);
|
||||
}
|
||||
}
|
||||
const scoreKeys = ["supplier_or_contractor", "bank_or_credit", "tax_or_state", "other"];
|
||||
const toCategory = (scores) => {
|
||||
let winner = "other";
|
||||
let best = Number.NEGATIVE_INFINITY;
|
||||
for (const key of scoreKeys) {
|
||||
const score = scores[key];
|
||||
if (score > best) {
|
||||
best = score;
|
||||
winner = key;
|
||||
}
|
||||
}
|
||||
if (best <= 0) {
|
||||
return "other";
|
||||
}
|
||||
return winner;
|
||||
};
|
||||
return Array.from(byCounterparty.values())
|
||||
.map((item) => ({
|
||||
...item.base,
|
||||
category: toCategory(item.categoryScores),
|
||||
category: resolvePayablesLiabilityCategory(item.categoryScores),
|
||||
categoryReasons: Array.from(item.reasons).slice(0, 2)
|
||||
}))
|
||||
.sort((left, right) => {
|
||||
@@ -606,6 +626,88 @@ function buildPayablesCounterpartyRiskAggregate(rows) {
|
||||
return left.name.localeCompare(right.name);
|
||||
});
|
||||
}
|
||||
function buildPayablesConfirmedBalanceAggregate(rows, asOfDate) {
|
||||
const byCounterparty = new Map();
|
||||
const asOfTimestamp = toUtcDayTimestamp(asOfDate);
|
||||
for (const row of rows) {
|
||||
const name = extractCounterpartyName(row);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
const rowTimestamp = toUtcDayTimestamp(row.period);
|
||||
if (asOfTimestamp !== null && rowTimestamp !== null && rowTimestamp > asOfTimestamp) {
|
||||
continue;
|
||||
}
|
||||
const amount = row.amount;
|
||||
if (typeof amount !== "number" || !Number.isFinite(amount)) {
|
||||
continue;
|
||||
}
|
||||
const absAmount = Math.abs(amount);
|
||||
let delta = 0;
|
||||
if (hasPayablesSectionPrefix(row.account_kt)) {
|
||||
delta += absAmount;
|
||||
}
|
||||
if (hasPayablesSectionPrefix(row.account_dt)) {
|
||||
delta -= absAmount;
|
||||
}
|
||||
if (Math.abs(delta) <= 0.0000001) {
|
||||
continue;
|
||||
}
|
||||
const classified = classifyPayablesLiabilityCategory(row, name);
|
||||
const current = byCounterparty.get(name);
|
||||
if (!current) {
|
||||
byCounterparty.set(name, {
|
||||
outstandingAmount: delta,
|
||||
operations: 1,
|
||||
firstPeriod: row.period,
|
||||
lastPeriod: row.period,
|
||||
categoryScores: {
|
||||
supplier_or_contractor: classified.scores.supplier_or_contractor,
|
||||
bank_or_credit: classified.scores.bank_or_credit,
|
||||
tax_or_state: classified.scores.tax_or_state,
|
||||
other: classified.scores.other
|
||||
},
|
||||
reasons: new Set(classified.reasons)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
current.outstandingAmount += delta;
|
||||
current.operations += 1;
|
||||
if ((row.period ?? "") < (current.firstPeriod ?? "")) {
|
||||
current.firstPeriod = row.period;
|
||||
}
|
||||
if ((row.period ?? "") > (current.lastPeriod ?? "")) {
|
||||
current.lastPeriod = row.period;
|
||||
}
|
||||
current.categoryScores.supplier_or_contractor += classified.scores.supplier_or_contractor;
|
||||
current.categoryScores.bank_or_credit += classified.scores.bank_or_credit;
|
||||
current.categoryScores.tax_or_state += classified.scores.tax_or_state;
|
||||
current.categoryScores.other += classified.scores.other;
|
||||
for (const reason of classified.reasons) {
|
||||
current.reasons.add(reason);
|
||||
}
|
||||
}
|
||||
return Array.from(byCounterparty.entries())
|
||||
.map(([name, item]) => ({
|
||||
name,
|
||||
outstandingAmount: item.outstandingAmount,
|
||||
operations: item.operations,
|
||||
firstPeriod: item.firstPeriod,
|
||||
lastPeriod: item.lastPeriod,
|
||||
category: resolvePayablesLiabilityCategory(item.categoryScores),
|
||||
categoryReasons: Array.from(item.reasons).slice(0, 2)
|
||||
}))
|
||||
.filter((item) => item.outstandingAmount > 0.005)
|
||||
.sort((left, right) => {
|
||||
if (right.outstandingAmount !== left.outstandingAmount) {
|
||||
return right.outstandingAmount - left.outstandingAmount;
|
||||
}
|
||||
if (right.operations !== left.operations) {
|
||||
return right.operations - left.operations;
|
||||
}
|
||||
return left.name.localeCompare(right.name);
|
||||
});
|
||||
}
|
||||
function buildCounterpartyRiskAggregate(rows) {
|
||||
const byCounterparty = new Map();
|
||||
for (const row of rows) {
|
||||
@@ -1663,61 +1765,206 @@ function composeFactualReply(intent, rows, options = {}) {
|
||||
text: lines.join("\n")
|
||||
};
|
||||
}
|
||||
if (intent === "list_payables_counterparties") {
|
||||
const counterparties = buildPayablesCounterpartyRiskAggregate(rows);
|
||||
const scopeLine = (() => {
|
||||
const asOfDate = normalizeIsoDateOnly(options.asOfDate);
|
||||
if (asOfDate) {
|
||||
return `Дата среза: ${formatDateRu(asOfDate)}.`;
|
||||
}
|
||||
const periodFrom = normalizeIsoDateOnly(options.periodFrom);
|
||||
const periodTo = normalizeIsoDateOnly(options.periodTo);
|
||||
if (periodFrom || periodTo) {
|
||||
return `Период анализа: ${formatDateRu(periodFrom ?? "...")}..${formatDateRu(periodTo ?? "...")}.`;
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
if (intent === "payables_confirmed_as_of_date") {
|
||||
const payablesAsOfDate = resolvePayablesAsOfDate(options);
|
||||
const confirmedBalances = buildPayablesConfirmedBalanceAggregate(rows, payablesAsOfDate);
|
||||
const asOfDate = normalizeIsoDateOnly(options.asOfDate);
|
||||
const periodFrom = normalizeIsoDateOnly(options.periodFrom);
|
||||
const periodTo = normalizeIsoDateOnly(options.periodTo);
|
||||
const scopeLine = asOfDate
|
||||
? `- Дата среза: ${formatDateRu(asOfDate)}.`
|
||||
: periodFrom || periodTo
|
||||
? `- Период анализа: ${formatDateRu(periodFrom ?? "...")}..${formatDateRu(periodTo ?? "...")}.`
|
||||
: null;
|
||||
const carryoverLine = asOfDate || periodFrom || periodTo
|
||||
? "- В срез могут входить обязательства, возникшие до периода, если они оставались открытыми на дату среза."
|
||||
: null;
|
||||
const categoryCounts = confirmedBalances.reduce((acc, item) => {
|
||||
acc[item.category] += 1;
|
||||
return acc;
|
||||
}, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 });
|
||||
const lines = [
|
||||
"Коротко: собран shortlist кандидатов на ручную проверку по потенциально незакрытым обязательствам (контур 60/76).",
|
||||
"",
|
||||
"Что это значит:",
|
||||
"- Режим результата: эвристический скоринг по движениям.",
|
||||
"- Это не финальный подтвержденный остаток к оплате.",
|
||||
...(scopeLine ? ["", scopeLine] : []),
|
||||
"",
|
||||
`Строк в выборке: ${rows.length}.`,
|
||||
`Контрагентов-кандидатов: ${counterparties.length}.`
|
||||
"Блок 1. Статус результата",
|
||||
"- Режим результата: подтвержденный срез обязательств к оплате (exact route).",
|
||||
"- Эвристический shortlist в этом режиме не используется."
|
||||
];
|
||||
if (counterparties.length > 0) {
|
||||
const categoryCounts = counterparties.reduce((acc, item) => {
|
||||
acc[item.category] += 1;
|
||||
return acc;
|
||||
}, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 });
|
||||
lines.push("");
|
||||
lines.push("Категории обязательств:");
|
||||
lines.push(`- ${liabilityCategoryLabel("supplier_or_contractor")}: ${categoryCounts.supplier_or_contractor}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("bank_or_credit")}: ${categoryCounts.bank_or_credit}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("tax_or_state")}: ${categoryCounts.tax_or_state}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("other")}: ${categoryCounts.other}`);
|
||||
lines.push("");
|
||||
lines.push("Приоритет ручной проверки (по сумме/частоте сигналов):");
|
||||
lines.push(...counterparties
|
||||
.slice(0, 8)
|
||||
.map((item, index) => `${index + 1}. ${item.name} | категория: ${liabilityCategoryLabel(item.category)} | сумма сигнала: ${formatMoney(item.totalAmount)} | операций: ${item.operations}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""} | статус: требует ручной проверки${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}`));
|
||||
lines.push("");
|
||||
lines.push("Примеры исходных строк:");
|
||||
lines.push(...formatTopRows(rows, 4));
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(payablesAsOfDate)}.`);
|
||||
if (scopeLine) {
|
||||
lines.push(scopeLine);
|
||||
}
|
||||
lines.push("- Контур: обязательства по счетам 60/76.");
|
||||
if (carryoverLine) {
|
||||
lines.push(carryoverLine);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push(`- Строк в выборке: ${rows.length}.`);
|
||||
lines.push(`- Контрагентов с подтвержденным остатком к оплате: ${confirmedBalances.length}.`);
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории обязательств");
|
||||
lines.push(`- ${liabilityCategoryLabel("supplier_or_contractor")}: ${categoryCounts.supplier_or_contractor}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("bank_or_credit")}: ${categoryCounts.bank_or_credit}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("tax_or_state")}: ${categoryCounts.tax_or_state}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("other")}: ${categoryCounts.other}`);
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Подтвержденные позиции к оплате");
|
||||
if (confirmedBalances.length > 0) {
|
||||
lines.push(...confirmedBalances.slice(0, 10).map((item, index) => `${index + 1}. ${item.name} | категория: ${liabilityCategoryLabel(item.category)} | остаток: ${formatMoney(item.outstandingAmount)} | операций: ${item.operations}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}`));
|
||||
}
|
||||
else {
|
||||
lines.push("");
|
||||
lines.push("Явных кандидатов на незакрытые обязательства по текущему срезу не найдено.");
|
||||
lines.push("");
|
||||
lines.push("Примеры исходных строк:");
|
||||
lines.push(...formatTopRows(rows, 6));
|
||||
lines.push("- Подтвержденных открытых обязательств к оплате на дату среза не найдено.");
|
||||
}
|
||||
return {
|
||||
responseType: confirmedBalances.length > 0 ? "FACTUAL_LIST" : "FACTUAL_SUMMARY",
|
||||
text: lines.join("\n"),
|
||||
semantics: {
|
||||
result_mode: "confirmed_balance",
|
||||
evidence_strength: confirmedBalances.length > 0 ? "strong" : "medium",
|
||||
balance_confirmed: true
|
||||
}
|
||||
};
|
||||
}
|
||||
if (intent === "list_payables_counterparties") {
|
||||
const counterparties = buildPayablesCounterpartyRiskAggregate(rows);
|
||||
const payablesAsOfDate = resolvePayablesAsOfDate(options);
|
||||
const asOfDate = normalizeIsoDateOnly(options.asOfDate);
|
||||
const periodFrom = normalizeIsoDateOnly(options.periodFrom);
|
||||
const periodTo = normalizeIsoDateOnly(options.periodTo);
|
||||
const scopeLine = asOfDate
|
||||
? `- Дата среза: ${formatDateRu(asOfDate)}.`
|
||||
: periodFrom || periodTo
|
||||
? `- Период анализа: ${formatDateRu(periodFrom ?? "...")}..${formatDateRu(periodTo ?? "...")}.`
|
||||
: null;
|
||||
const carryoverLine = asOfDate || periodFrom || periodTo
|
||||
? "- В список могут попадать обязательства, возникшие раньше выбранного периода, если они потенциально оставались открытыми на дату среза."
|
||||
: null;
|
||||
const formatHeuristicItem = (item, index) => `${index + 1}. ${item.name} | сумма сигнала: ${formatMoney(item.totalAmount)} | операций: ${item.operations}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}`;
|
||||
const pushCategorySlice = (lines, title, items, limit) => {
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
lines.push("");
|
||||
lines.push(title);
|
||||
lines.push(...items.slice(0, limit).map(formatHeuristicItem));
|
||||
};
|
||||
const buildHeuristicLines = (forcedFallbackFromConfirmed) => {
|
||||
const lines = [
|
||||
"Блок 1. Статус результата",
|
||||
forcedFallbackFromConfirmed
|
||||
? "- Режим результата: эвристический скоринг в рамках fallback, потому что подтвержденный срез обязательств к оплате недоступен."
|
||||
: "- Режим результата: эвристический скоринг (shortlist кандидатов по признакам незакрытых обязательств в контуре 60/76).",
|
||||
"- Тип результата: кандидаты для ручной проверки, а не финальный платежный реестр.",
|
||||
"",
|
||||
"Блок 2. Как читать результат",
|
||||
"- Это shortlist кандидатов: нужна ручная проверка бухгалтером.",
|
||||
"- Это не подтвержденный остаток к оплате и не готовое платежное поручение.",
|
||||
...(scopeLine ? [scopeLine] : []),
|
||||
...(carryoverLine ? [carryoverLine] : []),
|
||||
"",
|
||||
"Блок 3. Сводка выборки",
|
||||
`- Строк в выборке: ${rows.length}.`,
|
||||
`- Контрагентов-кандидатов: ${counterparties.length}.`
|
||||
];
|
||||
if (counterparties.length > 0) {
|
||||
const categoryCounts = counterparties.reduce((acc, item) => {
|
||||
acc[item.category] += 1;
|
||||
return acc;
|
||||
}, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 });
|
||||
const suppliers = counterparties.filter((item) => item.category === "supplier_or_contractor");
|
||||
const banks = counterparties.filter((item) => item.category === "bank_or_credit");
|
||||
const taxOrState = counterparties.filter((item) => item.category === "tax_or_state");
|
||||
const other = counterparties.filter((item) => item.category === "other");
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории обязательств");
|
||||
lines.push(`- ${liabilityCategoryLabel("supplier_or_contractor")}: ${categoryCounts.supplier_or_contractor}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("bank_or_credit")}: ${categoryCounts.bank_or_credit}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("tax_or_state")}: ${categoryCounts.tax_or_state}`);
|
||||
lines.push(`- ${liabilityCategoryLabel("other")}: ${categoryCounts.other}`);
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Кандидаты на проверку в первую очередь");
|
||||
pushCategorySlice(lines, "5.1 Поставщики/подрядчики:", suppliers, 6);
|
||||
pushCategorySlice(lines, "5.2 Банки/кредиты:", banks, 4);
|
||||
pushCategorySlice(lines, "5.3 Налоги/госорганы:", taxOrState, 4);
|
||||
pushCategorySlice(lines, "5.4 Прочие:", other, 4);
|
||||
lines.push("");
|
||||
lines.push("Блок 6. Примеры исходных строк");
|
||||
lines.push(...formatTopRows(rows, 4));
|
||||
}
|
||||
else {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории обязательств");
|
||||
lines.push("- Явных кандидатов на незакрытые обязательства по доступному срезу не найдено.");
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Примеры исходных строк");
|
||||
lines.push(...formatTopRows(rows, 6));
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
if (options.requestedResultMode === "confirmed_balance") {
|
||||
const confirmedBalances = buildPayablesConfirmedBalanceAggregate(rows, payablesAsOfDate);
|
||||
if (confirmedBalances.length > 0) {
|
||||
const categoryCounts = confirmedBalances.reduce((acc, item) => {
|
||||
acc[item.category] += 1;
|
||||
return acc;
|
||||
}, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 });
|
||||
const lines = [
|
||||
"Блок 1. Статус результата",
|
||||
"- Режим результата: подтвержденный срез обязательств к оплате по состоянию на дату среза в контуре 60/76.",
|
||||
"- Тип результата: подтвержденные остатки к оплате.",
|
||||
"",
|
||||
"Блок 2. Что учтено",
|
||||
`- Дата среза: ${formatDateRu(payablesAsOfDate)}.`,
|
||||
...(periodFrom || periodTo
|
||||
? [`- Период анализа: ${formatDateRu(periodFrom ?? "...")}..${formatDateRu(periodTo ?? "...")}.`]
|
||||
: []),
|
||||
"- Основание: движения обязательств и оплат в пределах доступного live-среза.",
|
||||
...(carryoverLine ? [carryoverLine] : []),
|
||||
"",
|
||||
"Блок 3. Сводка выборки",
|
||||
`- Строк в выборке: ${rows.length}.`,
|
||||
`- Контрагентов с подтвержденным остатком: ${confirmedBalances.length}.`,
|
||||
"",
|
||||
"Блок 4. Категории обязательств",
|
||||
`- ${liabilityCategoryLabel("supplier_or_contractor")}: ${categoryCounts.supplier_or_contractor}`,
|
||||
`- ${liabilityCategoryLabel("bank_or_credit")}: ${categoryCounts.bank_or_credit}`,
|
||||
`- ${liabilityCategoryLabel("tax_or_state")}: ${categoryCounts.tax_or_state}`,
|
||||
`- ${liabilityCategoryLabel("other")}: ${categoryCounts.other}`,
|
||||
"",
|
||||
"Блок 5. Кому нужно заплатить в первую очередь (по сумме остатка):",
|
||||
...confirmedBalances.slice(0, 10).map((item, index) => `${index + 1}. ${item.name} | категория: ${liabilityCategoryLabel(item.category)} | остаток к оплате: ${formatMoney(item.outstandingAmount)} | операций в срезе: ${item.operations}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}`)
|
||||
];
|
||||
return {
|
||||
responseType: "FACTUAL_LIST",
|
||||
text: lines.join("\n"),
|
||||
semantics: {
|
||||
result_mode: "confirmed_balance",
|
||||
evidence_strength: "strong",
|
||||
balance_confirmed: true
|
||||
}
|
||||
};
|
||||
}
|
||||
const fallbackLines = buildHeuristicLines(true);
|
||||
return {
|
||||
responseType: "FACTUAL_LIST",
|
||||
text: fallbackLines.join("\n"),
|
||||
semantics: {
|
||||
result_mode: "heuristic_candidates",
|
||||
evidence_strength: counterparties.length > 0 ? "medium" : "weak",
|
||||
balance_confirmed: false
|
||||
}
|
||||
};
|
||||
}
|
||||
const lines = buildHeuristicLines(false);
|
||||
return {
|
||||
responseType: "FACTUAL_LIST",
|
||||
text: lines.join("\n")
|
||||
text: lines.join("\n"),
|
||||
semantics: {
|
||||
result_mode: "heuristic_candidates",
|
||||
evidence_strength: counterparties.length > 0 ? "medium" : "weak",
|
||||
balance_confirmed: false
|
||||
}
|
||||
};
|
||||
}
|
||||
if (intent === "list_receivables_counterparties") {
|
||||
|
||||
@@ -350,7 +350,9 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intent === "open_items_by_counterparty_or_contract" || intent === "list_open_contracts") {
|
||||
if (intent === "open_items_by_counterparty_or_contract" ||
|
||||
intent === "list_open_contracts" ||
|
||||
intent === "payables_confirmed_as_of_date") {
|
||||
const inheritedContract = previousContract ?? (followupContext.previous_anchor_type === "contract" ? previousAnchorValue : null);
|
||||
const currentContract = toNonEmptyString(merged.contract);
|
||||
const shouldInheritContract = !currentContract ||
|
||||
@@ -370,6 +372,13 @@ function mergeFollowupFilters(current, intent, userMessage, followupContext) {
|
||||
merged.counterparty = inheritedCounterparty;
|
||||
reasons.push(currentCounterparty ? "counterparty_replaced_from_followup_context" : "counterparty_from_followup_context");
|
||||
}
|
||||
if (sameDateRequested) {
|
||||
const inheritedAsOfDate = previousAsOfDate ?? previousPeriodTo ?? previousPeriodFrom;
|
||||
if (inheritedAsOfDate && merged.as_of_date !== inheritedAsOfDate) {
|
||||
merged.as_of_date = inheritedAsOfDate;
|
||||
reasons.push("as_of_date_from_followup_context");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allTimeRequested) {
|
||||
if (toNonEmptyString(merged.period_from) || toNonEmptyString(merged.period_to)) {
|
||||
@@ -424,6 +433,7 @@ function resolveMissingRequiredFilters(intent, filters) {
|
||||
const requiredByIntent = {
|
||||
account_balance_snapshot: ["account", "as_of_date"],
|
||||
documents_forming_balance: ["account", "as_of_date"],
|
||||
payables_confirmed_as_of_date: ["as_of_date"],
|
||||
list_documents_by_counterparty: ["counterparty"],
|
||||
bank_operations_by_counterparty: ["counterparty"],
|
||||
list_contracts_by_counterparty: ["counterparty"],
|
||||
|
||||
+3
-1
@@ -91,7 +91,9 @@ function inferAggregationProfile(intent, shape) {
|
||||
intent === "vat_payable_forecast") {
|
||||
return "management_profile";
|
||||
}
|
||||
if (intent === "account_balance_snapshot" || intent === "documents_forming_balance") {
|
||||
if (intent === "account_balance_snapshot" ||
|
||||
intent === "documents_forming_balance" ||
|
||||
intent === "payables_confirmed_as_of_date") {
|
||||
return "balance_snapshot";
|
||||
}
|
||||
if (intent === "open_items_by_counterparty_or_contract" ||
|
||||
|
||||
Reference in New Issue
Block a user