"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveAddressIntent = resolveAddressIntent; const inventoryLifecycleCueHelpers_1 = require("./inventoryLifecycleCueHelpers"); const RECEIVABLES_STRONG = [ "кто должен нам", "кто нам должен", "кто нам должэн", "нам должны", "нам должен", "нам должэны", "who owes us", "receivable", "receivables", "debtor", "debtors", "дебитор", "дебиторск" ]; const PAYABLES_STRONG = [ "кому должны мы", "кому должэны мы", "кому мы должны", "кому мы должэны", "мы должны", "мы должэны", "who we owe", "payable", "payables", "creditor", "creditors", "кредитор", "кредиторск" ]; const ACCOUNT_BALANCE_HINTS = [ "account balance", "balance by account", "saldo", "баланс", "остаток по счет", "сальдо по счет", "по счету", "что на счете", "что на счёте", "на конец" ]; const DOCUMENTS_FORMING_BALANCE_HINTS = [ "documents forming balance", "docs forming balance", "documents form balance", "docs form balance", "balance documents", "documents for balance", "which documents form balance", "из чего состоит остаток", "какие документы формируют остаток", "раскрой остаток по документам", "документы под остатком" ]; const OPEN_CONTRACTS_HINTS = [ "open contracts", "unclosed contracts", "незакрыт", "не закрыт", "открыт", "договор", "контракт" ]; const OPEN_ITEMS_HINTS = [ "open items", "unclosed items", "хвост", "висят", "незакрыт", "открыт", "долг", "задолж", "позици" ]; const DOCUMENTS_BY_COUNTERPARTY_HINTS = [ "documents by counterparty", "docs by counterparty", "documents by company", "documents by supplier", "documents by customer", "documents by client", "documents by partner", "show documents by counterparty", "list documents by counterparty", "документы по", "доступные документы", "список документов", "документ", "доки", "доки по", "док по", "doki", "docy", "doci", "по контрагент" ]; const BANK_OPERATIONS_BY_COUNTERPARTY_HINTS = [ "bank operations by counterparty", "bank payments by counterparty", "payment orders by counterparty", "bank operations by company", "bank operations by supplier", "bank operations by customer", "show bank operations by counterparty", "bank ops", "bank oper", "transactions by counterparty", "транзак", "банк", "банков", "по банку", "опер", "выписк", "платеж", "платёж", "оплат", "списан", "списани", "поступлен", "поступлени", "движени" ]; const DOCUMENTS_BY_CONTRACT_HINTS = [ "documents by contract", "docs by contract", "show documents by contract", "list documents by contract", "документы по договору", "доки по договору", "док по договору", "документы договор", "договор", "документы по контракту", "доки по контракту", "контракт" ]; const BANK_OPERATIONS_BY_CONTRACT_HINTS = [ "bank operations by contract", "bank payments by contract", "payment orders by contract", "transactions by contract", "bank ops by contract", "банковские операции по договору", "платежи по договору", "выписка по договору", "банковские операции по контракту", "платежи по контракту", "выписка по контракту" ]; const BANK_OPERATION_CORE_HINTS = [ "банк", "банков", "операц", "опер", "выписк", "платеж", "платёж", "оплат", "списан", "поступлен", "движени", "транзак", "bank", "payment", "payments", "transaction", "transactions", "statement", "wire" ]; const PERIOD_COVERAGE_PROFILE_HINTS = [ "за какие годы", "за какие года", "в базе есть данные", "покрытие периодов", "диапазон лет", "профиль данных", "самый активный год", "самый активный месяц", "самый пассивный год", "самый пассивный месяц", "наименее активный год", "наименее активный месяц", "минимум документов по году", "минимум операций по месяцу", "год с минимальным количеством документов", "месяц с минимальным количеством операций", "активный год по количеству документов", "активный месяц по количеству операций", "most active year", "most active month", "least active year", "least active month", "year coverage", "data coverage" ]; const DOCUMENT_TYPE_AND_ACCOUNT_SECTION_PROFILE_HINTS = [ "типы документов", "типы доков", "документы чаще всего", "документы реже всего", "редкие типы документов", "наименее используемые типы документов", "частые типы документов", "сводка по типам документов", "доля типов документов", "разделы учета", "разделы учёта", "наиболее заполнены", "наименее заполнены", "почти не используются", "account section", "document types usage", "document type profile" ]; const COUNTERPARTY_POPULATION_AND_ROLES_HINTS = [ "сколько всего контрагентов", "сколько уникальных контрагентов", "сколько контрагентов в базе", "сколько заказчиков", "сколько поставщиков", "сколько клиентов", "сколько покупателей", "скока всего контрагентов", "скока уникальных контрагентов", "скока контрагентов в базе", "скока заказчиков", "скока поставщиков", "скока клиентов", "скока покупателей", "скок контрагентов", "скок контрагентов в базе", "скок заказчиков", "скок поставщиков", "скок клиентов", "скок покупателей", "сколько смешанных контрагентов", "типы контрагентов", "разбей контрагентов", "раздели контрагентов", "counterparty population", "counterparty roles", "customer supplier split" ]; const COUNTERPARTY_ACTIVITY_LIFECYCLE_HINTS = [ "какие заказчики работали", "какие заказчики активны", "какие клиенты работали", "какие клиенты активны", "какие контрагенты работали", "какие поставщики работали", "список заказчиков", "список клиентов", "список заказчиков за все время", "список клиентов за все время", "список активных заказчиков", "список активных клиентов", "новые заказчики", "новые клиенты", "новые контрагенты", "впервые в", "кто исчез", "кто ушел", "кто ушёл", "только один раз", "дольше всего", "дольше всех", "долгоживущие контрагенты", "регулярные поставщики", "эпизодические поставщики", "давно не использовались поставщики", "всех заков", "кто был активен", "потом отвалился", "ровно один раз", "и пропал", "самые старые по сотрудничеству", "разбей поставщиков на регуляр и разовые", "кто новые в этом году", "active customers", "сколько лет активности в базе", "сколько лет активности в 1с", "сколько лет в базе 1с", "какой первый платеж", "какое первое поступление", "когда была первая активность", "когда была последняя активность", "первая активность в базе", "последняя активность в базе", "customer activity list", "counterparty lifecycle" ]; const CONTRACT_USAGE_OVERVIEW_HINTS = [ "сколько всего договоров", "сколько договоров заведено", "сколько договоров в базе", "сколько договоров использовались", "сколько договоров использовалось", "договоры total vs used", "обзор договорной базы", "договорная база total used", "неиспользуемые договоры", "давно не использовались договоры", "мертвые договоры", "мёртвые договоры", "stale contracts", "unused contracts", "contracts total used", "contract usage overview" ]; const CUSTOMER_REVENUE_AND_PAYMENTS_HINTS = [ "самые доходные клиенты", "самые доходные заказчики", "самые ликвидные клиенты", "самые ликвидные заказчики", "самых ликвидних заказчиков", "топ клиентов по сумме поступлений", "топ заказчиков по сумме поступлений", "кто больше всего принес денег", "кто больше всего принёс денег", "кто принес больше всего денег", "кто принёс больше всего денег", "кто нам больше денег принес", "кто нам больше денег принёс", "кто нам принес больше денег", "кто нам принёс больше денег", "кто нам больше всего занес", "кто нам больше всего занёс", "кто нам принес больше всего", "кто нам принёс больше всего", "кто платит чаще всего", "средний чек клиентов", "средний чек заказчиков", "крупные сделки по поступлениям", "маленькие сделки по поступлениям", "smallest deals by inflow", "largest deals by inflow", "top customers by inflow", "top customers by revenue" ]; const SUPPLIER_PAYOUTS_PROFILE_HINTS = [ "топ поставщиков по сумме выплат", "кому мы больше всего заплатили", "кому ушло больше всего денег", "кому мы больше всего сгрузили денег", "поставщики по выплатам", "поставщики по исходящим платежам", "поставщики с максимальным числом выплат", "крупные разовые выплаты поставщикам", "top suppliers by payouts", "top suppliers by outgoing payments" ]; const CONTRACT_USAGE_AND_VALUE_HINTS = [ "договоры по обороту", "договоры по сумме оборота", "топ договоров по обороту", "контракты по обороту", "контракты по сумме оборота", "топ контрактов по обороту", "договоры с минимальным бюджетом", "договоры с самым маленьким бюджетом", "контракты с минимальным бюджетом", "контракты с самым маленьким бюджетом", "активные договоры по бюджету", "активные контракты по бюджету", "контрагенты с несколькими договорами", "несколько договоров у контрагента", "мультидоговорные контрагенты", "какие договоры активны", "какие контракты активны", "рабочие договоры", "рабочие контракты", "contracts by turnover", "contracts by budget" ]; const CONTRACT_LIST_BY_COUNTERPARTY_HINTS = [ "договоры по", "договора по", "список договоров по", "покажи договоры по", "выведи договоры по", "контракты по", "список контрактов по", "покажи контракты по", "выведи контракты по", "contracts by counterparty", "list contracts by counterparty", "show contracts by counterparty" ]; function hasAny(text, patterns) { return patterns.some((item) => text.includes(item)); } function hasFlexibleReceivablesDebtSignal(text) { const normalized = String(text ?? ""); if (!normalized) { return false; } const hasFlexibleWhoOwesUs = /(?:\u043a\u0442\u043e(?:\s+\S+){0,4}\s+\u043d\u0430\u043c(?:\s+\S+){0,4}\s+\u0434\u043e\u043b\u0436)/iu.test(normalized) || /(?:\u043d\u0430\u043c(?:\s+\S+){0,4}\s+\u043a\u0442\u043e(?:\s+\S+){0,4}\s+\u0434\u043e\u043b\u0436)/iu.test(normalized); const hasTorchatToUsSignal = /(?:\u043d\u0430\u043c(?:\s+\S+){0,3}\s+\u0442\u043e\u0440\u0447(?:\u0430\u0442|\u0438\u0442)|\u0442\u043e\u0440\u0447(?:\u0430\u0442|\u0438\u0442)(?:\s+\S+){0,3}\s+\u043d\u0430\u043c)/iu.test(normalized); return hasFlexibleWhoOwesUs || hasTorchatToUsSignal; } function hasFlexiblePayablesDebtSignal(text) { const normalized = String(text ?? ""); if (!normalized) { return false; } return (/(?:кому(?:\s+\S+){0,4}\s+мы(?:\s+\S+){0,4}\s+долж)/iu.test(normalized) || /(?:мы(?:\s+\S+){0,4}\s+кому(?:\s+\S+){0,4}\s+долж)/iu.test(normalized)); } function tokenizeText(text) { return String(text ?? "") .toLowerCase() .split(/[^a-zа-яё0-9]+/iu) .map((token) => token.trim()) .filter((token) => token.length > 0); } function trimRussianEnding(token) { return token.replace(/(?:иями|ями|ами|ого|ему|ому|ыми|ими|ией|ей|ий|ый|ой|ях|ах|ов|ев|ам|ям|ом|ем|ы|и|а|я|у|ю|е|о)$/u, ""); } function normalizeLexemeToken(rawToken) { const token = String(rawToken ?? "").toLowerCase().replace(/[^a-zа-яё0-9]+/gu, ""); if (!token) { return ""; } if (/^[a-z0-9]+$/u.test(token)) { return token; } return trimRussianEnding(token); } function levenshteinDistance(a, b) { if (a === b) { return 0; } if (!a.length) { return b.length; } if (!b.length) { return a.length; } const prev = new Array(b.length + 1); const curr = new Array(b.length + 1); for (let j = 0; j <= b.length; j += 1) { prev[j] = j; } for (let i = 1; i <= a.length; i += 1) { curr[0] = i; for (let j = 1; j <= b.length; j += 1) { const cost = a[i - 1] === b[j - 1] ? 0 : 1; curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost); } for (let j = 0; j <= b.length; j += 1) { prev[j] = curr[j]; } } return prev[b.length]; } function hasFuzzyLexeme(text, lexemeRoots) { const normalizedRoots = lexemeRoots .map((root) => normalizeLexemeToken(root)) .filter((root) => root.length > 0); if (normalizedRoots.length === 0) { return false; } const tokens = tokenizeText(text) .map((token) => normalizeLexemeToken(token)) .filter((token) => token.length >= 4); for (const token of tokens) { for (const root of normalizedRoots) { if (token.includes(root)) { return true; } if (root.includes(token) && token.length >= 5) { return true; } const maxDistance = root.length >= 7 ? 2 : 1; if (Math.abs(token.length - root.length) > maxDistance) { continue; } if (levenshteinDistance(token, root) <= maxDistance) { return true; } } } return false; } function hasCompactAccountCodeToken(text) { // Match compact account tokens while reducing false positives on short-year literals like "22 год". const source = String(text ?? ""); if (!source) { return false; } // Safe compact form: 60.01 / 62.1 if (/(? token.replace(/^[^\p{L}\p{N}#№]+|[^\p{L}\p{N}./_-]+$/gu, "").trim()) .filter((token) => token.length > 0); for (const rawToken of rawTokens) { const token = String(rawToken ?? "").trim(); if (!/^\d{1,6}[./_-]\d{1,6}(?:[./_-]\d{1,6})?$/u.test(token)) { continue; } if (!token) { continue; } if (/^\d{1,2}\.\d{1,2}$/u.test(token)) { // Likely an account code like 60.01/51.00, not a contract number. continue; } const parts = token.split(/[./_-]+/u).map((part) => Number(part)); if (!parts.every((part) => Number.isFinite(part))) { return true; } if (parts.length === 2) { const [a, b] = parts; const yearFirst = a >= 1900 && a <= 2099 && b >= 1 && b <= 12; const yearSecond = b >= 1900 && b <= 2099 && a >= 1 && a <= 12; if (yearFirst || yearSecond) { continue; } return true; } if (parts.length === 3) { const [a, b, c] = parts; const ymd = a >= 1900 && a <= 2099 && b >= 1 && b <= 12 && c >= 1 && c <= 31; const dmy = c >= 1900 && c <= 2099 && a >= 1 && a <= 31 && b >= 1 && b <= 12; if (ymd || dmy) { continue; } return true; } return true; } return false; } function hasContractAnchorSignal(text) { if (hasContractAnchorMention(text)) { return true; } // Allow short forms like "19/15" for follow-up prompts if document/bank signal exists. return hasContractNumberLikeToken(text) && hasDocsOrBankSignal(text); } function hasLooseByAnchorMention(text) { const match = text.match(/(?:^|\s)по\s+([a-zа-яё][a-zа-яё0-9._-]{1,})(?=[\s,.;:!?)]|$)/iu); if (!match) { return false; } const token = String(match[1] ?? "").toLowerCase(); if (!token) { return false; } const stopWords = new Set([ "контрагенту", "контрагента", "контре", "компании", "компанию", "организации", "организацию", "поставщику", "поставщика", "клиенту", "клиента", "покупателю", "покупателя", "партнеру", "партнера", "договору", "договора", "счету", "счёту", "дате", "периоду", "период", "документам", "докам", "количество", "количеству", "количества", "количеством", "активности", "пассивности", "наименее", "минимум" ]); return !stopWords.has(token); } function hasImplicitCounterpartyAnchorAroundDocs(text) { const beforeDocsMatch = text.match(/(?:^|\s)([a-zа-яё][a-zа-яё0-9._-]{1,})\s+(?:док(?:и|ум(?:ент(?:ы|ов|ам|а)?)?)|docs?|documents?|doki|docy|doci)(?=[\s,.;:!?)]|$)/iu); if (beforeDocsMatch && isLikelyCounterpartyToken(String(beforeDocsMatch[1] ?? ""))) { return true; } const afterDocsMatch = text.match(/(?:док(?:и|ум(?:ент(?:ы|ов|ам|а)?)?)|docs?|documents?|doki|docy|doci)\s+(?:по\s+)?([a-zа-яё][a-zа-яё0-9._-]{1,})(?=[\s,.;:!?)]|$)/iu); if (afterDocsMatch && isLikelyCounterpartyToken(String(afterDocsMatch[1] ?? ""))) { return true; } return false; } function hasDocsOrBankSignal(text) { return /(?:док(?:и|умент|ументы|ументов)|docs?|documents?|doki|docy|doci|банк|выписк|платеж|платёж|оплат|transactions?|bank\s+ops|bank\s+operations?)/iu.test(text); } function hasBankOperationSignal(text) { return hasAny(text, BANK_OPERATION_CORE_HINTS) || hasAny(text, BANK_OPERATIONS_BY_COUNTERPARTY_HINTS) || hasAny(text, BANK_OPERATIONS_BY_CONTRACT_HINTS); } function hasDocumentSignal(text) { return (text.includes("док") || text.includes("доки") || text.includes("документ") || text.includes("doki") || text.includes("docy") || text.includes("doci") || text.includes("docs") || text.includes("documents")); } function hasHeuristicCounterpartyAnchor(text) { if (!hasDocsOrBankSignal(text) && !hasBankOperationSignal(text)) { return false; } const tokens = String(text ?? "") .split(/[^a-zа-яё0-9._-]+/iu) .map((item) => item.trim()) .filter((item) => item.length > 0); for (const token of tokens) { const lowered = token.toLowerCase(); if (!isLikelyCounterpartyToken(lowered)) { continue; } if (/^\d{2}$/.test(lowered) || /^\d{4}$/.test(lowered)) { continue; } if (/(?:^за$|^for$|^from$|^to$|^по$|^с$|^год$|^года$|^г$|^year$)/iu.test(lowered)) { continue; } return true; } return false; } function hasGenericAddressLookupSignal(text) { return (/\bесть\b/iu.test(text) || /\bпокажи\b/iu.test(text) || /\bвыведи\b/iu.test(text) || /\bкакие\b/iu.test(text) || /\bчто(?:-|\s)?то\b/iu.test(text) || /за\s+любой\s+период/iu.test(text) || /за\s+вс[её]\s+время/iu.test(text) || /for\s+all\s+time/iu.test(text) || /all\s+time/iu.test(text)); } function hasAccountNumberAnchor(text) { return /(?:account|сч[её]т|счет)\D{0,12}\d{2}(?:[.,]\d{1,2})?/i.test(text); } function hasInventoryAccount41Anchor(text) { return /(?:сч[её]т(?:а|е|у)?|счет(?:а|е|у)?)\D{0,12}41(?:[.,]0?1)?/iu.test(text) || /41(?:[.,]0?1)?\D{0,12}(?:сч[её]т(?:а|е|у)?|счет(?:а|е|у)?)/iu.test(text); } function hasInventoryAsOfCue(text) { return /(?:сейчас|текущ|на\s+дату|по\s+состоянию|срез|на\s+конец|date|as\s+of|current|now|today)/iu.test(text); } function hasInventoryOnHandSignal(text) { const hasColloquialStockSnapshotCue = /(?:что|ч[еёо])\s+(?:у\s+нас\s+)?на\s+склад(?:е|у|ом|ах)(?=$|[\s,.;:!?])/iu.test(text); const hasStockStateCue = /(?:(?:что|ч[еёо])\s+там\s+на\s+склад(?:е|у|ом|ах)|(?:что|ч[еёо]).*происход(?:ит|ило|ящее).*(?:на\s+)?склад(?:е|у|ом|ах)|происход(?:ит|ило|ящее)\s+на\s+склад(?:е|у|ом|ах)|ситуац(?:ия|ии)\s+на\s+склад(?:е|у|ом|ах)|обстановк(?:а|и)\s+на\s+склад(?:е|у|ом|ах)|what(?:'s| is)?\s+(?:there\s+)?(?:on|in)\s+(?:the\s+)?(?:warehouse|stock)|what(?:'s| is)?\s+happening\s+(?:on|in)\s+(?:the\s+)?(?:warehouse|stock))/iu.test(text); const hasAccount41Anchor = hasInventoryAccount41Anchor(text); const hasStockLexeme = /(?:склад(?:е|у|ом|ы|ов)?|warehouse|stock(?:room)?|inventory|on[\s-]?hand)/iu.test(text); if (!hasStockLexeme && !hasAccount41Anchor) { return false; } if (hasInventoryProvenanceSignalV2(text) || hasInventoryPurchaseDocumentsSignalV2(text) || hasInventorySaleTraceSignalV2(text) || hasInventoryAgingSignal(text) || hasInventoryPurchaseToSaleChainSignal(text)) { return false; } const hasGoodsLexeme = /(?:товар(?:ы|ов|ом|а|ные)?|номенклатур|материал(?:ы|ов|а|ам)?|item(?:s)?|sku|product(?:s)?)/iu.test(text); const hasBalanceLexeme = /(?:леж(?:ит|ат)|есть|числ(?:ит(?:ся|сь)|ятся)|остат(?:ок|ки)|срез|на\s+дат|по\s+состоянию|на\s+конец|происход(?:ит|ило|ящее)|ситуац(?:ия|ии)|обстановк(?:а|и)|today|now|current|as\s+of)/iu.test(text); const hasRequestCue = /(?:покажи|показать|выведи|дай|какие|что|ч[еёо]|какой|сколько|проверь|проверить|чекни|check|show|list|which|what)/iu.test(text); if (hasAccount41Anchor && (hasGoodsLexeme || hasBalanceLexeme || hasRequestCue || hasInventoryAsOfCue(text))) { return true; } return (hasGoodsLexeme || hasBalanceLexeme || hasColloquialStockSnapshotCue || hasStockStateCue) && (hasRequestCue || hasBalanceLexeme || hasColloquialStockSnapshotCue || hasStockStateCue); } function hasInventoryProvenanceSignal(text) { return /(?:поставщик|закупк|РїСЂРѕРёСЃС…РѕР¶Рґ|откуда|РєРѕРіРґР° был куплен|активная закупк|purchase provenance|purchase date|supplier provenance|stock overlap)/iu.test(text); } function hasInventoryPurchaseDocumentsSignal(text) { return /(?:РїРѕ каким документам|документы закупки|purchase documents|documents of purchase|through which documents|chain of documents)/iu.test(text); } function hasInventorySaleTraceSignal(text) { return /(?:продаж|покупател|buyer|sale trace|purchase[\s-]?to[\s-]?sale|purchase -> warehouse -> sale|закупка.*продаж)/iu.test(text); } function hasSelectedObjectInventoryCue(text) { return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+нему|по\s+ней|по\s+ним|по\s+нему\s+же|по\s+ней\s+же|selected\s+object)/iu.test(String(text ?? "")); } function hasSelectedObjectInventoryProvenanceSignal(text) { return hasSelectedObjectInventoryCue(text) && (0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(text); } function hasSelectedObjectInventoryPurchaseDocumentsSignal(text) { const hasPurchaseDocumentsCue = /(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(text) || /(?:(?:по\s+каким|какими)\s+док[а-яё]*[\s\S]{0,80}(?:купил|куплен)|док(?:и|умент[а-яё]*)[\s\S]{0,80}(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару)|операци)|(?:по\s+(?:ним|ней|нему|этой\s+позиции|этому\s+товару))[\s\S]{0,80}док(?:и|умент[а-яё]*))/iu.test(text); return (hasSelectedObjectInventoryCue(text) && hasPurchaseDocumentsCue); } function hasSelectedObjectInventorySaleTraceSignal(text) { return hasSelectedObjectInventoryCue(text) && (0, inventoryLifecycleCueHelpers_1.hasInventorySaleCue)(text); } function hasSelectedObjectInventoryProfitabilitySignal(text) { return hasSelectedObjectInventoryCue(text) && (0, inventoryLifecycleCueHelpers_1.hasInventoryProfitabilityCue)(text); } function hasInventoryProvenanceSignalV2(text) { const hasItemCue = /(?:товар|номенклатур|sku|item|product|остат(?:ок|ки)|склад)/iu.test(text); const hasSupplierCue = (0, inventoryLifecycleCueHelpers_1.hasInventorySupplierCue)(text) || /кем\s+поставлен/iu.test(text); const hasPurchaseCue = /(?:куплен(?:ы|а|о)?|закупк|происхождени|откуда|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|товар|позицию))?|где\s+куплено|когда\s+был\s+куплен|когда\s+куплен|дата\s+закупк|кто\s+(?:нам\s+)?поставил|кем\s+поставлен|поставлен(?:ы|а)?|purchase\s+provenance|purchase\s+date)/iu.test(text) || (0, inventoryLifecycleCueHelpers_1.hasInventoryPurchaseStem)(text); return hasItemCue && hasSupplierCue && hasPurchaseCue; } function hasInventoryPurchaseDateSignal(text) { const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text); const hasPurchaseDateCue = /(?:когда\s+(?:примерно\s+)?(?:мы\s+)?купили|когда\s+был\s+куплен|когда\s+куплен|дата\s+закупк|purchase\s+date)/iu.test(text); return hasItemCue && hasPurchaseDateCue; } function hasInventoryPurchaseDocumentsSignalV2(text) { const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text); const hasPurchaseDocCue = /(?:по\s+каким\s+документам\s+был\s+куплен|по\s+каким\s+документам\s+куплен|какими\s+документами\s+был\s+куплен|документ(?:ам|ы)\s+закупк|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(text); return hasItemCue && hasPurchaseDocCue; } function hasInventorySaleTraceSignalV2(text) { const hasItemCue = /(?:товар|номенклатур|sku|item|product|позици(?:я|ю|и)|продукци(?:я|ю|и))/iu.test(text); const hasTraceCue = /(?:кому\s+(?:в\s+итоге\s+)?(?:мы\s+)?продали|кому\s+был\s+продан|куда\s+(?:в\s+итоге\s+)?(?:мы\s+)?продали(?:\s+(?:это|его|товар|позицию))?|куда\s+(?:была\s+)?реализована\s+(?:позиция|номенклатура|продукция)|кто\s+купил|buyer|sale\s+trace|trace\s+of\s+sale|через\s+какие\s+документы\s+прош[её]л\s+путь\s+товара|закупк.*склад.*продаж|purchase[\s-]?to[\s-]?sale|purchase\s*->\s*warehouse\s*->\s*sale|purchase\s*->\s*stock\s*->\s*sale)/iu.test(text); return hasItemCue && hasTraceCue; } function hasInventorySupplierStockOverlapSignal(text) { const hasDirectSingleItemSupplierQuestion = /(?:от\s+какого\s+поставщика\s+куплен\s+(?:товар|номенклатур(?:а|у|ы)|позици(?:я|ю|и))|от\s+кого\s+куплен\s+(?:товар|номенклатур(?:а|у|ы)|позици(?:я|ю|и)))/iu.test(text); if (hasDirectSingleItemSupplierQuestion) { return false; } const hasSupplierCue = /(?:поставщик|supplier|vendor|от\s+поставщика|у\s+поставщика)/iu.test(text); const hasStockCue = /(?:склад|остат(?:ок|ке|ков)|лежат|лежит|сейчас\s+еще|сейчас\s+ещ[её]|на\s+дату|по\s+состоянию\s+на\s+дату|current\s+stock|stock\s+overlap|что\s+сейчас\s+лежит)/iu.test(text); return hasSupplierCue && hasStockCue; } function hasInventoryAgingSignal(text) { const hasResidueCue = /(?:остат(?:ок|ки)|в\s+остатке|среди\s+текущих\s+остатков|на\s+складе|stock\s+residue|stock\s+balance)/iu.test(text); const hasAgingCue = /(?:стар(?:ые|ым|ых)\s+закупк|стары(?:м|х)\s+закупк(?:ам|и|ах)|относит(?:ся|ся\s+ли)?\s+.*\s+к\s+старым\s+закупк|закупал(?:ись|ся)\s+очень\s+давно|очень\s+давно|давно\s+куплен|давно\s+приобретен|куплен\s+задолго\s+до(?:\s+даты)?|закуплен(?:ы|а)?\s+давно|приобретен\s+давно|задолго\s+до(?:\s+даты)?|возраст\s+остатк|возраст\s+закупк|aged?\s+stock|old\s+purchase|old\s+purchases|old\s+stock|bought\s+long\s+ago|purchased\s+long\s+ago|aging\s+by\s+purchase\s+date|very\s+old\s+stock|very\s+old\s+purchase|old\s+procurement|older\s+purchases|aged\s+items|old\s+goods)/iu.test(text); return hasAgingCue || (hasResidueCue && /(?:давно\s+куплен|давно\s+приобретен|задолго\s+до)/iu.test(text)); } function hasInventoryPurchaseToSaleChainSignal(text) { const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text); const hasChainCue = /(?:закупк.*склад.*продаж|purchase[\s-]?to[\s-]?sale|purchase\s*->\s*(?:warehouse|stock)\s*->\s*sale|закупка\s*->\s*склад\s*->\s*продажа|цепочк[аи]\s+движен|документально\s+подтвержденн\w+\s+цепочк|supplier\s*->\s*item\s*->\s*(?:buyer|customer)|supplier\s+to\s+buyer|supplier\s+to\s+item\s+to\s+buyer)/iu.test(text) || text.includes("->"); return hasItemCue && hasChainCue; } function hasInventorySupplierToBuyerChainSignal(text) { const hasSupplierCue = /(?:поставщик|supplier|vendor)/iu.test(text); const hasBuyerCue = /(?:покупател|buyer|customer|client)/iu.test(text); const hasItemCue = /(?:товар|номенклатур|sku|item|product)/iu.test(text); const hasChainCue = /(?:документально\s+подтвержденн\w+\s+цепочк|supplier\s*->\s*item\s*->\s*buyer|supplier\s*->\s*item\s*->\s*customer|supplier\s*->\s*buyer|supplier\s+to\s+buyer|supplier\s+to\s+buyer\s+chain|supplier\s+to\s+item\s+to\s+buyer|поставщик\s*->\s*товар\s*->\s*покупател|поставщик\s*->\s*товар\s*->\s*клиент|поставщик\s*->\s*товар\s*->\s*покупатель|поставщик\s+к\s+покупател|поставщик\s+к\s+клиент|поставщик\s+к\s+товару\s+и\s+покупателю)/iu.test(text) || text.includes("->"); return hasSupplierCue && hasBuyerCue && hasItemCue && hasChainCue; } function resolveAddressIntent(userMessage) { const text = String(userMessage ?? "").trim().toLowerCase(); if (hasVatLiabilityConfirmedTaxPeriodSignal(text)) { return { intent: "vat_liability_confirmed_for_tax_period", confidence: "high", reasons: ["vat_liability_confirmed_tax_period_signal_detected"] }; } if (hasForecastTaxSignal(text)) { return { intent: "vat_payable_forecast", confidence: "high", reasons: ["forecast_tax_signal_detected"] }; } if (hasVatPayableConfirmedSignal(text)) { return { intent: "vat_payable_confirmed_as_of_date", confidence: "high", reasons: ["vat_payable_confirmed_signal_detected"] }; } if (hasAny(text, RECEIVABLES_STRONG) || hasFlexibleReceivablesDebtSignal(text)) { const receivablesDebtLifecycleSignal = hasReceivablesDebtLifecycleSignal(text) || hasFlexibleReceivablesDebtSignal(text); const reasons = ["receivables_signal_detected"]; if (receivablesDebtLifecycleSignal) { reasons.push("receivables_debt_lifecycle_signal_detected"); if (hasFlexibleReceivablesDebtSignal(text)) { reasons.push("receivables_signal_detected_flexible_phrase"); } } return { intent: receivablesDebtLifecycleSignal ? "receivables_confirmed_as_of_date" : "list_receivables_counterparties", confidence: "high", reasons }; } if (hasAny(text, PAYABLES_STRONG) || hasFlexiblePayablesDebtSignal(text)) { const reasons = ["payables_signal_detected"]; const payablesDebtLifecycleSignal = hasPayablesDebtLifecycleSignal(text) || hasFlexiblePayablesDebtSignal(text); if (payablesDebtLifecycleSignal) { reasons.push("payables_debt_lifecycle_signal_detected"); if (hasFlexiblePayablesDebtSignal(text)) { reasons.push("payables_signal_detected_flexible_phrase"); } } return { intent: payablesDebtLifecycleSignal ? "payables_confirmed_as_of_date" : "list_payables_counterparties", confidence: "high", reasons }; } if (hasSettlementGapSignal(text)) { return { intent: "list_open_contracts", confidence: "medium", reasons: ["settlement_gap_signal_detected"] }; } if (hasReconciliationMismatchSignal(text)) { return { intent: "list_open_contracts", confidence: "medium", reasons: ["reconciliation_mismatch_signal_detected"] }; } if (hasReceivablesLatencyRiskSignal(text)) { return { intent: "list_receivables_counterparties", confidence: "medium", reasons: ["receivables_payment_lag_signal_detected"] }; } if (hasCounterpartyDebtLongevitySignal(text)) { return { intent: "list_receivables_counterparties", confidence: "medium", reasons: ["receivables_debt_lifecycle_signal_detected"] }; } if (hasSupplierTailRiskSignal(text)) { return { intent: "list_payables_counterparties", confidence: "medium", reasons: ["supplier_tail_risk_signal_detected", "payables_debt_lifecycle_signal_detected"] }; } if (hasDocumentsFormingBalanceSignal(text) && hasDocumentsFormingBalanceAccountAnchor(text)) { return { intent: "documents_forming_balance", confidence: "high", reasons: ["documents_forming_balance_signal_detected"] }; } if (hasDocumentsByAccountDrilldownSignal(text)) { return { intent: "documents_forming_balance", confidence: "medium", reasons: ["documents_by_account_drilldown_signal_detected"] }; } if (/(?:старым\s+закупк(?:ам|и|ах)|относится\s+ли\s+.*\s+к\s+старым\s+закупк(?:ам|и|ах)|очень\s+давно|давно\s+куплен|давно\s+приобретен|old\s+stock|old\s+purchase|aging\s+by\s+purchase\s+date)/iu.test(text)) { return { intent: "inventory_aging_by_purchase_date", confidence: "high", reasons: ["inventory_aging_signal_detected_strong"] }; } if (hasInventoryAccount41Anchor(text) && hasInventoryAsOfCue(text)) { return { intent: "inventory_on_hand_as_of_date", confidence: "high", reasons: ["inventory_account_41_as_of_date_signal_detected"] }; } if (/(?:без\s+понятн(?:ой|ого)\s+привязк(?:и|а)\s+к\s+поставщик|без\s+привязк(?:и|а)\s+к\s+поставщик|unresolved\s+supplier\s+link)/iu.test(text)) { return { intent: "inventory_supplier_stock_overlap_as_of_date", confidence: "medium", reasons: ["inventory_unresolved_provenance_signal_detected"] }; } if (hasInventorySupplierStockOverlapSignal(text)) { return { intent: "inventory_supplier_stock_overlap_as_of_date", confidence: "medium", reasons: ["inventory_supplier_stock_overlap_signal_detected"] }; } if (/(?:supplier\s*->\s*buyer|supplier\s+to\s+buyer|supplier\s+to\s+buyer\s+chain|поставщик\s+к\s+покупателю|поставщик\s*->\s*товар\s*->\s*покупател|документально\s+подтвержденн\w+\s+цепочк)/iu.test(text) && /(?:поставщик|supplier|vendor)/iu.test(text) && /(?:покупател|buyer|customer|client)/iu.test(text) && /(?:товар|номенклатур|sku|item|product)/iu.test(text)) { return { intent: "inventory_purchase_to_sale_chain", confidence: "high", reasons: ["inventory_supplier_to_buyer_chain_signal_detected_strong"] }; } if (hasInventoryPurchaseToSaleChainSignal(text)) { return { intent: "inventory_purchase_to_sale_chain", confidence: "medium", reasons: ["inventory_purchase_to_sale_chain_signal_detected"] }; } if (hasInventoryAgingSignal(text)) { return { intent: "inventory_aging_by_purchase_date", confidence: "medium", reasons: ["inventory_aging_signal_detected"] }; } if (hasSelectedObjectInventoryProvenanceSignal(text)) { return { intent: "inventory_purchase_provenance_for_item", confidence: "medium", reasons: ["inventory_selected_object_provenance_signal_detected"] }; } if (hasInventoryProvenanceSignalV2(text)) { return { intent: "inventory_purchase_provenance_for_item", confidence: "medium", reasons: ["inventory_provenance_signal_detected"] }; } if (hasInventoryPurchaseDateSignal(text)) { return { intent: "inventory_purchase_provenance_for_item", confidence: "medium", reasons: ["inventory_purchase_date_signal_detected"] }; } if (hasSelectedObjectInventoryPurchaseDocumentsSignal(text)) { return { intent: "inventory_purchase_documents_for_item", confidence: "medium", reasons: ["inventory_selected_object_purchase_documents_signal_detected"] }; } if (hasInventoryPurchaseDocumentsSignalV2(text)) { return { intent: "inventory_purchase_documents_for_item", confidence: "medium", reasons: ["inventory_purchase_documents_signal_detected"] }; } if (hasSelectedObjectInventoryProfitabilitySignal(text)) { return { intent: "inventory_profitability_for_item", confidence: "medium", reasons: ["inventory_selected_object_profitability_signal_detected"] }; } if (hasSelectedObjectInventorySaleTraceSignal(text)) { return { intent: "inventory_sale_trace_for_item", confidence: "medium", reasons: ["inventory_selected_object_sale_trace_signal_detected"] }; } if (/(?:кому\s+(?:мы\s+)?впарили(?:\s+(?:это|его|товар|позицию))?|кому\s+в\s+итоге\s+мы\s+впарили)/iu.test(text) && /(?:товар|номенклатур|sku|item|product|позици(?:я|ю|и)|продукци(?:я|ю|и))/iu.test(text)) { return { intent: "inventory_sale_trace_for_item", confidence: "medium", reasons: ["inventory_sale_trace_signal_detected"] }; } if (hasInventorySaleTraceSignalV2(text)) { return { intent: "inventory_sale_trace_for_item", confidence: "medium", reasons: ["inventory_sale_trace_signal_detected"] }; } if (hasInventorySupplierToBuyerChainSignal(text)) { return { intent: "inventory_purchase_to_sale_chain", confidence: "medium", reasons: ["inventory_supplier_to_buyer_chain_signal_detected"] }; } if (hasInventoryOnHandSignal(text)) { return { intent: "inventory_on_hand_as_of_date", confidence: "high", reasons: ["inventory_on_hand_signal_detected"] }; } if (hasOpenContractsListSignal(text)) { return { intent: "open_contracts_confirmed_as_of_date", confidence: "medium", reasons: ["open_contract_signal_detected"] }; } if (hasAny(text, OPEN_ITEMS_HINTS) && !hasCounterpartyDebtLongevitySignal(text) && !hasInventoryAgingSignal(text) && !hasInventoryProvenanceSignalV2(text) && !hasInventoryPurchaseDocumentsSignalV2(text) && !hasInventorySaleTraceSignalV2(text) && (/(?:контраг|договор|контракт|counterparty|contract|покупател|клиент|заказчик|customer|client|buyer|supplier|поставщик)/iu.test(text) || hasAccountNumberAnchor(text) || hasCompactAccountCodeToken(text))) { return { intent: "open_items_by_counterparty_or_contract", confidence: "medium", reasons: ["open_items_signal_detected"] }; } if (hasPeriodCoverageProfileSignal(text) && !hasPartyAnchorMention(text) && !hasContractAnchorSignal(text) && !hasAccountBalanceSignal(text)) { return { intent: "period_coverage_profile", confidence: "high", reasons: ["period_coverage_profile_signal_detected"] }; } if (hasDocumentTypeAndAccountSectionProfileSignal(text) && !hasPartyAnchorMention(text) && !hasContractAnchorSignal(text) && !hasAccountBalanceSignal(text)) { return { intent: "document_type_and_account_section_profile", confidence: "high", reasons: ["document_type_and_account_section_profile_signal_detected"] }; } if (hasCounterpartyPopulationAndRolesSignal(text) && !hasContractAnchorSignal(text) && !hasAccountBalanceSignal(text)) { return { intent: "counterparty_population_and_roles", confidence: "high", reasons: ["counterparty_population_and_roles_signal_detected"] }; } if (hasCounterpartyActivityLifecycleSignal(text) && !hasContractAnchorSignal(text) && !hasAccountBalanceSignal(text)) { return { intent: "counterparty_activity_lifecycle", confidence: "high", reasons: ["counterparty_activity_lifecycle_signal_detected"] }; } if (hasContractUsageOverviewSignal(text) && !hasAccountBalanceSignal(text) && !hasOpenContractsListSignal(text)) { return { intent: "contract_usage_overview", confidence: "high", reasons: ["contract_usage_overview_signal_detected"] }; } if (hasCustomerRevenueAndPaymentsSignal(text) && !hasAccountBalanceSignal(text)) { return { intent: "customer_revenue_and_payments", confidence: "high", reasons: ["customer_revenue_and_payments_signal_detected"] }; } if (hasSupplierPayoutsProfileSignal(text) && !hasAccountBalanceSignal(text)) { return { intent: "supplier_payouts_profile", confidence: "high", reasons: ["supplier_payouts_profile_signal_detected"] }; } if (hasContractUsageAndValueSignal(text) && !hasAccountBalanceSignal(text) && !hasOpenContractsListSignal(text)) { return { intent: "contract_usage_and_value", confidence: "high", reasons: ["contract_usage_and_value_signal_detected"] }; } if (hasContractListByCounterpartySignal(text)) { return { intent: "list_contracts_by_counterparty", confidence: "medium", reasons: ["contracts_by_counterparty_signal_detected"] }; } if (hasContractAnchorSignal(text) && hasBankOperationSignal(text)) { return { intent: "bank_operations_by_contract", confidence: "medium", reasons: ["bank_ops_by_contract_signal_detected"] }; } if (hasContractAnchorSignal(text) && (hasAny(text, DOCUMENTS_BY_CONTRACT_HINTS) || hasDocumentSignal(text))) { return { intent: "list_documents_by_contract", confidence: "medium", reasons: ["documents_by_contract_signal_detected"] }; } if (hasAny(text, BANK_OPERATIONS_BY_COUNTERPARTY_HINTS) && (hasPartyAnchorMention(text) || hasLooseByAnchorMention(text) || hasHeuristicCounterpartyAnchor(text))) { return { intent: "bank_operations_by_counterparty", confidence: "medium", reasons: ["bank_ops_by_counterparty_signal_detected"] }; } if ((hasAny(text, DOCUMENTS_BY_COUNTERPARTY_HINTS) || hasCounterpartyShipmentItemFlowSignal(text)) && (hasPartyAnchorMention(text) || hasLooseByAnchorMention(text) || hasImplicitCounterpartyAnchorAroundDocs(text) || hasHeuristicCounterpartyAnchor(text) || hasCounterpartyShipmentItemFlowSignal(text))) { return { intent: "list_documents_by_counterparty", confidence: "medium", reasons: [ hasCounterpartyShipmentItemFlowSignal(text) ? "counterparty_item_flow_signal_detected" : "documents_by_counterparty_signal_detected" ] }; } if (hasAccountBalanceSignal(text)) { return { intent: "account_balance_snapshot", confidence: "high", reasons: ["account_balance_signal_detected"] }; } if (hasLooseByAnchorMention(text) && hasGenericAddressLookupSignal(text)) { return { intent: "list_documents_by_counterparty", confidence: "low", reasons: ["generic_lookup_with_loose_anchor_fallback"] }; } if (hasAny(text, OPEN_CONTRACTS_HINTS) && (text.includes("договор") || text.includes("контракт") || text.includes("contract"))) { return { intent: "open_contracts_confirmed_as_of_date", confidence: "medium", reasons: ["open_contract_signal_detected"] }; } return { intent: "unknown", confidence: "low", reasons: ["intent_not_supported_in_v1"] }; }