Files
iistwin/server/finance/views.ts
Ильяс Султанов 1f5ecb6da4 fix(number-fields): избегаем потери точности длинных чисел
- number-поля теперь рендерятся как text + inputMode=numeric,
  чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage

Closes: искажение расчётного счёта и других длинных числовых полей
2026-07-07 21:03:40 +03:00

90 lines
2.5 KiB
TypeScript

import { query } from "./db-client";
import { getTableConfig } from "./table-config";
export async function ensureFinanceTables(): Promise<void> {
try {
// Overrides tables
await query(`
CREATE TABLE IF NOT EXISTS income_category_overrides (
record_id TEXT PRIMARY KEY,
category_id TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
await query(`
CREATE TABLE IF NOT EXISTS expense_category_overrides (
record_id TEXT PRIMARY KEY,
category_id TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`);
// Tags tables
await query(`
CREATE TABLE IF NOT EXISTS node_tags (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
color TEXT NOT NULL DEFAULT '#2563eb'
)
`);
await query(`
CREATE TABLE IF NOT EXISTS node_tag_assignments (
id SERIAL PRIMARY KEY,
node_id TEXT NOT NULL,
tag_id INTEGER NOT NULL REFERENCES node_tags(id) ON DELETE CASCADE,
context TEXT NOT NULL DEFAULT 'income',
UNIQUE(node_id, tag_id, context)
)
`);
} catch (err) {
console.error("[FINANCE] Failed to ensure metadata tables:", err);
}
}
async function tableExists(tableName: string): Promise<boolean> {
try {
const result = await query(
`SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = $1
) as exists`,
[tableName]
);
return result.rows[0]?.exists === true;
} catch {
return false;
}
}
export async function ensureViews(): Promise<void> {
const config = getTableConfig();
try {
const incomeExists = await tableExists(config.incomeTable);
const expenseExists = await tableExists(config.expenseTable);
if (!incomeExists && !expenseExists) {
return;
}
if (incomeExists) {
await query(`
CREATE OR REPLACE VIEW vw_income_effective AS
SELECT t.*, o.category_id as _override_category_id
FROM "${config.incomeTable}" t
LEFT JOIN income_category_overrides o ON o.record_id = t.id::text
`);
}
if (expenseExists) {
await query(`
CREATE OR REPLACE VIEW vw_expense_effective AS
SELECT t.*, o.category_id as _override_category_id
FROM "${config.expenseTable}" t
LEFT JOIN expense_category_overrides o ON o.record_id = t.id::text
`);
}
} catch (err) {
console.error("[FINANCE] Failed to create views:", err);
}
}