fix(number-fields): избегаем потери точности длинных чисел

- number-поля теперь рендерятся как text + inputMode=numeric,
  чтобы браузер не округлял значения через input type=number
- пробелы при вставке в number-поля удаляются
- бэкенд нормализует значения number-полей в строку перед сохранением
- добавлен хелпер normalizeFieldValueForStorage

Closes: искажение расчётного счёта и других длинных числовых полей
This commit is contained in:
2026-07-07 21:03:40 +03:00
commit 1f5ecb6da4
1089 changed files with 237546 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
-- Finance module tables
CREATE TABLE IF NOT EXISTS income_categories (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
tree_id UUID NOT NULL DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL,
categories JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE UNIQUE INDEX IF NOT EXISTS income_categories_org_tree_idx ON income_categories(organization_id, tree_id);
CREATE TABLE IF NOT EXISTS expense_categories (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
tree_id UUID NOT NULL DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL,
categories JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE UNIQUE INDEX IF NOT EXISTS expense_categories_org_tree_idx ON expense_categories(organization_id, tree_id);
CREATE TABLE IF NOT EXISTS income_category_overrides (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
record_id VARCHAR(100) NOT NULL,
category_id VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(organization_id, record_id)
);
CREATE INDEX IF NOT EXISTS income_overrides_org_record_idx ON income_category_overrides(organization_id, record_id);
CREATE TABLE IF NOT EXISTS expense_category_overrides (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
record_id VARCHAR(100) NOT NULL,
category_id VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(organization_id, record_id)
);
CREATE INDEX IF NOT EXISTS expense_overrides_org_record_idx ON expense_category_overrides(organization_id, record_id);
CREATE TABLE IF NOT EXISTS auto_detect_trees (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
context VARCHAR(20) NOT NULL CHECK (context IN ('income', 'expense')),
tree_id UUID NOT NULL DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL,
config JSONB NOT NULL DEFAULT '{}',
is_primary BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(organization_id, context, tree_id)
);
CREATE INDEX IF NOT EXISTS auto_detect_trees_org_idx ON auto_detect_trees(organization_id, context);
CREATE TABLE IF NOT EXISTS node_tags (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
color VARCHAR(20) NOT NULL DEFAULT '#2563eb',
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(organization_id, name)
);
CREATE INDEX IF NOT EXISTS node_tags_org_idx ON node_tags(organization_id);
CREATE TABLE IF NOT EXISTS node_tag_assignments (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
node_id VARCHAR(100) NOT NULL,
tag_id INTEGER NOT NULL REFERENCES node_tags(id) ON DELETE CASCADE,
context VARCHAR(20) NOT NULL CHECK (context IN ('income', 'expense')),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(organization_id, node_id, tag_id, context)
);
CREATE INDEX IF NOT EXISTS node_tag_assignments_org_idx ON node_tag_assignments(organization_id, context, node_id);
CREATE TABLE IF NOT EXISTS finance_table_settings (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
section VARCHAR(20) NOT NULL CHECK (section IN ('income', 'expense')),
visible_cols JSONB NOT NULL DEFAULT '[]',
col_order JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(organization_id, section)
);