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"); }); 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: "2016-03-31" }, 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?.baseReasons).toContain("period_from_followup_context"); }); 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"); }); });