Поля и валидация
Страница фиксирует фактическую серверную валидацию входных данных на 15 июля 2026 года. Источник истины — HTTP DTO в internal/modules/*/transport_http.go, общие правила из internal/transport/validation.go и дополнительные проверки сервисов.
IMPORTANT
Метка «не готово» означает, что API принимает поле, но не проверяет важный формат, диапазон, длину или допустимый набор значений. Это не описание желаемого поведения, а перечень текущих технических пробелов.
Как читать таблицы
| Метка | Значение |
|---|---|
| ✅ | Основная проверка поля реализована |
| 🟡 | Проверка есть, но покрывает поле не полностью или отличается между API |
| 🔴 | Предметная валидация не готова; работает только JSON-тип или проверка БД |
Общие правила всех JSON-запросов:
- неизвестные поля отклоняются (
DisallowUnknownFields); - неверный JSON-тип возвращает
validation_errorс указанием поля; requiredпроверяет присутствие/ненулевое значение, аnotblank— строку послеTrimSpace;- идентификаторы с правилом
gt=0должны быть положительными; omitemptyозначает, что поле можно не передавать; если оно передано, остальные правила применяются;- денежный тип
money.Moneyпринимает JSON-число или строку с числом и округляет до 2 знаков. Знак и верхняя граница проверяются только там, где это отдельно делает сервис.
Подсказки под полем
Фронтенд показывает details[].message прямо под полем формы, поэтому это короткая русская подсказка без кодов полей и значений. Правила валидатора (internal/transport/validation.go, validationMessage) дают:
| Правило | Подсказка |
|---|---|
required, notblank, gt=0 у строки или списка | «Обязательное поле» |
min / gte, max / lte у строки | «Не короче 3 символов» / «Не длиннее 200 символов» |
min / gte, max / lte у числа | «Не меньше 3» / «Не больше 1000» |
min, max у списка | «Не меньше 2 элементов» / «Не больше 10 элементов» |
gt, lt у числа | «Больше 0» / «Меньше 24» |
len | «Ровно 9 символов» |
email | «Некорректная почта» |
phone | «Некорректный телефон» |
inn / kpp | «ИНН — 10 или 12 цифр» / «КПП — 9 цифр» |
http_url | «Ссылка должна начинаться с http:// или https://» |
oneof и прочие правила | «Недопустимое значение» (коды значений не перечисляются) |
datetime=2006-01-02, даты в параметрах | «Дата в формате ГГГГ-ММ-ДД» |
| неизвестное поле / неверный JSON-тип | «Неизвестное поле» / «Неверный тип значения» |
Ручные проверки в обработчиках и сервисах (transport.NewFieldError, (*apperr.Error).WithField, apperr.WithDetail) пишут подсказки в том же стиле; общие тексты — константы transport.Hint*. Правила текста проверяют тесты: internal/transport/field_hints_test.go прогоняет validationMessage по всем правилам, которые встречаются в DTO, а internal/app/error_texts_test.go — details всех зарегистрированных ошибок и все литералы подсказок в исходниках (см. Ошибки API).
Общие форматы
| Формат | Фактическое правило | Состояние |
|---|---|---|
email | стандартная проверка validator/v10 | ✅ |
phone | +, цифры, пробелы, (), -; от 7 до 20 символов | 🟡 Нет нормализации и проверки реального телефонного плана |
inn | ровно 10 или 12 цифр | 🟡 Контрольные суммы не проверяются |
kpp | ровно 9 цифр | 🟡 Структура КПП не проверяется |
http_url | абсолютный URL со схемой http или https | ✅ |
| дата | где указано — строго YYYY-MM-DD | ✅ |
| дата-время | где указано — RFC 3339 | ✅ |
Money | число/числовая строка, округление до копеек | 🟡 Сам тип не запрещает отрицательные и очень большие суммы |
Пользователи и авторизация
| Поля | Фактическая валидация | Состояние |
|---|---|---|
email | обязательно, формат email | ✅ |
password при входе | обязательно, без ограничения длины | 🔴 Не готов лимит длины |
password при регистрации /auth/register | обязательно, минимум 6 символов | 🟡 Не совпадает с правилом создания пользователя |
password при создании пользователя | 8–128 символов, не только пробелы | ✅ |
new_password при сбросе | обязательно, минимум 6 символов | 🟡 Не совпадает с правилом создания пользователя |
refresh_token, token | обязательно | 🟡 Нет ограничения длины |
full_name | если передано: 2–150 символов, не только пробелы | ✅ |
first_name, last_name, middle_name | до 80 символов; при создании не только пробелы | 🟡 При обновлении профиля пустые/пробельные значения допустимы |
| ФИО целиком | требуется full_name либо набор частей имени | ✅ Проверяется отдельным правилом |
role | обязательно при создании, не пусто; существование роли проверяет сервис | ✅ |
department_id, department_ids[] | каждый ID больше 0 | ✅ |
phone, phone_work | общий формат телефона | 🟡 См. ограничения формата phone |
bio, job_title, location, telegram | максимум 500 / 100 / 100 / 50 символов | 🟡 Пробельные строки допустимы |
timezone | только строковый тип | 🔴 Не готова проверка IANA timezone |
show_in_directory, notification_preferences, is_active | только JSON-тип/структура | 🟡 Для настроек уведомлений нет полного набора предметных ограничений |
CRM: компании, контакты и реквизиты
| Модуль и поля | Фактическая валидация | Состояние |
|---|---|---|
Компания: name | обязательно, 2–200 символов, не только пробелы | ✅ |
Компания: legal_name | до 200, не только пробелы | ✅ |
Компания: inn, kpp | 10/12 цифр и 9 цифр соответственно | 🟡 Без контрольных сумм |
Компания: ogrn | не пусто, максимум 15 символов | 🔴 Не готовы точная длина, только цифры и контрольная сумма |
Компания: type | не пусто, максимум 100 | 🔴 Нет справочника допустимых типов |
Компания: address, director_name, director_title | максимум 500 / 200 / 200, не только пробелы | ✅ |
Компания: email, phone, website | email / телефон / HTTP(S) URL | ✅ |
Компания: notes, manager_id | до 2000; ID больше 0 | ✅ |
Контакт: company_id | ID больше 0 | ✅ |
Контакт: first_name, last_name | до 100, не только пробелы; сервис требует хотя бы одно имя | ✅ |
Контакт: email, phone | email / телефон; сервис требует хотя бы один канал связи | ✅ |
Контакт: position, notes | до 150 / 2000, не только пробелы | ✅ |
Связь компании и контакта: contact_id, role | ID больше 0; роль до 100 и не пустая | ✅ |
Реквизиты: owner_type | обязательно, только непустая строка | 🔴 Не готов список допустимых владельцев |
Реквизиты: owner_id | обязательно, больше 0 | ✅ |
Реквизиты: name, legal_name | до 200 / 500, не только пробелы | ✅ |
Реквизиты: inn, kpp | сервис проверяет 10/12 цифр и 9 цифр соответственно | 🟡 Проверка есть не в DTO и не включает контрольные суммы |
Реквизиты: ogrn | не пусто, максимум 15 | 🔴 Нет проверки цифр, длины и контрольной суммы |
Реквизиты: bik, account, corr_account | БИК — 9 цифр; счета — 20 цифр; р/с не начинается с 301, к/с начинается с 301; контрольный ключ по БИК и весам 7,1,3 | ✅ |
Реквизиты: email, phone | email / общий формат телефона | ✅ |
Реквизиты: vat_mode, vat_rate | none/included/on_top; ставка 0–100 | ✅ |
Банковский счет: name, bank_name | до 200 / 500, не только пробелы | ✅ |
Банковский счет: bik | сервис требует ровно 9 цифр | ✅ |
Банковский счет: bank_inn, bank_kpp, account, corr_account | ИНН/КПП пока проверяются базово; счета — 20 цифр, тип р/с/к/с и контрольный ключ проверяются общим валидатором по БИК | 🟡 Для ИНН/КПП банка нет контрольных сумм |
Банковский счет: bank_address | не только пробелы | 🟡 Нет максимальной длины |
Поставщик: name | обязательно; сервис отсекает пробельную строку | 🟡 Нет максимальной длины |
Поставщик: email, inn | сервис проверяет email и ИНН из 10/12 цифр | 🟡 Для ИНН нет контрольной суммы |
Поставщик: phone, kpp, notes | строки нормализуются, но формат и длина не проверяются | 🔴 Валидация не готова |
Проекты, задачи и команда
| Модуль и поля | Фактическая валидация | Состояние |
|---|---|---|
Проект: name | обязательно, 2–200, не только пробелы | ✅ |
Проект: description | до 2000 | 🟡 Пробельная строка допустима |
Проект: event_category | exhibition, conference, presentation, meeting, forum, webinar, training, corporate_event, other | ✅ |
Проект: company_id, contact_id, location_id, department_id, manager_id | переданный ID больше 0; дополнительно требуется клиент и отдел по бизнес-правилам | ✅ |
Проект: status | planned/estimating/approved/in_progress/done/closed/cancelled | ✅ |
Проект: payer_requisite_id | допускается 0 или положительный ID | 🟡 Ноль используется как специальное значение; контракт не объясняет его отдельно |
Проект: vat_mode, vat_rate | none/included/on_top/inherit; 0–100 | ✅ |
Спецификация: file_id, комментарий отказа | ID больше 0; комментарий обязателен, до 2000, не пустой | ✅ |
Задача: project_id, assignee_id, co_assignee_ids[] | положительные ID | ✅ |
Задача: title, description | заголовок 3–200; описание до 5000; не только пробелы | ✅ |
Задача: status | todo/in_progress/blocked/done/cancelled | ✅ |
Задача: planned_hours, spent_hours | больше 0 и не более 1000 | ✅ |
Задача: due_date | YYYY-MM-DD | ✅ |
Комментарий задачи: body | обязательно, 1–4000, не только пробелы | ✅ |
Учет времени: project_id, user_id | положительные ID | ✅ |
Учет времени: task_id | только JSON-тип | 🔴 Не готово правило gt=0 |
Учет времени: date, hours, comment | YYYY-MM-DD; часы (0;24]; комментарий до 2000 | ✅ |
Заявка на команду: items | обязательный массив | 🟡 Не проверяется минимальное число элементов |
Заявка на команду: department_id, role_code | required, но ID не проверяется на >0, код — на непустоту/справочник | 🔴 Валидация не готова |
Заявка на команду: preferred_employees[], assignee_id | только JSON-тип | 🔴 Не проверяются положительные ID |
Заявка на команду: status | обязательно и не пусто | 🔴 Нет списка допустимых статусов |
Команда проекта: project_id, user_id | required, но без gt=0 | 🟡 Путь проверяется как число, тело — слабее |
Команда проекта: role_in_project, role_codes[], hourly_rate, reason | только JSON-тип | 🔴 Не готовы справочники, длины и неотрицательная ставка |
Команда проекта: source | допустимость проверяется отдельным парсером | ✅ |
Секционное разрешение: permission | обязательно; допустимость проверяет сервис | ✅ |
Оборудование, локации, логистика и командировки
| Модуль и поля | Фактическая валидация | Состояние |
|---|---|---|
Оборудование: name, category | обязательно; сервис отсекает пробелы | 🟡 Нет максимальной длины |
Оборудование: location_id, supplier_id, project_id | положительные ID | ✅ |
Оборудование: cost / unit_cost | сервис запрещает отрицательное и конфликт двух полей | ✅ |
Оборудование: quantity | больше 0 | ✅ |
Оборудование: status, unit, condition | сервис нормализует и проверяет поддерживаемые значения | ✅ |
Оборудование: identifier, broken_description, notes | только JSON-тип | 🔴 Нет длины/непустоты |
Локация: name | обязательно; сервис отсекает пробелы | 🟡 Нет максимальной длины |
Локация: type | сервис проверяет city/warehouse/venue/office/client_site/other | ✅ |
Локация: latitude, longitude, manager_id | [-90;90], [-180;180], ID больше 0 | ✅ |
Локация: contact_phone | только строковый тип | 🔴 Не используется валидатор телефона |
| Локация: адреса, инструкции, контакты, часы, заметки | сервис проверяет только непустоту части переданных строк | 🔴 Нет единых ограничений длины и форматов |
Заявка на оборудование: name, request_kind, type | имя обязательно до 200; значения из фиксированных списков | ✅ |
| Заявка: ID локаций/ответственного/оборудования/источника | переданные ID больше 0 | ✅ |
| Заявка: количества, дни аренды, цена | больше 0; цена неотрицательна | ✅ |
Заявка: items, item_ids, operations | массив обязателен | 🟡 Не везде есть min=1 и dive, поэтому пустые массивы/невалидные элементы могут пройти transport-слой |
Заявка: planned_departure, planned_arrival | строки без transport-формата | 🟡 Часть сценариев проверяет RFC 3339 глубже, единое правило отсутствует |
Логистика: name | обязательно | 🟡 Нет notblank и максимальной длины при создании |
Логистика: status, transport_type | строка без oneof в DTO | 🔴 Нет единого списка на transport-слое |
Логистика: fulfillment_type, carrier_type | фиксированные списки есть только в части create/update сценариев | 🟡 Валидация непоследовательна |
| Логистика: ID проекта/локаций и количества в create/auto-create | часть полей только JSON-тип | 🔴 Не готовы gt=0 и проверки количества для всех сценариев |
| Логистика: даты движения | RFC 3339 разбирается обработчиком | ✅ |
| Логистика: перевозчик, водитель, номера, адреса, заметки | ограничения длины есть в update/plan, но не во всех create-сценариях | 🟡 Валидация непоследовательна |
Командировка: все *_id | переданные ID больше 0 | ✅ |
Командировка: status, transport_type | фиксированные списки | ✅ |
| Командировка: адреса/цель/детали/проживание/заметки | максимум 500 / 1000 / 500 / 500 / 2000; значимые поля не только пробелы | ✅ |
| Командировка: даты | RFC 3339; сервис проверяет порядок дат | ✅ |
Командировка: planned_cost, actual_cost | сервис запрещает отрицательные суммы | ✅ |
Финансы, договоры, бюджет и зарплата
| Модуль и поля | Фактическая валидация | Состояние |
|---|---|---|
Бюджет: markup_percent | 0–10000 | ✅ |
Бюджет: usn_percent | 0–100 | ✅ |
Бюджет: usn_display_mode, vat_display_mode | separate/included | ✅ |
Бюджет: notes | только JSON-тип | 🔴 Нет ограничения длины |
Статья затрат: project_id, category | ID больше 0; категория из фиксированного списка | ✅ |
Статья затрат: planned_amount, actual_amount | сервис запрещает отрицательные суммы | ✅ |
Статья затрат: notes | до 2000 | ✅ |
Платежный счет: name, kind, company_bank_account_id | имя обязательно до 200; bank/cash; ID больше 0 | ✅ |
Платежный счет: currency | до 8 символов | 🔴 Нет ISO 4217/списка допустимых валют |
Платеж: account_id, direction | положительный ID; incoming/outgoing | ✅ |
Платеж: date | YYYY-MM-DD, если передано | ✅ |
Платеж: counterparty_type, counterparty_id | company/contact/employee/none; ID больше 0, согласованность проверяет сервис | ✅ |
Платеж: method, status | фиксированные списки | ✅ |
| Платеж: суммы и распределения | парсятся как Money, сервис проверяет положительность и баланс распределений | ✅ |
Договор: number | обязательно, не пусто, до 200 | ✅ |
Договор: counterparty_type, counterparty_id | company/contact; ID больше 0 | ✅ |
Договор: subject, notes | до 2000 / 4000 | 🟡 Пробельные строки допустимы |
Договор: signed_at, effective_from, effective_to, due_date | YYYY-MM-DD | ✅ |
Договор: state, тип связи, тип дедлайна | фиксированные списки | ✅ |
Договор: amount | числовой Money, сервис запрещает отрицательную сумму | 🟡 Не готова верхняя граница |
Профиль зарплаты: user_id, kind | ID больше 0; salary/hourly | ✅ |
Профиль зарплаты: base_amount | числовой Money, сервис запрещает отрицательное значение | 🟡 Ноль разрешен, верхняя граница не задана |
Профиль зарплаты: effective_from, effective_to | YYYY-MM-DD | ✅ |
Расчет зарплаты: period | сервис проверяет YYYY-MM | ✅ |
Выплата зарплаты: account_id | положительный ID | ✅ |
Системные модули
| Модуль и поля | Фактическая валидация | Состояние |
|---|---|---|
Чат: type | direct/group | ✅ |
Чат: title, project_id, participant_ids[] | только JSON-тип | 🔴 Нет длины, положительных ID, уникальности и минимального состава |
Сообщение: text, content, file_ids[], mention_user_ids[] | сервис требует содержимое сообщения | 🟡 Нет длины текста и transport-проверок ID |
Реакция: type | обязательно | 🔴 Нет списка допустимых реакций/длины |
Реакция: target_type, target_id | только JSON-тип | 🔴 Нет oneof и gt=0 для attachment |
Пересылка: chat_ids[] | минимум 1 элемент | 🟡 Элементы не проверяются на gt=0 |
Файлы: entity_type, category | обязательные строки; категория не пустая | 🔴 Нет справочника типов/категорий и ограничений длины |
Файлы: entity_id | required, но без gt=0 | 🔴 Положительный ID не гарантирован |
Файлы: folder_id, bucket, key | только тип | 🔴 Нет gt=0, длины и безопасного формата ключа |
| Файлы: содержимое | файл обязателен | 🟡 Размер/MIME/расширение зависят от storage/service, единого allowlist в DTO нет |
Связи сущностей: source_type, target_type | обязательно | 🔴 Нет списка типов сущностей |
Связи сущностей: source_id, target_id | положительные ID | ✅ |
Связи сущностей: relation_type | до 100 | 🔴 Нет списка типов связи |
Отдел: name | обязательно; сервис отсекает пробелы | 🟡 Нет максимальной длины |
Отдел: parent_id | положительный ID; циклы контролирует сервис/БД | ✅ |
Должность: name, description, level | имя 1–120; описание до 500; уровень 1–100 | 🟡 Для имени нет notblank |
Должность: role_codes[] | каждый код не пустой; существование проверяет сервис | ✅ |
RBAC: roles[], permission_codes[] | поле должно присутствовать, элементы не пустые; пустой массив разрешен для очистки | ✅ |
RBAC: permissions[] | поле должно присутствовать, структура элементов проверяется сервисом | ✅ |
| RBAC: имя/описание роли | имя обязательно и не пусто; описание только строковый тип | 🟡 Нет ограничений длины |
Что еще не готово
Ниже приоритетный перечень пробелов. Он дублирует красные строки выше, чтобы его можно было использовать как backlog.
- Унифицировать пароль: один минимум и максимум для регистрации, создания пользователя и сброса.
- Унифицировать применение
inn,kpp, а также строгих правил ОГРН, БИК и расчетных счетов во всех компаниях, поставщиках, реквизитах и банковских счетах. - Добавить общий валидатор дат (
date) и RFC 3339, чтобы правила не зависели от конкретного handler/service. - Закрыть логистические DTO: статусы, тип транспорта, положительные ID/количества, непустые массивы и одинаковые правила create/update.
- Добавить ограничения строк и справочники для локаций, поставщиков, проектной команды, чатов, файлов и связей сущностей.
- Валидировать IANA timezone, ISO 4217 currency и допустимые настройки уведомлений.
- Добавить
dive,gt=0иmin=1во все массивы идентификаторов, где пустой список не является явной командой очистки. - Зафиксировать допустимый знак и бизнес-диапазон для каждого денежного поля, прежде всего договоров и профилей зарплаты.
- Сделать правила create/update симметричными там, где поле имеет одинаковый смысл.
- Добавить проверки контрольных сумм ИНН/ОГРН и, при необходимости, нормализацию телефонов.
Поддержка страницы
При добавлении или изменении входного поля необходимо одновременно:
- обновить
binding-правило или сервисную проверку; - добавить позитивный и негативный тест;
- обновить эту страницу;
- при изменении API-контракта выполнить
go run ./cmd/generate_contractsиgo run ./cmd/generate_endpoints.