ДОМЕНЫ - ВОПРОСЫ - СКЛАД - Починить каскад складских follow-up: удержать дату, поднять прямой ответ вверх и усилить analyst-loop

This commit is contained in:
2026-04-14 15:11:46 +03:00
parent d41819eabd
commit 27bd4f18fb
16 changed files with 486 additions and 46 deletions
@@ -0,0 +1,74 @@
import { describe, expect, it } from "vitest";
import { runAddressDecomposeStage } from "../src/services/address_runtime/decomposeStage";
import { composeFactualReply } from "../src/services/address_runtime/composeStage";
describe("inventory aging follow-up", () => {
it("keeps carried date window for 'на эту дату' aging follow-up", () => {
const result = runAddressDecomposeStage("Какие остатки по товарам на эту дату относятся к старым закупкам", {
previous_intent: "inventory_purchase_provenance_for_item",
previous_filters: {
item: "Четки Пост (84*117)",
as_of_date: "2021-09-30",
period_from: "2021-09-01",
period_to: "2021-09-30",
warehouse: "Основной склад",
organization: "ООО \\Альтернатива Плюс\\"
},
previous_anchor_type: "unknown",
previous_anchor_value: null
});
expect(result).not.toBeNull();
expect(result?.intent.intent).toBe("inventory_aging_by_purchase_date");
expect(result?.filters.extracted_filters.as_of_date).toBe("2021-09-30");
expect(result?.filters.extracted_filters.period_from).toBe("2021-09-01");
expect(result?.filters.extracted_filters.period_to).toBe("2021-09-30");
expect(result?.filters.extracted_filters.sort).toBe("period_asc");
expect(result?.baseReasons).toContain("as_of_date_from_followup_context");
expect(result?.baseReasons).toContain("period_from_followup_context");
});
it("renders aging answer as oldest-first item list instead of raw documents", () => {
const reply = composeFactualReply(
"inventory_aging_by_purchase_date",
[
{
period: "2019-02-06T00:00:00Z",
registrator: "Поступление товаров и услуг 00000000004 от 06.02.2019 0:00:00",
account_dt: "41.01",
account_kt: "60.01",
amount: 833.33,
analytics: ["Четки Пост (84*117)", "Основной склад", "Торговый дом \\Союз\\"],
item: "Четки Пост (84*117)",
warehouse: "Основной склад",
organization: "ООО \\Альтернатива Плюс\\"
},
{
period: "2020-11-20T12:00:00Z",
registrator: "Поступление товаров и услуг 00000000030 от 20.11.2020 12:00:00",
account_dt: "41.01",
account_kt: "60.01",
amount: 7250,
analytics: ["Зеркало для инвалидов поворотное травмобезопасное", "Основной склад", "ВИЗАНТИЯ"],
item: "Зеркало для инвалидов поворотное травмобезопасное",
warehouse: "Основной склад",
organization: "ООО \\Альтернатива Плюс\\"
}
],
{
asOfDate: "2021-09-30",
useRubCurrency: true
}
);
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
expect(reply.text).toContain("К старым закупкам на 30.09.2021");
expect(reply.text).toContain("Блок 3. Позиции от самых старых закупок");
expect(reply.text).not.toContain("Блок 3. Опорные документы");
expect(reply.text.indexOf("1. Четки Пост (84*117)")).toBeGreaterThan(-1);
expect(reply.text.indexOf("2. Зеркало для инвалидов поворотное травмобезопасное")).toBeGreaterThan(
reply.text.indexOf("1. Четки Пост (84*117)")
);
});
});
@@ -22,7 +22,7 @@ afterEach(() => {
});
describe("inventory selected-object follow-up", () => {
it("auto-broadens dated stock follow-up window for inventory provenance", async () => {
it("inherits dated stock window for selected-object provenance and then auto-broadens history", async () => {
executeAddressMcpQueryMock
.mockResolvedValueOnce({
fetched_rows: 1,
@@ -79,16 +79,36 @@ describe("inventory selected-object follow-up", () => {
const service = new AddressQueryService();
const result = await service.tryHandle(
'По выбранному объекту "Кромка с клеем 33 альмандин 137 м | склад: Основной склад | количество: 1,000 | стоимость: 165,83 ₽ | организация: ООО \\\\Альтернатива Плюс\\\\ | дата строки: 2021-03-31T23:59:59Z": От какого поставщика куплен товар'
'По выбранному объекту "Кромка с клеем 33 альмандин 137 м | склад: Основной склад | количество: 1,000 | стоимость: 165,83 ₽ | организация: ООО \\\\Альтернатива Плюс\\\\ | дата строки: 2021-03-31T23:59:59Z": От какого поставщика куплен товар',
{
followupContext: {
previous_intent: "inventory_on_hand_as_of_date",
previous_filters: {
as_of_date: "2021-03-31",
period_from: "2021-03-01",
period_to: "2021-03-31",
warehouse: "Основной склад",
organization: "ООО \\Альтернатива Плюс\\"
},
previous_anchor_type: "unknown",
previous_anchor_value: null
}
}
);
expect(result?.handled).toBe(true);
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
expect(result?.debug.detected_intent).toBe("inventory_purchase_provenance_for_item");
expect(result?.debug.extracted_filters?.item).toBe("Кромка с клеем 33 альмандин 137 м");
expect(result?.debug.extracted_filters?.as_of_date).toBe("2021-03-31");
expect(result?.debug.extracted_filters?.period_from).toBe("2021-03-01");
expect(result?.debug.extracted_filters?.period_to).toBe("2021-03-31");
expect(result?.debug.reasons).toContain("period_window_auto_broadened_to_available_data");
expect(result?.debug.limitations).toContain("period_window_auto_broadened_to_available_data");
expect(String(result?.reply_text ?? "")).toContain("Торговый дом \\Союз МСК\\");
const replyLines = String(result?.reply_text ?? "").split("\n");
expect(replyLines[0]).toContain("Товар Кромка с клеем 33 альмандин 137 м");
expect(replyLines[0]).toContain("Торговый дом \\Союз МСК\\");
expect(replyLines[1]).toContain("По окну 2021-03-01..2021-03-31 строк не найдено");
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(2);
});
});