Open-World: включить маржинальность выбранной номенклатуры
This commit is contained in:
@@ -85,7 +85,7 @@ describe("address capability policy", () => {
|
||||
expect(chainDecision.capability_route_mode).toBe("exact");
|
||||
});
|
||||
|
||||
it("enables purchase-to-sale trace and aging by purchase date", () => {
|
||||
it("enables purchase-to-sale trace, item profitability, and aging by purchase date", () => {
|
||||
const chainDecision = resolveAddressCapabilityRouteDecision("inventory_purchase_to_sale_chain");
|
||||
expect(chainDecision.capability_id).toBe("inventory_inventory_purchase_to_sale_chain");
|
||||
expect(chainDecision.capability_route_mode).toBe("exact");
|
||||
@@ -93,6 +93,14 @@ describe("address capability policy", () => {
|
||||
expect(chainDecision.capability_route_reason).toBe("inventory_purchase_to_sale_chain_route_enabled");
|
||||
expect(isCapabilityRouteBlocked(chainDecision)).toBe(false);
|
||||
|
||||
const profitabilityDecision = resolveAddressCapabilityRouteDecision("inventory_profitability_for_item");
|
||||
expect(profitabilityDecision.capability_id).toBe("inventory_inventory_profitability_for_item");
|
||||
expect(profitabilityDecision.capability_layer).toBe("compute");
|
||||
expect(profitabilityDecision.capability_route_mode).toBe("exact");
|
||||
expect(profitabilityDecision.capability_route_enabled).toBe(true);
|
||||
expect(profitabilityDecision.capability_route_reason).toBe("inventory_profitability_route_enabled");
|
||||
expect(isCapabilityRouteBlocked(profitabilityDecision)).toBe(false);
|
||||
|
||||
const agingDecision = resolveAddressCapabilityRouteDecision("inventory_aging_by_purchase_date");
|
||||
expect(agingDecision.capability_id).toBe("inventory_inventory_aging_by_purchase_date");
|
||||
expect(agingDecision.capability_route_mode).toBe("exact");
|
||||
|
||||
+46
-6
@@ -49,19 +49,59 @@ describe("inventory profitability selected-object regressions", () => {
|
||||
expect(result?.filters.extracted_filters.period_to).toBe("2020-05-31");
|
||||
});
|
||||
|
||||
it("returns a truthful recipe visibility gap until item profitability gets a dedicated recipe", async () => {
|
||||
it("answers selected-object profitability with a bounded document spread instead of a recipe gap", async () => {
|
||||
executeAddressMcpQueryMock.mockResolvedValueOnce({
|
||||
fetched_rows: 2,
|
||||
matched_rows: 2,
|
||||
raw_rows: [
|
||||
{
|
||||
Period: "2020-05-10T00:00:00Z",
|
||||
Registrator: "Поступление товаров и услуг 000000001 от 10.05.2020",
|
||||
AccountDt: "41.01",
|
||||
AccountKt: "60.01",
|
||||
Amount: 500,
|
||||
Quantity: 10,
|
||||
SubcontoDt1: "Четки Пост (84*117)",
|
||||
SubcontoDt3: "Основной склад",
|
||||
Counterparty: "ООО \\Поставщик\\",
|
||||
Organization: "ООО \\Альтернатива Плюс\\"
|
||||
},
|
||||
{
|
||||
Period: "2020-05-20T00:00:00Z",
|
||||
Registrator: "Реализация товаров и услуг 000000017 от 20.05.2020",
|
||||
AccountDt: "62.01",
|
||||
AccountKt: "41.01",
|
||||
Amount: 900,
|
||||
Quantity: 10,
|
||||
SubcontoKt1: "Четки Пост (84*117)",
|
||||
SubcontoKt3: "Основной склад",
|
||||
Counterparty: "ИП Покупатель",
|
||||
Organization: "ООО \\Альтернатива Плюс\\"
|
||||
}
|
||||
],
|
||||
rows: [],
|
||||
error: null
|
||||
});
|
||||
|
||||
const service = new AddressQueryService();
|
||||
const result = await service.tryHandle(selectedObjectProfitabilityMessage, {
|
||||
followupContext
|
||||
});
|
||||
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(result?.response_type).toBe("LIMITED_WITH_REASON");
|
||||
expect(result?.response_type).toBe("FACTUAL_SUMMARY");
|
||||
expect(result?.debug.detected_intent).toBe("inventory_profitability_for_item");
|
||||
expect(result?.debug.selected_recipe).toBeNull();
|
||||
expect(result?.debug.limited_reason_category).toBe("recipe_visibility_gap");
|
||||
expect(result?.debug.selected_recipe).toBe("address_inventory_profitability_for_item_v1");
|
||||
expect(result?.debug.capability_id).toBe("inventory_inventory_profitability_for_item");
|
||||
expect(result?.debug.mcp_call_status).toBe("matched_non_empty");
|
||||
expect(result?.debug.extracted_filters?.item).toBe("Четки Пост (84*117)");
|
||||
expect(String(result?.reply_text ?? "")).toContain("Четки Пост (84*117)");
|
||||
expect(executeAddressMcpQueryMock).not.toHaveBeenCalled();
|
||||
const reply = String(result?.reply_text ?? "");
|
||||
expect(reply).toContain("Четки Пост (84*117)");
|
||||
expect(reply).toContain("900");
|
||||
expect(reply).toContain("500");
|
||||
expect(reply).toContain("400");
|
||||
expect(reply).toContain("Маржинальность");
|
||||
expect(reply).toContain("не чистая прибыль компании");
|
||||
expect(executeAddressMcpQueryMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -355,6 +355,20 @@ describe("address query shape classifier", () => {
|
||||
expect(plan.query).toContain("ПРЕДСТАВЛЕНИЕ(Товары.Ссылка.Контрагент) КАК Контрагент");
|
||||
});
|
||||
|
||||
it("builds item profitability query from purchase and sale document rows", () => {
|
||||
const selected = selectAddressRecipe("inventory_profitability_for_item", {
|
||||
item: "Шкаф картотечный"
|
||||
});
|
||||
expect(selected.selected_recipe?.recipe_id).toBe("address_inventory_profitability_for_item_v1");
|
||||
const plan = buildAddressRecipePlan(selected.selected_recipe!, {
|
||||
item: "Шкаф картотечный"
|
||||
});
|
||||
expect(plan.query).toContain("Документ.ПоступлениеТоваровУслуг.Товары КАК Товары");
|
||||
expect(plan.query).toContain("ОБЪЕДИНИТЬ ВСЕ");
|
||||
expect(plan.query).toContain("Документ.РеализацияТоваровУслуг.Товары КАК Товары");
|
||||
expect(plan.query).toContain("ПРЕДСТАВЛЕНИЕ(Товары.Ссылка.Контрагент) КАК Контрагент");
|
||||
});
|
||||
|
||||
it("renders inventory purchase documents from purchase-side 41.01 movements", () => {
|
||||
const reply = composeFactualReply(
|
||||
"inventory_purchase_documents_for_item",
|
||||
|
||||
@@ -71,6 +71,18 @@ describe("assistant runtime contract registry", () => {
|
||||
expect(contract?.required_scenario_families).toContain("pronoun_followup");
|
||||
});
|
||||
|
||||
it("declares selected-item profitability as a bounded focus-object contract", () => {
|
||||
const contract = getAssistantCapabilityContractByIntent("inventory_profitability_for_item");
|
||||
expect(contract?.capability_id).toBe("inventory_inventory_profitability_for_item");
|
||||
expect(contract?.requires_focus_object).toBe(true);
|
||||
expect(contract?.accepted_focus_object_kinds).toEqual(["inventory_item", "item"]);
|
||||
expect(contract?.supported_transition_classes).toEqual(["T3", "T4", "T5", "T7"]);
|
||||
expect(contract?.required_anchors).toEqual(["item"]);
|
||||
expect(contract?.result_shape).toBe("item_revenue_purchase_cost_spread_margin_proxy");
|
||||
expect(contract?.answer_object_shape).toBe("inventory_profitability_bundle");
|
||||
expect(contract?.bundle_reuse_policy).toBe("sale_trace_bundle_preferred");
|
||||
});
|
||||
|
||||
it("declares root financial exact capabilities for debt and vat snapshots", () => {
|
||||
const receivables = getAssistantCapabilityContract("confirmed_receivables_as_of_date");
|
||||
const payables = getAssistantCapabilityContract("confirmed_payables_as_of_date");
|
||||
|
||||
Reference in New Issue
Block a user