- 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.
369 lines
14 KiB
Markdown
369 lines
14 KiB
Markdown
# Black - Логика задач
|
||
|
||
[← Главная](index.md) | [События](events.md)
|
||
|
||
Описание логики всех типов задач для банка **Black** (appCode: `black`).
|
||
|
||
> Black — аргентинское банковское приложение. Интерфейс на испанском. Валюта: ARS (код 32). Таймзона: America/Argentina/Buenos_Aires (UTC-3).
|
||
|
||
---
|
||
|
||
## Общие механизмы (CommonScript)
|
||
|
||
### Запуск приложения (`runAppAndGoToHomeScreen`)
|
||
|
||
1. Убивает приложение если запущено
|
||
2. Запускает приложение
|
||
3. Ждёт 4.5 секунды
|
||
4. Цикл до 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 сек между попытками
|
||
5. При неудаче — скриншот
|
||
|
||
### Прокрутка к кнопке (`swipeToButton`)
|
||
|
||
- До 10 свайпов вверх
|
||
- После каждого свайпа парсит XML и ищет кнопку
|
||
- Аналогично Ozon
|
||
|
||
### Создание банковского профиля (`createBankProfileIfNeeded`)
|
||
|
||
- Аналогично Ozon (общий код в CommonScript)
|
||
- Валюта по умолчанию: ARS→32
|
||
|
||
---
|
||
|
||
## FETCH_PROFILE
|
||
|
||
**Скрипты:** `Black::LoginAndCheckAccountsScript` → `Black::GetProfileInfoScript` → `Black::GetCardInfoScript`
|
||
|
||
### Цель
|
||
|
||
Авторизация, сбор профиля (ФИО, телефон, email), получение CVU и баланса.
|
||
|
||
### Шаги LoginAndCheckAccountsScript
|
||
|
||
1. **Валидация** — получение DeviceInfo и BankProfileInfo из БД
|
||
2. **Запуск приложения** — `runAppAndGoToHomeScreen()` с PIN-кодом
|
||
3. **Запуск GetProfileInfoScript** — синхронно, для сбора профиля
|
||
4. При ошибке: скриншот + лог + обновление комментария в `BankProfileDAO`
|
||
|
||
### Шаги GetProfileInfoScript
|
||
|
||
1. **Домашний экран** — проверка/перезапуск
|
||
|
||
2. **Парсинг с домашнего экрана**:
|
||
- fullName из `parseUserNameFromHomeScreen()` — формат "¡Hola!\nИМЯ\ninitials"
|
||
- Баланс из `parseBalanceFromHomeScreen()` — формат "$1.857,00" → 1857.00
|
||
- Обновление баланса в `BankProfileDAO`
|
||
|
||
3. **Навигация в боковое меню**:
|
||
- Поиск кнопки "¡Hola!" (аватар)
|
||
- Тап → ожидание 2 сек
|
||
- Ожидание `isSideMenu()` (5 × 1 сек) — маркеры: "Tu Información" + "Cerrar sesión"
|
||
|
||
4. **Навигация к профилю**:
|
||
- Тап "Tu Información" (content-desc)
|
||
- Ожидание 2.5 сек
|
||
- Ожидание `isProfilePage()` (5 × 1 сек) — маркер: content-desc "Tu información"
|
||
|
||
5. **Парсинг профиля**:
|
||
- Nombre, Apellido, Email — по hint-атрибутам EditText
|
||
- Свайп вниз → ожидание 1.5 сек
|
||
- Телефон — из hint-атрибута
|
||
- fullName = "nombre apellido" (или fallback на имя с домашнего экрана)
|
||
|
||
6. **Сохранение**: `BankProfileDAO::updateProfileInfo(id, fullName, phone, email)`
|
||
|
||
7. **Синхронизация с сервером**: `createBankProfileIfNeeded()`
|
||
|
||
8. **Отправка аккаунтов**: `postAccountsData()` для аккаунтов без materialId
|
||
|
||
9. **Завершение** — убийство приложения
|
||
|
||
### Шаги GetCardInfoScript (CVU)
|
||
|
||
1. **Домашний экран** — проверка/перезапуск
|
||
|
||
2. **Парсинг баланса** → обновление в БД
|
||
|
||
3. **Навигация к CVU**:
|
||
- Поиск кнопки "Tu CVU" (exact match)
|
||
- Тап → ожидание 2 сек
|
||
- Ожидание `isCVUScreen()` (5 × 1 сек)
|
||
|
||
4. **Парсинг CVU**:
|
||
- `parseCVUNumber()` — 22-значный номер после маркера "CVU"
|
||
- lastNumbers = последние 4 цифры CVU
|
||
|
||
5. **Сохранение в БД**:
|
||
- `MaterialDAO::upsertAccount()` с:
|
||
- cardNumber = полный CVU
|
||
- lastNumbers = CVU[-4:]
|
||
- currency = "USD" (хардкод)
|
||
- description = "CVU"
|
||
|
||
6. **Отправка материала на сервер**: `ns.postMaterial()` (если есть bankProfileId и нет materialId)
|
||
|
||
7. **Завершение** — убийство приложения
|
||
|
||
### Ответ серверу (при успехе)
|
||
|
||
```json
|
||
{
|
||
"type": "FETCH_PROFILE",
|
||
"material_id": "...",
|
||
"bank_name": "black",
|
||
"data": {
|
||
"bank_account_id": "",
|
||
"amount": 185700,
|
||
"currency": 32
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## FETCH_TRANSACTIONS
|
||
|
||
**Скрипт:** `Black::GetLastTransactionsScript`
|
||
|
||
### Цель
|
||
|
||
Получение списка последних транзакций из истории операций.
|
||
|
||
### Шаги
|
||
|
||
1. **Домашний экран** — проверка/перезапуск
|
||
|
||
2. **Навигация к истории транзакций**:
|
||
- Поиск кнопки "Ver más" после секции "Tu actividad" через `findVerMasAfterTuActividad()`
|
||
- Если не найдена — до 5 свайпов вниз (1 сек между свайпами)
|
||
- Тап "Ver más" → ожидание 2 сек
|
||
|
||
3. **Ожидание загрузки** (15 × 1.5 сек):
|
||
- `isTransactionLoadingScreen()` = "Tu Actividad" есть, но транзакций нет (загрузка)
|
||
- `isTransactionListScreen()` = "Tu Actividad" + видимые транзакции
|
||
- Если после 15 попыток всё ещё loading → пустой аккаунт (нормально), возврат без ошибки
|
||
|
||
4. **Парсинг транзакций** (`parseTransactionItems()`):
|
||
- Источник: ImageView ноды с текстом формата:
|
||
```
|
||
Transferencia
|
||
DD de MES de YYYY - HH:MM
|
||
$amount,cents
|
||
```
|
||
или (входящие):
|
||
```
|
||
Transferencia externa de ИМЯ
|
||
DD de MES de YYYY - HH:MM
|
||
$amount,cents
|
||
```
|
||
- Отрицательные суммы: `$-amount` (исходящие переводы)
|
||
- Парсинг испанских дат (`parseSpanishDate()`): enero, febrero, ..., diciembre
|
||
|
||
5. **Сохранение в БД** (для каждой транзакции):
|
||
- bankName = "black"
|
||
- status = Complete
|
||
- timestamp = currentDateTimeUtc()
|
||
- Конвертация времени: парсинг испанской даты → таймзона Buenos Aires
|
||
- `TransactionDAO::insertTransactionIfNotExists()`
|
||
|
||
6. **Возврат** — `goBack()` → ожидание 1 сек
|
||
|
||
### Retry-логика (EventHandler)
|
||
|
||
Аналогична Ozon:
|
||
```
|
||
"Transaction not found" + < 10 мин → повтор через 15 сек
|
||
"Transaction not found" + ≥ 10 мин → ошибка
|
||
```
|
||
|
||
### Ответ серверу
|
||
|
||
```json
|
||
{
|
||
"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` — аккаунт-отправитель
|
||
|
||
### Шаги
|
||
|
||
1. **Домашний экран** — проверка/перезапуск
|
||
|
||
2. **Поиск кнопки оплаты**:
|
||
- `findNodeByContentDesc("Enviar a\nCBU o alias", contains=true)`
|
||
- Если не найдена — до 5 свайпов вниз
|
||
- Тап → ожидание 2 сек
|
||
|
||
3. **Ввод реквизитов** (10 × 1.5 сек ожидание экрана):
|
||
- Экран `isPayInputCardNumberScreen()` — hint "Ingresar CBU, CVU o Alias"
|
||
- Тап на EditText → ожидание 500мс
|
||
- Ввод CBU/CVU/Alias
|
||
- Тап "Continuar" → ожидание 2 сек
|
||
|
||
4. **Подтверждение получателя** (10 × 1.5 сек):
|
||
- Экран `isPayTransferConfirmScreen()` — кнопка "Transferir" + поле "Alias:"
|
||
- Тап "Transferir" → ожидание 2 сек
|
||
|
||
5. **Ввод суммы** (10 × 1.5 сек):
|
||
- Экран `isPayInputSumScreen()` — "Cuánto vas a transferír"
|
||
- Тап на EditText → ожидание 500мс
|
||
- **Очистка поля**: 20 × keyevent 67 (backspace)
|
||
- **Ввод суммы в сентаво**: `int(amount * 100)` → строка
|
||
- Пример: 500.00 → "50000"
|
||
- Проверка: перечитывает поле, если не совпадает — повторяет ввод
|
||
- Тап "Continuar" → ожидание 2 сек
|
||
|
||
6. **Финальное подтверждение** (10 × 1.5 сек):
|
||
- Экран `isPayConfirmScreen()` — "Vas a transferir \na ИМЯ_ПОЛУЧАТЕЛЯ"
|
||
- Парсинг имени получателя (`parseRecipientNameFromConfirm()`)
|
||
- Тап "Transferir" → ожидание 3 сек
|
||
|
||
7. **Ввод PIN** (если запрошен):
|
||
- `isPinCodeScreen()` → `inputPinCode()`
|
||
- Ожидание 3 сек
|
||
|
||
8. **Проверка результата** (15 × 2 сек):
|
||
- **"Dinero enviado!"** → успех:
|
||
- Парсинг bankTime: `parsePaySuccessDateTime()` → "DD/MM/YY HH:MM"
|
||
- Парсинг ID: `parsePaySuccessCoelsaId()` → Coelsa transaction ID
|
||
- Скриншот как чек
|
||
- Если снова PIN-экран → повторный ввод PIN
|
||
- Таймаут → ошибка
|
||
|
||
9. **Сохранение транзакции**:
|
||
- amount = -m_amount (отрицательная)
|
||
- fee = 0
|
||
- type = Card, status = Complete
|
||
- name = имя получателя
|
||
- description = "Transferencia a ИМЯ"
|
||
- phone = CBU/CVU/Alias
|
||
- bankTrExternalId = Coelsa ID
|
||
- receiptImage = скриншот
|
||
- `TransactionDAO::insertTransaction()`
|
||
|
||
### Ответ серверу
|
||
|
||
```json
|
||
{
|
||
"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 | декабрь |
|