Open-World: добавить торговый margin proxy в бизнес-обзор
This commit is contained in:
@@ -294,11 +294,22 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
{ Регистратор: "VAT_BOOK_PURCHASES", СчетДт: "19", Сумма: 12000 }
|
||||
]
|
||||
},
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-03-01T00:00:00", Amount: 200000, Item: "Товар А", Counterparty: "Клиент А", СчетКт: "41.01" },
|
||||
{ Period: "2020-02-01T00:00:00", Amount: 120000, Item: "Товар А", Counterparty: "Поставщик А", СчетДт: "41.01" }
|
||||
]
|
||||
}
|
||||
])
|
||||
);
|
||||
@@ -306,12 +317,20 @@ describe("assistant MCP discovery answer adapter", () => {
|
||||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.headline).toContain("НДС-позиция");
|
||||
expect(draft.headline).toContain("торговый margin proxy");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("НДС-позиция за 2020");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("нетто к уплате 28 000 руб.");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("Торговый margin proxy за 2020");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("валовый спред proxy 80 000 руб.");
|
||||
expect(draft.confirmed_lines.join("\n")).toContain("не чистая прибыль");
|
||||
expect(draft.inference_lines.join("\n")).toContain("торговый спред proxy 80 000 руб.");
|
||||
expect(draft.inference_lines.join("\n")).toContain("не прибыль и не маржа");
|
||||
expect(draft.unknown_lines.join("\n")).toContain("Чистая прибыль");
|
||||
expect(draft.unknown_lines.join("\n")).not.toContain("Налоговая/VAT-позиция");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_tax_position");
|
||||
expect(draft.reason_codes).toContain("answer_contains_business_overview_trading_margin_proxy");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview cash-flow spread as profit or margin.");
|
||||
expect(draft.must_not_claim).toContain("Do not present business overview trading-margin proxy as clean profit, accounting financial result, or exact cost-of-sales margin.");
|
||||
});
|
||||
|
||||
it("surfaces checked debt-position and open-settlement quality without treating them as overdue debt", async () => {
|
||||
|
||||
@@ -151,6 +151,12 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-03-01T00:00:00", Amount: 200000, Item: "Товар А", Counterparty: "Клиент А", СчетКт: "41.01" },
|
||||
{ Period: "2020-02-01T00:00:00", Amount: 120000, Item: "Товар А", Counterparty: "Поставщик А", СчетДт: "41.01" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -251,6 +257,12 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
{ Период: "2020-01-15T00:00:00", Регистратор: "Поступление 1" },
|
||||
{ Период: "2020-12-15T00:00:00", Регистратор: "Поступление 2" }
|
||||
]
|
||||
},
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-03-01T00:00:00", Amount: 200000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" },
|
||||
{ Period: "2020-02-01T00:00:00", Amount: 120000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -266,15 +278,96 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
net_vat_amount: 28000,
|
||||
net_vat_direction: "vat_to_pay"
|
||||
});
|
||||
expect(result.derived_business_overview?.trading_margin_proxy).toMatchObject({
|
||||
period_scope: "2020",
|
||||
sales_rows_with_amount: 1,
|
||||
purchase_rows_with_amount: 1,
|
||||
sales_revenue: 200000,
|
||||
purchase_cost_proxy: 120000,
|
||||
gross_spread_proxy: 80000,
|
||||
margin_to_revenue_pct: 40
|
||||
});
|
||||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("tax_position");
|
||||
expect(result.derived_business_overview?.missing_signal_families).not.toContain("profit_margin");
|
||||
expect(result.derived_business_overview?.missing_signal_families).toContain("accounting_profit_margin");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("НДС-позиция за 2020 подтверждена");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("Торговый margin proxy за 2020");
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain("Чистая прибыль");
|
||||
expect(result.evidence.unknown_facts.join("\n")).not.toContain("Налоговая/VAT-позиция этим бизнес-обзором не подтверждена");
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_tax_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_tax_position_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(9);
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_trading_margin_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_trading_margin_proxy_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(10);
|
||||
const taxCall = deps.executeAddressMcpQuery.mock.calls[2]?.[0];
|
||||
const tradingMarginCall = deps.executeAddressMcpQuery.mock.calls[9]?.[0];
|
||||
expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПродаж");
|
||||
expect(String(taxCall?.query ?? "")).toContain("НДСЗаписиКнигиПокупок");
|
||||
expect(String(tradingMarginCall?.query ?? "")).toContain("Документ.РеализацияТоваровУслуг.Товары");
|
||||
expect(String(tradingMarginCall?.query ?? "")).toContain("Документ.ПоступлениеТоваровУслуг.Товары");
|
||||
});
|
||||
|
||||
it("keeps negative trading-margin proxy signed instead of reporting an absolute spread", async () => {
|
||||
const planner = planAssistantMcpDiscovery({
|
||||
dataNeedGraph: {
|
||||
schema_version: "assistant_data_need_graph_v1",
|
||||
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||
subject_candidates: [],
|
||||
business_fact_family: "business_overview",
|
||||
action_family: "broad_evaluation",
|
||||
aggregation_need: null,
|
||||
time_scope_need: "explicit_period",
|
||||
comparison_need: null,
|
||||
ranking_need: null,
|
||||
proof_expectation: "bounded_inference",
|
||||
clarification_gaps: [],
|
||||
decomposition_candidates: [
|
||||
"collect_scoped_movements",
|
||||
"aggregate_checked_amounts",
|
||||
"aggregate_ranked_axis_values",
|
||||
"fetch_supporting_documents",
|
||||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
],
|
||||
forbidden_overclaim_flags: ["no_raw_model_claims", "no_profit_or_margin_claim_without_evidence"],
|
||||
reason_codes: ["data_need_graph_built", "data_need_graph_family_business_overview"]
|
||||
},
|
||||
turnMeaning: {
|
||||
asked_domain_family: "business_overview",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
});
|
||||
const deps = buildSequentialDeps([
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{ rows: [] },
|
||||
{
|
||||
rows: [
|
||||
{ Period: "2020-03-01T00:00:00", Amount: 100000, Item: "Товар А", Counterparty: "Клиент А", AccountKt: "41.01" },
|
||||
{ Period: "2020-02-01T00:00:00", Amount: 150000, Item: "Товар А", Counterparty: "Поставщик А", AccountDt: "41.01" }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
|
||||
expect(result.derived_business_overview?.trading_margin_proxy).toMatchObject({
|
||||
sales_revenue: 100000,
|
||||
purchase_cost_proxy: 150000,
|
||||
gross_spread_proxy: -50000,
|
||||
gross_spread_proxy_human_ru: "-50 000 руб.",
|
||||
margin_to_revenue_pct: -50
|
||||
});
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("валовый спред proxy -50 000 руб.");
|
||||
expect(result.evidence.confirmed_facts.join("\n")).toContain("не чистая прибыль");
|
||||
});
|
||||
|
||||
it("adds a checked debt-position family to business overview only as an as-of-date snapshot", async () => {
|
||||
@@ -337,7 +430,8 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{ rows: [] }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
@@ -393,7 +487,7 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_open_contracts_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_open_settlement_quality_from_confirmed_rows");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_debt_age_signal_from_contract_dates");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(9);
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(10);
|
||||
const receivablesCall = deps.executeAddressMcpQuery.mock.calls[3]?.[0];
|
||||
const payablesCall = deps.executeAddressMcpQuery.mock.calls[4]?.[0];
|
||||
const openContractsCall = deps.executeAddressMcpQuery.mock.calls[5]?.[0];
|
||||
@@ -458,7 +552,8 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
{ Period: "2020-01-15T00:00:00", Registrator: "Поступление 1" },
|
||||
{ Period: "2020-12-15T00:00:00", Registrator: "Поступление 2" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{ rows: [] }
|
||||
]);
|
||||
|
||||
const result = await executeAssistantMcpDiscoveryPilot(planner, deps);
|
||||
@@ -490,7 +585,7 @@ describe("assistant MCP discovery pilot executor", () => {
|
||||
expect(result.evidence.unknown_facts.join("\n")).toContain("оборачиваемость");
|
||||
expect(result.reason_codes).toContain("pilot_business_overview_inventory_query_mcp_executed");
|
||||
expect(result.reason_codes).toContain("pilot_derived_business_overview_inventory_position_from_confirmed_rows");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(9);
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(10);
|
||||
const inventoryCall = deps.executeAddressMcpQuery.mock.calls[6]?.[0];
|
||||
expect(inventoryCall?.account_scope).toContain("41.01");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user