Укрепить банковские границы денежных потоков phase97

This commit is contained in:
2026-05-13 01:53:05 +03:00
parent e997449785
commit 04e670ab76
21 changed files with 861 additions and 12 deletions
@@ -277,6 +277,77 @@ function isDirectBalanceQuestion(userMessage) {
}
return /(?:кто|кому|сколько|какой|какая|какие|есть\s+ли|долж|дебитор|кредитор|payables?|receivables?|who|how\s+much)/iu.test(text);
}
function hasBankIncomingRoleBoundaryQuestion(userMessage) {
const text = normalizeQuestionText(userMessage);
return (/(?:входящ|поступлен|клиентск|выручк|кредит|депозит|возврат)/iu.test(text) &&
/(?:банк|сбербанк|финанс)/iu.test(text));
}
function hasBankOutgoingRoleBoundaryQuestion(userMessage) {
const text = normalizeQuestionText(userMessage);
return (/(?:исходящ|списан|платеж|поставщик|закуп|выплат)/iu.test(text) &&
/(?:банк|сбербанк|финанс)/iu.test(text));
}
function bankOperationDirection(row) {
const text = normalizeQuestionText(`${row.registrator} ${row.operation_kind ?? ""}`);
if (/(?:поступлени[ея]\s+на\s+расчетн|bank\s+receipt|incoming)/iu.test(text)) {
return "incoming";
}
if (/(?:списани[ея]\s+с\s+расчетн|bank\s+payment|outgoing|write[-\s]?off)/iu.test(text)) {
return "outgoing";
}
return "unknown";
}
function bankOperationDirectionLabel(direction) {
if (direction === "incoming") {
return "входящее поступление";
}
if (direction === "outgoing") {
return "исходящее списание";
}
return "банковская операция без надежно распознанного направления";
}
function bankOperationEvidenceLine(rows) {
const sample = rows[0];
if (!sample) {
return "Проверенная строка 1С не найдена.";
}
const direction = bankOperationDirection(sample);
const parts = [`тип по документу: ${bankOperationDirectionLabel(direction)}`];
const operationKind = String(sample.operation_kind ?? "").trim();
const paymentPurpose = String(sample.payment_purpose ?? "").trim();
const contract = String(sample.contract ?? "").trim();
if (operationKind) {
parts.push(`вид операции: ${operationKind}`);
}
if (paymentPurpose) {
parts.push(`назначение платежа: ${paymentPurpose}`);
}
if (contract) {
parts.push(`договор: ${contract}`);
}
if (!operationKind && !paymentPurpose && !contract) {
parts.push("вид операции/назначение платежа/договор в материализованной строке не заполнены");
}
return `Основание 1С: ${parts.join("; ")}.`;
}
function bankRoleBoundaryLine(userMessage, rows) {
const incomingBoundary = hasBankIncomingRoleBoundaryQuestion(userMessage);
const outgoingBoundary = hasBankOutgoingRoleBoundaryQuestion(userMessage);
if (!incomingBoundary && !outgoingBoundary) {
return null;
}
const directions = rows.map(bankOperationDirection);
const hasIncomingRow = directions.includes("incoming");
const hasOutgoingRow = directions.includes("outgoing");
if (incomingBoundary) {
return hasIncomingRow
? "Выручкой от обычного клиента это не называю автоматически: для банка/финорганизации нужен вид операции, назначение платежа и договор; кредитный, депозитный или возвратный смысл без этих полей не исключаю и не притягиваю."
: hasOutgoingRow
? "В найденных строках по банку подтверждено исходящее списание, а входящее поступление от банка в этом срезе не подтверждено; клиентскую выручку, кредит или депозит по этой строке не доказываю."
: "Входящее поступление от банка в найденных строках не подтверждено; клиентскую выручку, кредитный или депозитный смысл без вида операции/назначения платежа не доказываю.";
}
return "Обычным поставщиком это не называю автоматически: для банка/финорганизации нужен вид операции, назначение платежа и договор; текущий срез подтверждает банковский платежный контур, а не бизнес-роль поставщика.";
}
function hasInventoryPurchaseDateActionFocus(userMessage) {
const text = normalizeQuestionText(userMessage);
if (!text) {
@@ -3820,9 +3891,15 @@ function composeFactualReplyBody(intent, rows, options = {}) {
};
}
if (intent === "bank_operations_by_counterparty") {
const rowCounterparties = uniqueStrings(rows
.map((row) => extractCounterpartyName(row))
.filter((item) => Boolean(item)));
const counterparty = resolvePreferredCounterpartyDisplayLabel(options.counterpartyHint, rowCounterparties);
const roleBoundary = bankRoleBoundaryLine(options.userMessage, rows);
const lines = [
`Коротко: найдено банковских операций по контрагенту — ${rows.length}.`,
"Показываю подтвержденные банковские операции из текущего среза.",
`Коротко: найдено банковских операций${counterparty ? ` по ${counterparty}` : " по контрагенту"} — ${rows.length}.`,
roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.",
bankOperationEvidenceLine(rows),
...formatTopRows(rows, rows.length)
];
return {