АРЧ АП11 - Архитектура после регресса: Архитектура: сделать confirmed payables и receivables snapshots business-first без нумерованного report framing

This commit is contained in:
2026-04-18 16:09:55 +03:00
parent 49f291a14b
commit 53e9151d5a
5 changed files with 287 additions and 119 deletions
@@ -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)}`,