1
0
forked from BRT/arc

Add PAN field detection and account number UI updates.

- Extend `ScreenXmlParser` with `findPanCardNumberInput` for detecting empty PAN fields in Ozon WebView.
- Update `PayByCardScript` to handle missing hints for recipient card input.
- Temporarily disable advanced account number parsing in `GetCardInfoScript`.
- Add `patchMaterial` method to `NetworkService` for updating account details.
- Update card management UI with refined "Switch" button logic and label changes.
This commit is contained in:
slava 2026-05-13 02:03:20 +07:00
parent 52d261a060
commit 3751406c36
8 changed files with 271 additions and 178 deletions

View File

@ -162,76 +162,77 @@ void GetCardInfoScript::doStart() {
} }
// Достаём номер счёта: Настройки карты → Реквизиты карты → таб "Реквизиты счета". // Достаём номер счёта: Настройки карты → Реквизиты карты → таб "Реквизиты счета".
// ВРЕМЕННО ОТКЛЮЧЕНО
QString accountNumber; QString accountNumber;
const Node settingsBtn = xmlScreenParser.findCardSettingsButton(); // const Node settingsBtn = xmlScreenParser.findCardSettingsButton();
if (settingsBtn.x() > 0) { // if (settingsBtn.x() > 0) {
AdbUtils::makeTap(m_deviceId, settingsBtn.x(), settingsBtn.y()); // AdbUtils::makeTap(m_deviceId, settingsBtn.x(), settingsBtn.y());
QThread::msleep(1500); // QThread::msleep(1500);
//
bool sheetFound = false; // bool sheetFound = false;
for (int attempt = 0; attempt < 5; ++attempt) { // for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardSettingsBottomSheet()) { // if (xmlScreenParser.isCardSettingsBottomSheet()) {
sheetFound = true; // sheetFound = true;
break; // break;
} // }
QThread::msleep(1000); // QThread::msleep(1000);
} // }
//
if (sheetFound) { // if (sheetFound) {
const Node requisitesItem = xmlScreenParser.findCardRequisitesMenuItem(); // const Node requisitesItem = xmlScreenParser.findCardRequisitesMenuItem();
if (requisitesItem.x() > 0) { // if (requisitesItem.x() > 0) {
AdbUtils::makeTap(m_deviceId, requisitesItem.x(), requisitesItem.y()); // AdbUtils::makeTap(m_deviceId, requisitesItem.x(), requisitesItem.y());
QThread::msleep(1500); // QThread::msleep(1500);
//
bool reqScreenFound = false; // bool reqScreenFound = false;
for (int attempt = 0; attempt < 5; ++attempt) { // for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardRequisitesScreen()) { // if (xmlScreenParser.isCardRequisitesScreen()) {
reqScreenFound = true; // reqScreenFound = true;
break; // break;
} // }
QThread::msleep(1000); // QThread::msleep(1000);
} // }
//
if (reqScreenFound) { // if (reqScreenFound) {
const Node accountTab = xmlScreenParser.findAccountRequisitesTab(); // const Node accountTab = xmlScreenParser.findAccountRequisitesTab();
if (accountTab.x() > 0) { // if (accountTab.x() > 0) {
AdbUtils::makeTap(m_deviceId, accountTab.x(), accountTab.y()); // AdbUtils::makeTap(m_deviceId, accountTab.x(), accountTab.y());
QThread::msleep(1500); // QThread::msleep(1500);
//
// Несколько попыток — данные могут подгружаться // // Несколько попыток — данные могут подгружаться
for (int attempt = 0; attempt < 5; ++attempt) { // for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
accountNumber = xmlScreenParser.parseAccountNumber(); // accountNumber = xmlScreenParser.parseAccountNumber();
if (!accountNumber.isEmpty()) break; // if (!accountNumber.isEmpty()) break;
QThread::msleep(1000); // QThread::msleep(1000);
} // }
qDebug() << "[Dushanbe::GetCardInfo] accountNumber for card" << i // qDebug() << "[Dushanbe::GetCardInfo] accountNumber for card" << i
<< ":" << accountNumber; // << ":" << accountNumber;
} else { // } else {
qWarning() << "[Dushanbe::GetCardInfo] Account tab not found for card" << i; // qWarning() << "[Dushanbe::GetCardInfo] Account tab not found for card" << i;
} // }
} else { // } else {
qWarning() << "[Dushanbe::GetCardInfo] Requisites screen not detected for card" << i; // qWarning() << "[Dushanbe::GetCardInfo] Requisites screen not detected for card" << i;
} // }
} else { // } else {
qWarning() << "[Dushanbe::GetCardInfo] 'Реквизиты карты' menu item not found for card" << i; // qWarning() << "[Dushanbe::GetCardInfo] 'Реквизиты карты' menu item not found for card" << i;
} // }
} else { // } else {
qWarning() << "[Dushanbe::GetCardInfo] Card settings bottom sheet not detected for card" << i; // qWarning() << "[Dushanbe::GetCardInfo] Card settings bottom sheet not detected for card" << i;
} // }
//
// Возврат на экран детали карты — bottom sheet и/или экран Реквизитов // // Возврат на экран детали карты — bottom sheet и/или экран Реквизитов
for (int attempt = 0; attempt < 4; ++attempt) { // for (int attempt = 0; attempt < 4; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardDetailScreen()) break; // if (xmlScreenParser.isCardDetailScreen()) break;
AdbUtils::goBack(m_deviceId); // AdbUtils::goBack(m_deviceId);
QThread::msleep(1200); // QThread::msleep(1200);
} // }
} else { // } else {
qWarning() << "[Dushanbe::GetCardInfo] 'Настройки карты' button not found for card" << i; // qWarning() << "[Dushanbe::GetCardInfo] 'Настройки карты' button not found for card" << i;
} // }
MaterialInfo account; MaterialInfo account;
account.deviceId = device.id; account.deviceId = device.id;

View File

@ -415,103 +415,104 @@ void GetCardInfoScript::doStart() {
} }
// 2.5. Достаём номер счёта (общий для всех карт): Основной счёт → Посмотреть реквизиты // 2.5. Достаём номер счёта (общий для всех карт): Основной счёт → Посмотреть реквизиты
QString accountNumber; // ВРЕМЕННО ОТКЛЮЧЕНО
{ // QString accountNumber;
bool needFetch = false; // {
for (const MaterialInfo &c : cards) { // bool needFetch = false;
const MaterialInfo dbCard = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers); // for (const MaterialInfo &c : cards) {
if (dbCard.id != -1 && dbCard.accountNumber.isEmpty()) { needFetch = true; break; } // const MaterialInfo dbCard = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers);
} // if (dbCard.id != -1 && dbCard.accountNumber.isEmpty()) { needFetch = true; break; }
// }
if (needFetch) { //
// Возвращаемся на главный экран — критерий именно по наличию кнопки // if (needFetch) {
// "Основной счёт ... ₽". isHomeScreen() для Озона нестрогий // // Возвращаемся на главный экран — критерий именно по наличию кнопки
// (Перевести/Пополнить присутствуют и на других экранах). // // "Основной счёт ... ₽". isHomeScreen() для Озона нестрогий
Node mainAcc; // // (Перевести/Пополнить присутствуют и на других экранах).
for (int attempt = 0; attempt < 6; ++attempt) { // Node mainAcc;
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // for (int attempt = 0; attempt < 6; ++attempt) {
mainAcc = xmlScreenParser.findMainAccountButton(); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (mainAcc.x() > 0) break; // mainAcc = xmlScreenParser.findMainAccountButton();
AdbUtils::goBack(m_deviceId); // if (mainAcc.x() > 0) break;
QThread::msleep(1500); // AdbUtils::goBack(m_deviceId);
} // QThread::msleep(1500);
// }
if (mainAcc.x() > 0) { //
AdbUtils::makeTap(m_deviceId, mainAcc.x(), mainAcc.y()); // if (mainAcc.x() > 0) {
QThread::msleep(2000); // AdbUtils::makeTap(m_deviceId, mainAcc.x(), mainAcc.y());
// QThread::msleep(2000);
// Скроллим WebView, пока "Посмотреть реквизиты" не окажется в верхней половине, //
// чтобы тап не ушёл в системный нав-бар. // // Скроллим WebView, пока "Посмотреть реквизиты" не окажется в верхней половине,
Node showReq; // // чтобы тап не ушёл в системный нав-бар.
const int targetMaxY = device.screenHeight / 2; // Node showReq;
for (int attempt = 0; attempt < 6; ++attempt) { // const int targetMaxY = device.screenHeight / 2;
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // for (int attempt = 0; attempt < 6; ++attempt) {
Node candidate = xmlScreenParser.findShowRequisitesButton(); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (candidate.x() > 0 && candidate.y() <= targetMaxY) { // Node candidate = xmlScreenParser.findShowRequisitesButton();
showReq = candidate; // if (candidate.x() > 0 && candidate.y() <= targetMaxY) {
break; // showReq = candidate;
} // break;
AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2, // }
device.screenHeight * 2 / 3, // AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2,
device.screenWidth / 2, // device.screenHeight * 2 / 3,
device.screenHeight / 3); // device.screenWidth / 2,
QThread::msleep(800); // device.screenHeight / 3);
} // QThread::msleep(800);
// }
if (showReq.x() > 0) { //
AdbUtils::makeTap(m_deviceId, showReq.x(), showReq.y()); // if (showReq.x() > 0) {
QThread::msleep(2000); // AdbUtils::makeTap(m_deviceId, showReq.x(), showReq.y());
// QThread::msleep(2000);
bool reqScreen = false; //
for (int attempt = 0; attempt < 6; ++attempt) { // bool reqScreen = false;
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // for (int attempt = 0; attempt < 6; ++attempt) {
if (xmlScreenParser.isAccountRequisitesScreen()) { // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
reqScreen = true; // if (xmlScreenParser.isAccountRequisitesScreen()) {
break; // reqScreen = true;
} // break;
QThread::msleep(1000); // }
} // QThread::msleep(1000);
// }
if (reqScreen) { //
for (int attempt = 0; attempt < 5; ++attempt) { // if (reqScreen) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // for (int attempt = 0; attempt < 5; ++attempt) {
accountNumber = xmlScreenParser.parseAccountNumberFromRequisites(); // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!accountNumber.isEmpty()) break; // accountNumber = xmlScreenParser.parseAccountNumberFromRequisites();
QThread::msleep(800); // if (!accountNumber.isEmpty()) break;
} // QThread::msleep(800);
qDebug() << "[GetCardInfo] accountNumber:" << accountNumber; // }
} else { // qDebug() << "[GetCardInfo] accountNumber:" << accountNumber;
qWarning() << "[GetCardInfo] Account requisites screen not detected"; // } else {
} // qWarning() << "[GetCardInfo] Account requisites screen not detected";
} else { // }
qWarning() << "[GetCardInfo] 'Посмотреть реквизиты' not reached"; // } else {
} // qWarning() << "[GetCardInfo] 'Посмотреть реквизиты' not reached";
} else { // }
qWarning() << "[GetCardInfo] 'Основной счёт' button not found on home"; // } else {
} // qWarning() << "[GetCardInfo] 'Основной счёт' button not found on home";
// }
// Возврат на главный — критерий по кнопке "Основной счёт ... ₽" //
for (int attempt = 0; attempt < 5; ++attempt) { // // Возврат на главный — критерий по кнопке "Основной счёт ... ₽"
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); // for (int attempt = 0; attempt < 5; ++attempt) {
if (xmlScreenParser.findMainAccountButton().x() > 0) break; // xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
AdbUtils::goBack(m_deviceId); // if (xmlScreenParser.findMainAccountButton().x() > 0) break;
QThread::msleep(1500); // AdbUtils::goBack(m_deviceId);
} // QThread::msleep(1500);
// }
// Записываем номер счёта во все материалы device/app //
if (!accountNumber.isEmpty()) { // // Записываем номер счёта во все материалы device/app
for (const MaterialInfo &c : cards) { // if (!accountNumber.isEmpty()) {
MaterialInfo upd = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers); // for (const MaterialInfo &c : cards) {
if (upd.id == -1) continue; // MaterialInfo upd = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers);
upd.accountNumber = accountNumber; // if (upd.id == -1) continue;
upd.updateTime = QDateTime::currentDateTimeUtc(); // upd.accountNumber = accountNumber;
if (!MaterialDAO::upsertAccount(upd)) { // upd.updateTime = QDateTime::currentDateTimeUtc();
qWarning() << "[GetCardInfo] Failed to save accountNumber for" << upd.lastNumbers; // if (!MaterialDAO::upsertAccount(upd)) {
} // qWarning() << "[GetCardInfo] Failed to save accountNumber for" << upd.lastNumbers;
} // }
} // }
} // }
} // }
// }
// 3. Отправляем материалы (карты) — после OCR, когда номера карт уже в БД // 3. Отправляем материалы (карты) — после OCR, когда номера карт уже в БД
const BankProfileInfo updatedApp = BankProfileDAO::getApplication(device.id, m_appCode); const BankProfileInfo updatedApp = BankProfileDAO::getApplication(device.id, m_appCode);
@ -530,7 +531,7 @@ void GetCardInfoScript::doStart() {
QJsonObject materialBody; QJsonObject materialBody;
materialBody["bank_profile_id"] = updatedApp.bankProfileId; materialBody["bank_profile_id"] = updatedApp.bankProfileId;
materialBody["card_number"] = dbCard.cardNumber; materialBody["card_number"] = dbCard.cardNumber;
// materialBody["account_number"] = dbCard.accountNumber; materialBody["account_number"] = dbCard.accountNumber;
materialBody["name"] = updatedApp.fullName; materialBody["name"] = updatedApp.fullName;
materialBody["state"] = materialStatusToString(dbCard.status) == "active" ? "enabled" : "disabled"; materialBody["state"] = materialStatusToString(dbCard.status) == "active" ? "enabled" : "disabled";
materialBody["currency"] = currencyCodes.value(dbCard.currency.toUpper(), 643); materialBody["currency"] = currencyCodes.value(dbCard.currency.toUpper(), 643);

View File

@ -248,6 +248,12 @@ void PayByCardScript::makePayment(
for (int attempt = 0; attempt < 10; ++attempt) { for (int attempt = 0; attempt < 10; ++attempt) {
if (!checkApp()) return; if (!checkApp()) return;
cardInput = xmlScreenParser.findEditTextByHint("Номер карты получателя"); cardInput = xmlScreenParser.findEditTextByHint("Номер карты получателя");
if (cardInput.isEmpty()) {
// На части сборок Ozon WebView hint у поля PAN пустой, а лейбла
// "Номер карты получателя" на экране нет (есть только "КУДА").
// Поле PAN всегда внутри элемента resource-id="movable-pan-iframe".
cardInput = xmlScreenParser.findPanCardNumberInput();
}
if (!cardInput.isEmpty()) break; if (!cardInput.isEmpty()) break;
qDebug() << "[PayByCard] Waiting for 'Номер карты получателя' input, attempt" << attempt + 1 << "/ 10"; qDebug() << "[PayByCard] Waiting for 'Номер карты получателя' input, attempt" << attempt + 1 << "/ 10";
QThread::msleep(1500); QThread::msleep(1500);

View File

@ -176,6 +176,47 @@ Node ScreenXmlParser::findEditTextByHint(const QString &hintPrefix) {
return best; return best;
} }
static QDomElement findPanIframeElement(const QDomElement &root) {
if (root.isNull()) return {};
if (root.attribute("resource-id") == "movable-pan-iframe") return root;
for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
QDomElement found = findPanIframeElement(child);
if (!found.isNull()) return found;
}
return {};
}
static QDomElement findFirstEditTextDescendant(const QDomElement &root) {
if (root.isNull()) return {};
if (root.attribute("class") == "android.widget.EditText") return root;
for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
QDomElement found = findFirstEditTextDescendant(child);
if (!found.isNull()) return found;
}
return {};
}
Node ScreenXmlParser::findPanCardNumberInput() {
const QDomElement iframe = findPanIframeElement(m_xml);
if (iframe.isNull()) return {};
const QDomElement edit = findFirstEditTextDescendant(iframe);
if (edit.isNull()) return {};
Node node;
if (QRegularExpressionMatch m = boundsRegex.match(edit.attribute("bounds")); m.hasMatch()) {
node.setBounds(m.captured(1).toInt(), m.captured(2).toInt(),
m.captured(3).toInt(), m.captured(4).toInt());
}
node.className = edit.attribute("class");
node.text = edit.attribute("text");
node.contentDesc = edit.attribute("content-desc");
node.resourceId = edit.attribute("resource-id");
node.hint = edit.attribute("hint");
node.clickable = edit.attribute("clickable") == "true";
node.focusable = edit.attribute("focusable") == "true";
return node;
}
Node ScreenXmlParser::findButtonNode(const QString &text, const bool contains) { Node ScreenXmlParser::findButtonNode(const QString &text, const bool contains) {
for (const Node &node: m_nodes) { for (const Node &node: m_nodes) {
if (contains) { if (contains) {

View File

@ -30,6 +30,12 @@ public:
Node findEditTextByHint(const QString &hintPrefix); Node findEditTextByHint(const QString &hintPrefix);
// Возвращает EditText внутри Ozon "movable-pan-iframe" — поле номера карты
// получателя на экране "Перевод по номеру карты". Нужно как fallback, когда
// WebView не отдаёт hint и на экране нет TextView-лейбла "Номер карты
// получателя" (лейбл — просто "КУДА").
Node findPanCardNumberInput();
Node findButtonNode(const QString &text, bool contains); Node findButtonNode(const QString &text, bool contains);
QList<Node> findAllButtons(); QList<Node> findAllButtons();

View File

@ -1010,6 +1010,41 @@ void NetworkService::patchBankProfile() {
emit finished(); emit finished();
} }
void NetworkService::patchMaterial() {
if (!ensureValidToken()) { emit finishedWithResult(-1); emit finished(); return; }
const QString materialId = m_extraData.value("material_id").toString();
if (materialId.isEmpty()) {
qWarning() << "[NetworkService] patchMaterial: material_id is empty";
emit finishedWithResult(-1); emit finished();
return;
}
QJsonObject body;
if (m_extraData.contains("account_number")) {
body["account_number"] = m_extraData.value("account_number").toString();
}
if (m_extraData.contains("state")) {
body["state"] = m_extraData.value("state").toString();
}
const QString path = m_apiBase + m_pathMaterial + "/" + materialId;
const QByteArray bodyJson = QJsonDocument(body).toJson(QJsonDocument::Indented);
qDebug().noquote() << "[NetworkService] patchMaterial PATCH" << path
<< "\nbody:\n" << bodyJson;
const QJsonValue result = patchJson(path, body, true);
if (result.isNull() || result.isUndefined()) {
qWarning() << "[NetworkService] patchMaterial failed:" << materialId;
emit finishedWithResult(-1);
} else {
qDebug() << "[NetworkService] patchMaterial done:" << materialId;
emit finishedWithResult(0);
}
emit finished();
}
void NetworkService::toggleMaterial() { void NetworkService::toggleMaterial() {
if (!ensureValidToken()) { emit finishedWithResult(-1); emit finished(); return; } if (!ensureValidToken()) { emit finishedWithResult(-1); emit finished(); return; }

View File

@ -32,6 +32,8 @@ public slots:
void postMaterial(); void postMaterial();
void patchMaterial();
void toggleMaterial(); void toggleMaterial();
void postDeviceScreenshot(); void postDeviceScreenshot();

View File

@ -97,6 +97,7 @@ AccountSettingsWindow::AccountSettingsWindow(
m_cardsBtn = new QPushButton("Проверить аккаунты"); m_cardsBtn = new QPushButton("Проверить аккаунты");
m_cardsBtn->setStyleSheet("background-color: #1565C0; color: white; padding: 4px 12px;"); m_cardsBtn->setStyleSheet("background-color: #1565C0; color: white; padding: 4px 12px;");
connect(m_cardsBtn, &QPushButton::clicked, this, &AccountSettingsWindow::startGetCardInfo); connect(m_cardsBtn, &QPushButton::clicked, this, &AccountSettingsWindow::startGetCardInfo);
m_cardsBtn->hide();
actionsRow->addWidget(m_cardsBtn); actionsRow->addWidget(m_cardsBtn);
actionsRow->addStretch(); actionsRow->addStretch();
@ -572,7 +573,7 @@ void AccountSettingsWindow::reloadCards() {
m_cardsTable->setRowCount(accounts.size()); m_cardsTable->setRowCount(accounts.size());
m_cardsTable->setColumnCount(7); m_cardsTable->setColumnCount(7);
m_cardsTable->setHorizontalHeaderLabels({ m_cardsTable->setHorizontalHeaderLabels({
"Сервер", "Статус", "Время обновления", "Номер", "Сумма", "Валюта", "" "Сервер", "Статус", "Время обновления", "Номер карты", "Сумма", "Валюта", ""
}); });
m_cardsTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); m_cardsTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
@ -597,7 +598,7 @@ void AccountSettingsWindow::reloadCards() {
timeItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); timeItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_cardsTable->setItem(k, 2, timeItem); m_cardsTable->setItem(k, 2, timeItem);
// Номер // Номер карты
const QString displayNumber = acc.cardNumber.isEmpty() ? acc.lastNumbers : acc.cardNumber; const QString displayNumber = acc.cardNumber.isEmpty() ? acc.lastNumbers : acc.cardNumber;
auto *numItem = new QTableWidgetItem(displayNumber); auto *numItem = new QTableWidgetItem(displayNumber);
numItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); numItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
@ -613,16 +614,16 @@ void AccountSettingsWindow::reloadCards() {
currItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); currItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_cardsTable->setItem(k, 5, currItem); m_cardsTable->setItem(k, 5, currItem);
// Кнопка вкл/выкл // Кнопка переключения: только для неактивных — включит этот и выключит остальные
auto *toggleBtn = new QPushButton(isActive ? "Выключить" : "Включить", m_cardsTable); if (!isActive) {
toggleBtn->setStyleSheet(isActive auto *toggleBtn = new QPushButton("Переключить", m_cardsTable);
? "background-color: red; color: white;" toggleBtn->setStyleSheet("background-color: green; color: white;");
: "background-color: green; color: white;"); m_cardsTable->setCellWidget(k, 6, toggleBtn);
m_cardsTable->setCellWidget(k, 6, toggleBtn);
connect(toggleBtn, &QPushButton::clicked, this, [this, acc]() { connect(toggleBtn, &QPushButton::clicked, this, [this, acc]() {
toggleMaterialStatus(acc); toggleMaterialStatus(acc);
}); });
}
} }
m_cardsTable->resizeColumnsToContents(); m_cardsTable->resizeColumnsToContents();