NODEDC_1C/llm_normalizer/backend/tests/addressFollowupTemporalRegr...

525 lines
26 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 { describe, expect, it } from "vitest";
import { runAddressDecomposeStage } from "../src/services/address_runtime/decomposeStage";
describe("address follow-up temporal regressions", () => {
it("replaces VAT month-only follow-up with the previous follow-up year", () => {
const result = runAddressDecomposeStage("а на март", {
previous_intent: "vat_payable_confirmed_as_of_date",
previous_filters: {
period_from: "2019-05-01",
period_to: "2019-05-31",
as_of_date: "2019-05-31"
},
previous_anchor_type: "unknown",
previous_anchor_value: null
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_payable_confirmed_as_of_date");
expect(result?.filters.extracted_filters.period_from).toBe("2019-03-01");
expect(result?.filters.extracted_filters.period_to).toBe("2019-03-31");
expect(result?.filters.extracted_filters.as_of_date).toBe("2019-03-31");
expect(result?.baseReasons).toContain("period_derived_from_followup_context_year");
});
it("replaces VAT month-only follow-up with a later month from the same inherited year", () => {
const result = runAddressDecomposeStage("на сентябрь", {
previous_intent: "vat_payable_confirmed_as_of_date",
previous_filters: {
period_from: "2019-05-01",
period_to: "2019-05-31",
as_of_date: "2019-05-31"
},
previous_anchor_type: "unknown",
previous_anchor_value: null
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_payable_confirmed_as_of_date");
expect(result?.filters.extracted_filters.period_from).toBe("2019-09-01");
expect(result?.filters.extracted_filters.period_to).toBe("2019-09-30");
expect(result?.filters.extracted_filters.as_of_date).toBe("2019-09-30");
expect(result?.baseReasons).toContain("period_derived_from_followup_context_year");
});
it("inherits the previous VAT period for 'за этот период' follow-up", () => {
const result = runAddressDecomposeStage("какой НДС мы должны примерно заплатить за этот период", {
previous_intent: "vat_payable_confirmed_as_of_date",
previous_filters: {
period_from: "2017-05-01",
period_to: "2017-05-31",
as_of_date: "2017-05-31"
},
previous_anchor_type: "unknown",
previous_anchor_value: null
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2017-05-01");
expect(result?.filters.extracted_filters.period_to).toBe("2017-05-31");
expect(result?.filters.extracted_filters.as_of_date).toBe("2017-05-31");
expect(result?.baseReasons).toContain("period_from_from_followup_context");
});
it("keeps inherited period when a follow-up retargets from receivables into VAT for the same period", () => {
const result = runAddressDecomposeStage("а какой НДС мы должны примерно заплатить за этот период", {
previous_intent: "receivables_confirmed_as_of_date",
target_intent: "vat_payable_confirmed_as_of_date",
previous_filters: {
period_from: "2017-05-01",
period_to: "2017-05-31",
as_of_date: "2017-05-31"
},
previous_anchor_type: "organization",
previous_anchor_value: 'ООО "Альтернатива Плюс"'
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2017-05-01");
expect(result?.filters.extracted_filters.period_to).toBe("2017-05-31");
expect(result?.filters.extracted_filters.as_of_date).toBe("2017-05-31");
});
it("uses follow-up target intent for short debt mirror like 'а нам?'", () => {
const result = runAddressDecomposeStage("а нам?", {
previous_intent: "payables_confirmed_as_of_date",
target_intent: "receivables_confirmed_as_of_date",
previous_filters: {
as_of_date: "2026-04-16"
},
previous_anchor_type: "organization",
previous_anchor_value: 'ООО "Альтернатива Плюс"'
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("receivables_confirmed_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-04-16");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
});
it("does not carry self-scope pronouns as counterparty anchors for short debt mirror follow-up", () => {
const result = runAddressDecomposeStage("а нам?", {
previous_intent: "payables_confirmed_as_of_date",
target_intent: "receivables_confirmed_as_of_date",
previous_filters: {
as_of_date: "2026-05-12",
organization: "ООО Альтернатива Плюс",
counterparty: "нас"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "нас"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("receivables_confirmed_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-05-12");
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
});
it("suppresses stale counterparty filters from organization-scoped short debt mirror follow-up", () => {
const result = runAddressDecomposeStage("а нам?", {
previous_intent: "payables_confirmed_as_of_date",
target_intent: "receivables_confirmed_as_of_date",
previous_filters: {
as_of_date: "2026-05-17",
organization: "ООО Альтернатива Плюс",
counterparty: "нас самый доходный клиент"
},
previous_anchor_type: "organization",
previous_anchor_value: "ООО Альтернатива Плюс"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("receivables_confirmed_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-05-17");
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
expect(result?.baseReasons).toContain("counterparty_carryover_suppressed_for_short_debt_mirror");
expect(result?.baseReasons).not.toContain("counterparty_from_followup_context");
});
it("keeps resolved counterparty focus for short debt mirror follow-up", () => {
const result = runAddressDecomposeStage("а нам?", {
previous_intent: "payables_confirmed_as_of_date",
target_intent: "receivables_confirmed_as_of_date",
previous_filters: {
as_of_date: "2026-05-17",
organization: "ООО Альтернатива Плюс",
counterparty: "Группа СВК"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "Группа СВК",
resolved_counterparty_from_display: true
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("receivables_confirmed_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-05-17");
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
expect(result?.filters.extracted_filters.counterparty).toBe("Группа СВК");
expect(result?.baseReasons).toContain("counterparty_from_followup_context");
expect(result?.baseReasons).not.toContain("counterparty_carryover_suppressed_for_short_debt_mirror");
});
it("keeps explicit year period for selected counterparty payout follow-up", () => {
const result = runAddressDecomposeStage("а теперь сколько заплатили?", {
previous_intent: "customer_revenue_and_payments" as const,
target_intent: "customer_revenue_and_payments" as const,
previous_filters: {
counterparty: "Группа СВК",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty" as const,
previous_anchor_value: "Группа СВК",
resolved_counterparty_from_display: true
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("customer_revenue_and_payments");
expect(result?.filters.extracted_filters.counterparty).toBe("Группа СВК");
expect(result?.filters.extracted_filters.period_from).toBe("2020-01-01");
expect(result?.filters.extracted_filters.period_to).toBe("2020-12-31");
expect(result?.baseReasons).toContain("period_from_followup_context");
});
it("keeps same-date inventory pivot anchored to the previous VAT date", () => {
const result = runAddressDecomposeStage("какие остатки по складу на эту же дату", {
previous_intent: "vat_payable_confirmed_as_of_date",
previous_filters: {
period_from: "2019-09-01",
period_to: "2019-09-30",
as_of_date: "2019-09-30"
},
previous_anchor_type: "unknown",
previous_anchor_value: null
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("inventory_on_hand_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2019-09-30");
expect(result?.filters.extracted_filters.warehouse).toBeUndefined();
expect(result?.baseReasons).toContain("as_of_date_from_followup_context");
});
it("keeps period window on inventory same-date follow-up phrased as 'по этой же дате'", () => {
const result = runAddressDecomposeStage(
"\u043f\u043e\u043a\u0430\u0436\u0438 \u043e\u0441\u0442\u0430\u0442\u043a\u0438 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434\u0435 \u043f\u043e \u044d\u0442\u043e\u0439 \u0436\u0435 \u0434\u0430\u0442\u0435",
{
previous_intent: "receivables_confirmed_as_of_date",
previous_filters: {
organization: "\u0420\u0410\u0419\u041c",
period_from: "2020-03-01",
period_to: "2020-03-31",
as_of_date: "2020-03-31"
},
previous_anchor_type: "organization",
previous_anchor_value: "\u0420\u0410\u0419\u041c"
}
);
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("inventory_on_hand_as_of_date");
expect(result?.filters.extracted_filters.organization).toBe("\u0420\u0410\u0419\u041c");
expect(result?.filters.extracted_filters.as_of_date).toBe("2020-03-31");
expect(result?.filters.extracted_filters.period_from).toBe("2020-03-01");
expect(result?.filters.extracted_filters.period_to).toBe("2020-03-31");
expect(result?.filters.extracted_filters.warehouse).toBeUndefined();
expect(result?.baseReasons).toContain("period_from_from_followup_context");
expect(result?.baseReasons).toContain("period_to_from_followup_context");
});
it("does not inherit stale inventory as-of date over an explicit fresh snapshot date", () => {
const result = runAddressDecomposeStage(
"Покажи складской срез Альтернативы Плюс на 2026-04-16: что есть в остатках, какие самые заметные позиции, и что это говорит о бизнесе.",
{
previous_intent: "inventory_on_hand_as_of_date",
target_intent: "inventory_on_hand_as_of_date",
previous_filters: {
organization: "ООО Альтернатива Плюс",
period_from: "2020-01-01",
period_to: "2020-12-31",
as_of_date: "2020-12-31"
},
previous_anchor_type: "organization",
previous_anchor_value: "ООО Альтернатива Плюс"
}
);
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("inventory_on_hand_as_of_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-04-16");
expect(result?.filters.extracted_filters.period_from).toBe("2026-04-01");
expect(result?.filters.extracted_filters.period_to).toBe("2026-04-30");
expect(result?.baseReasons).not.toContain("as_of_date_from_open_items_followup_context");
});
it("retargets inventory purchase-date VAT bridge into confirmed VAT period with inherited purchase month", () => {
const result = runAddressDecomposeStage("ндс можешь прикинуть на дату покупки рабочей станции?", {
previous_intent: "inventory_purchase_provenance_for_item",
target_intent: "vat_liability_confirmed_for_tax_period",
previous_filters: {
item: "Рабочая станция универсального специалиста",
organization: 'ООО "Альтернатива Плюс"',
period_from: "2015-02-01",
period_to: "2015-02-28",
as_of_date: "2015-02-28",
purchase_date_bridge_selected: "2015-02-05",
purchase_date_bridge_first: "2015-02-05",
purchase_date_bridge_last: "2015-07-22",
purchase_date_bridge_basis: "first_confirmed_purchase_default",
purchase_date_bridge_has_multiple: true
},
previous_anchor_type: "item",
previous_anchor_value: "Рабочая станция универсального специалиста"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2015-02-01");
expect(result?.filters.extracted_filters.period_to).toBe("2015-02-28");
expect(result?.filters.extracted_filters.as_of_date).toBe("2015-02-28");
expect(result?.filters.extracted_filters.purchase_date_bridge_selected).toBe("2015-02-05");
expect(result?.filters.extracted_filters.purchase_date_bridge_has_multiple).toBe(true);
expect(result?.baseReasons).toContain("period_from_purchase_date_bridge_followup_context");
expect(result?.baseReasons).toContain("purchase_date_bridge_from_followup_context");
});
it("does not reuse purchase-date VAT bridge when the next VAT question has an explicit period", () => {
const result = runAddressDecomposeStage("а какой ндс мы должны сгрузить на март 2020?", {
previous_intent: "vat_liability_confirmed_for_tax_period",
target_intent: "vat_liability_confirmed_for_tax_period",
previous_filters: {
item: "Рабочая станция универсального специалиста",
organization: 'ООО "Альтернатива Плюс"',
period_from: "2015-02-01",
period_to: "2015-02-28",
as_of_date: "2015-02-28",
purchase_date_bridge_selected: "2015-02-05",
purchase_date_bridge_first: "2015-02-05",
purchase_date_bridge_last: "2015-07-22",
purchase_date_bridge_basis: "first_confirmed_purchase_default",
purchase_date_bridge_has_multiple: true
},
previous_anchor_type: "organization",
previous_anchor_value: 'ООО "Альтернатива Плюс"'
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.period_from).toBe("2020-03-01");
expect(result?.filters.extracted_filters.period_to).toBe("2020-03-31");
expect(result?.filters.extracted_filters.as_of_date).toBeUndefined();
expect(result?.filters.extracted_filters.purchase_date_bridge_selected).toBeUndefined();
expect(result?.baseReasons).toContain("purchase_date_bridge_suppressed_by_explicit_temporal_scope");
expect(result?.baseReasons).not.toContain("period_from_purchase_date_bridge_followup_context");
});
it("keeps first confirmed purchase VAT recalculation in the VAT lane without repeating VAT wording", () => {
const result = runAddressDecomposeStage("если у нее несколько закупок, посчитай по первой подтвержденной покупке", {
previous_intent: "vat_liability_confirmed_for_tax_period",
target_intent: "vat_liability_confirmed_for_tax_period",
previous_filters: {
organization: 'ООО "Альтернатива Плюс"',
period_from: "2015-02-01",
period_to: "2015-02-28",
as_of_date: "2015-02-28",
purchase_date_bridge_selected: "2015-02-05",
purchase_date_bridge_first: "2015-02-05",
purchase_date_bridge_last: "2015-07-22",
purchase_date_bridge_basis: "first_confirmed_purchase_default",
purchase_date_bridge_has_multiple: true
},
previous_anchor_type: "organization",
previous_anchor_value: 'ООО "Альтернатива Плюс"'
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.purchase_date_bridge_selected).toBe("2015-02-05");
expect(result?.filters.extracted_filters.purchase_date_bridge_basis).toBe("first_confirmed_purchase");
expect(result?.filters.extracted_filters.period_from).toBe("2015-02-01");
expect(result?.filters.extracted_filters.period_to).toBe("2015-02-28");
expect(result?.baseReasons).toContain("intent_adjusted_to_inventory_purchase_date_vat_bridge");
expect(result?.baseReasons).toContain("purchase_date_bridge_from_followup_context");
});
it("keeps last confirmed purchase VAT recalculation and moves the tax period to the last purchase month", () => {
const result = runAddressDecomposeStage("а теперь по последней подтвержденной покупке", {
previous_intent: "vat_liability_confirmed_for_tax_period",
target_intent: "vat_liability_confirmed_for_tax_period",
previous_filters: {
organization: 'ООО "Альтернатива Плюс"',
period_from: "2015-02-01",
period_to: "2015-02-28",
as_of_date: "2015-02-28",
purchase_date_bridge_selected: "2015-02-05",
purchase_date_bridge_first: "2015-02-05",
purchase_date_bridge_last: "2015-07-22",
purchase_date_bridge_basis: "first_confirmed_purchase",
purchase_date_bridge_has_multiple: true
},
previous_anchor_type: "organization",
previous_anchor_value: 'ООО "Альтернатива Плюс"'
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("vat_liability_confirmed_for_tax_period");
expect(result?.filters.extracted_filters.purchase_date_bridge_selected).toBe("2015-07-22");
expect(result?.filters.extracted_filters.purchase_date_bridge_basis).toBe("last_confirmed_purchase");
expect(result?.filters.extracted_filters.period_from).toBe("2015-07-01");
expect(result?.filters.extracted_filters.period_to).toBe("2015-07-31");
expect(result?.filters.extracted_filters.as_of_date).toBe("2015-07-31");
});
it("clears counterparty noise from bare organization clarification selections that resume inventory", () => {
const result = runAddressDecomposeStage("АЛЬТЕРНАТИВА", {
previous_intent: "inventory_on_hand_as_of_date",
target_intent: "inventory_on_hand_as_of_date",
previous_filters: {
organization: "ООО Альтернатива Плюс",
as_of_date: "2026-04-19"
},
previous_anchor_type: "organization",
previous_anchor_value: "ООО Альтернатива Плюс"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("inventory_on_hand_as_of_date");
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
expect(result?.filters.extracted_filters.as_of_date).toBe("2026-04-19");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
});
it("does not re-inherit organization alias as counterparty into historical inventory follow-up", () => {
const result = runAddressDecomposeStage("давай на июль 2017", {
previous_intent: "inventory_on_hand_as_of_date",
target_intent: "inventory_on_hand_as_of_date",
previous_filters: {
organization: "ООО Альтернатива Плюс",
counterparty: "АЛЬТЕРНАТИВА",
as_of_date: "2026-04-19"
},
previous_anchor_type: "counterparty",
previous_anchor_value: "АЛЬТЕРНАТИВА",
root_intent: "inventory_on_hand_as_of_date",
root_filters: {
organization: "ООО Альтернатива Плюс",
as_of_date: "2026-04-19"
},
root_anchor_type: "organization",
root_anchor_value: "ООО Альтернатива Плюс",
current_frame_kind: "inventory_root"
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("inventory_on_hand_as_of_date");
expect(result?.filters.extracted_filters.organization).toBe("ООО Альтернатива Плюс");
expect(result?.filters.extracted_filters.period_from).toBe("2017-07-01");
expect(result?.filters.extracted_filters.period_to).toBe("2017-07-31");
expect(result?.filters.extracted_filters.counterparty).toBeUndefined();
expect(result?.baseReasons).toContain("counterparty_cleared_as_organization_scope_alias");
});
it("does not inherit stale historical period into a fresh counterparty document root", () => {
const result = runAddressDecomposeStage("по чепурнову покажи все доки", {
previous_intent: "inventory_on_hand_as_of_date",
target_intent: "list_documents_by_counterparty",
previous_filters: {
organization: "ООО Альтернатива Плюс",
period_from: "2016-03-01",
period_to: "2016-03-31",
as_of_date: "2016-03-31"
},
previous_anchor_type: "organization",
previous_anchor_value: "ООО Альтернатива Плюс"
}, {
scope_target_kind: "counterparty",
scope_target_text: "Чепурнов",
date_scope_kind: "missing",
self_scope_detected: false,
selected_object_scope_detected: false
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("list_documents_by_counterparty");
expect(result?.filters.extracted_filters.counterparty).toBeTruthy();
expect(result?.filters.extracted_filters.period_from).toBeUndefined();
expect(result?.filters.extracted_filters.period_to).toBeUndefined();
});
it("replaces document and movement service-word anchors from counterparty follow-up context", () => {
const followupContext = {
previous_intent: "customer_revenue_and_payments" as const,
target_intent: "list_documents_by_counterparty" as const,
previous_filters: {
organization: "ООО Альтернатива Плюс",
counterparty: "Группа СВК",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty" as const,
previous_anchor_value: "Группа СВК",
resolved_counterparty_from_display: true
};
const documents = runAddressDecomposeStage("документы по контрагенту документам", followupContext);
const movements = runAddressDecomposeStage("Проверить движение по счетам или документам", followupContext);
expect(documents?.intent.intent).toBe("list_documents_by_counterparty");
expect(documents?.filters.extracted_filters.counterparty).toBe("Группа СВК");
expect(documents?.baseReasons).toContain("counterparty_from_followup_context");
expect(movements?.intent.intent).toBe("list_documents_by_counterparty");
expect(movements?.filters.extracted_filters.counterparty).toBe("Группа СВК");
expect(movements?.baseReasons).toContain("counterparty_from_followup_context");
});
it("keeps value-flow period for counterparty document evidence follow-up", () => {
const followupContext = {
previous_intent: "customer_revenue_and_payments" as const,
target_intent: "list_documents_by_counterparty" as const,
previous_filters: {
counterparty: "Группа СВК",
period_from: "2020-01-01",
period_to: "2020-12-31"
},
previous_anchor_type: "counterparty" as const,
previous_anchor_value: "Группа СВК",
resolved_counterparty_from_display: true
};
const documents = runAddressDecomposeStage("а по документам?", followupContext);
expect(documents?.intent.intent).toBe("list_documents_by_counterparty");
expect(documents?.filters.extracted_filters.counterparty).toBe("Группа СВК");
expect(documents?.filters.extracted_filters.period_from).toBe("2020-01-01");
expect(documents?.filters.extracted_filters.period_to).toBe("2020-12-31");
expect(documents?.baseReasons).toContain("period_from_followup_context");
});
it("replaces pronoun chain anchors from counterparty follow-up context", () => {
const followupContext = {
previous_intent: "customer_revenue_and_payments" as const,
target_intent: "list_documents_by_counterparty" as const,
previous_filters: {
organization: "ООО Альтернатива Плюс",
counterparty: "Группа СВК"
},
previous_anchor_type: "counterparty" as const,
previous_anchor_value: "Группа СВК",
resolved_counterparty_from_display: true
};
const documents = runAddressDecomposeStage(
"покажи документы по этой цепочке и не смешивай Группа СВК с организацией ООО Альтернатива Плюс",
followupContext
);
expect(documents?.intent.intent).toBe("list_documents_by_counterparty");
expect(documents?.filters.extracted_filters.counterparty).toBe("Группа СВК");
expect(documents?.baseReasons).toContain("counterparty_from_followup_context");
});
});