АРЧ АП11 - Архитектура после регресса: + Архитектура: восстановить bridge от provenance выбранной позиции к VAT-периоду и закрыть phase10 replay
This commit is contained in:
+16
-4
@@ -639,13 +639,18 @@ export function composeCounterpartyAnalyticsReply(
|
||||
|
||||
if (focus === "top_years_by_total") {
|
||||
const visible = rankedByYearTotal.slice(0, limit);
|
||||
const heading = isSupplier
|
||||
? `Топ-${visible.length} лет по сумме выплат:`
|
||||
: `Топ-${visible.length} лет по сумме поступлений:`;
|
||||
lines.unshift(heading);
|
||||
if (visible.length === 0) {
|
||||
lines.push("По доступному окну не удалось собрать годовые агрегаты по суммам.");
|
||||
} else {
|
||||
const strongestYear = visible[0];
|
||||
const directAnswerLine = isSupplier
|
||||
? `Самый крупный год по подтвержденным выплатам: ${strongestYear.year} (${deps.formatMoneyRub(strongestYear.total)} по ${strongestYear.ops} операциям).`
|
||||
: `Самый доходный год по подтвержденным поступлениям: ${strongestYear.year} (${deps.formatMoneyRub(strongestYear.total)} по ${strongestYear.ops} операциям). Это денежный поток, а не чистая прибыль.`;
|
||||
const heading = isSupplier
|
||||
? `Топ-${visible.length} лет по сумме выплат:`
|
||||
: `Топ-${visible.length} лет по сумме поступлений:`;
|
||||
lines.unshift(heading);
|
||||
lines.unshift(directAnswerLine);
|
||||
lines.push(
|
||||
...visible.map(
|
||||
(item, index) =>
|
||||
@@ -742,7 +747,14 @@ export function composeCounterpartyAnalyticsReply(
|
||||
const heading = isSupplier
|
||||
? `Топ-${visible.length} поставщиков по сумме выплат:`
|
||||
: `Топ-${visible.length} заказчиков по сумме поступлений:`;
|
||||
const leadingCounterparty = visible[0] ?? null;
|
||||
lines.unshift(heading);
|
||||
if (leadingCounterparty) {
|
||||
const directAnswerLine = isSupplier
|
||||
? `Крупнейший поставщик по подтвержденным выплатам за доступное время: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям).`
|
||||
: `Самый доходный клиент за доступное время по подтвержденным поступлениям: ${leadingCounterparty.name} (${deps.formatMoneyRub(leadingCounterparty.total)} по ${leadingCounterparty.ops} операциям). Это денежный поток, а не чистая прибыль.`;
|
||||
lines.unshift(directAnswerLine);
|
||||
}
|
||||
lines.push(
|
||||
...visible.map((item, index) => {
|
||||
const avgCheck = item.ops > 0 ? item.total / item.ops : 0;
|
||||
|
||||
@@ -691,6 +691,19 @@ export function hasInventoryPurchaseToSaleChainFollowupCue(text: string): boolea
|
||||
);
|
||||
}
|
||||
|
||||
export function hasInventoryPurchaseDateVatBridgeCue(text: string): boolean {
|
||||
const normalized = String(text ?? "").trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
/(?:ндс|vat)/iu.test(normalized) &&
|
||||
/(?:на\s+дат[ауеы]\s+покупк|на\s+дат[ауеы]\s+закупк|по\s+дат[еу]\s+покупк|по\s+дат[еу]\s+закупк|дата\s+покупк|дата\s+закупк|purchase\s+date)/iu.test(
|
||||
normalized
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function hasAddressFollowupContextSignal(text: string): boolean {
|
||||
const normalized = String(text ?? "").trim();
|
||||
if (!normalized) {
|
||||
@@ -728,6 +741,9 @@ export function hasAddressFollowupContextSignal(text: string): boolean {
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (tokenCount <= 14 && hasInventoryPurchaseDateVatBridgeCue(normalized)) {
|
||||
return true;
|
||||
}
|
||||
const hasPeriodLiteral = /\b(?:19|20)\d{2}(?:[./-](?:0?[1-9]|1[0-2]))?\b/.test(normalized);
|
||||
if (tokenCount <= 8 && hasPeriodLiteral) {
|
||||
return true;
|
||||
@@ -1314,6 +1330,21 @@ function deriveIntentWithFollowupContext(
|
||||
const previousIsInventoryFamily = isInventoryIntent(sourceIntent ?? undefined);
|
||||
const inventorySelectedObjectFollowup =
|
||||
hasSelectedObjectInventorySignal(normalizedMessage) || (previousIsInventoryFamily && hasFollowupSignal);
|
||||
const inventoryPurchaseDateVatBridge =
|
||||
inventorySelectedObjectFollowup && hasInventoryPurchaseDateVatBridgeCue(normalizedMessage);
|
||||
|
||||
if (
|
||||
inventoryPurchaseDateVatBridge &&
|
||||
(detectedIntent.intent === "unknown" ||
|
||||
detectedIntent.intent === sourceIntent ||
|
||||
detectedIntent.intent === "vat_payable_confirmed_as_of_date")
|
||||
) {
|
||||
return {
|
||||
intent: "vat_liability_confirmed_for_tax_period",
|
||||
confidence: "low",
|
||||
reasons: [...detectedIntent.reasons, "intent_adjusted_to_inventory_purchase_date_vat_bridge"]
|
||||
};
|
||||
}
|
||||
|
||||
if (detectedIntent.intent === "unknown" && isVatFollowup) {
|
||||
const vatIntent: AddressIntent = hasVatTaxPaymentCue(normalizedMessage)
|
||||
|
||||
@@ -316,8 +316,8 @@ export function composeInventoryReply(
|
||||
.join("; ");
|
||||
const directAnswerLine =
|
||||
agingItems.length > 0
|
||||
? `К старым закупкам на ${deps.formatDateRu(asOfDate)} в первую очередь относятся позиции с самой ранней первой закупкой: ${oldestAnswerPreview}.`
|
||||
: `По доступному закупочному следу на ${deps.formatDateRu(asOfDate)} позиции старых закупок не материализованы.`;
|
||||
? `К самым старым закупкам в текущем подтвержденном срезе относятся позиции с самой ранней первой закупкой: ${oldestAnswerPreview}.`
|
||||
: "По доступному закупочному следу позиции со старыми закупками не материализованы.";
|
||||
const lines: string[] = [directAnswerLine];
|
||||
appendInventoryBulletSection(lines, "Сводка:", [
|
||||
`Дата среза: ${deps.formatDateRu(asOfDate)}.`,
|
||||
|
||||
Reference in New Issue
Block a user