АРЧ АП11 - Архитектура после регресса: Архитектура: сделать counterparty и contract ranking-ответы business-first без отчетного preamble
This commit is contained in:
parent
a51fc2a70d
commit
0c6440fc5e
|
|
@ -252,7 +252,11 @@ Latest continuity-authority convergence evidence after the current route pass:
|
|||
- `vat_payable_forecast` and `vat_liability_confirmed_for_tax_period` now open with a business-first `Коротко: ...` lead, while the detailed calculation stays in the secondary block;
|
||||
- service-flavored top lines like `Собран прогноз...`, `Режим результата...`, and `Строк агрегата...` are removed from the first screen of the reply, which makes VAT answers read like user-facing guidance instead of an engine report;
|
||||
- VAT reply tests now explicitly protect this top-block shape, so future changes cannot silently reintroduce the same mechanical preamble;
|
||||
- this is still not the end of shaping work: some ranking families and long evidence-heavy replies still need the same cleanup;
|
||||
- the next human-answer-shaping cleanup pass is now applied to counterparty ranking/profile replies:
|
||||
- `counterparty_activity_lifecycle`, `contract_usage_overview`, `customer_revenue_and_payments`, `supplier_payouts_profile`, and `contract_usage_and_value` now open with business-first wording instead of service-flavored `профиль собран / строк агрегата / строк источника`;
|
||||
- ranking and contract replies now preserve user wording better in the visible heading layer, including `минимальный бюджет` phrasing for low-turnover active contracts;
|
||||
- targeted ranking/profile tests now protect the new top-block shape, so these families are less likely to regress back into report-like wording during later route/domain work;
|
||||
- this is still not the end of shaping work: some long evidence-heavy replies and residual catalog-style blocks still need the same cleanup;
|
||||
- this pass does not yet finish full single-owner continuity, but it narrows one of the remaining seams where route arbitration and scope memory could disagree about whether the session was still grounded.
|
||||
|
||||
## Next Execution Slice (2026-04-18)
|
||||
|
|
|
|||
|
|
@ -304,14 +304,12 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
|
|||
}
|
||||
const lines = longevityQuestion
|
||||
? [
|
||||
`Заказчиков с самым длинным горизонтом сотрудничества: ${counterparties.length}.`,
|
||||
"Собран профиль длительности сотрудничества по годам и частоте активности.",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
`Коротко: заказчиков с самым длинным горизонтом сотрудничества — ${counterparties.length}.`,
|
||||
`Оценка собрана по подтвержденной активности в 1С: ${rows.length} строк в выборке.`
|
||||
]
|
||||
: [
|
||||
`Активные заказчики ${scopeLabel}: ${counterparties.length}.`,
|
||||
"Собран профиль активности заказчиков по платежным документам.",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
`Коротко: активных заказчиков ${scopeLabel} — ${counterparties.length}.`,
|
||||
`Оценка собрана по подтвержденным платежным документам: ${rows.length} строк в выборке.`
|
||||
];
|
||||
if (counterparties.length === 0) {
|
||||
lines.push(longevityQuestion
|
||||
|
|
@ -351,9 +349,8 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
|
|||
? `Использованных договоров: ${usedContracts} из ${totalContracts}${usedShare ? ` (${usedShare})` : ""}.`
|
||||
: `Использованных договоров с подтвержденной связью с операциями: ${usedContracts}.`;
|
||||
const lines = [
|
||||
usageLead,
|
||||
"Профиль договорной базы собран по справочнику и подтвержденным операциям.",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
`Коротко: ${usageLead.replace(/\.$/, "")}.`,
|
||||
`Что видно по договорной базе: ${rows.length} строк в подтвержденной выборке.`
|
||||
];
|
||||
if (totalContracts > 0) {
|
||||
lines.push(`Всего договоров в базе: ${totalContracts}.`);
|
||||
|
|
@ -454,10 +451,9 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
|
|||
.sort((a, b) => a.amount - b.amount || (a.period ?? "").localeCompare(b.period ?? ""));
|
||||
const lines = [
|
||||
isSupplier
|
||||
? "Собран профиль выплат поставщикам по платежным документам."
|
||||
: "Собран профиль поступлений от заказчиков по платежным документам.",
|
||||
`Строк источника: ${rows.length}.`,
|
||||
`Уникальных контрагентов: ${profileRows.length}.`
|
||||
? `Коротко: в выборке ${profileRows.length} поставщиков по ${rows.length} подтвержденным платежным строкам.`
|
||||
: `Коротко: в выборке ${profileRows.length} заказчиков по ${rows.length} подтвержденным платежным строкам.`,
|
||||
`Подтвержденный денежный поток в выборке: ${deps.formatMoneyRub(totalFlow)}.`
|
||||
];
|
||||
if (profileRows.length === 0) {
|
||||
lines.push("По выбранному окну данных платежные строки не найдены.");
|
||||
|
|
@ -614,10 +610,8 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
|
|||
.filter((item) => item.docs > 0 && item.turnover > 0)
|
||||
.sort((a, b) => a.turnover - b.turnover || b.docs - a.docs || a.contract.localeCompare(b.contract));
|
||||
const lines = [
|
||||
`Активных договоров: ${contractRows.length}.`,
|
||||
"Собран профиль договоров по обороту и подтвержденным операциям.",
|
||||
`Строк источника: ${rows.length}.`,
|
||||
`Договорных агрегатов: ${contractRows.length}.`
|
||||
`Коротко: активных договоров в выборке — ${contractRows.length}.`,
|
||||
`Подтвержденных операций по договорам: ${rows.length}.`
|
||||
];
|
||||
if (contractRows.length === 0) {
|
||||
lines.push("В выбранном окне не найдено операций, связанных с договорами.");
|
||||
|
|
@ -631,7 +625,7 @@ function composeCounterpartyAnalyticsReply(intent, rows, options = {}, deps) {
|
|||
}
|
||||
if (focus === "bottom_by_turnover_active") {
|
||||
const visible = rankedBottomActive.slice(0, limit);
|
||||
lines.unshift(`Топ-${visible.length} активных договоров с минимальным оборотом:`);
|
||||
lines.unshift(`Топ-${visible.length} активных договоров с минимальным бюджетом:`);
|
||||
lines.push(...visible.map((item, index) => `${index + 1}. ${item.contract} | оборот: ${deps.formatMoneyRub(item.turnover)} | операций: ${item.docs} | последняя активность: ${formatOptionalDate(item.lastPeriod, deps.formatDateRu)}`));
|
||||
return (0, replyContracts_1.buildFactualListReply)(lines);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -419,14 +419,12 @@ export function composeCounterpartyAnalyticsReply(
|
|||
|
||||
const lines: string[] = longevityQuestion
|
||||
? [
|
||||
`Заказчиков с самым длинным горизонтом сотрудничества: ${counterparties.length}.`,
|
||||
"Собран профиль длительности сотрудничества по годам и частоте активности.",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
`Коротко: заказчиков с самым длинным горизонтом сотрудничества — ${counterparties.length}.`,
|
||||
`Оценка собрана по подтвержденной активности в 1С: ${rows.length} строк в выборке.`
|
||||
]
|
||||
: [
|
||||
`Активные заказчики ${scopeLabel}: ${counterparties.length}.`,
|
||||
"Собран профиль активности заказчиков по платежным документам.",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
`Коротко: активных заказчиков ${scopeLabel} — ${counterparties.length}.`,
|
||||
`Оценка собрана по подтвержденным платежным документам: ${rows.length} строк в выборке.`
|
||||
];
|
||||
|
||||
if (counterparties.length === 0) {
|
||||
|
|
@ -481,9 +479,8 @@ export function composeCounterpartyAnalyticsReply(
|
|||
: `Использованных договоров с подтвержденной связью с операциями: ${usedContracts}.`;
|
||||
|
||||
const lines: string[] = [
|
||||
usageLead,
|
||||
"Профиль договорной базы собран по справочнику и подтвержденным операциям.",
|
||||
`Строк агрегата: ${rows.length}.`
|
||||
`Коротко: ${usageLead.replace(/\.$/, "")}.`,
|
||||
`Что видно по договорной базе: ${rows.length} строк в подтвержденной выборке.`
|
||||
];
|
||||
|
||||
if (totalContracts > 0) {
|
||||
|
|
@ -598,10 +595,9 @@ export function composeCounterpartyAnalyticsReply(
|
|||
|
||||
const lines: string[] = [
|
||||
isSupplier
|
||||
? "Собран профиль выплат поставщикам по платежным документам."
|
||||
: "Собран профиль поступлений от заказчиков по платежным документам.",
|
||||
`Строк источника: ${rows.length}.`,
|
||||
`Уникальных контрагентов: ${profileRows.length}.`
|
||||
? `Коротко: в выборке ${profileRows.length} поставщиков по ${rows.length} подтвержденным платежным строкам.`
|
||||
: `Коротко: в выборке ${profileRows.length} заказчиков по ${rows.length} подтвержденным платежным строкам.`,
|
||||
`Подтвержденный денежный поток в выборке: ${deps.formatMoneyRub(totalFlow)}.`
|
||||
];
|
||||
|
||||
if (profileRows.length === 0) {
|
||||
|
|
@ -809,10 +805,8 @@ export function composeCounterpartyAnalyticsReply(
|
|||
.sort((a, b) => a.turnover - b.turnover || b.docs - a.docs || a.contract.localeCompare(b.contract));
|
||||
|
||||
const lines: string[] = [
|
||||
`Активных договоров: ${contractRows.length}.`,
|
||||
"Собран профиль договоров по обороту и подтвержденным операциям.",
|
||||
`Строк источника: ${rows.length}.`,
|
||||
`Договорных агрегатов: ${contractRows.length}.`
|
||||
`Коротко: активных договоров в выборке — ${contractRows.length}.`,
|
||||
`Подтвержденных операций по договорам: ${rows.length}.`
|
||||
];
|
||||
|
||||
if (contractRows.length === 0) {
|
||||
|
|
@ -834,7 +828,7 @@ export function composeCounterpartyAnalyticsReply(
|
|||
|
||||
if (focus === "bottom_by_turnover_active") {
|
||||
const visible = rankedBottomActive.slice(0, limit);
|
||||
lines.unshift(`Топ-${visible.length} активных договоров с минимальным оборотом:`);
|
||||
lines.unshift(`Топ-${visible.length} активных договоров с минимальным бюджетом:`);
|
||||
lines.push(
|
||||
...visible.map(
|
||||
(item, index) =>
|
||||
|
|
|
|||
|
|
@ -1387,7 +1387,7 @@ describe("address compose stage utf8 headers", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Заказчиков с самым длинным горизонтом сотрудничества (по годам)");
|
||||
expect(reply.text).toContain("Коротко: заказчиков с самым длинным горизонтом сотрудничества");
|
||||
expect(reply.text).toContain("Топ-");
|
||||
expect(reply.text).toContain("лет в базе");
|
||||
expect(reply.text).toContain("НОРТОН");
|
||||
|
|
@ -1448,9 +1448,9 @@ describe("address compose stage utf8 headers", () => {
|
|||
}
|
||||
]);
|
||||
|
||||
expect(reply.text).toContain("Профиль договорной базы собран");
|
||||
expect(reply.text).toContain("Коротко:");
|
||||
expect(reply.text).toContain("Всего договоров в базе: 520.");
|
||||
expect(reply.text).toContain("Использованных договоров (есть factual связь с операциями): 148.");
|
||||
expect(reply.text).toContain("Использованных договоров");
|
||||
expect(reply.text).toContain("Неиспользуемых договоров: 372.");
|
||||
});
|
||||
|
||||
|
|
@ -1487,6 +1487,7 @@ describe("address compose stage utf8 headers", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Коротко:");
|
||||
expect(reply.text).toContain("Топ-2 заказчиков по сумме поступлений:");
|
||||
expect(reply.text).toContain("1. Клиент А | сумма: 800");
|
||||
expect(reply.text).toContain("2. Клиент Б | сумма: 700");
|
||||
|
|
@ -1591,6 +1592,7 @@ describe("address compose stage utf8 headers", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Коротко:");
|
||||
expect(reply.text).toContain("Топ-2 поставщиков по количеству исходящих платежных операций:");
|
||||
expect(reply.text).toContain("1. Поставщик А | операций: 2");
|
||||
});
|
||||
|
|
@ -1628,6 +1630,7 @@ describe("address compose stage utf8 headers", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Коротко:");
|
||||
expect(reply.text).toContain("активных договоров с минимальным бюджетом");
|
||||
expect(reply.text).toContain("1. Договор 02/20 | оборот: 100");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue