Files
iistwin/server/finance-di2/table-config.ts
Ильяс Султанов 254950f151 DI2 внедрение, таск 1: серверная интеграция Data-Insight2 (server/finance-di2)
- Копия DI2-сервера в server/finance-di2/ с правками: schema/db-client/cache/table-config/override-tables/google-auth/audit-agent/routes
- db-client — обёртка над существующим пулом server/finance/db-client (второй пул не создаётся, добавлен экспорт isConnectionError)
- ai-config.ts — чистое IO конфигов из ai-agent.ts без Telegram-поллинга; роуты /api/ai/toggle и /api/ai/status удалены, без compression/startAuditScheduler/autoStartIfEnabled
- Обёртка registerDi2Routes с auth-gate (authenticateToken + finance.manage), регистрация строго перед registerFinanceRoutes
- Статика /di2 из dist/public-di2 перед веткой vite/static (dev и prod)
- Фикс предсуществующего бага DI2: buildGlobalExclusionConditions без cats в /api/profitability
2026-09-06 19:27:43 +03:00

155 lines
5.5 KiB
TypeScript

import fs from "fs";
import path from "path";
import type { TableConfig } from "./schema";
const TABLE_CONFIG_FILE = path.join(process.cwd(), "data", "table-config.json");
const SYSTEM_COLUMNS = ["id", "dataobnovleniyazapisi"];
const DEFAULT_COLUMN_LABELS: Record<string, string> = {
data: "Дата",
summa: "Сумма",
summadokumenta: "Сумма документа",
statya: "Статья",
zakazchik: "Заказчик",
dochka: "Дочка",
vnuchka: "Внучка",
kommentarii: "Комментарий",
stavkands: "Ставка НДС",
naznachenieplatezha: "Назначение платежа",
otvetstvenniipokontraktu: "Ответственный",
dogovorkontragenta: "Договор контрагента",
dogovorzakazchika: "Договор заказчика",
statyadop: "Статья (доп.)",
schetnaoplatu: "Счет на оплату",
nomervhodyaschegodokumenta: "Номер вх. документа",
ssilkanomervhodyaschegodokumenta: "Номер вх. документа",
kontragent: "Контрагент",
dogovor: "Договор",
summadolga: "Сумма долга",
summaprosrocheno: "Сумма просрочено",
period: "Период",
klassprosrochki: "Класс просрочки",
otvetstvennii: "Ответственный",
organizaciya: "Организация",
dokumentraschetov: "Документ расчётов",
summaoplacheno: "Сумма оплачено",
dneiprosrocheno: "Дней просрочено",
dneidolga: "Дней долга",
dataobrazovaniyadolga: "Дата образования долга",
planovayadatapogasheniya: "Плановая дата погашения",
otsrochkapodogovoru: "Отсрочка по договору",
kontragentinn: "ИНН контрагента",
valyuta: "Валюта",
summadolgaval: "Сумма долга (вал.)",
schet: "Счёт",
klassdolga: "Класс долга",
dataobnovleniyazapisi: "Дата обновления",
};
export const DEFAULT_TABLE_CONFIG: TableConfig = {
incomeTable: "opdohodi",
expenseTable: "oprashodi",
debtTable: "vw_debt_active",
incomeSumColumn: "summa",
expenseSumColumn: "summadokumenta",
incomeColumns: ["id", "data", "summa", "statya", "zakazchik", "dochka", "vnuchka", "kommentarii", "stavkands", "naznachenieplatezha", "otvetstvenniipokontraktu", "dogovorkontragenta", "schetnaoplatu", "dogovorzakazchika", "statyadop", "nomervhodyaschegodokumenta", "dataobnovleniyazapisi"],
expenseColumns: ["id", "data", "summadokumenta", "statya", "zakazchik", "dochka", "vnuchka", "kommentarii", "stavkands", "naznachenieplatezha", "otvetstvenniipokontraktu", "dogovorkontragenta", "dogovorzakazchika", "statyadop", "ssilkanomervhodyaschegodokumenta", "kontragent", "dataobnovleniyazapisi"],
columnLabels: { ...DEFAULT_COLUMN_LABELS },
};
let cachedConfig: TableConfig | null = null;
export function loadTableConfig(): TableConfig {
if (cachedConfig) return cachedConfig;
try {
const raw = fs.readFileSync(TABLE_CONFIG_FILE, "utf-8");
const parsed = JSON.parse(raw);
cachedConfig = { ...DEFAULT_TABLE_CONFIG, ...parsed, columnLabels: { ...DEFAULT_COLUMN_LABELS, ...(parsed.columnLabels || {}) } };
return cachedConfig!;
} catch {
cachedConfig = { ...DEFAULT_TABLE_CONFIG };
return cachedConfig;
}
}
export function saveTableConfig(cfg: Partial<TableConfig>) {
const current = loadTableConfig();
const merged: TableConfig = {
...current,
...cfg,
columnLabels: { ...current.columnLabels, ...(cfg.columnLabels || {}) },
};
if (!fs.existsSync("data")) fs.mkdirSync("data", { recursive: true });
fs.writeFileSync(TABLE_CONFIG_FILE, JSON.stringify(merged, null, 2), "utf-8");
cachedConfig = merged;
}
export function getIncomeTable(): string {
return "vw_income_effective";
}
export function getExpenseTable(): string {
return "vw_expense_effective";
}
export function getIncomeBaseTable(): string {
return loadTableConfig().incomeTable;
}
export function getExpenseBaseTable(): string {
return loadTableConfig().expenseTable;
}
export function getIncomeEffectiveView(): string {
return "vw_income_effective";
}
export function getExpenseEffectiveView(): string {
return "vw_expense_effective";
}
export function getDebtTable(): string {
return loadTableConfig().debtTable;
}
export function getIncomeSumColumn(): string {
return loadTableConfig().incomeSumColumn;
}
export function getExpenseSumColumn(): string {
return loadTableConfig().expenseSumColumn;
}
export function getAllowedIncomeColumns(): string[] {
const cfg = loadTableConfig();
return cfg.incomeColumns.filter(c => !SYSTEM_COLUMNS.includes(c));
}
export function getAllowedExpenseColumns(): string[] {
const cfg = loadTableConfig();
return cfg.expenseColumns.filter(c => !SYSTEM_COLUMNS.includes(c));
}
export function getAutoDetectAllowedColumns(type: "income" | "expense"): string[] {
const allowed = type === "income" ? getAllowedIncomeColumns() : getAllowedExpenseColumns();
return allowed.filter(c => c !== "data" && c !== "id" && c !== "summa" && c !== "summadokumenta" && c !== "dataobnovleniyazapisi");
}
export function getColumnLabel(col: string): string {
const cfg = loadTableConfig();
return cfg.columnLabels[col] || col;
}
export function getAllIncomeColumns(): string[] {
return loadTableConfig().incomeColumns;
}
export function getAllExpenseColumns(): string[] {
return loadTableConfig().expenseColumns;
}
export function invalidateTableConfigCache() {
cachedConfig = null;
}