Files
iistwin/server/finance-di2/schema.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

367 lines
12 KiB
TypeScript

import { z } from "zod";
export const dbConnectionSchema = z.object({
host: z.string().min(1),
port: z.coerce.number().int().positive(),
database: z.string().min(1),
user: z.string().min(1),
password: z.string().min(1),
});
export type DbConnectionConfig = z.infer<typeof dbConnectionSchema>;
export interface TableConfig {
incomeTable: string;
expenseTable: string;
debtTable: string;
incomeSumColumn: string;
expenseSumColumn: string;
incomeColumns: string[];
expenseColumns: string[];
columnLabels: Record<string, string>;
}
export interface IncomeRow {
id: string;
data: string | null;
summa: number | null;
stavkands: string | null;
dogovorkontragenta: string | null;
zakazchik: string | null;
dogovorzakazchika: string | null;
otvetstvenniipokontraktu: string | null;
statyadop: string | null;
kommentarii: string | null;
statya: string | null;
dochka: string | null;
vnuchka: string | null;
naznachenieplatezha: string | null;
schetnaoplatu: string | null;
overrideCategoryId?: string;
overrideCategoryName?: string;
}
export interface ExpenseRow {
id: string;
data: string | null;
summadokumenta: number | null;
stavkands: string | null;
dogovorkontragenta: string | null;
zakazchik: string | null;
dogovorzakazchika: string | null;
otvetstvenniipokontraktu: string | null;
statyadop: string | null;
kommentarii: string | null;
statya: string | null;
dochka: string | null;
vnuchka: string | null;
naznachenieplatezha: string | null;
ssilkanomervhodyaschegodokumenta: string | null;
kontragent: string | null;
dataobnovleniyazapisi: string | null;
overrideCategoryId?: string;
overrideCategoryName?: string;
}
export interface DebtRow {
period: string | null;
schet: string | null;
kontragent: string | null;
dogovor: string | null;
dokumentraschetov: string | null;
organizaciya: string | null;
summadolga: number | null;
summadolgaval: number | null;
valyuta: string | null;
otsrochkapodogovoru: number | null;
kontragentinn: string | null;
dataobrazovaniyadolga: string | null;
summadokumenta: number | null;
planovayadatapogasheniya: string | null;
dneiprosrocheno: number | null;
dneidolga: number | null;
otvetstvennii: string | null;
klassprosrochki: string | null;
klassdolga: string | null;
summaprosrocheno: number | null;
summaoplacheno: number | null;
}
export interface DashboardStats {
incomeTotal: number;
incomeCount: number;
expenseTotal: number;
expenseCount: number;
debtTotal: number;
debtOverdue: number;
debtCounterparts: number;
monthlyIncome: { month: string; total: number }[];
monthlyExpense: { month: string; total: number }[];
debtByClass: { class: string; total: number }[];
billedVsPaid: { month: string; billed: number; paid: number; debt: number }[];
}
export interface FilterOptions {
statya: string[];
zakazchik: string[];
dochka: string[];
vnuchka: string[];
otvetstvennii?: string[];
organizaciya?: string[];
klassprosrochki?: string[];
}
export interface PaginatedResult<T> {
rows: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
totalSum?: number;
}
export type RuleOperator = "equals" | "not_equals" | "contains" | "greater_than" | "less_than" | "equal_number";
export interface IncomeCategoryRule {
column: string;
operator: RuleOperator;
value: string;
}
export const MAX_CATEGORY_DEPTH = 5;
export interface IncomeCategory {
id: string;
name: string;
rules: IncomeCategoryRule[];
ruleLogic?: "and" | "or";
excluded?: boolean;
children?: IncomeCategory[];
autoDetected?: boolean;
treeId?: string;
}
export const NUMERIC_OPERATORS: { value: RuleOperator; label: string }[] = [
{ value: "equal_number", label: "равно (=)" },
{ value: "greater_than", label: "больше (>)" },
{ value: "less_than", label: "меньше (<)" },
];
export const TEXT_OPERATORS: { value: RuleOperator; label: string }[] = [
{ value: "equals", label: "равно (=)" },
{ value: "not_equals", label: "не равно (≠)" },
{ value: "contains", label: "содержит (∋)" },
];
export interface ProfitabilityRow {
zakazchik: string;
totalIncome: number;
totalExpense: number;
profit: number;
margin: number;
contractCount: number;
}
export interface ContractProfitRow {
dogovorzakazchika: string;
totalIncome: number;
totalExpense: number;
profit: number;
}
export interface MonthlyProfitRow {
month: string;
income: number;
expense: number;
profit: number;
}
export interface ProfitabilitySummary {
totalIncome: number;
totalExpense: number;
totalProfit: number;
counterpartCount: number;
}
export interface ProfitabilityResponse {
rows: ProfitabilityRow[];
total: number;
page: number;
pageSize: number;
totalPages: number;
summary: ProfitabilitySummary;
}
export interface DebtByEntity {
name: string;
total: number;
cls0: number;
cls1_30: number;
cls31_60: number;
cls61_90: number;
cls91_365: number;
cls365plus: number;
}
export interface DebtOverdueClass {
cls: string;
total: number;
}
export interface DebtAnalytics {
summary: {
updatedAt: string | null;
totalDebt: number;
totalOverdue: number;
};
byKontragent: DebtByEntity[];
byManager: DebtByEntity[];
byOverdueClass: DebtOverdueClass[];
table: {
rows: DebtTableGroup[];
total: number;
page: number;
pageSize: number;
totalPages: number;
};
}
export interface DebtTableRow {
dogovor: string;
summadokumenta: number;
dokumentraschetov: string;
summaoplacheno: number;
summadolga: number;
summaprosrocheno: number;
otsrochkapodogovoru: number;
dneiprosrocheno: number;
}
export interface DebtTableGroup {
kontragent: string;
totalDebt: number;
totalOverdue: number;
rows: DebtTableRow[];
}
export interface DebtDynamicsRow {
period: string;
totalDebt: number;
totalOverdue: number;
}
export interface DebtDynamicsKontragent {
kontragent: string;
summadokumenta: number;
summaoplacheno: number;
summadolga: number;
summaprosrocheno: number;
dneiprosrocheno: number;
}
export interface DebtFiltersResponse {
periods: string[];
klassprosrochki: string[];
organizaciya: string[];
otvetstvennii: string[];
kontragenty: string[];
}
export const INCOME_FILTERABLE_COLUMNS = [
{ key: "data", label: "Дата" },
{ key: "summa", label: "Сумма" },
{ key: "statya", label: "Статья" },
{ key: "zakazchik", label: "Заказчик" },
{ key: "dochka", label: "Дочка" },
{ key: "vnuchka", label: "Внучка" },
{ key: "otvetstvenniipokontraktu", label: "Ответственный" },
{ key: "stavkands", label: "Ставка НДС" },
{ key: "kommentarii", label: "Комментарий" },
{ key: "dogovorkontragenta", label: "Договор контрагента" },
{ key: "schetnaoplatu", label: "Счет на оплату" },
{ key: "naznachenieplatezha", label: "Назначение платежа" },
{ key: "dogovorzakazchika", label: "Договор заказчика" },
{ key: "statyadop", label: "Статья (доп.)" },
{ key: "nomervhodyaschegodokumenta", label: "Номер вх. документа" },
] as const;
export const ALL_INCOME_COLUMNS: { key: string; label: string; type: "date" | "number" | "text" }[] = [
{ key: "data", label: "Дата", type: "date" },
{ key: "summa", label: "Сумма", type: "number" },
{ key: "statya", label: "Статья", type: "text" },
{ key: "zakazchik", label: "Заказчик", type: "text" },
{ key: "dochka", label: "Дочка", type: "text" },
{ key: "vnuchka", label: "Внучка", type: "text" },
{ key: "otvetstvenniipokontraktu", label: "Ответственный", type: "text" },
{ key: "stavkands", label: "Ставка НДС", type: "text" },
{ key: "kommentarii", label: "Комментарий", type: "text" },
{ key: "dogovorkontragenta", label: "Договор контрагента", type: "text" },
{ key: "schetnaoplatu", label: "Счет на оплату", type: "text" },
{ key: "naznachenieplatezha", label: "Назначение платежа", type: "text" },
{ key: "dogovorzakazchika", label: "Договор заказчика", type: "text" },
{ key: "statyadop", label: "Статья (доп.)", type: "text" },
{ key: "nomervhodyaschegodokumenta", label: "Номер вх. документа", type: "text" },
{ key: "id", label: "ID", type: "text" },
];
export const DEFAULT_VISIBLE_COLUMNS = ["data", "summa", "statya", "zakazchik", "dochka", "vnuchka", "kommentarii"];
export const ALL_EXPENSE_COLUMNS: { key: string; label: string; type: "date" | "number" | "text" }[] = [
{ key: "data", label: "Дата", type: "date" },
{ key: "summadokumenta", label: "Сумма документа", type: "number" },
{ key: "statya", label: "Статья", type: "text" },
{ key: "zakazchik", label: "Заказчик", type: "text" },
{ key: "dochka", label: "Дочка", type: "text" },
{ key: "vnuchka", label: "Внучка", type: "text" },
{ key: "kommentarii", label: "Комментарий", type: "text" },
{ key: "stavkands", label: "Ставка НДС", type: "text" },
{ key: "naznachenieplatezha", label: "Назначение платежа", type: "text" },
{ key: "otvetstvenniipokontraktu", label: "Ответственный", type: "text" },
{ key: "dogovorkontragenta", label: "Договор контрагента", type: "text" },
{ key: "dogovorzakazchika", label: "Договор заказчика", type: "text" },
{ key: "statyadop", label: "Статья (доп.)", type: "text" },
{ key: "ssilkanomervhodyaschegodokumenta", label: "Номер вх. документа", type: "text" },
{ key: "kontragent", label: "Контрагент", type: "text" },
];
export const EXPENSE_FILTERABLE_COLUMNS = [
{ key: "data", label: "Дата" },
{ key: "summadokumenta", label: "Сумма документа" },
{ key: "statya", label: "Статья" },
{ key: "zakazchik", label: "Заказчик" },
{ key: "dochka", label: "Дочка" },
{ key: "vnuchka", label: "Внучка" },
{ key: "kommentarii", label: "Комментарий" },
{ key: "stavkands", label: "Ставка НДС" },
{ key: "naznachenieplatezha", label: "Назначение платежа" },
{ key: "otvetstvenniipokontraktu", label: "Ответственный" },
{ key: "dogovorkontragenta", label: "Договор контрагента" },
{ key: "dogovorzakazchika", label: "Договор заказчика" },
{ key: "statyadop", label: "Статья (доп.)" },
{ key: "ssilkanomervhodyaschegodokumenta", label: "Номер вх. документа" },
{ key: "kontragent", label: "Контрагент" },
] as const;
export const DEFAULT_VISIBLE_EXPENSE_COLUMNS = ["data", "summadokumenta", "statya", "zakazchik", "dochka", "vnuchka", "kommentarii"];
export const CATEGORY_RULE_COLUMNS = [
...INCOME_FILTERABLE_COLUMNS.map(c => ({ ...c, type: "text" as const })),
{ key: "summa", label: "Сумма", type: "number" as const },
];
export const EXPENSE_CATEGORY_RULE_COLUMNS = [
...EXPENSE_FILTERABLE_COLUMNS.filter(c => c.key !== "summadokumenta").map(c => ({ ...c, type: "text" as const })),
{ key: "summadokumenta", label: "Сумма документа", type: "number" as const },
];
export interface ExpenseCategory {
id: string;
name: string;
rules: IncomeCategoryRule[];
ruleLogic?: "and" | "or";
excluded?: boolean;
children?: ExpenseCategory[];
autoDetected?: boolean;
treeId?: string;
}