Починить Hm-прогон ассистента по НДС, остаткам и MCP-ответам
This commit is contained in:
@@ -244,7 +244,12 @@ describe("address follow-up temporal regressions", () => {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
period_from: "2015-02-01",
|
||||
period_to: "2015-02-28",
|
||||
as_of_date: "2016-03-31"
|
||||
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: "Рабочая станция универсального специалиста"
|
||||
@@ -254,7 +259,98 @@ describe("address follow-up temporal regressions", () => {
|
||||
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");
|
||||
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("АЛЬТЕРНАТИВА", {
|
||||
|
||||
@@ -4,6 +4,44 @@ import { runAddressDecomposeStage } from "../src/services/address_runtime/decomp
|
||||
import { composeFactualReply } from "../src/services/address_runtime/composeStage";
|
||||
|
||||
describe("inventory aging follow-up", () => {
|
||||
it("clears organization alias item on root old-stock questions with LLM organization semantics", () => {
|
||||
const result = runAddressDecomposeStage(
|
||||
'Есть ли у ООО "Альтернатива Плюс" остатки товара, которые закупались очень давно',
|
||||
null,
|
||||
{
|
||||
scope_target_kind: "organization",
|
||||
scope_target_text: 'ООО "Альтернатива Плюс"',
|
||||
date_scope_kind: "implicit_current",
|
||||
self_scope_detected: false,
|
||||
selected_object_scope_detected: false
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.intent.intent).toBe("inventory_aging_by_purchase_date");
|
||||
expect(result?.filters.extracted_filters.organization).toBe('ООО "Альтернатива Плюс"');
|
||||
expect(result?.filters.extracted_filters.item).toBeUndefined();
|
||||
expect(result?.baseReasons).toContain("item_cleared_as_organization_scope_alias_for_stock_aging");
|
||||
});
|
||||
|
||||
it("does not mistake an explicit organization name for an inventory item in old-stock questions", () => {
|
||||
const result = runAddressDecomposeStage('Есть ли у ООО "Альтернатива Плюс" остатки товара, которые закупались очень давно', {
|
||||
previous_intent: "inventory_aging_by_purchase_date",
|
||||
previous_filters: {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2026-05-25"
|
||||
},
|
||||
previous_anchor_type: "organization",
|
||||
previous_anchor_value: 'ООО "Альтернатива Плюс"'
|
||||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.intent.intent).toBe("inventory_aging_by_purchase_date");
|
||||
expect(result?.filters.extracted_filters.organization).toBe('ООО "Альтернатива Плюс"');
|
||||
expect(result?.filters.extracted_filters.item).toBeUndefined();
|
||||
expect(result?.baseReasons).toContain("item_cleared_as_organization_scope_alias_for_stock_aging");
|
||||
});
|
||||
|
||||
it("keeps carried date window for 'на эту дату' aging follow-up", () => {
|
||||
const result = runAddressDecomposeStage("Какие остатки по товарам на эту дату относятся к старым закупкам", {
|
||||
previous_intent: "inventory_purchase_provenance_for_item",
|
||||
|
||||
@@ -2182,6 +2182,49 @@ describe("address compose stage utf8 headers", () => {
|
||||
expect(reply.text).toContain("книг продаж/покупок");
|
||||
});
|
||||
|
||||
it("discloses ambiguous inventory purchase-date VAT bridge instead of hiding the selected date", () => {
|
||||
const reply = composeFactualReply(
|
||||
"vat_liability_confirmed_for_tax_period",
|
||||
[
|
||||
{
|
||||
period: "2015-02-28T23:59:59Z",
|
||||
registrator: "VAT_BOOK_SALES",
|
||||
account_dt: "68.02",
|
||||
account_kt: "",
|
||||
amount: 3500000,
|
||||
analytics: []
|
||||
},
|
||||
{
|
||||
period: "2015-02-28T23:59:59Z",
|
||||
registrator: "VAT_BOOK_PURCHASES",
|
||||
account_dt: "19",
|
||||
account_kt: "",
|
||||
amount: 868612,
|
||||
analytics: []
|
||||
}
|
||||
],
|
||||
{
|
||||
userMessage: "ндс можешь прикинуть на дату покупки рабочей станции?",
|
||||
periodFrom: "2015-02-01",
|
||||
periodTo: "2015-02-28",
|
||||
asOfDate: "2015-02-28",
|
||||
organizationHint: "ООО Альтернатива Плюс",
|
||||
purchaseDateBridge: {
|
||||
selectedPurchaseDate: "2015-02-05",
|
||||
firstPurchaseDate: "2015-02-05",
|
||||
lastPurchaseDate: "2015-07-22",
|
||||
basis: "first_confirmed_purchase_default",
|
||||
hasMultiplePurchaseDates: true
|
||||
},
|
||||
useRubCurrency: true
|
||||
}
|
||||
);
|
||||
|
||||
expect(reply.text).toContain("если брать первую подтвержденную дату закупки 05.02.2015");
|
||||
expect(reply.text).toContain("у позиции несколько подтвержденных дат закупки");
|
||||
expect(reply.text).toContain("05.02.2015..22.07.2015");
|
||||
});
|
||||
|
||||
it("formats VAT forecast amounts in rubles and emphasizes numbers when requested", () => {
|
||||
const reply = composeFactualReply(
|
||||
"vat_payable_forecast",
|
||||
@@ -5741,6 +5784,24 @@ it("routes old purchase residue questions to aging-by-purchase-date", () => {
|
||||
expect(plan.query).toContain("ПОДСТРОКА(ЕСТЬNULL(Остатки.Счет.Код, \"\"), 1, 5) = \"41.01\"");
|
||||
});
|
||||
|
||||
it("builds old-stock aging query from positive stock snapshot plus purchase history", () => {
|
||||
const selected = selectAddressRecipe("inventory_aging_by_purchase_date", {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2026-04-18"
|
||||
});
|
||||
expect(selected.selected_recipe?.recipe_id).toBe("address_inventory_aging_by_purchase_date_v1");
|
||||
const plan = buildAddressRecipePlan(selected.selected_recipe!, {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2026-04-18"
|
||||
});
|
||||
|
||||
expect(plan.query).toContain("РегистрБухгалтерии.Хозрасчетный.Остатки(");
|
||||
expect(plan.query).toContain("Документ.ПоступлениеТоваровУслуг.Товары");
|
||||
expect(plan.query).toContain("Остатки.КоличествоРазвернутыйОстатокДт > 0");
|
||||
expect(plan.query).toContain("Товары.Номенклатура В");
|
||||
expect(plan.query).toContain("Товары.Ссылка.Дата <= ДАТАВРЕМЯ(2026, 4, 18, 23, 59, 59)");
|
||||
});
|
||||
|
||||
it("renders confirmed inventory-on-hand snapshot from normalized rows", () => {
|
||||
const reply = composeFactualReply(
|
||||
"inventory_on_hand_as_of_date",
|
||||
@@ -5776,6 +5837,59 @@ it("routes old purchase residue questions to aging-by-purchase-date", () => {
|
||||
expect(reply.semantics?.balance_confirmed).toBe(true);
|
||||
});
|
||||
|
||||
it("renders old-stock aging only for items that are still in positive stock", () => {
|
||||
const reply = composeFactualReply(
|
||||
"inventory_aging_by_purchase_date",
|
||||
[
|
||||
{
|
||||
period: "2015-02-05T00:00:00Z",
|
||||
registrator: "Поступление товаров и услуг 00000000001 от 05.02.2015",
|
||||
account_dt: "41.01",
|
||||
account_kt: "",
|
||||
amount: 1000,
|
||||
analytics: ["Старый проданный товар", 'ООО "Альтернатива Плюс"', "Поставщик А"],
|
||||
item: "Старый проданный товар",
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
counterparty: "Поставщик А",
|
||||
quantity: 1
|
||||
},
|
||||
{
|
||||
period: "2017-03-10T00:00:00Z",
|
||||
registrator: "Поступление товаров и услуг 00000000002 от 10.03.2017",
|
||||
account_dt: "41.01",
|
||||
account_kt: "",
|
||||
amount: 2500,
|
||||
analytics: ["Товар в остатке", 'ООО "Альтернатива Плюс"', "Поставщик Б"],
|
||||
item: "Товар в остатке",
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
counterparty: "Поставщик Б",
|
||||
quantity: 2
|
||||
},
|
||||
{
|
||||
period: "2026-04-18T23:59:59Z",
|
||||
registrator: "Остатки на дату",
|
||||
account_dt: "41.01",
|
||||
account_kt: "",
|
||||
amount: 2500,
|
||||
analytics: ["Товар в остатке", "Основной склад", 'ООО "Альтернатива Плюс"'],
|
||||
item: "Товар в остатке",
|
||||
warehouse: "Основной склад",
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
quantity: 2
|
||||
}
|
||||
],
|
||||
{
|
||||
asOfDate: "2026-04-18",
|
||||
useRubCurrency: true
|
||||
}
|
||||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_SUMMARY");
|
||||
expect(reply.text.split("\n")[0]).toContain("Товар в остатке");
|
||||
expect(reply.text).toContain("положительным остатком");
|
||||
expect(reply.text).not.toContain("Старый проданный товар");
|
||||
});
|
||||
|
||||
it("sorts inventory-on-hand positions by amount before rendering the top list", () => {
|
||||
const reply = composeFactualReply(
|
||||
"inventory_on_hand_as_of_date",
|
||||
|
||||
@@ -270,7 +270,7 @@ describe("address reply builders regressions", () => {
|
||||
);
|
||||
|
||||
const firstLine = result?.text.split("\n")[0] ?? "";
|
||||
expect(firstLine).toContain("К самым старым закупкам");
|
||||
expect(firstLine).toContain("фактических положительных остатков");
|
||||
expect(firstLine).not.toContain("2026-04-18");
|
||||
});
|
||||
|
||||
|
||||
@@ -245,4 +245,90 @@ describe("assistant deep turn response runtime adapter", () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("sanitizes guarded MCP discovery candidates before finalizing the visible reply", () => {
|
||||
const runPackagingRuntime = vi.fn(() => ({
|
||||
messageId: "msg-a1",
|
||||
investigationStateSnapshot: null,
|
||||
droppedIntentSegments: [],
|
||||
analysisContextForContract: null,
|
||||
routesForDebug: [],
|
||||
resolvedExecutionState: [],
|
||||
safeAssistantReplyBase: "bad-base",
|
||||
safeAssistantReply: "bad deep partial answer",
|
||||
debug: { trace_id: "trace-1" },
|
||||
assistantItem: {
|
||||
message_id: "msg-a1",
|
||||
session_id: "asst-1",
|
||||
role: "assistant",
|
||||
text: "bad deep partial answer",
|
||||
reply_type: "partial_coverage",
|
||||
created_at: "2026-04-10T00:00:00.000Z",
|
||||
trace_id: "trace-1",
|
||||
debug: null
|
||||
},
|
||||
deepAnalysisLogDetails: {}
|
||||
}));
|
||||
const sanitizeReply = vi.fn((value: string) =>
|
||||
value.replace(/^\u041a\u043e\u0440\u043e\u0442\u043a\u043e:\s*/u, "")
|
||||
);
|
||||
const runFinalizeDeepTurn = vi.fn((input) => ({
|
||||
response: {
|
||||
ok: true,
|
||||
session_id: "asst-1",
|
||||
assistant_reply: input.assistantReply,
|
||||
reply_type: input.replyType,
|
||||
conversation_item: input.assistantItem,
|
||||
conversation: [],
|
||||
debug: input.debug
|
||||
}
|
||||
}));
|
||||
|
||||
const runtime = runAssistantDeepTurnResponseRuntime(
|
||||
buildBaseInput({
|
||||
composition: { reply_type: "partial_coverage" },
|
||||
sanitizeReply,
|
||||
addressRuntimeMetaForDeep: {
|
||||
mcpDiscoveryRuntimeEntryPoint: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
|
||||
entry_status: "bridge_executed",
|
||||
hot_runtime_wired: false,
|
||||
discovery_attempted: true,
|
||||
turn_input: { should_run_discovery: true },
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "Коротко: Discovery answer",
|
||||
confirmed_lines: ["Confirmed fact"],
|
||||
inference_lines: [],
|
||||
unknown_lines: [],
|
||||
limitation_lines: [],
|
||||
next_step_line: null
|
||||
}
|
||||
},
|
||||
reason_codes: []
|
||||
}
|
||||
},
|
||||
runPackagingRuntime,
|
||||
runFinalizeDeepTurn
|
||||
})
|
||||
);
|
||||
|
||||
expect(sanitizeReply).toHaveBeenCalledWith(expect.not.stringContaining("Коротко:"), "bad deep partial answer");
|
||||
expect(runtime.response.assistant_reply).toContain("Discovery answer");
|
||||
expect(runtime.response.assistant_reply).not.toContain("Коротко:");
|
||||
expect(runFinalizeDeepTurn).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
assistantReply: expect.not.stringContaining("Коротко:"),
|
||||
assistantItem: expect.objectContaining({
|
||||
text: expect.not.stringContaining("Коротко:")
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,6 +48,37 @@ describe("assistant MCP discovery response policy", () => {
|
||||
expect(result.reason_codes).toContain("mcp_discovery_response_policy_candidate_applied");
|
||||
});
|
||||
|
||||
it("removes routine short-answer prefix from applied guarded candidates", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "route is not wired",
|
||||
currentReplySource: "deterministic_unsupported_current_turn_boundary",
|
||||
modeDecisionReason: "unsupported_current_turn_meaning_boundary",
|
||||
addressRuntimeMeta: {
|
||||
mcpDiscoveryRuntimeEntryPoint: entryPoint({
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
user_facing_response_allowed: true,
|
||||
business_fact_answer_allowed: true,
|
||||
requires_user_clarification: false,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference",
|
||||
headline: "Коротко: Confirmed scoped answer.",
|
||||
confirmed_lines: ["Коротко: Confirmed fact"],
|
||||
inference_lines: [],
|
||||
unknown_lines: [],
|
||||
limitation_lines: [],
|
||||
next_step_line: null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.applied).toBe(true);
|
||||
expect(result.reply_text).toContain("Confirmed scoped answer.");
|
||||
expect(result.reply_text).not.toContain("Коротко:");
|
||||
});
|
||||
|
||||
it("keeps the current reply when the turn is not an unsupported boundary", () => {
|
||||
const result = applyAssistantMcpDiscoveryResponsePolicy({
|
||||
currentReply: "regular chat",
|
||||
|
||||
@@ -214,6 +214,152 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(contract.decision).toBe("continue_previous");
|
||||
});
|
||||
|
||||
it("carries explicit purchase-date VAT bridge facts without silently losing ambiguity", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
role: "assistant",
|
||||
text: [
|
||||
"По позиции Рабочая станция до 31.03.2016 однозначный поставщик не подтвержден.",
|
||||
"- Первая найденная дата закупки: 05.02.2015.",
|
||||
"- Последняя найденная дата закупки: 22.07.2015."
|
||||
].join("\n"),
|
||||
debug: {
|
||||
detected_intent: "inventory_purchase_provenance_for_item",
|
||||
extracted_filters: {
|
||||
item: "Рабочая станция",
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2016-03-31"
|
||||
},
|
||||
anchor_type: "item",
|
||||
anchor_value_resolved: "Рабочая станция"
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"ндс можешь прикинуть на дату покупки рабочей станции?",
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
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
|
||||
});
|
||||
});
|
||||
|
||||
it("can retarget a follow-up VAT recalculation to the last confirmed purchase date", () => {
|
||||
const items = [
|
||||
{
|
||||
role: "assistant",
|
||||
text: [
|
||||
"По позиции Рабочая станция до 31.03.2016 однозначный поставщик не подтвержден.",
|
||||
"- Первая найденная дата закупки: 05.02.2015.",
|
||||
"- Последняя найденная дата закупки: 22.07.2015."
|
||||
].join("\n"),
|
||||
debug: {
|
||||
detected_intent: "inventory_purchase_provenance_for_item",
|
||||
extracted_filters: {
|
||||
item: "Рабочая станция",
|
||||
organization: 'ООО "Альтернатива Плюс"'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
text: "Коротко: если брать первую подтвержденную дату закупки 05.02.2015, НДС к уплате 100.",
|
||||
debug: {
|
||||
detected_intent: "vat_liability_confirmed_for_tax_period",
|
||||
extracted_filters: {
|
||||
item: "Рабочая станция",
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
purchase_date_bridge_selected: "2015-02-05"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: (sourceItems: unknown[]) => {
|
||||
const list = sourceItems as Record<string, unknown>[];
|
||||
return list[list.length - 1];
|
||||
}
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"а теперь по последней подтвержденной покупке",
|
||||
items,
|
||||
null,
|
||||
null,
|
||||
{
|
||||
session_context: {
|
||||
active_focus_object: {
|
||||
object_type: "item",
|
||||
label: "Рабочая станция"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
period_from: "2015-07-01",
|
||||
period_to: "2015-07-31",
|
||||
as_of_date: "2015-07-31",
|
||||
purchase_date_bridge_selected: "2015-07-22",
|
||||
purchase_date_bridge_basis: "last_confirmed_purchase"
|
||||
});
|
||||
});
|
||||
|
||||
it("continues purchase-date VAT recalculation from prior VAT bridge filters without relying on active item focus", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
role: "assistant",
|
||||
text: "Коротко: если брать первую подтвержденную дату закупки 05.02.2015, НДС к уплате 100.",
|
||||
debug: {
|
||||
detected_intent: "vat_liability_confirmed_for_tax_period",
|
||||
extracted_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
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"если у нее несколько закупок, посчитай по первой подтвержденной покупке",
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.target_intent).toBe("vat_liability_confirmed_for_tax_period");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
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_basis: "first_confirmed_purchase",
|
||||
purchase_date_bridge_has_multiple: true
|
||||
});
|
||||
});
|
||||
|
||||
it("retargets displayed counterparty follow-up through shared referenced-entity carryover", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
|
||||
Reference in New Issue
Block a user