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

165
FIELD_TYPES.md Normal file
View File

@@ -0,0 +1,165 @@
# Реестр типов полей iistwin
> Единый источник правды: `shared/field-types.ts`
> Дата актуализации: 2026-05-18
---
## Сводная таблица всех типов
| # | ID | Название | Иконка (Lucide) | Опции | Формы | Глобальные | Таблицы |
|---|-----|----------|-----------------|-------|:-----:|:----------:|:-------:|
| 1 | `text` | Текстовое поле | `Type` | ❌ | ✅ | ✅ | ✅ |
| 2 | `number` | Числовое поле | `Hash` | ❌ | ✅ | ✅ | ✅ |
| 3 | `date` | Дата | `Calendar` | ❌ | ✅ | ✅ | ✅ |
| 4 | `datetime` | Дата и время | `Clock` | ❌ | ✅ | ✅ | ✅ |
| 5 | `select` | Выпадающий список | `ChevronDown` | ✅ | ✅ | ✅ | ✅ |
| 6 | `checkbox` | Чекбокс | `CheckSquare` | ❌ | ✅ | ✅ | ✅ |
| 7 | `toggle` | Переключатель | `ToggleLeft` | ❌ | ✅ | ✅ | ✅ |
| 8 | `button` | Кнопка | `MousePointer` | ❌ | ✅ | ❌ | ❌ |
| 9 | `user` | Выбор пользователя | `Users` | ❌ | ✅ | ✅ | ✅ |
| 10 | `file` | Загрузка файлов | `Paperclip` | ❌ | ✅ | ✅ | ✅ |
| 11 | `textarea` | Многострочный текст | `AlignLeft` | ❌ | ✅ | ✅ | ✅ |
| 12 | `rich-text` | Богатый текстовый редактор | `FileText` | ❌ | ✅ | ✅ | ✅ |
| 13 | `history-number` | Числовое поле с историей | `History` | ❌ | ❌ | ❌ | ✅ |
| 14 | `table` | Выбор из таблицы | `Table` | ❌ | ❌ | ❌ | ✅ |
| 15 | `task` | Связь с задачей | `Link2` | ❌ | ✅ | ❌ | ✅ |
| 16 | `company` | Организация (DaData) | `Building2` | ❌ | ✅ | ✅ | ✅ |
| 17 | `geo` | Адрес с координатами | `MapPin` | ❌ | ✅ | ✅ | ✅ |
| 18 | `formula` | Формула | `Calculator` | ❌ | ❌ | ❌ | ✅ |
**Легенда:**
- **Опции** — поддерживает ли тип предустановленный список значений (только `select`)
- **Формы** — можно создать поле этого типа в редакторе форм
- **Глобальные** — можно создать reusable шаблон глобального поля
- **Таблицы** — можно использовать как тип колонки в справочнике/таблице
---
## Использование по контекстам
### 1. Формы (`FORM_FIELD_TYPES`) — 17 типов
Файлы:
- `client/src/pages/FormEditor.tsx` — редактор форм
- `client/src/pages/TaskDetail.tsx` — карточка задачи
- `server/mcp.ts` → tool `add_form_field`
| Тип | Особенности |
|-----|-------------|
| `button` | Только в формах. Действия: `open_url`, `create_task` |
| `task` | Связь задача→задача (parent/child). `taskRelationType` |
| `company` | Интеграция DaData (автозаполнение ИНН, названия) |
| `geo` | Адрес с координатами, интеграция карт |
| `file` | Загрузка в MinIO S3. `maxFileCount`, `maxFileSizeMB` |
| `select` | Единственный тип с `supportsOptions: true` |
---
### 2. Глобальные поля (`GLOBAL_FIELD_TYPES`) — 16 типов
Файлы:
- `client/src/pages/GlobalFields.tsx` — UI управления
- `server/mcp.ts` → tool `create_global_field`
**Исключены:** `button`, `history-number`, `formula`
Глобальные поля — это reusable шаблоны. Создаются один раз, затем вставляются в любую форму через `add_global_field_to_form` или `add_globals_to_table_tab`.
---
### 3. Таблицы / Справочники (`TABLE_FIELD_TYPES`) — 16 типов
Файлы:
- `client/src/pages/Tables.tsx` — страница справочников
- `client/src/components/tabs/types.ts` — типизация колонок
- `server/mcp.ts` → tool `add_globals_to_table_tab`
**Исключён:** `button`
| Тип | Особенности |
|-----|-------------|
| `history-number` | Хранит историю изменений значения (audit trail) |
| `formula` | Вычисляемое поле. Движок: `client/src/lib/formulaEngine.ts` |
| `table` | Ссылка на другой справочник (foreign key) |
---
### 4. Пользовательские поля (User Custom Fields) — 6 типов ⚠️
Файлы:
- `shared/schema.ts:698`
- `client/src/pages/Users.tsx`
```ts
z.enum(["text", "number", "date", "select", "user", "checkbox"])
```
**Проблема:** не синхронизирован с `shared/field-types.ts`. Нет: `datetime`, `toggle`, `file`, `textarea`, `rich-text`, `company`, `geo`.
---
### 5. API эндпоинт
```
GET /api/field-types
```
Возвращает полный реестр с метаданными:
```json
{
"success": true,
"fieldTypes": [
{ "id": "text", "label": "Текстовое поле", "iconName": "Type", "supportsOptions": false, "allowedIn": ["form","global","table"] },
...
]
}
```
---
### 6. MCP Tools
| Tool | Допустимые типы |
|------|-----------------|
| `add_form_field` | `FORM_FIELD_TYPES` (17 типов) |
| `create_global_field` | `GLOBAL_FIELD_TYPES` (16 типов) |
| `add_globals_to_table_tab` | фильтрует по `TABLE_FIELD_TYPES` |
---
## Техдолг и планы
| Место | Проблема | Приоритет |
|-------|----------|-----------|
| `User Custom Fields` | 6 hardcoded типов, не использует `shared/field-types.ts` | 🔴 Высокий |
| `client/src/lib/fieldTypes.ts` | Дублирует реестр (React-иконки Lucide). Можно сделать динамический импорт | 🟡 Средний |
| `shared/schema.ts:1474` | Hardcoded union `'text' \| 'number' \| ...` | 🟡 Средний |
---
## Как добавить новый тип поля
### Шаг 1. Реестр
Добавить в `shared/field-types.ts`:
```ts
{ id: "rating", label: "Рейтинг", iconName: "Star", supportsOptions: false, allowedIn: ["form", "global", "table"] }
```
### Шаг 2. Иконка (если нужна на фронте)
Добавить в `client/src/lib/fieldTypes.ts`:
```ts
import { Star } from 'lucide-react';
// ...
{ value: 'rating', label: 'Рейтинг', icon: Star },
```
### Шаг 3. Рендерер (если нужна кастомная логика)
- `FormEditor.tsx` — UI в редакторе форм
- `TaskDetail.tsx` — отображение в карточке задачи
### Шаг 4. Бэкенд (если нужна кастомная валидация)
- `server/services/form.service.ts` — валидация значений
- `server/services/task.service.ts` — обработка при создании/обновлении
> Всё остальное (MCP, `/api/field-types`, `GlobalFields.tsx`, `Tables.tsx`, `Users.tsx`) синхронизируется **автоматически**.