added pull from e taxes site to company (first page)
This commit is contained in:
parent
e6a3dac967
commit
3893f44be1
2421
invoice_az/api.py
2421
invoice_az/api.py
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,629 @@
|
|||
import frappe
|
||||
import requests
|
||||
import json
|
||||
from frappe.utils import nowdate, now, cint, now_datetime, get_datetime
|
||||
import random
|
||||
import time
|
||||
|
||||
# ======= КОНСТАНТЫ =======
|
||||
RENEW_URL = "https://new.e-taxes.gov.az/api/po/auth/public/v1/renew"
|
||||
BASE_URL = "https://new.e-taxes.gov.az/api/po"
|
||||
MAX_RETRY_COUNT = 3
|
||||
ACTIVITY_TIMEOUT_MINUTES = 30
|
||||
|
||||
DEFAULT_HEADERS = {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
"Cache-Control": "no-cache",
|
||||
"Pragma": "no-cache",
|
||||
"Referer": "https://new.e-taxes.gov.az/eportal/az/login/asan"
|
||||
}
|
||||
|
||||
# ======= ОБЩИЕ УТИЛИТЫ =======
|
||||
def handle_api_response(response):
|
||||
"""Общая обработка ответов API"""
|
||||
if response.status_code == 500:
|
||||
return {
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
"status_code": 500
|
||||
}
|
||||
elif response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again.",
|
||||
"status_code": 401
|
||||
}
|
||||
return None
|
||||
|
||||
def make_api_request(url, method="GET", headers=None, data=None, timeout=10):
|
||||
"""Общая функция для API запросов с обработкой ошибок"""
|
||||
try:
|
||||
if method.upper() == "POST":
|
||||
response = requests.post(url, headers=headers, data=data, timeout=timeout)
|
||||
else:
|
||||
response = requests.get(url, headers=headers, timeout=timeout)
|
||||
|
||||
# Проверка стандартных ошибок
|
||||
error_response = handle_api_response(response)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
response.raise_for_status()
|
||||
return response
|
||||
|
||||
except requests.RequestException as e:
|
||||
if hasattr(e, 'response') and e.response:
|
||||
error_response = handle_api_response(e.response)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
frappe.log_error(f"API request error: {str(e)}", "API Error")
|
||||
return {"error": "network_error", "message": "An unknown error occurred, please try again in a few minutes."}
|
||||
|
||||
def log_error_safe(message, title):
|
||||
"""Безопасное логирование ошибок"""
|
||||
try:
|
||||
frappe.log_error(message, title)
|
||||
except:
|
||||
pass
|
||||
|
||||
# ======= УПРАВЛЕНИЕ АКТИВНОСТЬЮ =======
|
||||
@frappe.whitelist()
|
||||
def record_etaxes_activity(asan_login_name=None):
|
||||
"""Записывает время последней активности с e-taxes"""
|
||||
try:
|
||||
if not asan_login_name:
|
||||
default_settings = frappe.get_all(
|
||||
"Asan Login",
|
||||
filters={"is_default": 1},
|
||||
fields=["name"],
|
||||
limit=1
|
||||
)
|
||||
if not default_settings:
|
||||
return
|
||||
asan_login_name = default_settings[0].name
|
||||
|
||||
frappe.enqueue(
|
||||
'invoice_az.auth.update_activity_time',
|
||||
asan_login_name=asan_login_name,
|
||||
queue='short',
|
||||
timeout=300
|
||||
)
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error recording activity: {str(e)}", "Activity Recording Error")
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_activity_time(asan_login_name):
|
||||
"""Обновляет время активности"""
|
||||
try:
|
||||
time.sleep(2)
|
||||
frappe.db.set_value("Asan Login", asan_login_name, "last_activity_time", now_datetime(), update_modified=False)
|
||||
frappe.db.commit()
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error updating activity time: {str(e)}", "Activity Update Error")
|
||||
|
||||
def check_recent_activity(asan_login_name=None):
|
||||
"""Проверяет наличие недавней активности"""
|
||||
try:
|
||||
if not asan_login_name:
|
||||
default_settings = frappe.get_all(
|
||||
"Asan Login",
|
||||
filters={"is_default": 1},
|
||||
fields=["name"],
|
||||
limit=1
|
||||
)
|
||||
if not default_settings:
|
||||
return False
|
||||
asan_login_name = default_settings[0].name
|
||||
|
||||
last_activity = frappe.db.get_value("Asan Login", asan_login_name, "last_activity_time")
|
||||
if not last_activity:
|
||||
return False
|
||||
|
||||
time_diff = now_datetime() - get_datetime(last_activity)
|
||||
minutes_diff = time_diff.total_seconds() / 60
|
||||
return minutes_diff <= ACTIVITY_TIMEOUT_MINUTES
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error checking activity: {str(e)}", "Activity Check Error")
|
||||
return False
|
||||
|
||||
# ======= УПРАВЛЕНИЕ ТОКЕНАМИ =======
|
||||
@frappe.whitelist()
|
||||
def renew_token(asan_login_name=None, retry_count=0, force=False):
|
||||
"""Обновляет главный токен"""
|
||||
try:
|
||||
record_etaxes_activity(asan_login_name)
|
||||
|
||||
if not asan_login_name:
|
||||
default_settings = frappe.get_all(
|
||||
"Asan Login",
|
||||
filters={"is_default": 1},
|
||||
fields=["name"],
|
||||
limit=1
|
||||
)
|
||||
if not default_settings:
|
||||
return {"success": False, "message": "No default Asan Login found"}
|
||||
asan_login_name = default_settings[0].name
|
||||
|
||||
if not check_recent_activity(asan_login_name):
|
||||
return {"success": True, "message": "Token renewal skipped due to inactivity"}
|
||||
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
if not asan_login.main_token:
|
||||
return {"success": False, "message": "No main token found"}
|
||||
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
headers["x-authorization"] = f"Bearer {asan_login.main_token}"
|
||||
|
||||
response = make_api_request(RENEW_URL, "POST", headers)
|
||||
if isinstance(response, dict) and "error" in response:
|
||||
if response["error"] == "unauthorized":
|
||||
frappe.db.set_value("Asan Login", asan_login_name, "auth_status", "Not Authenticated")
|
||||
frappe.db.commit()
|
||||
return response
|
||||
|
||||
new_token = response.headers.get('x-authorization')
|
||||
if new_token and new_token != asan_login.main_token:
|
||||
frappe.db.set_value("Asan Login", asan_login_name, "main_token", new_token)
|
||||
frappe.db.commit()
|
||||
|
||||
return {"success": True, "message": "Token renewed successfully"}
|
||||
|
||||
except Exception as e:
|
||||
if retry_count < MAX_RETRY_COUNT:
|
||||
wait_time = (2 ** retry_count) * (0.5 + random.random())
|
||||
time.sleep(wait_time)
|
||||
return renew_token(asan_login_name, retry_count + 1)
|
||||
|
||||
log_error_safe(f"Token renewal error: {str(e)}", f"Token Renewal for {asan_login_name}")
|
||||
return {"success": False, "message": "Token renewal failed"}
|
||||
|
||||
@frappe.whitelist()
|
||||
def setup_token_renewal():
|
||||
"""Настраивает автоматическое обновление токенов"""
|
||||
try:
|
||||
job_exists = frappe.db.exists("Scheduled Job Type", "renew_etaxes_token")
|
||||
|
||||
if not job_exists:
|
||||
doc = frappe.new_doc("Scheduled Job Type")
|
||||
doc.update({
|
||||
"name": "renew_etaxes_token",
|
||||
"method": "invoice_az.auth.renew_token",
|
||||
"frequency": "Cron",
|
||||
"cron_format": "*/4 * * * *",
|
||||
"create_log": 1,
|
||||
"execute_alone": 1,
|
||||
"enabled": 1
|
||||
})
|
||||
doc.insert(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
return {"success": True, "message": "Scheduler job created"}
|
||||
|
||||
return {"success": True, "message": "Scheduler job already exists"}
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error setting up token renewal: {str(e)}", "Token Renewal Setup Error")
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
@frappe.whitelist()
|
||||
def check_token_validity(asan_login_name=None):
|
||||
"""Проверяет валидность токена"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
if not asan_login_name:
|
||||
default_settings = get_default_asan_login()
|
||||
if not default_settings.get("found", False):
|
||||
return {"valid": False, "message": "No Asan Login found."}
|
||||
asan_login_name = default_settings.get("name")
|
||||
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
if not asan_login.main_token:
|
||||
return {"valid": False, "message": "No main token found."}
|
||||
|
||||
if not check_recent_activity(asan_login_name):
|
||||
return {"valid": True, "message": "Token renewal skipped due to inactivity"}
|
||||
|
||||
# Импортируем функцию get_invoices из основного модуля
|
||||
from .api import get_invoices
|
||||
|
||||
minimal_filter = {"maxCount": 1, "offset": 0}
|
||||
invoices_result = get_invoices(asan_login.main_token, json.dumps(minimal_filter))
|
||||
|
||||
if invoices_result.get("error") == "unauthorized":
|
||||
asan_login.auth_status = "Not Authenticated"
|
||||
asan_login.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
return {"valid": False, "message": "Authentication required. Main token is expired."}
|
||||
|
||||
return {"valid": True, "message": "Main token is valid."}
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error checking token validity: {str(e)}", "Token Validity Check")
|
||||
return {"valid": False, "message": "An unknown error occurred"}
|
||||
|
||||
# ======= АУТЕНТИФИКАЦИЯ =======
|
||||
@frappe.whitelist()
|
||||
def get_auth_token(phone, user_id):
|
||||
"""Получает токен авторизации"""
|
||||
record_etaxes_activity()
|
||||
|
||||
url = f"{BASE_URL}/auth/public/v1/asanImza/start"
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
payload = {"phone": phone, "userId": user_id}
|
||||
|
||||
response = make_api_request(url, "POST", headers, json.dumps(payload))
|
||||
if isinstance(response, dict) and "error" in response:
|
||||
return response
|
||||
|
||||
bearer_token = response.headers.get('x-authorization', '')
|
||||
resp_data = response.json()
|
||||
|
||||
return {'bearer_token': bearer_token, 'response_data': resp_data}
|
||||
|
||||
@frappe.whitelist()
|
||||
def check_auth_status(bearer_token):
|
||||
"""Проверяет статус авторизации"""
|
||||
record_etaxes_activity()
|
||||
|
||||
url = f"{BASE_URL}/auth/public/v1/asanImza/status"
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
if bearer_token:
|
||||
headers["x-authorization"] = f"Bearer {bearer_token}"
|
||||
|
||||
response = make_api_request(url, "GET", headers)
|
||||
if isinstance(response, dict) and "error" in response:
|
||||
return response
|
||||
|
||||
return response.json()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_certificates(bearer_token):
|
||||
"""Получает доступные сертификаты"""
|
||||
record_etaxes_activity()
|
||||
|
||||
url = f"{BASE_URL}/auth/public/v1/asanImza/certificates"
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
headers["x-authorization"] = f"Bearer {bearer_token}"
|
||||
|
||||
response = make_api_request(url, "GET", headers)
|
||||
if isinstance(response, dict) and "error" in response:
|
||||
return response
|
||||
|
||||
return response.json()
|
||||
|
||||
@frappe.whitelist()
|
||||
def choose_taxpayer(bearer_token, owner_type, tin):
|
||||
"""Выбирает налогоплательщика"""
|
||||
record_etaxes_activity()
|
||||
|
||||
url = f"{BASE_URL}/auth/public/v1/asanImza/chooseTaxpayer"
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["x-authorization"] = f"Bearer {bearer_token}"
|
||||
|
||||
payload = {
|
||||
"ownerType": owner_type,
|
||||
"legalTin": tin if owner_type == "legal" else None,
|
||||
"individualFin": tin if owner_type == "individual" else None
|
||||
}
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
response = make_api_request(url, "POST", headers, json.dumps(payload))
|
||||
if isinstance(response, dict) and "error" in response:
|
||||
return response
|
||||
|
||||
main_token = response.headers.get('x-authorization', '')
|
||||
resp_data = response.json()
|
||||
|
||||
return {'main_token': main_token, 'response_data': resp_data}
|
||||
|
||||
# ======= ВЫСОКОУРОВНЕВЫЕ ФУНКЦИИ АУТЕНТИФИКАЦИИ =======
|
||||
@frappe.whitelist()
|
||||
def get_default_asan_login():
|
||||
"""Получает настройки аутентификации по умолчанию"""
|
||||
try:
|
||||
default_login = frappe.get_list(
|
||||
"Asan Login",
|
||||
filters={"is_default": 1},
|
||||
fields=["name"]
|
||||
)
|
||||
|
||||
if default_login:
|
||||
asan_login = frappe.get_doc("Asan Login", default_login[0].name)
|
||||
return {
|
||||
"found": True,
|
||||
"name": asan_login.name,
|
||||
"phone": asan_login.phone,
|
||||
"user_id": asan_login.user_id,
|
||||
"auth_status": asan_login.auth_status,
|
||||
"bearer_token": asan_login.bearer_token,
|
||||
"main_token": asan_login.main_token,
|
||||
"selected_certificate": asan_login.selected_certificate
|
||||
}
|
||||
|
||||
any_login = frappe.get_list("Asan Login", fields=["name"])
|
||||
if any_login:
|
||||
asan_login = frappe.get_doc("Asan Login", any_login[0].name)
|
||||
return {
|
||||
"found": True,
|
||||
"name": asan_login.name,
|
||||
"phone": asan_login.phone,
|
||||
"user_id": asan_login.user_id,
|
||||
"auth_status": asan_login.auth_status,
|
||||
"bearer_token": asan_login.bearer_token,
|
||||
"main_token": asan_login.main_token,
|
||||
"selected_certificate": asan_login.selected_certificate
|
||||
}
|
||||
|
||||
return {"found": False, "message": "No Asan Login settings found."}
|
||||
|
||||
except Exception as e:
|
||||
return {"found": False, "error": str(e)}
|
||||
|
||||
@frappe.whitelist()
|
||||
def handle_authentication(asan_login_name=None):
|
||||
"""Обрабатывает процесс аутентификации"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
if not asan_login_name:
|
||||
default_settings = get_default_asan_login()
|
||||
if not default_settings.get("found", False):
|
||||
return {"success": False, "message": "No Asan Login settings found."}
|
||||
asan_login_name = default_settings.get("name")
|
||||
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
|
||||
if not asan_login.phone or not asan_login.user_id:
|
||||
return {"success": False, "message": "Phone and User ID are required."}
|
||||
|
||||
auth_result = get_auth_token(asan_login.phone, asan_login.user_id)
|
||||
|
||||
if auth_result and auth_result.get("error"):
|
||||
return {"success": False, "message": auth_result.get("message")}
|
||||
|
||||
if auth_result and auth_result.get("bearer_token"):
|
||||
asan_login.bearer_token = auth_result.get("bearer_token")
|
||||
asan_login.auth_status = "Waiting for confirmation"
|
||||
asan_login.save()
|
||||
|
||||
verification_code = None
|
||||
if auth_result.get("response_data") and isinstance(auth_result.get("response_data"), dict):
|
||||
verification_code = auth_result.get("response_data").get("verificationCode", None)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Authentication initiated. Please confirm on your phone.",
|
||||
"bearer_token": auth_result.get("bearer_token"),
|
||||
"asan_login_name": asan_login_name,
|
||||
"verification_code": verification_code
|
||||
}
|
||||
|
||||
return {"success": False, "message": "Failed to get authentication token."}
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error in handle_authentication: {str(e)}", "Authentication Error")
|
||||
return {"success": False, "message": "An unknown error occurred"}
|
||||
|
||||
@frappe.whitelist()
|
||||
def poll_auth_status(asan_login_name, bearer_token):
|
||||
"""Проверяет статус аутентификации"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
status_result = check_auth_status(bearer_token)
|
||||
|
||||
if status_result and status_result.get("error"):
|
||||
return {"success": False, "message": status_result.get("message")}
|
||||
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
|
||||
if status_result and status_result.get("successful") == True:
|
||||
asan_login.auth_status = "Authenticated"
|
||||
if status_result.get("bearer_token"):
|
||||
asan_login.bearer_token = status_result.get("bearer_token")
|
||||
asan_login.save()
|
||||
|
||||
return {"success": True, "authenticated": True, "message": "Authentication successful."}
|
||||
|
||||
return {"success": True, "authenticated": False, "message": "Waiting for confirmation."}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "authenticated": False, "message": "An unknown error occurred"}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_auth_certificates(asan_login_name):
|
||||
"""Получает сертификаты для указанного Asan Login"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
|
||||
if asan_login.auth_status not in ["Authenticated", "Fully Authenticated"]:
|
||||
return {"success": False, "message": "Not authenticated. Please login first."}
|
||||
|
||||
certificates_result = get_certificates(asan_login.bearer_token)
|
||||
|
||||
if certificates_result.get("error"):
|
||||
return {"success": False, **certificates_result}
|
||||
|
||||
if certificates_result and certificates_result.get("certificates"):
|
||||
asan_login.certificates_json = json.dumps(certificates_result.get("certificates"))
|
||||
asan_login.save()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Certificates retrieved successfully.",
|
||||
"certificates": certificates_result.get("certificates")
|
||||
}
|
||||
|
||||
return {"success": False, "message": "No certificates found."}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "message": "An unknown error occurred"}
|
||||
|
||||
@frappe.whitelist()
|
||||
def select_certificate(asan_login_name, certificate_data, certificate_name):
|
||||
"""Выбирает сертификат"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
|
||||
if asan_login.auth_status not in ["Authenticated", "Fully Authenticated"]:
|
||||
return {"success": False, "message": "Not authenticated. Please login first."}
|
||||
|
||||
if isinstance(certificate_data, str):
|
||||
certificate_data = json.loads(certificate_data)
|
||||
|
||||
asan_login.selected_certificate = certificate_name
|
||||
asan_login.selected_certificate_json = json.dumps(certificate_data)
|
||||
asan_login.save()
|
||||
|
||||
return {"success": True, "message": "Certificate selected successfully."}
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error in select_certificate: {str(e)}", "Select Certificate Error")
|
||||
return {"success": False, "message": "An unknown error occurred"}
|
||||
|
||||
@frappe.whitelist()
|
||||
def select_taxpayer(asan_login_name):
|
||||
"""Выбирает налогоплательщика"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
|
||||
if asan_login.auth_status not in ["Authenticated", "Fully Authenticated"]:
|
||||
return {"success": False, "message": "Not authenticated. Please login first."}
|
||||
|
||||
if not asan_login.selected_certificate_json:
|
||||
return {"success": False, "message": "No certificate selected."}
|
||||
|
||||
cert_data = json.loads(asan_login.selected_certificate_json)
|
||||
|
||||
owner_type = cert_data.get("taxpayerType", "")
|
||||
tin = ""
|
||||
|
||||
if owner_type == "individual" and cert_data.get("individualInfo"):
|
||||
tin = cert_data.get("individualInfo", {}).get("fin", "")
|
||||
owner_type = "individual"
|
||||
elif cert_data.get("legalInfo"):
|
||||
owner_type = "legal"
|
||||
tin = cert_data.get("legalInfo", {}).get("tin", "") or cert_data.get("legalInfo", {}).get("voen", "")
|
||||
|
||||
if not owner_type or not tin:
|
||||
return {"success": False, "message": "Invalid certificate data. Cannot determine taxpayer type or ID."}
|
||||
|
||||
taxpayer_result = choose_taxpayer(asan_login.bearer_token, owner_type, tin)
|
||||
|
||||
if taxpayer_result.get("error"):
|
||||
return {"success": False, **taxpayer_result}
|
||||
|
||||
if taxpayer_result and taxpayer_result.get("main_token"):
|
||||
asan_login.main_token = taxpayer_result.get("main_token")
|
||||
asan_login.choose_taxpayer_response = json.dumps(taxpayer_result.get("response_data", {}))
|
||||
asan_login.auth_status = "Fully Authenticated"
|
||||
asan_login.save()
|
||||
|
||||
return {"success": True, "message": "Taxpayer selected successfully."}
|
||||
|
||||
return {"success": False, "message": "Failed to select taxpayer."}
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error in select_taxpayer: {str(e)}", "Choose Taxpayer Exception")
|
||||
return {"success": False, "message": "An unknown error occurred"}
|
||||
|
||||
@frappe.whitelist()
|
||||
def test_api_endpoints(asan_login_name, endpoint_path, method="GET", payload=None):
|
||||
"""Тестирует API endpoint'ы с разными токенами"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
if not asan_login_name:
|
||||
default_settings = get_default_asan_login()
|
||||
if not default_settings.get("found", False):
|
||||
return {"success": False, "message": "No Asan Login settings found."}
|
||||
asan_login_name = default_settings.get("name")
|
||||
|
||||
asan_login = frappe.get_doc("Asan Login", asan_login_name)
|
||||
|
||||
# Строим полный URL
|
||||
if endpoint_path.startswith('http'):
|
||||
url = endpoint_path
|
||||
else:
|
||||
endpoint_path = endpoint_path.lstrip('/')
|
||||
url = f"{BASE_URL}/{endpoint_path}"
|
||||
|
||||
results = []
|
||||
|
||||
# Пробуем с main_token
|
||||
if asan_login.main_token:
|
||||
result = try_api_call(url, method, asan_login.main_token, payload, "main_token")
|
||||
results.append(result)
|
||||
|
||||
# Пробуем с bearer_token
|
||||
if asan_login.bearer_token:
|
||||
result = try_api_call(url, method, asan_login.bearer_token, payload, "bearer_token")
|
||||
results.append(result)
|
||||
|
||||
# Пробуем без токена
|
||||
result = try_api_call(url, method, None, payload, "no_token")
|
||||
results.append(result)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"url": url,
|
||||
"method": method.upper(),
|
||||
"results": results
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
log_error_safe(f"Error in test_api_endpoints: {str(e)}", "API Test Error")
|
||||
return {"success": False, "message": f"Error: {str(e)}"}
|
||||
|
||||
def try_api_call(url, method, token, payload, token_type):
|
||||
"""Пробует API вызов с определенным токеном"""
|
||||
try:
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
|
||||
if token:
|
||||
headers["x-authorization"] = f"Bearer {token}"
|
||||
|
||||
if method.upper() == "POST" and payload:
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
# Делаем запрос
|
||||
if method.upper() == "POST":
|
||||
response = requests.post(url, headers=headers, data=payload, timeout=10)
|
||||
else:
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
|
||||
# Получаем данные ответа
|
||||
try:
|
||||
response_data = response.json()
|
||||
except:
|
||||
response_data = {"raw_response": response.text[:500]} # Первые 500 символов
|
||||
|
||||
return {
|
||||
"token_type": token_type,
|
||||
"status_code": response.status_code,
|
||||
"success": response.status_code < 400,
|
||||
"headers": dict(response.headers),
|
||||
"data": response_data,
|
||||
"token_preview": f"{token[:10]}...{token[-5:]}" if token else "No token"
|
||||
}
|
||||
|
||||
except requests.RequestException as e:
|
||||
return {
|
||||
"token_type": token_type,
|
||||
"status_code": None,
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"token_preview": f"{token[:10]}...{token[-5:]}" if token else "No token"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,26 +1,29 @@
|
|||
/**
|
||||
* Purchase Invoice Form and List Configuration
|
||||
* Optimized version using ETaxes common module patterns
|
||||
*/
|
||||
|
||||
frappe.ui.form.on('Purchase Invoice', {
|
||||
refresh: function(frm) {
|
||||
// Проверяем, установлена ли галочка is_taxes_doc
|
||||
// Check if is_taxes_doc flag is set
|
||||
if (frm.doc.is_taxes_doc) {
|
||||
// Делаем все поля read-only
|
||||
// Make all fields read-only
|
||||
frm.set_read_only(true);
|
||||
|
||||
// Альтернативно, можно добавить визуальное предупреждение
|
||||
// Add visual indicator
|
||||
frm.page.set_indicator(__('Tax Document'), 'blue');
|
||||
}
|
||||
},
|
||||
|
||||
// Делаем поле is_taxes_doc тоже read-only при открытии документа
|
||||
// Make is_taxes_doc field read-only when document loads
|
||||
onload: function(frm) {
|
||||
frm.set_df_property('is_taxes_doc', 'read_only', 1);
|
||||
}
|
||||
});
|
||||
|
||||
frappe.listview_settings['Purchase Invoice'] = {
|
||||
|
||||
onload: function(listview) {
|
||||
|
||||
// Добавляем поле как колонку (правильный способ)
|
||||
// Add field as column (proper way)
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
|
|
@ -30,16 +33,16 @@ frappe.listview_settings['Purchase Invoice'] = {
|
|||
width: 120
|
||||
});
|
||||
|
||||
// Принудительно обновляем список с новой колонкой
|
||||
// Force refresh list with new column
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
// Форматируем отображение поля is_taxes_doc
|
||||
// Format is_taxes_doc field display
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green"></span>` :
|
||||
`<span class="indicator-pill gray"></span>`;
|
||||
`<span class="indicator-pill green" title="${__('Tax Document')}"></span>` :
|
||||
`<span class="indicator-pill gray" title="${__('Regular Document')}"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -31,7 +31,7 @@ ETaxes.utils = {
|
|||
}
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.get_default_asan_login',
|
||||
method: 'invoice_az.auth.get_default_asan_login',
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
ETaxes.cache.defaultLogin = r.message;
|
||||
|
|
@ -48,7 +48,7 @@ ETaxes.utils = {
|
|||
// Проверка валидности токена
|
||||
checkTokenValidity: function(callback) {
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.check_token_validity',
|
||||
method: 'invoice_az.auth.check_token_validity',
|
||||
callback: function(r) {
|
||||
callback(r.message && r.message.valid);
|
||||
},
|
||||
|
|
@ -260,7 +260,7 @@ ETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.handle_authentication',
|
||||
method: 'invoice_az.auth.handle_authentication',
|
||||
args: { 'asan_login_name': asanLoginName },
|
||||
callback: function(r) {
|
||||
if (r.message && r.message.success) {
|
||||
|
|
@ -330,7 +330,7 @@ ETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.poll_auth_status',
|
||||
method: 'invoice_az.auth.poll_auth_status',
|
||||
args: {
|
||||
'asan_login_name': asanLoginName,
|
||||
'bearer_token': bearerToken
|
||||
|
|
@ -377,7 +377,7 @@ ETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.get_auth_certificates',
|
||||
method: 'invoice_az.auth.get_auth_certificates',
|
||||
args: { 'asan_login_name': asanLoginName },
|
||||
callback: function(certR) {
|
||||
if (certR.message && certR.message.success) {
|
||||
|
|
@ -430,7 +430,7 @@ ETaxes.auth = {
|
|||
// Выбор сертификата
|
||||
_selectCertificate: function(asanLoginName, certData, certName, callback) {
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_certificate',
|
||||
method: 'invoice_az.auth.select_certificate',
|
||||
args: {
|
||||
'asan_login_name': asanLoginName,
|
||||
'certificate_data': certData,
|
||||
|
|
@ -445,7 +445,7 @@ ETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_taxpayer',
|
||||
method: 'invoice_az.auth.select_taxpayer',
|
||||
args: { 'asan_login_name': asanLoginName },
|
||||
callback: function(r) {
|
||||
if (r.message && r.message.success) {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
/**
|
||||
* Sales Invoice Form and List Configuration
|
||||
* Optimized version using ETaxes common module patterns
|
||||
*/
|
||||
|
||||
frappe.ui.form.on('Sales Invoice', {
|
||||
refresh: function(frm) {
|
||||
// Проверяем, установлена ли галочка is_taxes_doc
|
||||
// Check if is_taxes_doc flag is set
|
||||
if (frm.doc.is_taxes_doc) {
|
||||
// Делаем все поля read-only
|
||||
// Make all fields read-only
|
||||
frm.set_read_only(true);
|
||||
|
||||
// Альтернативно, можно добавить визуальное предупреждение
|
||||
// Add visual indicator
|
||||
frm.page.set_indicator(__('Tax Document'), 'blue');
|
||||
}
|
||||
},
|
||||
|
||||
// Делаем поле is_taxes_doc тоже read-only при открытии документа
|
||||
// Make is_taxes_doc field read-only when document loads
|
||||
onload: function(frm) {
|
||||
frm.set_df_property('is_taxes_doc', 'read_only', 1);
|
||||
}
|
||||
});
|
||||
|
||||
frappe.listview_settings['Sales Invoice'] = {
|
||||
|
||||
onload: function(listview) {
|
||||
|
||||
// Добавляем поле как колонку (правильный способ)
|
||||
// Add field as column (proper way)
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
|
|
@ -30,16 +33,16 @@ frappe.listview_settings['Sales Invoice'] = {
|
|||
width: 120
|
||||
});
|
||||
|
||||
// Принудительно обновляем список с новой колонкой
|
||||
// Force refresh list with new column
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
// Форматируем отображение поля is_taxes_doc
|
||||
// Format is_taxes_doc field display
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green"></span>` :
|
||||
`<span class="indicator-pill gray"></span>`;
|
||||
`<span class="indicator-pill green" title="${__('Tax Document')}"></span>` :
|
||||
`<span class="indicator-pill gray" title="${__('Regular Document')}"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -31,7 +31,7 @@ SalesETaxes.utils = {
|
|||
}
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.get_default_asan_login',
|
||||
method: 'invoice_az.auth.get_default_asan_login',
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
SalesETaxes.cache.defaultLogin = r.message;
|
||||
|
|
@ -48,7 +48,7 @@ SalesETaxes.utils = {
|
|||
// Проверка валидности токена
|
||||
checkTokenValidity: function(callback) {
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.check_token_validity',
|
||||
method: 'invoice_az.auth.check_token_validity',
|
||||
callback: function(r) {
|
||||
callback(r.message && r.message.valid);
|
||||
},
|
||||
|
|
@ -260,7 +260,7 @@ SalesETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.handle_authentication',
|
||||
method: 'invoice_az.auth.handle_authentication',
|
||||
args: { 'asan_login_name': asanLoginName },
|
||||
callback: function(r) {
|
||||
if (r.message && r.message.success) {
|
||||
|
|
@ -330,7 +330,7 @@ SalesETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.poll_auth_status',
|
||||
method: 'invoice_az.auth.poll_auth_status',
|
||||
args: {
|
||||
'asan_login_name': asanLoginName,
|
||||
'bearer_token': bearerToken
|
||||
|
|
@ -377,7 +377,7 @@ SalesETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.get_auth_certificates',
|
||||
method: 'invoice_az.auth.get_auth_certificates',
|
||||
args: { 'asan_login_name': asanLoginName },
|
||||
callback: function(certR) {
|
||||
if (certR.message && certR.message.success) {
|
||||
|
|
@ -430,7 +430,7 @@ SalesETaxes.auth = {
|
|||
// Выбор сертификата
|
||||
_selectCertificate: function(asanLoginName, certData, certName, callback) {
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_certificate',
|
||||
method: 'invoice_az.auth.select_certificate',
|
||||
args: {
|
||||
'asan_login_name': asanLoginName,
|
||||
'certificate_data': certData,
|
||||
|
|
@ -445,7 +445,7 @@ SalesETaxes.auth = {
|
|||
);
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_taxpayer',
|
||||
method: 'invoice_az.auth.select_taxpayer',
|
||||
args: { 'asan_login_name': asanLoginName },
|
||||
callback: function(r) {
|
||||
if (r.message && r.message.success) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
import frappe
|
||||
import requests
|
||||
import json
|
||||
from frappe.utils import nowdate, now, cint, now_datetime, get_datetime
|
||||
from frappe.model.document import Document
|
||||
import time
|
||||
|
||||
# Константы
|
||||
BASE_URL = "https://new.e-taxes.gov.az/api/po"
|
||||
PROFILE_URL = f"{BASE_URL}/profile/public/v2/profile"
|
||||
DEFAULT_HEADERS = {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
"Cache-Control": "no-cache",
|
||||
"Pragma": "no-cache",
|
||||
"Referer": "https://new.e-taxes.gov.az/eportal/az/login/asan"
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_company_profile():
|
||||
"""Получает профиль компании из E-Taxes"""
|
||||
# Записываем активность
|
||||
try:
|
||||
from invoice_az.api import record_etaxes_activity
|
||||
record_etaxes_activity()
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Получаем настройки по умолчанию
|
||||
from invoice_az.auth import get_default_asan_login
|
||||
default_settings = get_default_asan_login()
|
||||
|
||||
if not default_settings.get('found'):
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'No Asan Login settings found'
|
||||
}
|
||||
|
||||
# Проверяем валидность токена
|
||||
from invoice_az.auth import check_token_validity
|
||||
token_check = check_token_validity(default_settings.get('name'))
|
||||
|
||||
if not token_check.get('valid'):
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'unauthorized',
|
||||
'message': token_check.get('message', 'Token is not valid')
|
||||
}
|
||||
|
||||
# Получаем main_token
|
||||
main_token = default_settings.get('main_token')
|
||||
if not main_token:
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'unauthorized',
|
||||
'message': 'Main token not found'
|
||||
}
|
||||
|
||||
# Подготавливаем заголовки запроса
|
||||
headers = DEFAULT_HEADERS.copy()
|
||||
headers["x-authorization"] = f"Bearer {main_token}"
|
||||
|
||||
# Отправляем запрос
|
||||
response = requests.get(PROFILE_URL, headers=headers, timeout=10)
|
||||
|
||||
# Проверяем статус ответа
|
||||
if response.status_code == 500:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
"status_code": 500
|
||||
}
|
||||
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again.",
|
||||
"status_code": 401
|
||||
}
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
# Получаем данные профиля
|
||||
profile_data = response.json()
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message': 'Company profile retrieved successfully',
|
||||
'profile_data': profile_data,
|
||||
'raw_response': response.text,
|
||||
'status_code': response.status_code,
|
||||
'headers': dict(response.headers)
|
||||
}
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
# Проверяем HTTP ошибки
|
||||
if e.response.status_code == 401:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again.",
|
||||
"status_code": 401
|
||||
}
|
||||
elif e.response.status_code == 500:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
"status_code": 500
|
||||
}
|
||||
|
||||
frappe.log_error(f"HTTP error in get_company_profile: {str(e)}", "API Error")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "http_error",
|
||||
"message": "An unknown error occurred, please try again in a few minutes."
|
||||
}
|
||||
|
||||
except requests.RequestException as e:
|
||||
frappe.log_error(f"Network error in get_company_profile: {str(e)}", "API Error")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "network_error",
|
||||
"message": f"Network error: {str(e)}"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error in get_company_profile: {str(e)}\n{frappe.get_traceback()}", "Company Profile Error")
|
||||
return {
|
||||
"success": False,
|
||||
"error": "unknown_error",
|
||||
"message": "An unknown error occurred, please try again in a few minutes."
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def test_profile_endpoint():
|
||||
"""Тестирует доступность endpoint'а профиля"""
|
||||
try:
|
||||
from invoice_az.api import record_etaxes_activity
|
||||
record_etaxes_activity()
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Получаем настройки по умолчанию
|
||||
from invoice_az.auth import get_default_asan_login
|
||||
default_settings = get_default_asan_login()
|
||||
|
||||
if not default_settings.get('found'):
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'No Asan Login settings found'
|
||||
}
|
||||
|
||||
asan_login_name = default_settings.get('name')
|
||||
|
||||
# Используем тестовую функцию из auth.py
|
||||
from invoice_az.auth import test_api_endpoints
|
||||
result = test_api_endpoints(
|
||||
asan_login_name=asan_login_name,
|
||||
endpoint_path="profile/public/v2/profile",
|
||||
method="GET"
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error in test_profile_endpoint: {str(e)}", "Profile Test Error")
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"Error testing endpoint: {str(e)}"
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ doctype_js = {
|
|||
"Purchase Order": "client/purchase_order.js",
|
||||
"Purchase Invoice": "client/purchase_invoice.js",
|
||||
"Sales Order": "client/sales_order.js",
|
||||
"Sales Invoice": "client/sales_invoice.js"
|
||||
"Sales Invoice": "client/sales_invoice.js",
|
||||
"Company": "client/company.js"
|
||||
}
|
||||
|
||||
doctype_list_js = {
|
||||
|
|
@ -36,13 +37,13 @@ doc_events = {
|
|||
scheduler_events = {
|
||||
"cron": {
|
||||
"*/4 * * * *": [
|
||||
"invoice_az.api.renew_token"
|
||||
"invoice_az.auth.renew_token"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
after_install = "invoice_az.api.setup_token_renewal"
|
||||
after_migrate = "invoice_az.api.setup_token_renewal"
|
||||
after_install = "invoice_az.auth.setup_token_renewal"
|
||||
after_migrate = "invoice_az.auth.setup_token_renewal"
|
||||
|
||||
# Apps
|
||||
# ------------------
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ frappe.ui.form.on('Asan Login', {
|
|||
|
||||
// Отправляем запрос для начала процесса аутентификации
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.handle_authentication',
|
||||
method: 'invoice_az.auth.handle_authentication',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name
|
||||
},
|
||||
|
|
@ -66,7 +66,7 @@ frappe.ui.form.on('Asan 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',
|
||||
method: 'invoice_az.auth.get_auth_certificates',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name
|
||||
},
|
||||
|
|
@ -107,7 +107,7 @@ frappe.ui.form.on('Asan Login', {
|
|||
frm.doc.selected_certificate) {
|
||||
frm.add_custom_button(__('Choose Taxpayer'), function() {
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_taxpayer',
|
||||
method: 'invoice_az.auth.select_taxpayer',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name
|
||||
},
|
||||
|
|
@ -301,7 +301,7 @@ function startPollingWithTimeout(frm, successCallback) {
|
|||
|
||||
// Проверяем статус авторизации
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.poll_auth_status',
|
||||
method: 'invoice_az.auth.poll_auth_status',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name,
|
||||
'bearer_token': frm.doc.bearer_token
|
||||
|
|
@ -473,7 +473,7 @@ function showCertificatesDialog(certificates, frm) {
|
|||
|
||||
// Отправляем запрос на выбор сертификата
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_certificate',
|
||||
method: 'invoice_az.auth.select_certificate',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name,
|
||||
'certificate_data': cert,
|
||||
|
|
@ -490,7 +490,7 @@ function showCertificatesDialog(certificates, frm) {
|
|||
|
||||
// После успешного выбора сертификата автоматически вызываем выбор налогоплательщика
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_taxpayer',
|
||||
method: 'invoice_az.auth.select_taxpayer',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name
|
||||
},
|
||||
|
|
@ -563,7 +563,7 @@ function showAuthDialog(frm) {
|
|||
|
||||
// Вызываем процесс аутентификации
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.handle_authentication',
|
||||
method: 'invoice_az.auth.handle_authentication',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name
|
||||
},
|
||||
|
|
@ -593,7 +593,7 @@ function showAuthDialog(frm) {
|
|||
|
||||
// Сначала восстанавливаем сертификат
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_certificate',
|
||||
method: 'invoice_az.auth.select_certificate',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name,
|
||||
'certificate_data': certData,
|
||||
|
|
@ -603,7 +603,7 @@ function showAuthDialog(frm) {
|
|||
if (r.message && r.message.success) {
|
||||
// Затем восстанавливаем налогоплательщика
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.select_taxpayer',
|
||||
method: 'invoice_az.auth.select_taxpayer',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name
|
||||
},
|
||||
|
|
@ -742,7 +742,7 @@ function pollAuthenticationStatus(frm, successCallback) {
|
|||
|
||||
// Проверяем статус авторизации
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.poll_auth_status',
|
||||
method: 'invoice_az.auth.poll_auth_status',
|
||||
args: {
|
||||
'asan_login_name': frm.doc.name,
|
||||
'bearer_token': frm.doc.bearer_token
|
||||
|
|
|
|||
Loading…
Reference in New Issue