From 0a9ad34362a6013d554c45725822a7e9466b8ace Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Mon, 12 May 2025 21:05:31 +0400 Subject: [PATCH] only 2 buttons in asan login, and re-auth system --- invoice_az/api.py | 38 +- .../doctype/asan_login/asan_login.js | 417 ++++++++++++++---- 2 files changed, 363 insertions(+), 92 deletions(-) diff --git a/invoice_az/api.py b/invoice_az/api.py index b48b48e..e7b5f0b 100644 --- a/invoice_az/api.py +++ b/invoice_az/api.py @@ -467,8 +467,9 @@ def select_certificate(asan_login_name, certificate_data, certificate_name): } except Exception as e: + frappe.log_error(f"Error in select_certificate: {str(e)}\n{frappe.get_traceback()}", "Select Certificate Error") return {"success": False, "message": f"Error: {str(e)}"} - + @frappe.whitelist() def select_taxpayer(asan_login_name): """Выбирает налогоплательщика для указанной настройки Asan Login""" @@ -501,6 +502,12 @@ def select_taxpayer(asan_login_name): if not owner_type or not tin: return {"success": False, "message": "Invalid certificate data. Cannot determine taxpayer type or ID."} + # Логируем попытку выбора налогоплательщика + frappe.log_error( + f"Attempting to choose taxpayer: type={owner_type}, tin={tin}, bearer_token={asan_login.bearer_token[:10]}...", + "Choose Taxpayer" + ) + # Выбираем налогоплательщика taxpayer_result = choose_taxpayer(asan_login.bearer_token, owner_type, tin) @@ -520,16 +527,29 @@ def select_taxpayer(asan_login_name): asan_login.auth_status = "Fully Authenticated" asan_login.save() + # Логируем успешный выбор налогоплательщика + frappe.log_error( + f"Successfully chose taxpayer: type={owner_type}, tin={tin}, main_token={asan_login.main_token[:10]}...", + "Choose Taxpayer Success" + ) + return { "success": True, "message": "Taxpayer selected successfully." } else: + # Логируем ошибку выбора налогоплательщика + frappe.log_error( + f"Failed to choose taxpayer: type={owner_type}, tin={tin}, response={taxpayer_result}", + "Choose Taxpayer Error" + ) + return {"success": False, "message": "Failed to select taxpayer."} except Exception as e: + frappe.log_error(f"Error in select_taxpayer: {str(e)}\n{frappe.get_traceback()}", "Choose Taxpayer Exception") return {"success": False, "message": f"Error: {str(e)}"} - + @frappe.whitelist() def import_invoice_to_purchase_order(invoice_data, purchase_order=None): """Импортирует данные счета-фактуры в Purchase Order""" @@ -2457,3 +2477,17 @@ def create_unmapped_parties(settings_name): "message": str(e) } +def handle_unauthorized_request(asan_login_name, operation_name="API request"): + """Общая функция для обработки неавторизованных запросов""" + # Логирование события + frappe.log_error( + f"Unauthorized request for operation: {operation_name}, asan_login_name: {asan_login_name}", + "Unauthorized API Request" + ) + + return { + "error": "unauthorized", + "message": "Authentication required. Please login again.", + "operation": operation_name + } + diff --git a/invoice_az/invoice_az/doctype/asan_login/asan_login.js b/invoice_az/invoice_az/doctype/asan_login/asan_login.js index 78b5af2..72b7e2f 100644 --- a/invoice_az/invoice_az/doctype/asan_login/asan_login.js +++ b/invoice_az/invoice_az/doctype/asan_login/asan_login.js @@ -48,80 +48,80 @@ frappe.ui.form.on('Asan Login', { }, __('Login')); // Кнопка для получения сертификатов (доступна только если авторизация успешна) - if (frm.doc.auth_status === 'Authenticated' || frm.doc.auth_status === 'Fully Authenticated') { - frm.add_custom_button(__('Get Certificates'), function() { - frappe.call({ - method: 'invoice_az.api.get_auth_certificates', - args: { - 'asan_login_name': frm.doc.name - }, - callback: function(r) { - if (r.message && r.message.success) { - // Обновляем форму после успешного получения сертификатов - frm.reload_doc(); + // if (frm.doc.auth_status === 'Authenticated' || frm.doc.auth_status === 'Fully Authenticated') { + // frm.add_custom_button(__('Get Certificates'), function() { + // frappe.call({ + // method: 'invoice_az.api.get_auth_certificates', + // args: { + // 'asan_login_name': frm.doc.name + // }, + // callback: function(r) { + // if (r.message && r.message.success) { + // // Обновляем форму после успешного получения сертификатов + // frm.reload_doc(); - // Показываем сообщение об успешном получении сертификатов - frappe.msgprint({ - title: __('Success'), - indicator: 'green', - message: __('Certificates retrieved successfully') - }); + // // Показываем сообщение об успешном получении сертификатов + // frappe.msgprint({ + // title: __('Success'), + // indicator: 'green', + // message: __('Certificates retrieved successfully') + // }); - // Отображаем список сертификатов в диалоговом окне - if (r.message.certificates) { - showCertificatesDialog(r.message.certificates, frm); - } - } else if (r.message && r.message.unauthorized) { - // Если ошибка авторизации, предлагаем авторизоваться заново - showAuthDialog(frm); - } else { - // Другие ошибки - frappe.msgprint({ - title: __('Error'), - indicator: 'red', - message: r.message ? r.message.message : __('Failed to get certificates') - }); - } - } - }); - }, __('Actions')); - } + // // Отображаем список сертификатов в диалоговом окне + // if (r.message.certificates) { + // showCertificatesDialog(r.message.certificates, frm); + // } + // } else if (r.message && r.message.unauthorized) { + // // Если ошибка авторизации, предлагаем авторизоваться заново + // showAuthDialog(frm); + // } else { + // // Другие ошибки + // frappe.msgprint({ + // title: __('Error'), + // indicator: 'red', + // message: r.message ? r.message.message : __('Failed to get certificates') + // }); + // } + // } + // }); + // }, __('Actions')); + // } // Кнопка для выбора налогоплательщика (доступна если авторизация успешна и сертификат выбран) - if ((frm.doc.auth_status === 'Authenticated' || frm.doc.auth_status === 'Fully Authenticated') && - frm.doc.selected_certificate) { - frm.add_custom_button(__('Choose Taxpayer'), function() { - frappe.call({ - method: 'invoice_az.api.select_taxpayer', - args: { - 'asan_login_name': frm.doc.name - }, - callback: function(r) { - if (r.message && r.message.success) { - // Обновляем форму после успешного выбора налогоплательщика - frm.reload_doc(); + // if ((frm.doc.auth_status === 'Authenticated' || frm.doc.auth_status === 'Fully Authenticated') && + // frm.doc.selected_certificate) { + // frm.add_custom_button(__('Choose Taxpayer'), function() { + // frappe.call({ + // method: 'invoice_az.api.select_taxpayer', + // args: { + // 'asan_login_name': frm.doc.name + // }, + // callback: function(r) { + // if (r.message && r.message.success) { + // // Обновляем форму после успешного выбора налогоплательщика + // frm.reload_doc(); - // Показываем сообщение об успешном выборе - frappe.msgprint({ - title: __('Success'), - indicator: 'green', - message: __('Taxpayer selected successfully') - }); - } else if (r.message && r.message.unauthorized) { - // Если ошибка авторизации, предлагаем авторизоваться заново - showAuthDialog(frm); - } else { - // Другие ошибки - frappe.msgprint({ - title: __('Error'), - indicator: 'red', - message: r.message ? r.message.message : __('Failed to select taxpayer') - }); - } - } - }); - }, __('Actions')); - } + // // Показываем сообщение об успешном выборе + // frappe.msgprint({ + // title: __('Success'), + // indicator: 'green', + // message: __('Taxpayer selected successfully') + // }); + // } else if (r.message && r.message.unauthorized) { + // // Если ошибка авторизации, предлагаем авторизоваться заново + // showAuthDialog(frm); + // } else { + // // Другие ошибки + // frappe.msgprint({ + // title: __('Error'), + // indicator: 'red', + // message: r.message ? r.message.message : __('Failed to select taxpayer') + // }); + // } + // } + // }); + // }, __('Actions')); + // } // Обработчик изменения выбранного сертификата if (frm.doc.certificates_json) { @@ -194,7 +194,7 @@ frappe.ui.form.on('Asan Login', { }); // Функция для запуска опроса через setTimeout вместо setInterval -function startPollingWithTimeout(frm) { +function startPollingWithTimeout(frm, successCallback) { console.log('startPollingWithTimeout function has been called'); // Отображаем индикатор загрузки @@ -257,15 +257,16 @@ function startPollingWithTimeout(frm) { `); $('.lds-dual-ring').css('display', 'none'); - // Обновляем поле auth_status + // Обновляем поле auth_status и сохраняем frm.set_value('auth_status', 'Failed'); - frm.save(); - // Закрываем диалог через 3 секунды - setTimeout(function() { + // Меняем кнопку на "Close" + status_dialog.set_primary_action(__('Close'), function() { status_dialog.hide(); - frm.reload_doc(); - }, 3000); + + // Сохраняем документ и затем обновляем его с сервера + frm.save().then(() => frm.reload_doc()); + }); } return; @@ -294,10 +295,28 @@ function startPollingWithTimeout(frm) { `); $('.lds-dual-ring').css('display', 'none'); - // Закрываем диалоговое окно через 2 секунды + // Обновляем кнопку + status_dialog.set_primary_action(__('Continue'), function() { + status_dialog.hide(); + + // Вызываем callback после успешной аутентификации, если он предоставлен + if (typeof successCallback === 'function') { + successCallback(); + } else { + frm.reload_doc(); + } + }); + + // Автоматически закрываем диалог через 2 секунды setTimeout(function() { status_dialog.hide(); - frm.reload_doc(); + + // Вызываем callback после успешной аутентификации, если он предоставлен + if (typeof successCallback === 'function') { + successCallback(); + } else { + frm.reload_doc(); + } }, 2000); } else { // Продолжаем опрос @@ -316,13 +335,14 @@ function startPollingWithTimeout(frm) { // Обновляем поле auth_status frm.set_value('auth_status', 'Failed'); - frm.save(); - // Закрываем диалог через 3 секунды - setTimeout(function() { + // Меняем кнопку на "Close" + status_dialog.set_primary_action(__('Close'), function() { status_dialog.hide(); - frm.reload_doc(); - }, 3000); + + // Сохраняем документ и затем обновляем его с сервера + frm.save().then(() => frm.reload_doc()); + }); } }, error: function(err) { @@ -496,22 +516,95 @@ function showAuthDialog(frm) { primary_action: function() { d.hide(); + // Показываем сообщение о начале аутентификации + frappe.show_alert({ + message: __('Starting authentication process...'), + indicator: 'blue' + }, 3); + // Вызываем процесс аутентификации frappe.call({ method: 'invoice_az.api.handle_authentication', args: { 'asan_login_name': frm.doc.name }, + freeze: true, + freeze_message: __('Sending authentication request...'), callback: function(r) { if (r.message && r.message.success) { - // Сохраняем токен в документе и запускаем опрос статуса - if (r.message.bearer_token) { - frm.set_value('bearer_token', r.message.bearer_token); - frm.save(); - } + // Получаем bearer_token из ответа + const bearerToken = r.message.bearer_token; - // Запускаем опрос статуса - startPollingWithTimeout(frm); + // Обновляем значение в документе + frm.set_value('bearer_token', bearerToken); + + // Запускаем опрос с передачей сертификата и налогоплательщика как callback + // для автоматического восстановления сессии + pollAuthenticationStatus(frm, function() { + // После успешной аутентификации проверяем, был ли ранее выбран сертификат + if (frm.doc.selected_certificate_json) { + try { + var certData = JSON.parse(frm.doc.selected_certificate_json); + var certName = frm.doc.selected_certificate; + + frappe.show_alert({ + message: __('Automatically restoring your session...'), + indicator: 'blue' + }, 3); + + // Сначала восстанавливаем сертификат + frappe.call({ + method: 'invoice_az.api.select_certificate', + args: { + 'asan_login_name': frm.doc.name, + 'certificate_data': certData, + 'certificate_name': certName + }, + callback: function(r) { + if (r.message && r.message.success) { + // Затем восстанавливаем налогоплательщика + frappe.call({ + method: 'invoice_az.api.select_taxpayer', + args: { + 'asan_login_name': frm.doc.name + }, + callback: function(r2) { + if (r2.message && r2.message.success) { + frappe.show_alert({ + message: __('Session restored successfully'), + indicator: 'green' + }, 5); + } else { + frappe.show_alert({ + message: __('Partial session restore - please select certificate manually'), + indicator: 'orange' + }, 5); + } + + // Обновляем документ с сервера + frm.reload_doc(); + } + }); + } else { + frappe.show_alert({ + message: __('Please select your certificate manually'), + indicator: 'orange' + }, 5); + + // Обновляем документ с сервера + frm.reload_doc(); + } + } + }); + } catch (e) { + console.error('Error restoring certificate:', e); + frm.reload_doc(); + } + } else { + // Если сертификат не выбран, просто обновляем документ + frm.reload_doc(); + } + }); } else { // Показываем сообщение об ошибке frappe.msgprint({ @@ -530,4 +623,148 @@ function showAuthDialog(frm) { }); d.show(); +} + +// Отдельная функция для опроса статуса аутентификации +function pollAuthenticationStatus(frm, successCallback) { + // Создаем всплывающее окно с информацией + const status_dialog = new frappe.ui.Dialog({ + title: __('Authentication Status'), + fields: [ + { + fieldname: 'status_html', + fieldtype: 'HTML', + options: ` +
Please check your phone and confirm the authentication request.
+The authentication request has timed out. Please try again.
+ `); + $('.lds-dual-ring').css('display', 'none'); + + // Меняем кнопку на "Close" + status_dialog.set_primary_action(__('Close'), function() { + status_dialog.hide(); + + // Устанавливаем статус "Failed" и перезагружаем документ + frm.set_value('auth_status', 'Failed'); + frm.save().then(() => frm.reload_doc()); + }); + } + + return; + } + + attempts++; + console.log(`Polling auth status, attempt ${attempts} of ${maxAttempts}`); + + // Проверяем статус авторизации + frappe.call({ + method: 'invoice_az.api.poll_auth_status', + args: { + 'asan_login_name': frm.doc.name, + 'bearer_token': frm.doc.bearer_token + }, + callback: function(r) { + if (r.message && r.message.success) { + if (r.message.authenticated) { + // Авторизация успешна + stopPolling = true; + + // Обновляем информацию в диалоговом окне + $('#poll_status_message').html(` +You are now authenticated.
+ `); + $('.lds-dual-ring').css('display', 'none'); + + // Меняем кнопку на "Continue" + status_dialog.set_primary_action(__('Continue'), function() { + status_dialog.hide(); + + // Выполняем callback, если он предоставлен + if (typeof successCallback === 'function') { + successCallback(); + } else { + frm.reload_doc(); + } + }); + } else { + // Продолжаем опрос + setTimeout(pollStatus, 6000); + } + } else { + // Если ошибка, останавливаем опрос и показываем сообщение + stopPolling = true; + + // Обновляем информацию в диалоговом окне + $('#poll_status_message').html(` +${r.message ? r.message.message : 'An unknown error occurred.'}
+ `); + $('.lds-dual-ring').css('display', 'none'); + + // Меняем кнопку на "Close" + status_dialog.set_primary_action(__('Close'), function() { + status_dialog.hide(); + + // Устанавливаем статус "Failed" и перезагружаем документ + frm.set_value('auth_status', 'Failed'); + frm.save().then(() => frm.reload_doc()); + }); + } + }, + error: function(err) { + // В случае ошибки продолжаем опрос + console.error('Error during status check:', err); + setTimeout(pollStatus, 6000); + } + }); + } + + // Начинаем опрос + pollStatus(); } \ No newline at end of file