NODEDC_1C/llm_normalizer/backend/src/services/addressInventoryIntentSigna...

335 lines
22 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { AddressIntentResolution } from "../types/addressQuery";
import {
hasInventoryProfitabilityCue,
hasInventoryPurchaseStem,
hasInventorySaleCue,
hasInventorySupplierCue
} from "./inventoryLifecycleCueHelpers";
function hasInventoryAccount41Anchor(text: string): boolean {
return /(?:СЃС[РµС]С(?:Р°|Рµ|Сѓ)?|СЃСРµС(?:Р°|Рµ|Сѓ)?)\D{0,12}41(?:[.,]0?1)?/iu.test(text) || /41(?:[.,]0?1)?\D{0,12}(?:СЃС[РµС]С(?:Р°|Рµ|Сѓ)?|СЃСРµС(?:Р°|Рµ|Сѓ)?)/iu.test(text);
}
function hasInventoryAsOfCue(text: string): boolean {
return /(?:СЃРµРСас|СекуС|РЅР°\s+РґР°ССѓ|РїРѕ\s+СЃРѕСЃСРѕСЏРЅРёСЋ|срез|РЅР°\s+РєРѕРЅРµС|date|as\s+of|current|now|today)/iu.test(
text
);
}
function hasInventoryOnHandSignal(text: string): boolean {
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 hasSelectedObjectInventoryCue(text: string): boolean {
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: string): boolean {
return hasSelectedObjectInventoryCue(text) && hasInventorySupplierCue(text);
}
function hasSelectedObjectInventoryPurchaseDocumentsSignal(text: string): boolean {
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: string): boolean {
return hasSelectedObjectInventoryCue(text) && hasInventorySaleCue(text);
}
function hasSelectedObjectInventoryProfitabilitySignal(text: string): boolean {
return hasSelectedObjectInventoryCue(text) && hasInventoryProfitabilityCue(text);
}
function hasInventoryProvenanceSignalV2(text: string): boolean {
const hasItemCue = /(?:Совар|номенклаССѓСЂ|sku|item|product|РѕСЃСР°С(?:РѕРє|РєРё)|склад)/iu.test(text);
const hasSupplierCue = 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
) || hasInventoryPurchaseStem(text);
return hasItemCue && hasSupplierCue && hasPurchaseCue;
}
function hasInventoryPurchaseDateSignal(text: string): boolean {
const hasItemCue =
/(?:Совар|номенклаССѓСЂ|sku|item|product)/iu.test(text) || hasSelectedObjectInventoryCue(text);
const hasPurchaseDateCue =
/(?:РєРѕРіРґР°\s+(?:примерно\s+)?(?:РјС\s+)?купили|РєРѕРіРґР°\s+Р±СР»\s+куплен|РєРѕРіРґР°\s+куплен|РґР°СР°\s+закупк|purchase\s+date)/iu.test(
text
) ||
/(?:РєРѕРіРґР°\s+Р±СР»(?:Р°|Рё|Рѕ)?\s+закупк\w*|РєРѕРіРґР°\s+закупк\w*)/iu.test(text);
return hasItemCue && hasPurchaseDateCue;
}
function hasInventoryPurchaseDocumentsSignalV2(text: string): boolean {
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: string): boolean {
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: string): boolean {
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: string): boolean {
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: string): boolean {
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: string): boolean {
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;
}
export function resolveInventoryAddressIntent(text: string): AddressIntentResolution | null {
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"]
};
}
return null;
}