- Implement `toggleMaterialStatus` in `AccountSettingsWindow` for UI control and threading integration. - Enhance `MaterialDAO` with numeric validation for `lastNumbers` field to ensure data integrity. - Update `PayByCardScript` and `PayByPhoneScript` to handle PIN entry if required after payment confirmation. - Introduce dynamic UI adjustments for material toggling, including a new toggle button and layout refinements. - Add documentation for task logic in `ozon_tasks.md` and `black_tasks.md`. - Switch default currency in `GetCardInfoScript` (Black app) from USD to ARS.
14 KiB
Black - Логика задач
Описание логики всех типов задач для банка Black (appCode: black).
Black — аргентинское банковское приложение. Интерфейс на испанском. Валюта: ARS (код 32). Таймзона: America/Argentina/Buenos_Aires (UTC-3).
Общие механизмы (CommonScript)
Запуск приложения (runAppAndGoToHomeScreen)
- Убивает приложение если запущено
- Запускает приложение
- Ждёт 4.5 секунды
- Цикл до 10 попыток:
- Получает XML-дамп экрана
- Проверяет
isHomeScreen()(наличие "Saldo disponible" + content-desc "Inicio") - Если экран с PIN-кодом (
isPinCodeScreen()— "¿Olvidaste tu PIN?" или "Ingresá tu PIN"):- Находит EditText с password=true
- Тапает для фокуса → ожидание 500мс
- Вводит PIN через
AdbUtils::inputText() - Ожидание 3 сек
- Ожидание 1 сек между попытками
- При неудаче — скриншот
Прокрутка к кнопке (swipeToButton)
- До 10 свайпов вверх
- После каждого свайпа парсит XML и ищет кнопку
- Аналогично Ozon
Создание банковского профиля (createBankProfileIfNeeded)
- Аналогично Ozon (общий код в CommonScript)
- Валюта по умолчанию: ARS→32
FETCH_PROFILE
Скрипты: Black::LoginAndCheckAccountsScript → Black::GetProfileInfoScript → Black::GetCardInfoScript
Цель
Авторизация, сбор профиля (ФИО, телефон, email), получение CVU и баланса.
Шаги LoginAndCheckAccountsScript
- Валидация — получение DeviceInfo и BankProfileInfo из БД
- Запуск приложения —
runAppAndGoToHomeScreen()с PIN-кодом - Запуск GetProfileInfoScript — синхронно, для сбора профиля
- При ошибке: скриншот + лог + обновление комментария в
BankProfileDAO
Шаги GetProfileInfoScript
-
Домашний экран — проверка/перезапуск
-
Парсинг с домашнего экрана:
- fullName из
parseUserNameFromHomeScreen()— формат "¡Hola!\nИМЯ\ninitials" - Баланс из
parseBalanceFromHomeScreen()— формат "$1.857,00" → 1857.00 - Обновление баланса в
BankProfileDAO
- fullName из
-
Навигация в боковое меню:
- Поиск кнопки "¡Hola!" (аватар)
- Тап → ожидание 2 сек
- Ожидание
isSideMenu()(5 × 1 сек) — маркеры: "Tu Información" + "Cerrar sesión"
-
Навигация к профилю:
- Тап "Tu Información" (content-desc)
- Ожидание 2.5 сек
- Ожидание
isProfilePage()(5 × 1 сек) — маркер: content-desc "Tu información"
-
Парсинг профиля:
- Nombre, Apellido, Email — по hint-атрибутам EditText
- Свайп вниз → ожидание 1.5 сек
- Телефон — из hint-атрибута
- fullName = "nombre apellido" (или fallback на имя с домашнего экрана)
-
Сохранение:
BankProfileDAO::updateProfileInfo(id, fullName, phone, email) -
Синхронизация с сервером:
createBankProfileIfNeeded() -
Отправка аккаунтов:
postAccountsData()для аккаунтов без materialId -
Завершение — убийство приложения
Шаги GetCardInfoScript (CVU)
-
Домашний экран — проверка/перезапуск
-
Парсинг баланса → обновление в БД
-
Навигация к CVU:
- Поиск кнопки "Tu CVU" (exact match)
- Тап → ожидание 2 сек
- Ожидание
isCVUScreen()(5 × 1 сек)
-
Парсинг CVU:
parseCVUNumber()— 22-значный номер после маркера "CVU"- lastNumbers = последние 4 цифры CVU
-
Сохранение в БД:
MaterialDAO::upsertAccount()с:- cardNumber = полный CVU
- lastNumbers = CVU[-4:]
- currency = "USD" (хардкод)
- description = "CVU"
-
Отправка материала на сервер:
ns.postMaterial()(если есть bankProfileId и нет materialId) -
Завершение — убийство приложения
Ответ серверу (при успехе)
{
"type": "FETCH_PROFILE",
"material_id": "...",
"bank_name": "black",
"data": {
"bank_account_id": "",
"amount": 185700,
"currency": 32
}
}
FETCH_TRANSACTIONS
Скрипт: Black::GetLastTransactionsScript
Цель
Получение списка последних транзакций из истории операций.
Шаги
-
Домашний экран — проверка/перезапуск
-
Навигация к истории транзакций:
- Поиск кнопки "Ver más" после секции "Tu actividad" через
findVerMasAfterTuActividad() - Если не найдена — до 5 свайпов вниз (1 сек между свайпами)
- Тап "Ver más" → ожидание 2 сек
- Поиск кнопки "Ver más" после секции "Tu actividad" через
-
Ожидание загрузки (15 × 1.5 сек):
isTransactionLoadingScreen()= "Tu Actividad" есть, но транзакций нет (загрузка)isTransactionListScreen()= "Tu Actividad" + видимые транзакции- Если после 15 попыток всё ещё loading → пустой аккаунт (нормально), возврат без ошибки
-
Парсинг транзакций (
parseTransactionItems()):- Источник: ImageView ноды с текстом формата:
или (входящие):Transferencia DD de MES de YYYY - HH:MM $amount,centsTransferencia externa de ИМЯ DD de MES de YYYY - HH:MM $amount,cents - Отрицательные суммы:
$-amount(исходящие переводы) - Парсинг испанских дат (
parseSpanishDate()): enero, febrero, ..., diciembre
- Источник: ImageView ноды с текстом формата:
-
Сохранение в БД (для каждой транзакции):
- bankName = "black"
- status = Complete
- timestamp = currentDateTimeUtc()
- Конвертация времени: парсинг испанской даты → таймзона Buenos Aires
TransactionDAO::insertTransactionIfNotExists()
-
Возврат —
goBack()→ ожидание 1 сек
Retry-логика (EventHandler)
Аналогична Ozon:
"Transaction not found" + < 10 мин → повтор через 15 сек
"Transaction not found" + ≥ 10 мин → ошибка
Ответ серверу
{
"type": "FETCH_TRANSACTIONS",
"material_id": "...",
"bank_name": "black",
"data": [
{
"bank_transaction_id": null,
"amount": 185700,
"currency": 32,
"transaction_type": "bank",
"bank_participator_id": "+5491112345678",
"status": "completed",
"extra": { "fee": 0 },
"created_at": "2025-01-15T14:30:00.000-03:00"
}
]
}
CREATE_TRANSACTION (bank — по CBU/CVU/Alias)
Скрипт: Black::PayByCardScript
Цель
Перевод на CBU, CVU или Alias получателя.
Входные параметры
event.phone— CBU/CVU/Alias получателя (полеm_cardNumber)event.amount— сумма переводаm_account— аккаунт-отправитель
Шаги
-
Домашний экран — проверка/перезапуск
-
Поиск кнопки оплаты:
findNodeByContentDesc("Enviar a\nCBU o alias", contains=true)- Если не найдена — до 5 свайпов вниз
- Тап → ожидание 2 сек
-
Ввод реквизитов (10 × 1.5 сек ожидание экрана):
- Экран
isPayInputCardNumberScreen()— hint "Ingresar CBU, CVU o Alias" - Тап на EditText → ожидание 500мс
- Ввод CBU/CVU/Alias
- Тап "Continuar" → ожидание 2 сек
- Экран
-
Подтверждение получателя (10 × 1.5 сек):
- Экран
isPayTransferConfirmScreen()— кнопка "Transferir" + поле "Alias:" - Тап "Transferir" → ожидание 2 сек
- Экран
-
Ввод суммы (10 × 1.5 сек):
- Экран
isPayInputSumScreen()— "Cuánto vas a transferír" - Тап на EditText → ожидание 500мс
- Очистка поля: 20 × keyevent 67 (backspace)
- Ввод суммы в сентаво:
int(amount * 100)→ строка- Пример: 500.00 → "50000"
- Проверка: перечитывает поле, если не совпадает — повторяет ввод
- Тап "Continuar" → ожидание 2 сек
- Экран
-
Финальное подтверждение (10 × 1.5 сек):
- Экран
isPayConfirmScreen()— "Vas a transferir \na ИМЯ_ПОЛУЧАТЕЛЯ" - Парсинг имени получателя (
parseRecipientNameFromConfirm()) - Тап "Transferir" → ожидание 3 сек
- Экран
-
Ввод PIN (если запрошен):
isPinCodeScreen()→inputPinCode()- Ожидание 3 сек
-
Проверка результата (15 × 2 сек):
- "Dinero enviado!" → успех:
- Парсинг bankTime:
parsePaySuccessDateTime()→ "DD/MM/YY HH:MM" - Парсинг ID:
parsePaySuccessCoelsaId()→ Coelsa transaction ID - Скриншот как чек
- Парсинг bankTime:
- Если снова PIN-экран → повторный ввод PIN
- Таймаут → ошибка
- "Dinero enviado!" → успех:
-
Сохранение транзакции:
- amount = -m_amount (отрицательная)
- fee = 0
- type = Card, status = Complete
- name = имя получателя
- description = "Transferencia a ИМЯ"
- phone = CBU/CVU/Alias
- bankTrExternalId = Coelsa ID
- receiptImage = скриншот
TransactionDAO::insertTransaction()
Ответ серверу
{
"type": "CREATE_TRANSACTION",
"material_id": "...",
"bank_name": "black",
"data": {
"amount": -50000,
"currency": 32,
"transaction_type": "bank",
"bank_participator_id": "0000003100012345678901",
"status": "completed",
"bank_transaction_id": "COELSA-12345",
"extra": { "fee": 0 },
"created_at": "2025-01-15T14:30:00.000-03:00"
}
}
ScreenXmlParser (Black)
Определение экранов
| Метод | Маркеры |
|---|---|
isHomeScreen() |
"Saldo disponible" + content-desc "Inicio" |
isPinCodeScreen() |
"¿Olvidaste tu PIN?" или "Ingresá tu PIN" |
isSideMenu() |
"Tu Información" + "Cerrar sesión" |
isProfilePage() |
content-desc "Tu información" |
isCVUScreen() |
маркер "CVU" |
isTransactionLoadingScreen() |
"Tu Actividad" без "Transferencia" |
isTransactionListScreen() |
"Tu Actividad" + транзакции |
isPayInputCardNumberScreen() |
EditText с hint "Ingresar CBU, CVU o Alias" |
isPayTransferConfirmScreen() |
"Transferir" + "Alias:" без "Vas a transferir" |
isPayInputSumScreen() |
"Cuánto vas a transferír" |
isPayConfirmScreen() |
"Vas a transferir" |
isPaySuccessScreen() |
"Dinero enviado!" |
Парсинг данных
| Метод | Формат / Логика |
|---|---|
parseUserNameFromHomeScreen() |
"¡Hola!\nИМЯ\ninitials" → ИМЯ |
parseBalanceFromHomeScreen() |
"$1.857,00" → 1857.00 (аргентинский формат) |
parseProfileNombre/Apellido/Email/Phone() |
По hint-атрибутам EditText |
parseCVUNumber() |
22-значный номер после маркера "CVU" |
parseTransactionItems() |
ImageView ноды с "Transferencia" |
parseSpanishDate() |
"DD de MES de YYYY - HH:MM" → QDateTime |
parseRecipientNameFromConfirm() |
"Vas a transferir \na ИМЯ" → ИМЯ |
parsePaySuccessDateTime() |
"DD/MM/YY HH:MM" |
parsePaySuccessCoelsaId() |
Текст после "Id Coelsa" |
Парсинг транзакций
Формат элемента списка (ImageView text):
Transferencia // тип
15 de enero de 2025 - 14:30 // дата (испанский)
$1.500,00 // сумма (положительная = входящая)
или:
Transferencia externa de Juan Perez // тип + имя отправителя
15 de enero de 2025 - 14:30
$-1.500,00 // отрицательная = исходящая
Испанские месяцы
| Месяц | Перевод |
|---|---|
| enero | январь |
| febrero | февраль |
| marzo | март |
| abril | апрель |
| mayo | май |
| junio | июнь |
| julio | июль |
| agosto | август |
| septiembre | сентябрь |
| octubre | октябрь |
| noviembre | ноябрь |
| diciembre | декабрь |