DI2 внедрение, таск 2: клиентский субапп Data-Insight2 (client-di2)
- client-di2/: копия фронтенда DI2 + shared/schema.ts, main.tsx обёрнут в wouter Router base=/di2, favicon относительный - client-di2/tailwind.config.ts (свои токены DI2) подключён через @config в index.css — иначе postcss взял бы корневой конфиг iistwin - vite.config.di2.ts: base /di2/, root client-di2, outDir dist/public-di2 - package.json: build/check через && включают сборку и typecheck di2 (build:di2, check:di2), новых зависимостей нет - Навигация финансов: ссылка «Data-Insight (полная версия)» → /di2 (target=_blank) в FinanceSidebar и FinanceBottomNav - Типовые фиксы предсуществующих ошибок DI2 (casts, ruleLogic в mutation-типах, Set<string>)
This commit is contained in:
366
client-di2/shared/schema.ts
Normal file
366
client-di2/shared/schema.ts
Normal file
@@ -0,0 +1,366 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user