АРЧ АП11 - Архитектура после регресса: Архитектура: сделать confirmed payables и receivables snapshots business-first без нумерованного report framing
This commit is contained in:
parent
49f291a14b
commit
53e9151d5a
|
|
@ -260,7 +260,16 @@ Latest continuity-authority convergence evidence after the current route pass:
|
|||
- `list_contracts_by_counterparty`, `list_documents_by_contract`, `bank_operations_by_counterparty`, `bank_operations_by_contract`, and the generic factual-list fallback no longer leak `live address lane / catalog address lane` wording into the user-facing answer;
|
||||
- these list replies now start with direct business-first leads and keep the selected rows below, which preserves factual usefulness without exposing internal routing labels;
|
||||
- targeted utf8 header tests now explicitly protect against `lane` leakage in these list families;
|
||||
- this is still not the end of shaping work: some long evidence-heavy replies and residual catalog-style blocks still need the same cleanup;
|
||||
- the next human-answer-shaping cleanup pass is now applied to open-contract evidence-heavy replies:
|
||||
- `list_open_contracts` and `open_contracts_confirmed_as_of_date` no longer open with numbered `Блок 1/2/3...` report framing and now start with direct business-first summaries;
|
||||
- section headings are still structured, but they now read like user-facing guidance instead of an internal audit report, while keeping the same factual slices and evidence detail below;
|
||||
- targeted open-contract tests now protect the no-`Блок 1` top-block shape, so future contour work cannot silently bring the report framing back;
|
||||
- the next human-answer-shaping cleanup pass is now applied to confirmed debt snapshots:
|
||||
- `payables_confirmed_as_of_date` and `receivables_confirmed_as_of_date` now open with business-first `Коротко: ...` summaries instead of numbered report framing;
|
||||
- debt snapshot sections now keep the same factual structure, but top-level headings are user-facing (`Что учтено`, `Сводка`, `Категории...`) rather than `Блок 1/2/3...`;
|
||||
- direct compose tests now protect the no-`Блок 1` top-block shape for both confirmed debt families;
|
||||
- isolated runtime proof for the `payables_confirmed_as_of_date` `tryHandle` path still needs a wider rerun, because the narrow harness invocation currently returns `undefined` before semantic assertions and therefore is not reliable evidence for this shaping pass by itself;
|
||||
- this is still not the end of shaping work: heuristic debt shortlists and some 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)
|
||||
|
|
|
|||
|
|
@ -2774,20 +2774,19 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
return `${index + 1}. ${item.contract} | контрагент: ${counterpartyLabel} | подтвержденный открытый остаток: ${formatMoneyRub(item.confirmedAmount)} | тип остатка: ${openContractSettlementKindLabel(item.settlementKind)} | операций: ${formatNumberWithDots(item.operations)}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""}${accountsLabel}${evidenceLabel}${refsLabel}${specialReasonLabel}`;
|
||||
});
|
||||
const lines = [
|
||||
`Собран подтвержденный срез открытых договоров на ${formatDateRu(asOfDate)}.`,
|
||||
`Чистый коммерческий остаток: ${openContractNetBalanceDirectionLabel(commercialNetTotal)} ${formatMoneyRub(Math.abs(commercialNetTotal))}.`,
|
||||
`Брутто коммерческих компонентов: ${formatMoneyRub(commercialGrossTotal)}.`,
|
||||
`Специальные финансовые позиции: ${formatNumberWithDots(specialProfiles.length)} на ${formatMoneyRub(specialTotal)}.`,
|
||||
`Спорные/некачественно нормализованные позиции: ${formatNumberWithDots(dirtyProfiles.length)} на ${formatMoneyRub(dirtyTotal)}.`,
|
||||
`Коротко: на ${formatDateRu(asOfDate)} подтверждено открытых договоров с коммерческим остатком ${openContractNetBalanceDirectionLabel(commercialNetTotal)} ${formatMoneyRub(Math.abs(commercialNetTotal))}.`,
|
||||
`Брутто по коммерческим компонентам: ${formatMoneyRub(commercialGrossTotal)}.`,
|
||||
`Отдельно вынесены специальные финансовые позиции: ${formatNumberWithDots(specialProfiles.length)} на ${formatMoneyRub(specialTotal)}.`,
|
||||
`Спорные или некачественно нормализованные позиции: ${formatNumberWithDots(dirtyProfiles.length)} на ${formatMoneyRub(dirtyTotal)}.`,
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: подтвержденный срез договоров с открытыми взаиморасчетами на дату.",
|
||||
"- База ответа: остатки по счетам 60/62/76 с договорной аналитикой, без эвристического shortlist.",
|
||||
"- Управленческий вид: по каждому договору показаны чистый остаток и состав по типам открытых расчетов.",
|
||||
"- Базовая единица детализации: одна строка = один договор, один контрагент и один тип открытого остатка."
|
||||
"Что это значит",
|
||||
"- Это подтвержденный срез договоров с открытыми взаиморасчетами на дату.",
|
||||
"- Основа ответа: остатки по счетам 60/62/76 с договорной аналитикой, без эвристического shortlist.",
|
||||
"- По каждому договору показаны чистый остаток и состав по типам открытых расчетов.",
|
||||
"- Одна строка = один договор, один контрагент и один тип открытого остатка."
|
||||
];
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push("Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(asOfDate)}.`);
|
||||
if (periodScopeLine) {
|
||||
lines.push(periodScopeLine);
|
||||
|
|
@ -2796,7 +2795,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
lines.push("- Контур: остатки по счетам 60/62/76.");
|
||||
lines.push("- Смешанные экономические смыслы не склеиваются: дебиторка, кредиторка, авансы и прочие остатки показаны раздельно.");
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push("Сводка");
|
||||
lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`);
|
||||
lines.push(`- Уникальных договоров: ${formatNumberWithDots(uniqueContracts.length)}.`);
|
||||
lines.push(`- Подтвержденных договор-контрагент профилей: ${formatNumberWithDots(contractProfiles.length)}.`);
|
||||
|
|
@ -2811,42 +2810,42 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
lines.push(`- Спорные/некачественно нормализованные позиции: ${formatNumberWithDots(dirtyProfiles.length)} на ${formatMoneyRub(dirtyTotal)}.`);
|
||||
if (commercialProfiles.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Чистый открытый остаток по договорам");
|
||||
lines.push("Чистый открытый остаток по договорам");
|
||||
lines.push(...renderContractProfileLines(commercialProfiles, false));
|
||||
}
|
||||
if (commercialReceivables.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Коммерческие дебиторские компоненты");
|
||||
lines.push("Коммерческие дебиторские компоненты");
|
||||
lines.push(...renderConfirmedContractLines(commercialReceivables, false));
|
||||
}
|
||||
if (commercialPayables.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 6. Коммерческие кредиторские компоненты");
|
||||
lines.push("Коммерческие кредиторские компоненты");
|
||||
lines.push(...renderConfirmedContractLines(commercialPayables, false));
|
||||
}
|
||||
if (commercialAdvances.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 7. Коммерческие авансовые компоненты");
|
||||
lines.push("Коммерческие авансовые компоненты");
|
||||
lines.push(...renderConfirmedContractLines(commercialAdvances, false));
|
||||
}
|
||||
if (commercialOther.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 8. Прочие компоненты по 76");
|
||||
lines.push("Прочие компоненты по 76");
|
||||
lines.push(...renderConfirmedContractLines(commercialOther, false));
|
||||
}
|
||||
if (specialProfiles.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 9. Финансовые/специальные позиции");
|
||||
lines.push("Финансовые и специальные позиции");
|
||||
lines.push(...renderContractProfileLines(specialProfiles, true));
|
||||
}
|
||||
if (dirtyProfiles.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 10. Спорные/некачественно нормализованные позиции");
|
||||
lines.push("Спорные и некачественно нормализованные позиции");
|
||||
lines.push(...renderContractProfileLines(dirtyProfiles, true));
|
||||
}
|
||||
if (confirmedContracts.length === 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Подтвержденные позиции");
|
||||
lines.push("Подтвержденные позиции");
|
||||
lines.push("- На дату среза подтвержденные договоры с открытыми взаиморасчетами не найдены.");
|
||||
}
|
||||
return {
|
||||
|
|
@ -2869,13 +2868,10 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
const specialContracts = contracts.filter((item) => item.category !== "commercial");
|
||||
const commercialTotal = commercialContracts.reduce((sum, item) => sum + item.totalAmount, 0);
|
||||
const lines = [
|
||||
`Итого по предварительному срезу открытых договоров${asOfDate ? ` на ${formatDateRu(asOfDate)}` : ""}: ${formatNumberWithDots(commercialContracts.length)} коммерческих договоров на ${formatMoneyRub(commercialTotal)}.`,
|
||||
`Коротко: по предварительному срезу открытых договоров${asOfDate ? ` на ${formatDateRu(asOfDate)}` : ""} найдено ${formatNumberWithDots(commercialContracts.length)} коммерческих договоров на ${formatMoneyRub(commercialTotal)}.`,
|
||||
"Это shortlist на проверку, а не финальный подтвержденный реестр.",
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: предварительный список договоров с возможными незакрытыми расчетами.",
|
||||
"- Перед финансовым решением нужна сверка карточек договоров и взаиморасчетов в 1С.",
|
||||
"",
|
||||
"Блок 2. Что учтено",
|
||||
"Что учтено",
|
||||
...(asOfDate
|
||||
? [`- Дата среза: ${formatDateRu(asOfDate)}.`]
|
||||
: periodFrom || periodTo
|
||||
|
|
@ -2883,7 +2879,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
: []),
|
||||
"- Контур: движения по счетам 60/62/76 и договорная аналитика.",
|
||||
"",
|
||||
"Блок 3. Сводка",
|
||||
"Сводка",
|
||||
`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`,
|
||||
`- Договоров-кандидатов всего: ${formatNumberWithDots(contracts.length)}.`,
|
||||
`- Основной список (коммерческие): ${formatNumberWithDots(commercialContracts.length)}.`,
|
||||
|
|
@ -2891,7 +2887,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
];
|
||||
if (commercialContracts.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Основной список (коммерческие договоры)");
|
||||
lines.push("Основной список (коммерческие договоры)");
|
||||
lines.push(...commercialContracts.slice(0, 10).map((item, index) => {
|
||||
const counterpartiesLabel = item.counterparties.length > 0 ? item.counterparties.join("; ") : "контрагент не определен";
|
||||
const sourceRefsSuffix = item.sourceRefs.length > 0 ? ` | source refs: ${item.sourceRefs.slice(0, 2).join("; ")}` : "";
|
||||
|
|
@ -2899,7 +2895,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
}));
|
||||
if (specialContracts.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Финансовые/спорные позиции (вынесены отдельно)");
|
||||
lines.push("Финансовые и спорные позиции (вынесены отдельно)");
|
||||
lines.push(...specialContracts.slice(0, 8).map((item, index) => {
|
||||
const counterpartiesLabel = item.counterparties.length > 0 ? item.counterparties.join("; ") : "контрагент не определен";
|
||||
const sourceRefsSuffix = item.sourceRefs.length > 0 ? ` | source refs: ${item.sourceRefs.slice(0, 2).join("; ")}` : "";
|
||||
|
|
@ -2909,7 +2905,7 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
}
|
||||
else if (counterparties.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Контрагенты с сигналом незакрытых расчетов");
|
||||
lines.push("Контрагенты с сигналом незакрытых расчетов");
|
||||
lines.push(`- Контрагентов с сигналом: ${formatNumberWithDots(counterparties.length)}.`);
|
||||
lines.push(...counterparties
|
||||
.slice(0, 8)
|
||||
|
|
@ -2918,9 +2914,9 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
}
|
||||
else {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Позиции не выделены");
|
||||
lines.push("- По текущему live-срезу не удалось выделить договоры с достаточным качеством идентификации.");
|
||||
lines.push("Блок 5. Примеры исходных строк");
|
||||
lines.push("Позиции не выделены");
|
||||
lines.push("- По текущему срезу не удалось выделить договоры с достаточным качеством идентификации.");
|
||||
lines.push("Примеры исходных строк");
|
||||
lines.push(...formatTopRows(rows, 6));
|
||||
}
|
||||
return {
|
||||
|
|
@ -2951,13 +2947,11 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
return acc;
|
||||
}, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 });
|
||||
const lines = [
|
||||
`Итого подтвержденный долг на ${formatDateRu(payablesAsOfDate)}: ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: подтвержденный срез обязательств к оплате."
|
||||
`Коротко: подтвержденный долг к оплате на ${formatDateRu(payablesAsOfDate)} — ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"Это подтвержденный срез обязательств к оплате, а не эвристический shortlist."
|
||||
];
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push("Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(payablesAsOfDate)}.`);
|
||||
if (periodScopeLine) {
|
||||
lines.push(periodScopeLine);
|
||||
|
|
@ -2967,17 +2961,17 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
lines.push(carryoverLine);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push("Сводка");
|
||||
lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`);
|
||||
lines.push(`- Контрагентов с подтвержденным остатком к оплате: ${formatNumberWithDots(confirmedBalances.length)}.`);
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории обязательств");
|
||||
lines.push("Категории обязательств");
|
||||
lines.push(`- ${liabilityCategoryLabel("supplier_or_contractor")}: ${formatNumberWithDots(categoryCounts.supplier_or_contractor)}.`);
|
||||
lines.push(`- ${liabilityCategoryLabel("bank_or_credit")}: ${formatNumberWithDots(categoryCounts.bank_or_credit)}.`);
|
||||
lines.push(`- ${liabilityCategoryLabel("tax_or_state")}: ${formatNumberWithDots(categoryCounts.tax_or_state)}.`);
|
||||
lines.push(`- ${liabilityCategoryLabel("other")}: ${formatNumberWithDots(categoryCounts.other)}.`);
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Подтвержденные позиции к оплате");
|
||||
lines.push("Подтвержденные позиции к оплате");
|
||||
if (confirmedBalances.length > 0) {
|
||||
lines.push(...confirmedBalances.slice(0, 10).flatMap((item, index) => [
|
||||
`${index + 1}. ${item.name} | категория: ${liabilityCategoryLabel(item.category)} | остаток: ${formatMoneyRub(item.outstandingAmount)} | операций: ${formatNumberWithDots(item.operations)}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}${formatPayablesEvidenceSuffix(item)}`,
|
||||
|
|
@ -3018,13 +3012,11 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
return acc;
|
||||
}, { supplier_or_contractor: 0, bank_or_credit: 0, tax_or_state: 0, other: 0 });
|
||||
const lines = [
|
||||
`Итого подтвержденная дебиторская задолженность на ${formatDateRu(receivablesAsOfDate)}: ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: подтвержденный срез дебиторской задолженности."
|
||||
`Коротко: подтвержденная дебиторская задолженность на ${formatDateRu(receivablesAsOfDate)} — ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"Это подтвержденный срез дебиторской задолженности, а не эвристический shortlist."
|
||||
];
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push("Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(receivablesAsOfDate)}.`);
|
||||
if (periodScopeLine) {
|
||||
lines.push(periodScopeLine);
|
||||
|
|
@ -3034,17 +3026,17 @@ function composeFactualReplyBody(intent, rows, options = {}) {
|
|||
lines.push(carryoverLine);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push("Сводка");
|
||||
lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`);
|
||||
lines.push(`- Контрагентов с подтвержденным остатком к получению: ${formatNumberWithDots(confirmedBalances.length)}.`);
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории дебиторской задолженности");
|
||||
lines.push("Категории дебиторской задолженности");
|
||||
lines.push(`- ${receivablesCategoryLabel("supplier_or_contractor")}: ${formatNumberWithDots(categoryCounts.supplier_or_contractor)}.`);
|
||||
lines.push(`- ${receivablesCategoryLabel("bank_or_credit")}: ${formatNumberWithDots(categoryCounts.bank_or_credit)}.`);
|
||||
lines.push(`- ${receivablesCategoryLabel("tax_or_state")}: ${formatNumberWithDots(categoryCounts.tax_or_state)}.`);
|
||||
lines.push(`- ${receivablesCategoryLabel("other")}: ${formatNumberWithDots(categoryCounts.other)}.`);
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Подтвержденные позиции к получению");
|
||||
lines.push("Подтвержденные позиции к получению");
|
||||
if (confirmedBalances.length > 0) {
|
||||
lines.push(...confirmedBalances.slice(0, 10).flatMap((item, index) => [
|
||||
`${index + 1}. ${item.name} | категория: ${receivablesCategoryLabel(item.category)} | остаток к получению: ${formatMoneyRub(item.outstandingAmount)} | операций: ${formatNumberWithDots(item.operations)}${item.lastPeriod ? ` | последнее движение: ${item.lastPeriod}` : ""}${item.categoryReasons.length > 0 ? ` | основание: ${item.categoryReasons.join(", ")}` : ""}${formatPayablesEvidenceSuffix(item)}`,
|
||||
|
|
|
|||
|
|
@ -3588,21 +3588,20 @@ function composeFactualReplyBody(
|
|||
});
|
||||
|
||||
const lines: string[] = [
|
||||
`Собран подтвержденный срез открытых договоров на ${formatDateRu(asOfDate)}.`,
|
||||
`Чистый коммерческий остаток: ${openContractNetBalanceDirectionLabel(commercialNetTotal)} ${formatMoneyRub(Math.abs(commercialNetTotal))}.`,
|
||||
`Брутто коммерческих компонентов: ${formatMoneyRub(commercialGrossTotal)}.`,
|
||||
`Специальные финансовые позиции: ${formatNumberWithDots(specialProfiles.length)} на ${formatMoneyRub(specialTotal)}.`,
|
||||
`Спорные/некачественно нормализованные позиции: ${formatNumberWithDots(dirtyProfiles.length)} на ${formatMoneyRub(dirtyTotal)}.`,
|
||||
`Коротко: на ${formatDateRu(asOfDate)} подтверждено открытых договоров с коммерческим остатком ${openContractNetBalanceDirectionLabel(commercialNetTotal)} ${formatMoneyRub(Math.abs(commercialNetTotal))}.`,
|
||||
`Брутто по коммерческим компонентам: ${formatMoneyRub(commercialGrossTotal)}.`,
|
||||
`Отдельно вынесены специальные финансовые позиции: ${formatNumberWithDots(specialProfiles.length)} на ${formatMoneyRub(specialTotal)}.`,
|
||||
`Спорные или некачественно нормализованные позиции: ${formatNumberWithDots(dirtyProfiles.length)} на ${formatMoneyRub(dirtyTotal)}.`,
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: подтвержденный срез договоров с открытыми взаиморасчетами на дату.",
|
||||
"- База ответа: остатки по счетам 60/62/76 с договорной аналитикой, без эвристического shortlist.",
|
||||
"- Управленческий вид: по каждому договору показаны чистый остаток и состав по типам открытых расчетов.",
|
||||
"- Базовая единица детализации: одна строка = один договор, один контрагент и один тип открытого остатка."
|
||||
"Что это значит",
|
||||
"- Это подтвержденный срез договоров с открытыми взаиморасчетами на дату.",
|
||||
"- Основа ответа: остатки по счетам 60/62/76 с договорной аналитикой, без эвристического shortlist.",
|
||||
"- По каждому договору показаны чистый остаток и состав по типам открытых расчетов.",
|
||||
"- Одна строка = один договор, один контрагент и один тип открытого остатка."
|
||||
];
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push("Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(asOfDate)}.`);
|
||||
if (periodScopeLine) {
|
||||
lines.push(periodScopeLine);
|
||||
|
|
@ -3612,7 +3611,7 @@ function composeFactualReplyBody(
|
|||
lines.push("- Смешанные экономические смыслы не склеиваются: дебиторка, кредиторка, авансы и прочие остатки показаны раздельно.");
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push("Сводка");
|
||||
lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`);
|
||||
lines.push(`- Уникальных договоров: ${formatNumberWithDots(uniqueContracts.length)}.`);
|
||||
lines.push(`- Подтвержденных договор-контрагент профилей: ${formatNumberWithDots(contractProfiles.length)}.`);
|
||||
|
|
@ -3638,49 +3637,49 @@ function composeFactualReplyBody(
|
|||
|
||||
if (commercialProfiles.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Чистый открытый остаток по договорам");
|
||||
lines.push("Чистый открытый остаток по договорам");
|
||||
lines.push(...renderContractProfileLines(commercialProfiles, false));
|
||||
}
|
||||
|
||||
if (commercialReceivables.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Коммерческие дебиторские компоненты");
|
||||
lines.push("Коммерческие дебиторские компоненты");
|
||||
lines.push(...renderConfirmedContractLines(commercialReceivables, false));
|
||||
}
|
||||
|
||||
if (commercialPayables.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 6. Коммерческие кредиторские компоненты");
|
||||
lines.push("Коммерческие кредиторские компоненты");
|
||||
lines.push(...renderConfirmedContractLines(commercialPayables, false));
|
||||
}
|
||||
|
||||
if (commercialAdvances.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 7. Коммерческие авансовые компоненты");
|
||||
lines.push("Коммерческие авансовые компоненты");
|
||||
lines.push(...renderConfirmedContractLines(commercialAdvances, false));
|
||||
}
|
||||
|
||||
if (commercialOther.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 8. Прочие компоненты по 76");
|
||||
lines.push("Прочие компоненты по 76");
|
||||
lines.push(...renderConfirmedContractLines(commercialOther, false));
|
||||
}
|
||||
|
||||
if (specialProfiles.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 9. Финансовые/специальные позиции");
|
||||
lines.push("Финансовые и специальные позиции");
|
||||
lines.push(...renderContractProfileLines(specialProfiles, true));
|
||||
}
|
||||
|
||||
if (dirtyProfiles.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 10. Спорные/некачественно нормализованные позиции");
|
||||
lines.push("Спорные и некачественно нормализованные позиции");
|
||||
lines.push(...renderContractProfileLines(dirtyProfiles, true));
|
||||
}
|
||||
|
||||
if (confirmedContracts.length === 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Подтвержденные позиции");
|
||||
lines.push("Подтвержденные позиции");
|
||||
lines.push("- На дату среза подтвержденные договоры с открытыми взаиморасчетами не найдены.");
|
||||
}
|
||||
|
||||
|
|
@ -3705,13 +3704,10 @@ function composeFactualReplyBody(
|
|||
const specialContracts = contracts.filter((item) => item.category !== "commercial");
|
||||
const commercialTotal = commercialContracts.reduce((sum, item) => sum + item.totalAmount, 0);
|
||||
const lines: string[] = [
|
||||
`Итого по предварительному срезу открытых договоров${asOfDate ? ` на ${formatDateRu(asOfDate)}` : ""}: ${formatNumberWithDots(commercialContracts.length)} коммерческих договоров на ${formatMoneyRub(commercialTotal)}.`,
|
||||
`Коротко: по предварительному срезу открытых договоров${asOfDate ? ` на ${formatDateRu(asOfDate)}` : ""} найдено ${formatNumberWithDots(commercialContracts.length)} коммерческих договоров на ${formatMoneyRub(commercialTotal)}.`,
|
||||
"Это shortlist на проверку, а не финальный подтвержденный реестр.",
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: предварительный список договоров с возможными незакрытыми расчетами.",
|
||||
"- Перед финансовым решением нужна сверка карточек договоров и взаиморасчетов в 1С.",
|
||||
"",
|
||||
"Блок 2. Что учтено",
|
||||
"Что учтено",
|
||||
...(asOfDate
|
||||
? [`- Дата среза: ${formatDateRu(asOfDate)}.`]
|
||||
: periodFrom || periodTo
|
||||
|
|
@ -3719,7 +3715,7 @@ function composeFactualReplyBody(
|
|||
: []),
|
||||
"- Контур: движения по счетам 60/62/76 и договорная аналитика.",
|
||||
"",
|
||||
"Блок 3. Сводка",
|
||||
"Сводка",
|
||||
`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`,
|
||||
`- Договоров-кандидатов всего: ${formatNumberWithDots(contracts.length)}.`,
|
||||
`- Основной список (коммерческие): ${formatNumberWithDots(commercialContracts.length)}.`,
|
||||
|
|
@ -3727,7 +3723,7 @@ function composeFactualReplyBody(
|
|||
];
|
||||
if (commercialContracts.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Основной список (коммерческие договоры)");
|
||||
lines.push("Основной список (коммерческие договоры)");
|
||||
lines.push(
|
||||
...commercialContracts.slice(0, 10).map((item, index) => {
|
||||
const counterpartiesLabel =
|
||||
|
|
@ -3739,7 +3735,7 @@ function composeFactualReplyBody(
|
|||
);
|
||||
if (specialContracts.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Финансовые/спорные позиции (вынесены отдельно)");
|
||||
lines.push("Финансовые и спорные позиции (вынесены отдельно)");
|
||||
lines.push(
|
||||
...specialContracts.slice(0, 8).map((item, index) => {
|
||||
const counterpartiesLabel =
|
||||
|
|
@ -3752,7 +3748,7 @@ function composeFactualReplyBody(
|
|||
}
|
||||
} else if (counterparties.length > 0) {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Контрагенты с сигналом незакрытых расчетов");
|
||||
lines.push("Контрагенты с сигналом незакрытых расчетов");
|
||||
lines.push(`- Контрагентов с сигналом: ${formatNumberWithDots(counterparties.length)}.`);
|
||||
lines.push(
|
||||
...counterparties
|
||||
|
|
@ -3765,9 +3761,9 @@ function composeFactualReplyBody(
|
|||
lines.push("- Договорные реквизиты выделены недостаточно надежно, поэтому показан контрагентный список для проверки.");
|
||||
} else {
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Позиции не выделены");
|
||||
lines.push("- По текущему live-срезу не удалось выделить договоры с достаточным качеством идентификации.");
|
||||
lines.push("Блок 5. Примеры исходных строк");
|
||||
lines.push("Позиции не выделены");
|
||||
lines.push("- По текущему срезу не удалось выделить договоры с достаточным качеством идентификации.");
|
||||
lines.push("Примеры исходных строк");
|
||||
lines.push(...formatTopRows(rows, 6));
|
||||
}
|
||||
return {
|
||||
|
|
@ -3805,14 +3801,12 @@ function composeFactualReplyBody(
|
|||
);
|
||||
|
||||
const lines: string[] = [
|
||||
`Итого подтвержденный долг на ${formatDateRu(payablesAsOfDate)}: ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: подтвержденный срез обязательств к оплате."
|
||||
`Коротко: подтвержденный долг к оплате на ${formatDateRu(payablesAsOfDate)} — ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"Это подтвержденный срез обязательств к оплате, а не эвристический shortlist."
|
||||
];
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push("Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(payablesAsOfDate)}.`);
|
||||
if (periodScopeLine) {
|
||||
lines.push(periodScopeLine);
|
||||
|
|
@ -3823,19 +3817,19 @@ function composeFactualReplyBody(
|
|||
}
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push("Сводка");
|
||||
lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`);
|
||||
lines.push(`- Контрагентов с подтвержденным остатком к оплате: ${formatNumberWithDots(confirmedBalances.length)}.`);
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории обязательств");
|
||||
lines.push("Категории обязательств");
|
||||
lines.push(`- ${liabilityCategoryLabel("supplier_or_contractor")}: ${formatNumberWithDots(categoryCounts.supplier_or_contractor)}.`);
|
||||
lines.push(`- ${liabilityCategoryLabel("bank_or_credit")}: ${formatNumberWithDots(categoryCounts.bank_or_credit)}.`);
|
||||
lines.push(`- ${liabilityCategoryLabel("tax_or_state")}: ${formatNumberWithDots(categoryCounts.tax_or_state)}.`);
|
||||
lines.push(`- ${liabilityCategoryLabel("other")}: ${formatNumberWithDots(categoryCounts.other)}.`);
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Подтвержденные позиции к оплате");
|
||||
lines.push("Подтвержденные позиции к оплате");
|
||||
if (confirmedBalances.length > 0) {
|
||||
lines.push(
|
||||
...confirmedBalances.slice(0, 10).flatMap((item, index) => [
|
||||
|
|
@ -3885,14 +3879,12 @@ function composeFactualReplyBody(
|
|||
);
|
||||
|
||||
const lines: string[] = [
|
||||
`Итого подтвержденная дебиторская задолженность на ${formatDateRu(receivablesAsOfDate)}: ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"",
|
||||
"Блок 1. Статус результата",
|
||||
"- Результат: подтвержденный срез дебиторской задолженности."
|
||||
`Коротко: подтвержденная дебиторская задолженность на ${formatDateRu(receivablesAsOfDate)} — ${formatMoneyRub(totalOutstandingAmount)}.`,
|
||||
"Это подтвержденный срез дебиторской задолженности, а не эвристический shortlist."
|
||||
];
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 2. Что учтено");
|
||||
lines.push("Что учтено");
|
||||
lines.push(`- Дата среза: ${formatDateRu(receivablesAsOfDate)}.`);
|
||||
if (periodScopeLine) {
|
||||
lines.push(periodScopeLine);
|
||||
|
|
@ -3903,19 +3895,19 @@ function composeFactualReplyBody(
|
|||
}
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 3. Сводка");
|
||||
lines.push("Сводка");
|
||||
lines.push(`- Строк в выборке: ${formatNumberWithDots(rows.length)}.`);
|
||||
lines.push(`- Контрагентов с подтвержденным остатком к получению: ${formatNumberWithDots(confirmedBalances.length)}.`);
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 4. Категории дебиторской задолженности");
|
||||
lines.push("Категории дебиторской задолженности");
|
||||
lines.push(`- ${receivablesCategoryLabel("supplier_or_contractor")}: ${formatNumberWithDots(categoryCounts.supplier_or_contractor)}.`);
|
||||
lines.push(`- ${receivablesCategoryLabel("bank_or_credit")}: ${formatNumberWithDots(categoryCounts.bank_or_credit)}.`);
|
||||
lines.push(`- ${receivablesCategoryLabel("tax_or_state")}: ${formatNumberWithDots(categoryCounts.tax_or_state)}.`);
|
||||
lines.push(`- ${receivablesCategoryLabel("other")}: ${formatNumberWithDots(categoryCounts.other)}.`);
|
||||
|
||||
lines.push("");
|
||||
lines.push("Блок 5. Подтвержденные позиции к получению");
|
||||
lines.push("Подтвержденные позиции к получению");
|
||||
if (confirmedBalances.length > 0) {
|
||||
lines.push(
|
||||
...confirmedBalances.slice(0, 10).flatMap((item, index) => [
|
||||
|
|
|
|||
|
|
@ -509,10 +509,10 @@ describe("address compose stage utf8 headers", () => {
|
|||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Итого по предварительному срезу открытых договоров");
|
||||
expect(reply.text).toContain("Блок 1. Статус результата");
|
||||
expect(reply.text).toContain("Результат: предварительный список договоров с возможными незакрытыми расчетами.");
|
||||
expect(reply.text).toContain("Блок 4. Основной список (коммерческие договоры)");
|
||||
expect(reply.text).toContain("Коротко: по предварительному срезу открытых договоров");
|
||||
expect(reply.text).toContain("Это shortlist на проверку");
|
||||
expect(reply.text).toContain("Основной список (коммерческие договоры)");
|
||||
expect(reply.text).not.toContain("Блок 1");
|
||||
expect(reply.semantics?.result_mode).toBe("heuristic_candidates");
|
||||
expect(reply.semantics?.balance_confirmed).toBe(false);
|
||||
});
|
||||
|
|
@ -539,12 +539,13 @@ 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("Блок 4. Чистый открытый остаток по договорам");
|
||||
expect(reply.text).toContain("Блок 6. Коммерческие кредиторские компоненты");
|
||||
expect(reply.text).toContain("Коротко: на 31.03.2020 подтверждено открытых договоров");
|
||||
expect(reply.text).toContain("Это подтвержденный срез договоров с открытыми взаиморасчетами на дату.");
|
||||
expect(reply.text).toContain("По каждому договору показаны чистый остаток и состав по типам открытых расчетов.");
|
||||
expect(reply.text).toContain("Одна строка = один договор, один контрагент и один тип открытого остатка.");
|
||||
expect(reply.text).toContain("Чистый открытый остаток по договорам");
|
||||
expect(reply.text).toContain("Коммерческие кредиторские компоненты");
|
||||
expect(reply.text).not.toContain("Блок 1");
|
||||
expect(reply.semantics?.result_mode).toBe("confirmed_balance");
|
||||
expect(reply.semantics?.balance_confirmed).toBe(true);
|
||||
});
|
||||
|
|
@ -586,15 +587,74 @@ describe("address compose stage utf8 headers", () => {
|
|||
}
|
||||
);
|
||||
|
||||
expect(reply.text).toContain("Блок 4. Чистый открытый остаток по договорам");
|
||||
expect(reply.text).toContain("Блок 5. Коммерческие дебиторские компоненты");
|
||||
expect(reply.text).toContain("Блок 6. Коммерческие кредиторские компоненты");
|
||||
expect(reply.text).toContain("Блок 10. Спорные/некачественно нормализованные позиции");
|
||||
expect(reply.text).toContain("Чистый открытый остаток по договорам");
|
||||
expect(reply.text).toContain("Коммерческие дебиторские компоненты");
|
||||
expect(reply.text).toContain("Коммерческие кредиторские компоненты");
|
||||
expect(reply.text).toContain("Спорные и некачественно нормализованные позиции");
|
||||
expect(reply.text).not.toContain("Блок 1");
|
||||
expect(reply.text).toContain("брутто компонентов");
|
||||
expect(reply.text).not.toContain("счета: 62.01; 0");
|
||||
expect(reply.text).toContain("договор не похож на устойчивый договорный реквизит");
|
||||
});
|
||||
|
||||
it("renders confirmed payables snapshot business-first without numbered report framing", () => {
|
||||
const reply = composeFactualReply(
|
||||
"payables_confirmed_as_of_date",
|
||||
[
|
||||
{
|
||||
period: "2020-05-31T23:59:59Z",
|
||||
registrator: "Остатки на дату",
|
||||
account_dt: "",
|
||||
account_kt: "60.01",
|
||||
amount: 125000,
|
||||
analytics: ["ООО Ромашка", "Договор №19/15"]
|
||||
}
|
||||
],
|
||||
{
|
||||
asOfDate: "2020-05-31",
|
||||
useRubCurrency: true
|
||||
}
|
||||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Коротко: подтвержденный долг к оплате на 31.05.2020");
|
||||
expect(reply.text).toContain("Это подтвержденный срез обязательств к оплате");
|
||||
expect(reply.text).toContain("Что учтено");
|
||||
expect(reply.text).toContain("Сводка");
|
||||
expect(reply.text).toContain("Категории обязательств");
|
||||
expect(reply.text).toContain("Подтвержденные позиции к оплате");
|
||||
expect(reply.text).not.toContain("Блок 1");
|
||||
});
|
||||
|
||||
it("renders confirmed receivables snapshot business-first without numbered report framing", () => {
|
||||
const reply = composeFactualReply(
|
||||
"receivables_confirmed_as_of_date",
|
||||
[
|
||||
{
|
||||
period: "2020-05-31T23:59:59Z",
|
||||
registrator: "Остатки на дату",
|
||||
account_dt: "62.01",
|
||||
account_kt: "",
|
||||
amount: 84000,
|
||||
analytics: ["ООО Ромашка", "Договор №19/15"]
|
||||
}
|
||||
],
|
||||
{
|
||||
asOfDate: "2020-05-31",
|
||||
useRubCurrency: true
|
||||
}
|
||||
);
|
||||
|
||||
expect(reply.responseType).toBe("FACTUAL_LIST");
|
||||
expect(reply.text).toContain("Коротко: подтвержденная дебиторская задолженность на 31.05.2020");
|
||||
expect(reply.text).toContain("Это подтвержденный срез дебиторской задолженности");
|
||||
expect(reply.text).toContain("Что учтено");
|
||||
expect(reply.text).toContain("Сводка");
|
||||
expect(reply.text).toContain("Категории дебиторской задолженности");
|
||||
expect(reply.text).toContain("Подтвержденные позиции к получению");
|
||||
expect(reply.text).not.toContain("Блок 1");
|
||||
});
|
||||
|
||||
it("renders period coverage summary for management profile intent", () => {
|
||||
const reply = composeFactualReply("period_coverage_profile", [
|
||||
{
|
||||
|
|
@ -3284,12 +3344,13 @@ describe("address query limited taxonomy and stage diagnostics", { timeout: 1500
|
|||
expect(reply.toLowerCase()).toContain("shortlist");
|
||||
} else {
|
||||
expect(result?.debug.balance_confirmed).toBe(true);
|
||||
expect(reply).toContain("Итого подтвержденный долг");
|
||||
expect(reply).toMatch(/Блок\s+(?:\*\*1\*\*|1)\.\s+Статус результата/u);
|
||||
expect(reply).toMatch(/\n\nБлок\s+(?:\*\*2\*\*|2)\.\s+Что учтено/u);
|
||||
expect(reply).toMatch(/\n\nБлок\s+(?:\*\*3\*\*|3)\.\s+Сводка/u);
|
||||
expect(reply).toMatch(/\n\nБлок\s+(?:\*\*4\*\*|4)\.\s+Категории обязательств/u);
|
||||
expect(reply).toMatch(/\n\nБлок\s+(?:\*\*5\*\*|5)\.\s+Подтвержденные позиции к оплате/u);
|
||||
expect(reply).toContain("Коротко: подтвержденный долг к оплате");
|
||||
expect(reply).toContain("Это подтвержденный срез обязательств к оплате");
|
||||
expect(reply).toMatch(/\n\nЧто учтено/u);
|
||||
expect(reply).toMatch(/\n\nСводка/u);
|
||||
expect(reply).toMatch(/\n\nКатегории обязательств/u);
|
||||
expect(reply).toMatch(/\n\nПодтвержденные позиции к оплате/u);
|
||||
expect(reply).not.toContain("Блок 1");
|
||||
expect(reply).not.toContain("эвристический");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"suite_id": "assistant_saved_session_runtime_job-mmTvku3mtK",
|
||||
"suite_version": "0.1.0",
|
||||
"schema_version": "assistant_saved_session_runtime_v0_1",
|
||||
"title": "БОЛЬШОЙ ОБЩИЙ Ручная сессия 16.04.2026, 21:26:06",
|
||||
"scenario_count": 1,
|
||||
"case_ids": [
|
||||
"SAVED-001"
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
"case_id": "SAVED-001",
|
||||
"scenario_tag": "saved_user_sessions_runtime",
|
||||
"title": "БОЛЬШОЙ ОБЩИЙ Ручная сессия 16.04.2026, 21:26:06",
|
||||
"question_type": "followup",
|
||||
"broadness_level": "medium",
|
||||
"turns": [
|
||||
{
|
||||
"user_message": "приветик - че как там дела"
|
||||
},
|
||||
{
|
||||
"user_message": "расскажи что можешь интересного"
|
||||
},
|
||||
{
|
||||
"user_message": "кайф - что там на складе по остаткам?"
|
||||
},
|
||||
{
|
||||
"user_message": "а исторические остатки на другие даты умеешь?"
|
||||
},
|
||||
{
|
||||
"user_message": "давай на июль 2017"
|
||||
},
|
||||
{
|
||||
"user_message": "март 2016"
|
||||
},
|
||||
{
|
||||
"user_message": "По выбранному объекту \"Рабочая станция универсального специалиста (индивидуальное изготовление)\": где взяли это?"
|
||||
},
|
||||
{
|
||||
"user_message": "а кому продали?"
|
||||
},
|
||||
{
|
||||
"user_message": "у тебя написано кто контрагент: рабочая станция - это ошибка?"
|
||||
},
|
||||
{
|
||||
"user_message": "ндс можешь прикинуть на дату покупки рабочей станции?"
|
||||
},
|
||||
{
|
||||
"user_message": "а какой ндс мы должны сгрузить на март 2020?"
|
||||
},
|
||||
{
|
||||
"user_message": "прикинь какой ндс нам надо заплатить на февраль 2017"
|
||||
},
|
||||
{
|
||||
"user_message": "кто у нас самый доходный клиент за все время"
|
||||
},
|
||||
{
|
||||
"user_message": "кто нам должен денег на май 2017"
|
||||
},
|
||||
{
|
||||
"user_message": "а какой ндс мы должны примерно заплатить за этот период?"
|
||||
},
|
||||
{
|
||||
"user_message": "мы должны комуто денег на сегодня?"
|
||||
},
|
||||
{
|
||||
"user_message": "а нам?"
|
||||
},
|
||||
{
|
||||
"user_message": "какой у нас самый доходный год"
|
||||
},
|
||||
{
|
||||
"user_message": "а за 2017 мы скок заработали?"
|
||||
},
|
||||
{
|
||||
"user_message": "сколько вообще денег мы заработали за все время?"
|
||||
},
|
||||
{
|
||||
"user_message": "ты умеешь считать дельту по договорам?"
|
||||
},
|
||||
{
|
||||
"user_message": "по чепурнову покажи все доки"
|
||||
},
|
||||
{
|
||||
"user_message": "а по свк"
|
||||
},
|
||||
{
|
||||
"user_message": "а сейчас у нас есть что на складе?"
|
||||
},
|
||||
{
|
||||
"user_message": "что нам отгружал чепурнов? какой товар или услугу?"
|
||||
},
|
||||
{
|
||||
"user_message": "какие остатки на складе на сегодня"
|
||||
},
|
||||
{
|
||||
"user_message": "остатки на март 2016"
|
||||
},
|
||||
{
|
||||
"user_message": "хвосты покажи по счету 60 на август 2022"
|
||||
},
|
||||
{
|
||||
"user_message": "Есть ли остатки товара, которые закупались очень давно"
|
||||
},
|
||||
{
|
||||
"user_message": "Какие конкретно номенклатуры формируют остаток по складу на май 2020"
|
||||
},
|
||||
{
|
||||
"user_message": "а по Альтернативе Плюс сколько лет активности в базе 1С?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue