Направление работ: Mobile Код раздела: 1.2.2 Авторизация > Регистрация > Подтверждение почты Статус: Готов Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 14.07.26


1. Назначение

Раздел описывает ввод контактных данных и подтверждение email в мобильном приложении после регистрации.

Интерфейс должен позволить пользователю указать имя и email, подтвердить email кодом из письма или пропустить заполнение данных на этапе завершения регистрации.

Раздел зависит от Back: 1.1 Организация аккаунтов и Back: 1.1.1 Регистрация.

2. Ключевые понятия

Контактные данные — имя пользователя и email, которые пользователь может указать после завершения обязательной регистрации.

Email-код — 6-значный код подтверждения почты.

Заполню потом — действие, позволяющее завершить шаг регистрации в мобильном приложении без ввода контактных данных.

3. Scope

Входит

  • Ввод имени и email
  • Проверка формата имени и email на фронте
  • Пропуск заполнения контактных данных
  • Отправка письма с кодом подтверждения email
  • Ввод и проверка email-кода
  • Повторная отправка email-кода
  • Ошибки ввода, проверки кода и технические ошибки
  • Переход к следующему шагу

Не входит

Mobile: 1.2 Регистрация

Mobile: 1.2.1 Задать код-пароль и биометрию

Back: 7.1 Шаблоны писем и правила доставки уведомлений

Mobile: 5.2.1 Редактирование учетных данных вне регистрации

4. BR

Бизнес-правила и ограничения указаны в Back: 1.1 Организация аккаунтов и Back: 1.1.1 Регистрация.

В этом документе связи с BR указаны только при прямом соответствии фронтового требования backend-правилу.

5. Функциональные требования (UC + FR)

FR – functional requirements, что интерфейс должен уметь делать по определенному UC (триггеру).

1.2.2 Подтверждение почты Обновлено 5 июня 2026, 17:05 Лайза Панкова

IDСценарийФункциональное требованиеBRВесСтатус
FR-1Пользователь открывает шаг контактных данныхИнтерфейс показывает поля имени и email, действие продолжения и возможность заполнить данные позже.1Реализовано \ #OQ1.2.2-5
FR-2Пользователь вводит имяИнтерфейс принимает имя и показывает ошибку, если значение содержит недопустимые символы.1Частично \ #OQ1.2.2-3 \ #OQ1.2.2-16
FR-3Пользователь вводит emailИнтерфейс принимает email и проверяет базовый формат адреса до отправки данных.Back: 1.1.1:1Реализовано
FR-4Пользователь нажимает продолжитьИнтерфейс отправляет введенные контактные данные и при успешном ответе переводит пользователя к вводу кода из письма.Back: 1.1: Back: 1.1.1:1Частично \ #OQ1.2.2-5 \ #OQ1.2.2-14
FR-5Пользователь нажимает “Заполню потом”Интерфейс пропускает ввод контактных данных и переводит пользователя к следующему шагу Mobile 2.1 Поиск сфер.1Частично \ #OQ1.2.2-1 \ #OQ1.2.2-15
FR-6Email или Имя имеют некорректный форматИнтерфейс показывает ошибку emai или Имениl и не отправляет данные до исправления.Back: 1.1.1:1Частично \ #OQ1.2.2-3 \ #OQ1.2.2-16
FR-7Ввод email-кодаИнтерфейс показывает email, на который отправлено письмо, поле ввода 6 цифр и таймер повторной отправки.Back: 1.1.1:1Реализовано \ #OQ1.2.2-11
FR-8Пользователь вводит email-кодИнтерфейс принимает 6 цифр и отправляет код на проверку после заполнения или явного действия пользователя.Back: 1.1.1:1Реализовано \ #OQ1.2.2-12
FR-9Статус Email-кодаПодтвержден: Интерфейс фиксирует успешное подтверждение и переводит пользователя к следующему шагу. Не подтвержден: Интерфейс показывает ошибку проверки и позволяет повторить ввод в пределах действия таймера (60сек).Back: 1.1.1:1Частично \ #OQ1.2.2-2 \ #OQ1.2.2-7
FR-10Срок действия email-кода истекИнтерфейс предлагает отправить новый код и возвращает пользователя к вводу нового кода после успешной отправки.Back: 1.1.1:2Не реализовано \ #OQ1.2.2-4
FR-11Пользователь запрашивает новый email-кодИнтерфейс блокирует повторную отправку до завершения таймера и перезапускает таймер после успешной отправки.Back: 1.1.1:2Реализовано \ #OQ1.2.2-2 \ #OQ1.2.2-13
FR-12Возникла техническая ошибкаИнтерфейс показывает общее состояние ошибки и позволяет повторить действие без потери текущего шага.2Частично \ #OQ1.2.2-14 \ #OQ1.2.2-15
FR-13Шаг подтверждения почты завершенИнтерфейс переводит пользователя к следующему продуктовому сценарию: Mobile 2.1 Поиск сфер.1Реализовано \ #OQ1.2.2-1

6. Открытые вопросы

Контекст сверки (пин). Версия BRD: 0.1 (шапка — обновлено 14.07.26; таблица ФТ — обновлено 05.06.26). Репозиторий: booking.mobile.flutter, ветка dev, commit 89ca502. Дата сверки: 2026-07-15. Прошлый обход: commit dad0c65 (2026-07-06). Переобход относительно dad0c65 (2026-07-06): снято 2 — OQ1.2.2-6 (finally-баг: «Продолжить» теперь await-ит запрос, «успешно» и переход только в ветке успеха) и OQ1.2.2-8 (при ошибке шаг больше не завершается переходом). Осталось открытыми 10 из 13 — OQ1.2.2-1…5, 7, 9, 10, 11, 13: по ним код не менялся; частично сдвинулся только OQ1.2.2-3 (экран регистрации теперь enforce-ит формат имени, но канонический формат с Back 1.1.1 не согласован). Появилось 3 новых — OQ1.2.2-14 (успех показывается и при ошибочном ответе бэкенда — остаток OQ1.2.2-6, уровнем ниже), OQ1.2.2-15 («Заполню потом» блокируется навсегда после ошибки — ловушка, которую активировал фикс OQ1.2.2-6), OQ1.2.2-16 (ошибка формата имени/email пользователю не показывается). OQ1.2.2-12 остаётся Closed — решение команды не пересматривалось. Якорь в коде: lib/src/feature/auth/widget/pages/reg_contact_data/reg_set_contact_data.dart, lib/src/feature/check_email_phone_modals/**, lib/src/core/components/repositories/identity_service/**, lib/src/feature/profile_page/profile/data/user_repository.dart, lib/src/core/components/grpc/**, lib/src/core/utils/regex.dart, lib/src/core/utils/validators.dart, lib/src/core/utils/custom_timer.dart. Стек: Flutter/Dart, Cubit (flutter_bloc), gRPC-gateway поверх REST (IdentityService, ClientsService). Обзор диффа dad0c65..89ca502: 14 коммитов, 75 файлов. Тему раздела задели 3fe6bab (1.2 bugfixes — фикс «Продолжить», валидаторы имени, заголовок экрана) и dd54f5d (registration code + pin — approveCode теперь принимает String). Коммиты 626cbde/7610f28 (AppLockGate поверх роутера) и снятие редиректа на LogPinRoute в app_router.dart:169-174 проверены: они управляют блокировкой уже авторизованного приложения и на шаг контактных данных / модалку почты напрямую не влияют — новых расхождений по FR-1…FR-13 не дают. Маршрутизация без сверки с гайдбуком (гайдбук в этот прогон не передавался). Правило вывода: ни одного утверждения без пруфа file:line; где не нашли за обход — помечено явно.

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
#OQ1.2.2-1Код FR-5, FR-13Требование говорит: после «Заполню потом» (FR-5) и завершения шага (FR-13) — переход к «2.1 Поиск сфер» (SpheresRoute). В коде оба случая делают replaceAll([HomeRoute()]), SpheresRoute закомментирован; HomeRoute — стартовый таб-контейнер, поиск сфер — вкладка внутри. \ reg_set_contact_data.dart:257,280,282; app_router.dart:340-347 \ Переобход 89ca502: без изменений, код тот же, пруф переехал по строкам — reg_set_contact_data.dart:265 («Продолжить»), :300 («Заполню потом»), :298 (SpheresRoute всё так же закомментирован); app_router.dart:334,341РазработкаClosedПродуктовое решение о формулировке приземления. (А) уточнить BRD, что «2.1 Поиск сфер» = домашний экран с активной вкладкой поиска (тогда HomeRoute корректен); (Б) навести навигацию на SpheresRoute внутри HomeRoute. Тот же узел приземления, что в 1.3/OQ1.3-4 и 1.2.1/OQ1.2.1-1.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.2-2Код FR-9, FR-11Требование говорит: окно повтора кода — 60 сек. В коде таймер ставится из ответа сервера (response.timeout), значения 60 в коде нет — длительность задаёт бэкенд, фронт отображает. \ check_contacts_modal_cubit.dart:129,136 \ Переобход 89ca502: без изменений — таймер по-прежнему ставится только из response.timeout, константы 60 в коде нет. \ check_contacts_modal_cubit.dart:129,136Бизнес/РазработкаClosedКто источник истины по окну повтора; согласовать с Back: 1.1.1. (А, рекоменд.) бэкенд задаёт timeout, а «60 сек» в BRD пометить как справочное/дефолт (фронт уже берёт из ответа); (Б) если 60 — жёсткое требование, на фронте сейчас нет защиты от иного значения.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.2-3Код FR-2, FR-6Требование говорит: имя принимается, при недопустимых символах — ошибка. В коде два несогласованных набора: Validator.validateName (мин. 2 буквы, запрет цифр/крайних пробелов/сдвоенных спецсимволов) на экране регистрации, а в модалке имя проверяется только length < 2, RegexString.checkNamePattern (2–50 символов, буквы + '-) не вызывается вовсе. \ validators.dart:8-50; reg_set_contact_data.dart:75; regex.dart:2-23; new_contact_enter.dart:106 \ Переобход 89ca502: частично исправлено — на экране регистрации формат имени теперь реально enforce-ится: подключён NameInputFormatter и maxLength: Validator.maxNameLength (50), в validateName добавлены проверка верхней границы 50 и запрет спецсимвола в начале. \ reg_set_contact_data.dart:114-115; validators.dart:7,34-35,55,103,129. Остаётся: канонический формат с Back 1.1.1 не согласован (вопрос бизнеса открыт); RegexString.checkNamePattern по-прежнему не вызывается ниоткуда (мёртвый код, regex.dart:2); в модалке имя всё так же проверяется только length < 2 (new_contact_enter.dart:106) — но на путь регистрации это не влияет, поле имени в модалке скрыто (isNameNeedForUpdate: false, reg_set_contact_data.dart:174; new_contact_enter.dart:206) и относится к сценарию заказа/профиля (см. OQ1.2.2-9).Бизнес/РазработкаOpenКанонический формат имени — согласовать с Back: 1.1.1. (А, рекоменд.) свести к одному валидатору в обоих местах (экран + модалка); сейчас модалка формат не enforce-ит, только длину. Уточнить верхнюю границу 50, запрет цифр, минимум 2 буквы.От бизнеса: внесли изменение в БРД 1.3.
#OQ1.2.2-4Код FR-10Требование говорит: отдельный сценарий «код истёк» — предложить новый и вернуть к вводу. В коде явного состояния нет: серверный codeTimeOut сводится к строке «Повторите через N минут», поле expiresIn из ответа нигде не читается; при проверке различается только «успех vs любая ошибка». \ update_data_response.dart:12,26; errors.dart:20-21; check_contacts_modal_cubit.dart:66-74 \ Переобход 89ca502: без изменений — expiresIn по-прежнему только объявлен и нигде не читается (update_data_response.dart:12,60 — единственные вхождения по репозиторию), codeTimeOut всё так же даёт литеральную строку «Повторите попытку через N минут» с неподставленным N (errors.dart:20-21).Бизнес/РазработкаOpenНужен ли явный сценарий истечения TTL и каков сам TTL — согласовать с Back: 1.1.1. (А) если да — задействовать expiresIn/codeTimeOut (уже приходят, но проигнорированы); (Б) оставить общей ошибкой и смягчить FR-10. Связать с OQ1.2.2-7.От бизнеса: доработка, вес 2. Внедрить текст ошибки.
#OQ1.2.2-5Код FR-1, FR-4Требование говорит: поля имени и email + продолжение (FR-1); «Продолжить» отправляет введённые контактные данные (FR-4). В коде «Продолжить» вызывает changeUserOnlyName(name:) — email в вызов не входит; email подтверждается отдельно в модалке при тапе на поле. «Отправка email» и «продолжение» разъединены. \ reg_set_contact_data.dart:244,160-169 \ Переобход 89ca502: без изменений по сути вопроса — «Продолжить» по-прежнему шлёт только имя (changeUserOnlyName(name: nameEditController.text)), email в вызов не входит. \ reg_set_contact_data.dart:258-259,166-190. Попутно в этом коммите исправлен заголовок экрана: было 'Вход по Код-паролю' — стало 'Персональные данные' (reg_set_contact_data.dart:88), поэтому по FR-1 (показ полей, продолжения и «заполню потом») расхождений не осталось — статус FR-1 поднят до «Реализовано».Бизнес/РазработкаClosedРешение: by design или дефект потока. (А) email обязательно подтверждается в модалке до «Продолжить», тогда «Продолжить» шлёт только имя — задокументировать в FR-4; (Б) «Продолжить» должен слать и email — добавить вызов updateEmailMobile в обработчик.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.2-6Код FR-4Проблема: «Продолжить» помечает успех и уходит на HomeRoute безусловно. changeUserOnlyName (async Future<bool>) вызывается без await, поэтому catch не ловит асинхронную ошибку сети/gRPC (блок мёртв), а finally показывает «Имя установлено успешно» и делает переход сразу. Ошибка ответа не ловится и не показывается. Противоречит FR-4 (переход только при успехе) и FR-12. \ reg_set_contact_data.dart:243-258; user_repository.dart:105 \ Переобход 89ca502: исправлено — блок finally убран, вызов теперь await userRepository.changeUserOnlyName(...), «Имя установлено успешно» + endInstallBiometric() + replaceAll([HomeRoute()]) перенесены в ветку успеха, в catch — «Ошибка установки имени» и isError = true без перехода; добавлены guard-ы if (!context.mounted) return. Обработчик стал async. \ reg_set_contact_data.dart:246,253-274. Остаток вынесен в OQ1.2.2-14: await чинит fire-and-forget и оживляет catch для брошенных исключений, но ошибочный ответ бэкенда исключением не является и по-прежнему приводит к «успеху».РазработкаClosedПрограммный дефект, продуктового вопроса нет. Закрываю сам: убрать безусловный успех/переход из finally; добавить await, перенести переход и «успешно» в ветку успеха, при ошибке — оставаться на шаге → тикет в Bitrix. Чинит FR-4 и FR-12 разом (см. OQ1.2.2-8).От разработки: реализовано.
#OQ1.2.2-7Код FR-9Требование говорит: осмысленный статус email-кода (подтверждён / не подтверждён). В коде огрублено до «state==6 vs всё остальное»: invalidCode, codeTimeOut, tooManyRequests не разводятся — одинаковый путь «показать ошибку, встряхнуть поле». Повтор в пределах таймера возможен. \ check_contacts_modal_cubit.dart:66-74; confirmed_pin_page.dart:216-222 \ Переобход 89ca502: без изменений — approveCode всё так же возвращает null только при state.value == 6, иначе строку ошибки; развода состояний нет. \ check_contacts_modal_cubit.dart:66-76; confirmed_pin_page.dart:216-222Бизнес/РазработкаClosed(А, рекоменд.) развести invalidCode/codeTimeOut/tooManyRequests в разные UX-состояния (данные уже приходят в StateResponseEmail); (Б) если продукту достаточно бинарного — зафиксировать в FR-9. Связать с OQ1.2.2-4.Задача на бэкенд: покрыть и проверить статус-коды ответа по подтверждению email.
#OQ1.2.2-8Код FR-12Требование говорит: общее состояние ошибки с повтором без потери шага. В коде внутри модалки покрыто (ErrorSendPage + «Попробовать ещё раз» → возврат на ввод), но на экране регистрации при ошибке имени шаг всё равно завершается переходом (из-за finally-бага, OQ1.2.2-6). \ error_modal.dart:66-84; reg_set_contact_data.dart:243-258 \ Переобход 89ca502: исправлено — той же правкой, что и OQ1.2.2-6: при брошенной ошибке экран регистрации остаётся на шаге (снекбар «Ошибка установки имени», isError = true, перехода нет), повтор через «Продолжить» доступен — кнопка гасится только валидацией полей. \ reg_set_contact_data.dart:266-274,244. Оговорки вынесены отдельно: OQ1.2.2-15 («Заполню потом» после ошибки блокируется навсегда) и OQ1.2.2-14 (ошибочный ответ бэкенда всё ещё уводит с шага).РазработкаClosedВ модалке FR-12 выполнен; «без потери шага» на экране регистрации ломает finally-баг. Закрываю сам: решается той же правкой, что OQ1.2.2-6 (перенос перехода в ветку успеха). Отдельного продуктового вопроса нет. Связать с OQ1.2.2-6.От разработки: реализовано.
#OQ1.2.2-9Код вне BRDЧто нашли: модалка check_email_phone_modals/ — generic для email и телефона (TypeContactData.email | phone, ветвления по всему кубиту). В scope 1.2.2 телефон не входит — код сверх текущего BRD. \ type_contact_data.dart:1-3; check_contacts_modal_cubit.dart; new_contact_enter.dart \ Переобход 89ca502: без изменений — модалка по-прежнему generic email/phone, ветвления по typeContactData на месте. \ check_contacts_modal_cubit.dart:55-64,98-142; new_contact_enter.dart:113-121Бизнес/РазработкаClosedНе дефект. Решение: пометить «телефонную» логику как generic-компонент вне scope 1.2.2 (используется в заказе/профиле), чтобы не считать её частью раздела. Дом — 5.2.1 Редактирование учётных данных / сценарий заказа.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.2-10Код вне BRDЧто нашли: признаки незавершённости в якорных файлах: _sendDataToServer(){} пустой; большие закомментированные блоки отправки/навигации; custom_timer.dart помечен «Возможно не используется FIXME»; отладочные print в проде. Код в состоянии черновика — влияет на достоверность «реализовано». \ reg_set_contact_data.dart:37,47-67,70; new_contact_enter.dart:344-391; confirmed_pin_page.dart:271-290,81,134,200; regex.dart:70; custom_timer.dart:4 \ Переобход 89ca502: без изменений — уборки не было: _sendDataToServer(){} (reg_set_contact_data.dart:37), закомментированный блок (:47-67), print (:70,:183; regex.dart:70; confirmed_pin_page.dart:81,134,200; check_contacts_modal_cubit.dart:67,106; error_modal.dart:70), закомментированные блоки (new_contact_enter.dart:344-391; confirmed_pin_page.dart:271-290), FIXME в custom_timer.dart:4. Уточнение по FIXME: CountDownTimer используетсяcheck_contacts_modal_cubit.dart:27 (CountDownTimer timerTimeout = CountDownTimer(0)), т.е. пометка «возможно не используется» неверна и вводит в заблуждение; удалять файл нельзя, надо снять комментарий. В этот же коммит добавлена инфраструктура логирования (app_talker.dart, debug_log_overlay.dart, override logs via defines) — на неё имеет смысл перевести оставшиеся print.РазработкаOpenТех-долг/уборка, продуктового вопроса нет. Закрываю сам: перед приёмкой убрать print, мёртвый/закомментированный код и _sendDataToServer(){}, определить судьбу CountDownTimer (FIXME) → тикет в Bitrix. До этого «реализовано» по разделу — предварительно.От бизнеса: чистка кода, вес 3.
#OQ1.2.2-11Код FR-7Что подтверждено: показ email-адреса и поле ввода 6 цифр есть (Pinput length: 6, заголовок «Подтвердите кодом из письма»). FR-7 выполнен. \ confirmed_pin_page.dart:184,158; check_contacts_texts.dart:17 \ Переобход 89ca502: без изменений, подтверждение в силе — length: 6 (confirmed_pin_page.dart:184), показ адреса (:158), текст (check_contacts_texts.dart:15,17). Замечание про захардкоженную длину остаётся.РазработкаClosedРасхождения по FR-7 нет. \ Подтверждение: FR-7 подтверждён кодом. Замечание: длина 6 захардкожена в общем компоненте email/phone — при расхождении длины email/sms-кода потребуется разветвление (на живучесть).Расхождений нет.
#OQ1.2.2-12Код FR-8Что подтверждено: код уходит на проверку автоматически по заполнению 6 цифр (из Pinput.validator). FR-8 допускает «после заполнения или явного действия» — вариант «после заполнения» покрыт. \ confirmed_pin_page.dart:202-229 \ Переобход 89ca502: решение команды не пересматривается, остаётся Closed. Механика та же (confirmed_pin_page.dart:202-229). Попутно в dd54f5d исправлен смежный дефект, ранее не зафиксированный: approveCode принимал int и вызывался как approveCode(int.parse(value)) — код с ведущим нулём («012345») терял его и уходил на бэкенд как 12345. Теперь сигнатура Future<String?> approveCode(String value) и код передаётся строкой без изменений. \ check_contacts_modal_cubit.dart:52; confirmed_pin_page.dart:211ClosedРасхождения нет. \ Подтверждение: FR-8 покрыт вариантом «после заполнения»; отдельной кнопки «Подтвердить» нет. Если продукт хочет явное действие — это уточнение FR-8, не дефект.Почистить код.
#OQ1.2.2-13Код FR-11Что подтверждено: повторная отправка до конца таймера заблокирована по UI — кнопка «Отправить новый код» рендерится только при remainingSeconds == 0. FR-11 по UI покрыт. \ confirmed_pin_page.dart:252-267 \ Переобход 89ca502: без изменений — блокировка по-прежнему только на уровне виджета (confirmed_pin_page.dart:252-267), updateContact остаток таймера не проверяет (check_contacts_modal_cubit.dart:84-143).РазработкаClosedРасхождения по UI нет. \ Подтверждение: блокировка на уровне виджета. Замечание: updateContact остаток таймера не проверяет — реальная защита только серверный tooManyRequests; опционально продублировать проверку в кубите.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.2-14Код FR-4, FR-12Требование говорит: «Продолжить» переводит пользователя дальше при успешном ответе (FR-4); при ошибке шаг не теряется (FR-12). В коде (остаток после фикса OQ1.2.2-6) await вернул контроль над ошибками, но только над брошенными: changeUserOnlyName вызывает _grpclient.sendRequest(...) и безусловно возвращает true, не проверяя res.hasError() — в отличие от соседнего getUser(), который такую проверку делает (user_repository.dart:52). При этом sendRequest возвращает GatewayResponse и на ошибку бэкенда не бросает исключение (grpc_client.dart:248; GatewayException бросает не он, а GrpcRequestHandler.handleRequestgateway_exception.dart:3-14). Плюс возвращаемый bool на месте вызова игнорируется. Итог: при ошибочном ответе бэкенда (например, сервер отклонил имя) пользователь всё равно видит «Имя установлено успешно» и уходит на HomeRoute. Ловится только транспортная ошибка. \ user_repository.dart:105-126,52,117-122; reg_set_contact_data.dart:258-265; grpc_client.dart:248,326-334РазработкаClosedПрограммный дефект, продуктового вопроса нет — прямое продолжение OQ1.2.2-6 на уровень ниже (репозиторий вместо виджета). Закрываю сам: в changeUserOnlyName проверять res.hasError() и не возвращать true вслепую (либо бросать по образцу GrpcRequestHandler.handleRequest, либо вернуть false), а на экране — не игнорировать результат: переход и «успешно» только при подтверждённом успехе → тикет в Bitrix. До этой правки «переход только при успехе» (FR-4) обеспечен лишь для транспортных сбоев.Доработать код: переводить пользователя дальше только при успешном ответе сервера, при ошибке оставаться на шаге.
#OQ1.2.2-15Код FR-5, FR-12Требование говорит: пользователь может пропустить ввод контактных данных и завершить шаг (FR-5); при технической ошибке действие можно повторить без потери шага (FR-12). В коде флаг isError выставляется в true в catch при неудачном сохранении имени и нигде не сбрасывается обратно (единственные вхождения — объявление bool isError = false и присваивание в catch), а «Заполню потом» отключается именно по нему: onTap: isError ? null : .... Итог: после одной неудачной попытки сохранить имя пропуск шага становится недоступен до конца жизни экрана — единственный выход остаётся через «Продолжить», которое требует валидных имени и email. До фикса OQ1.2.2-6 ветка catch была мёртвой и ловушка не срабатывала; сейчас она достижима. \ reg_set_contact_data.dart:35,271-273,291-293РазработкаOpenПрограммный дефект, продуктового вопроса нет. Закрываю сам: сбрасывать isError в false при новой попытке (в начале обработчика «Продолжить» и/или в onChanged полей), либо не завязывать «Заполню потом» на isError — пропуск шага не должен зависеть от неудачи сохранения имени → тикет в Bitrix. Уточнить у бизнеса стоит только одно: задумывалась ли блокировка пропуска после ошибки намеренно (в BRD такого условия нет).От бизнеса: доработка, вес 1.
#OQ1.2.2-16Код FR-2, FR-6Требование говорит: интерфейс показывает ошибку, если имя содержит недопустимые символы (FR-2), и показывает ошибку email или имени и не отправляет данные до исправления (FR-6). В коде ошибка пользователю не показывается ни для имени, ни для email: у обоих полей нет errorText, а результат валидации используется только для включения/выключения кнопки — checkFields() кладёт Validator.validateName(name) == null && RegexString.checkEmailPattern(email) в isBtnContinueActive, и текст ошибки из validateName («Цифры в имени не допускаются.», «Не более 50 символов.» и т.д.) никуда не выводится. Недопустимые символы в имени теперь молча не вводятся вообще — их отсекает NameInputFormatter (добавлен в 89ca502). Половина «не отправляет данные до исправления» выполнена (кнопка неактивна), половина «показывает ошибку» — нет: пользователь видит только неактивную кнопку без объяснения причины. \ reg_set_contact_data.dart:69-82,112-156,164-224,244; validators.dart:9-58Бизнес/РазработкаClosedРасхождение вскрыто на переобходе: сам разрыв существовал и раньше, но с приходом NameInputFormatter поведение «молча блокируем ввод» стало основным, поэтому вопрос вынесен явно. Решение: «предотвращать» или «объяснять». (А, рекоменд.) показывать текст ошибки под полем — validateName уже возвращает готовые человеческие сообщения, их достаточно пробросить в errorText; для email — сообщение о неверном формате. (Б) если продукт сознательно выбирает «не пустить ввод вместо ошибки» — смягчить формулировки FR-2/FR-6 («блокирует ввод недопустимых символов»), иначе требование в текущем виде не выполняется.Доработать код: показывать текст ошибки для имени и email, а не только гасить кнопку «Продолжить».

0 items under this folder.