Everything load from outbox and inbox
This commit is contained in:
parent
3893f44be1
commit
7e4a7afdc8
2402
invoice_az/api.py
2402
invoice_az/api.py
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
File diff suppressed because it is too large
Load Diff
|
|
@ -852,6 +852,26 @@ SalesETaxes.import = {
|
|||
fieldtype: 'Date',
|
||||
label: __('Date to'),
|
||||
default: moment().format('YYYY-MM-DD')
|
||||
},
|
||||
{
|
||||
fieldname: 'settings_section',
|
||||
fieldtype: 'Section Break',
|
||||
label: __('Settings')
|
||||
},
|
||||
{
|
||||
fieldname: 'warehouse',
|
||||
fieldtype: 'Link',
|
||||
label: __('Delivery warehouse'),
|
||||
options: 'Warehouse',
|
||||
reqd: true,
|
||||
get_query: function() {
|
||||
return {
|
||||
filters: {
|
||||
'is_group': 0,
|
||||
'disabled': 0
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
],
|
||||
primary_action_label: __('Search'),
|
||||
|
|
@ -873,11 +893,20 @@ SalesETaxes.import = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!values.warehouse) {
|
||||
frappe.msgprint({
|
||||
title: __('Warning'),
|
||||
indicator: 'orange',
|
||||
message: __('Please select a delivery warehouse')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const fromDate = moment(values.creationDateFrom).format('DD-MM-YYYY 00:00');
|
||||
const toDate = moment(values.creationDateTo).format('DD-MM-YYYY 23:59');
|
||||
|
||||
d.hide();
|
||||
SalesETaxes.import.loadInvoices(fromDate, toDate);
|
||||
SalesETaxes.import.loadInvoices(fromDate, toDate, values.warehouse);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -886,7 +915,7 @@ SalesETaxes.import = {
|
|||
},
|
||||
|
||||
// Загрузить инвойсы с фильтрацией дубликатов
|
||||
loadInvoices: function(fromDate, toDate, accumulatedInvoices = [], offset = 0) {
|
||||
loadInvoices: function(fromDate, toDate, warehouse, accumulatedInvoices = [], offset = 0) {
|
||||
SalesETaxes.cancelLoading = false;
|
||||
$(document).off('progress-cancel.etaxes_sales_import');
|
||||
|
||||
|
|
@ -935,7 +964,7 @@ SalesETaxes.import = {
|
|||
|
||||
if (hasMore && currentInvoices.length > 0) {
|
||||
const newOffset = offset + currentInvoices.length;
|
||||
SalesETaxes.import.loadInvoices(fromDate, toDate, allInvoices, newOffset);
|
||||
SalesETaxes.import.loadInvoices(fromDate, toDate, warehouse, allInvoices, newOffset);
|
||||
} else {
|
||||
if (allInvoices.length > 0) {
|
||||
frappe.show_alert({
|
||||
|
|
@ -943,7 +972,7 @@ SalesETaxes.import = {
|
|||
indicator: 'blue'
|
||||
}, 3);
|
||||
|
||||
SalesETaxes.import.showInvoiceSelection(allInvoices, mainToken);
|
||||
SalesETaxes.import.showInvoiceSelection(allInvoices, mainToken, warehouse);
|
||||
} else {
|
||||
frappe.msgprint({
|
||||
title: __('Information'),
|
||||
|
|
@ -957,7 +986,7 @@ SalesETaxes.import = {
|
|||
__('Your E-Taxes session has expired. Would you like to authenticate now?'),
|
||||
function() {
|
||||
SalesETaxes.auth.startProcess(function() {
|
||||
SalesETaxes.import.loadInvoices(fromDate, toDate);
|
||||
SalesETaxes.import.loadInvoices(fromDate, toDate, warehouse);
|
||||
});
|
||||
},
|
||||
function() {
|
||||
|
|
@ -995,7 +1024,7 @@ SalesETaxes.import = {
|
|||
},
|
||||
|
||||
// Показать диалог выбора инвойсов
|
||||
showInvoiceSelection: function(invoices, token) {
|
||||
showInvoiceSelection: function(invoices, token, warehouse) {
|
||||
let invoiceTable = '<div style="max-height: 500px; overflow-y: auto;"><table class="table table-bordered etaxes-sales-invoices-table" style="width: 100%; table-layout: fixed;">';
|
||||
invoiceTable += '<thead><tr>' +
|
||||
'<th style="width: 6%;"><input type="checkbox" class="select-all-sales-invoices"></th>' +
|
||||
|
|
@ -1046,6 +1075,12 @@ SalesETaxes.import = {
|
|||
fieldname: 'invoices_html',
|
||||
fieldtype: 'HTML',
|
||||
options: invoiceTable
|
||||
},
|
||||
{
|
||||
fieldname: 'warehouse_display',
|
||||
fieldtype: 'HTML',
|
||||
options: '<div class="row"><div class="col-xs-12"><div class="alert alert-info">' +
|
||||
__('Selected delivery warehouse: ') + '<strong>' + warehouse + '</strong></div></div></div>'
|
||||
}
|
||||
],
|
||||
primary_action_label: __('Load selected'),
|
||||
|
|
@ -1066,7 +1101,7 @@ SalesETaxes.import = {
|
|||
|
||||
d.hide();
|
||||
SalesETaxes.cancelLoading = false;
|
||||
SalesETaxes.import.loadSelectedInvoices(selectedInvoiceIds, token);
|
||||
SalesETaxes.import.loadSelectedInvoices(selectedInvoiceIds, token, warehouse);
|
||||
},
|
||||
secondary_action_label: __('Cancel'),
|
||||
secondary_action: function() {
|
||||
|
|
@ -1094,7 +1129,7 @@ SalesETaxes.import = {
|
|||
},
|
||||
|
||||
// Загрузка выбранных инвойсов
|
||||
loadSelectedInvoices: function(invoiceIds, token, processedCount = 0, currentIndex = 0) {
|
||||
loadSelectedInvoices: function(invoiceIds, token, warehouse, processedCount = 0, currentIndex = 0) {
|
||||
if (processedCount === 0 && currentIndex === 0) {
|
||||
SalesETaxes.loadingErrors = [];
|
||||
window.totalSalesInvoicesToProcess = invoiceIds.length;
|
||||
|
|
@ -1121,7 +1156,7 @@ SalesETaxes.import = {
|
|||
if (SalesETaxes.cancelLoading) {
|
||||
invoiceIds = [];
|
||||
frappe.hide_progress();
|
||||
SalesETaxes.import.loadSelectedInvoices(invoiceIds, token, processedCount, currentIndex);
|
||||
SalesETaxes.import.loadSelectedInvoices(invoiceIds, token, warehouse, processedCount, currentIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1165,11 +1200,12 @@ SalesETaxes.import = {
|
|||
args: {
|
||||
'invoice_data': r.message,
|
||||
'sales_order_name': null,
|
||||
'schedule_date': scheduleDate
|
||||
'schedule_date': scheduleDate,
|
||||
'warehouse': warehouse // ДОБАВЛЕНО: передача warehouse
|
||||
},
|
||||
callback: function(importR) {
|
||||
SalesETaxes.import._handleImportResult(importR, r.message, invoiceId, scheduleDate, receiverName, total,
|
||||
invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
const errorMessage = 'Network error during import: ' + (error || 'Unknown network error');
|
||||
|
|
@ -1179,7 +1215,7 @@ SalesETaxes.import = {
|
|||
xhr_response: xhr.responseText
|
||||
});
|
||||
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
}
|
||||
});
|
||||
} else if (r.message && r.message.error === 'unauthorized') {
|
||||
|
|
@ -1193,7 +1229,7 @@ SalesETaxes.import = {
|
|||
SalesETaxes.utils.getDefaultLogin(function(loginR) {
|
||||
if (loginR && loginR.found && loginR.main_token) {
|
||||
invoiceIds.unshift(invoiceId);
|
||||
SalesETaxes.import.loadSelectedInvoices(invoiceIds, loginR.main_token, processedCount, currentIndex - 1);
|
||||
SalesETaxes.import.loadSelectedInvoices(invoiceIds, loginR.main_token, warehouse, processedCount, currentIndex - 1);
|
||||
} else {
|
||||
SalesETaxes.errors.add({id: invoiceId}, 'Authentication Error',
|
||||
'Failed to get token after authentication');
|
||||
|
|
@ -1218,7 +1254,7 @@ SalesETaxes.import = {
|
|||
server_response: r.message
|
||||
});
|
||||
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
|
@ -1228,14 +1264,14 @@ SalesETaxes.import = {
|
|||
xhr_response: xhr.responseText
|
||||
});
|
||||
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Обработка результата импорта
|
||||
_handleImportResult: function(importR, invoiceMessage, invoiceId, scheduleDate, receiverName, total,
|
||||
invoiceIds, token, processedCount, currentIndex, isLastInvoice) {
|
||||
invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice) {
|
||||
if (importR.message && importR.message.success) {
|
||||
frappe.show_alert({
|
||||
message: __('Sales invoice successfully imported: ') + (invoiceMessage.serialNumber || ''),
|
||||
|
|
@ -1243,26 +1279,99 @@ SalesETaxes.import = {
|
|||
}, 3);
|
||||
|
||||
processedCount++;
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
} else if (importR.message && (importR.message.unmatched_items || importR.message.unmatched_parties || importR.message.unmatched_units)) {
|
||||
let errorType = '';
|
||||
const additionalData = { invoice_items: invoiceMessage.items || [] };
|
||||
|
||||
if (importR.message.unmatched_items) {
|
||||
errorType = 'Unmapped Items';
|
||||
additionalData.unmatched_items = importR.message.unmatched_items;
|
||||
additionalData.unmatched_items = importR.message.unmatched_items.map(function(item, index) {
|
||||
let itemName = '';
|
||||
let itemCode = '';
|
||||
|
||||
if (typeof item === 'string') {
|
||||
itemName = item;
|
||||
} else if (item && typeof item === 'object') {
|
||||
itemName = item.name || item.productName || item.item_name || '';
|
||||
itemCode = item.code || item.itemId || item.item_code || '';
|
||||
} else {
|
||||
if (invoiceMessage.items && invoiceMessage.items[index]) {
|
||||
const originalItem = invoiceMessage.items[index];
|
||||
itemName = originalItem.productName || originalItem.name || '';
|
||||
itemCode = originalItem.itemId || originalItem.code || '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!itemName) {
|
||||
itemName = 'Item ' + (index + 1) + ' (name not available)';
|
||||
}
|
||||
|
||||
return {
|
||||
name: itemName,
|
||||
code: itemCode
|
||||
};
|
||||
});
|
||||
} else if (importR.message.unmatched_parties) {
|
||||
errorType = 'Unmapped Parties';
|
||||
additionalData.unmatched_parties = importR.message.unmatched_parties;
|
||||
additionalData.unmatched_parties = importR.message.unmatched_parties.map(function(party, index) {
|
||||
let partyName = '';
|
||||
let partyTin = '';
|
||||
let partyType = '';
|
||||
|
||||
if (typeof party === 'string') {
|
||||
partyName = party;
|
||||
} else if (party && typeof party === 'object') {
|
||||
partyName = party.name || party.party_name || '';
|
||||
partyTin = party.tin || party.tax_id || '';
|
||||
partyType = party.type || party.party_type || '';
|
||||
} else {
|
||||
if (invoiceMessage.receiver) {
|
||||
partyName = invoiceMessage.receiver.name || '';
|
||||
partyTin = invoiceMessage.receiver.tin || '';
|
||||
partyType = 'Receiver';
|
||||
}
|
||||
}
|
||||
|
||||
if (!partyName) {
|
||||
partyName = 'Party ' + (index + 1) + ' (name not available)';
|
||||
}
|
||||
|
||||
return {
|
||||
name: partyName,
|
||||
tin: partyTin,
|
||||
type: partyType
|
||||
};
|
||||
});
|
||||
} else if (importR.message.unmatched_units) {
|
||||
errorType = 'Unmapped Units';
|
||||
additionalData.unmatched_units = importR.message.unmatched_units;
|
||||
additionalData.unmatched_units = importR.message.unmatched_units.map(function(unit, index) {
|
||||
let unitName = '';
|
||||
|
||||
if (typeof unit === 'string') {
|
||||
unitName = unit;
|
||||
} else if (unit && typeof unit === 'object') {
|
||||
unitName = unit.name || unit.unit_name || '';
|
||||
} else {
|
||||
if (invoiceMessage.items && invoiceMessage.items[index] && invoiceMessage.items[index].unit) {
|
||||
unitName = invoiceMessage.items[index].unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unitName) {
|
||||
unitName = 'Unit ' + (index + 1) + ' (name not available)';
|
||||
}
|
||||
|
||||
return {
|
||||
name: unitName
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
additionalData.server_response = importR.message;
|
||||
SalesETaxes.errors.add(invoiceMessage, errorType, importR.message.message, additionalData);
|
||||
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
} else {
|
||||
const errorMessage = importR.message ? importR.message.message : 'Unknown sales import error';
|
||||
const additionalData = {
|
||||
|
|
@ -1275,12 +1384,12 @@ SalesETaxes.import = {
|
|||
}
|
||||
|
||||
SalesETaxes.errors.add(invoiceMessage, 'Sales Import Error', errorMessage, additionalData);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, processedCount, currentIndex, isLastInvoice);
|
||||
SalesETaxes.import._continueOrFinish(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice);
|
||||
}
|
||||
},
|
||||
|
||||
// Продолжить или завершить обработку
|
||||
_continueOrFinish: function(invoiceIds, token, processedCount, currentIndex, isLastInvoice) {
|
||||
_continueOrFinish: function(invoiceIds, token, warehouse, processedCount, currentIndex, isLastInvoice) {
|
||||
if (isLastInvoice) {
|
||||
frappe.hide_progress();
|
||||
$(document).off('progress-cancel.loading_sales_invoices');
|
||||
|
|
@ -1300,7 +1409,7 @@ SalesETaxes.import = {
|
|||
frappe.hide_progress();
|
||||
}
|
||||
|
||||
SalesETaxes.import.loadSelectedInvoices(invoiceIds, token, processedCount, currentIndex);
|
||||
SalesETaxes.import.loadSelectedInvoices(invoiceIds, token, warehouse, processedCount, currentIndex);
|
||||
}, SalesETaxes.PROGRESS_UPDATE_DELAY);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,175 +0,0 @@
|
|||
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,8 +9,7 @@ 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",
|
||||
"Company": "client/company.js"
|
||||
"Sales Invoice": "client/sales_invoice.js"
|
||||
}
|
||||
|
||||
doctype_list_js = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue