fix: respect data loading checkboxes in reference data loading

Checkbox values (load_items, load_units, load_customers, load_suppliers)
were collected in the dialog but never passed through the loading pipeline,
causing all data types to always be created regardless of user selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali 2026-03-27 19:40:46 +04:00
parent dca5a13de3
commit d3775786a3
2 changed files with 198 additions and 175 deletions

View File

@ -3764,14 +3764,16 @@ def get_reference_data_lists(data_type, limit=100, offset=0):
return {'success': False, 'message': str(e)} return {'success': False, 'message': str(e)}
@frappe.whitelist() @frappe.whitelist()
def process_single_invoice_for_reference_data(token, invoice_id, source_type='purchase'): def process_single_invoice_for_reference_data(token, invoice_id, source_type='purchase',
load_items=1, load_units=1,
load_customers=1, load_suppliers=1):
"""Обрабатывает один инвойс и создает все 4 типа доктайпов""" """Обрабатывает один инвойс и создает все 4 типа доктайпов"""
record_etaxes_activity() record_etaxes_activity()
try: try:
# Получаем детали инвойса # Получаем детали инвойса
invoice_details = get_invoice_details(token, invoice_id) invoice_details = get_invoice_details(token, invoice_id)
# Проверяем на ошибку unauthorized и пробуем обновить токен # Проверяем на ошибку unauthorized и пробуем обновить токен
if isinstance(invoice_details, dict) and invoice_details.get('error') == 'unauthorized': if isinstance(invoice_details, dict) and invoice_details.get('error') == 'unauthorized':
# Пробуем получить новый токен из Asan Login # Пробуем получить новый токен из Asan Login
@ -3780,7 +3782,7 @@ def process_single_invoice_for_reference_data(token, invoice_id, source_type='pu
new_token = token_result.get('token') new_token = token_result.get('token')
# Повторяем запрос с новым токеном # Повторяем запрос с новым токеном
invoice_details = get_invoice_details(new_token, invoice_id) invoice_details = get_invoice_details(new_token, invoice_id)
# Если все еще ошибка - возвращаем с обновленным токеном для клиента # Если все еще ошибка - возвращаем с обновленным токеном для клиента
if isinstance(invoice_details, dict) and 'error' in invoice_details: if isinstance(invoice_details, dict) and 'error' in invoice_details:
return { return {
@ -3805,8 +3807,12 @@ def process_single_invoice_for_reference_data(token, invoice_id, source_type='pu
'message': invoice_details.get('message', 'Failed to get invoice details') 'message': invoice_details.get('message', 'Failed to get invoice details')
} }
# Создаем все доктайпы из одного инвойса # Создаем доктайпы из одного инвойса (только выбранные типы)
result = create_reference_data_from_single_invoice(invoice_details, source_type) result = create_reference_data_from_single_invoice(
invoice_details, source_type,
load_items=int(load_items), load_units=int(load_units),
load_customers=int(load_customers), load_suppliers=int(load_suppliers)
)
return result return result
@ -3818,178 +3824,182 @@ def process_single_invoice_for_reference_data(token, invoice_id, source_type='pu
} }
@frappe.whitelist() @frappe.whitelist()
def create_reference_data_from_single_invoice(invoice_details, source_type): def create_reference_data_from_single_invoice(invoice_details, source_type,
"""Создает все 4 типа доктайпов из одного инвойса с EQM кодами - оптимизированная версия""" load_items=1, load_units=1,
load_customers=1, load_suppliers=1):
"""Создает доктайпы из одного инвойса с EQM кодами - оптимизированная версия"""
try: try:
stats = { stats = {
'items_created': 0, 'items_created': 0,
'items_skipped': 0, 'items_skipped': 0,
'units_created': 0, 'units_created': 0,
'units_skipped': 0, 'units_skipped': 0,
'customers_created': 0, 'customers_created': 0,
'customers_skipped': 0, 'customers_skipped': 0,
'suppliers_created': 0, 'suppliers_created': 0,
'suppliers_skipped': 0 'suppliers_skipped': 0
} }
serial_number = invoice_details.get('serialNumber', '') serial_number = invoice_details.get('serialNumber', '')
items = invoice_details.get('items', []) items = invoice_details.get('items', [])
sender = invoice_details.get('sender', {}) sender = invoice_details.get('sender', {})
receiver = invoice_details.get('receiver', {}) receiver = invoice_details.get('receiver', {})
# === СОЗДАНИЕ ТОВАРОВ С EQM КОДАМИ === # === СОЗДАНИЕ ТОВАРОВ С EQM КОДАМИ ===
processed_items = set() if int(load_items):
for item in items: processed_items = set()
item_name = item.get('productName', '').strip() for item in items:
if not item_name: item_name = item.get('productName', '').strip()
continue if not item_name:
continue
# Обрезаем до 140 символов
if len(item_name) > 140:
item_name = item_name[:140]
if item_name in processed_items:
continue
processed_items.add(item_name)
# Проверяем существование
if frappe.db.exists('E-Taxes Item', item_name):
stats['items_skipped'] += 1
continue
try:
# Создаем товар
item_code = item.get('itemId', '') or f"CODE-{serial_number}"
# Обрабатываем группу товаров
product_group = item.get('productGroup', {})
product_group_code = product_group.get('code', '') if product_group else ''
product_group_type = product_group.get('type', '') if product_group else ''
# Получаем название группы товаров
product_group_name = ''
if product_group and product_group.get('name'):
names = product_group.get('name', {})
if isinstance(names, dict):
product_group_name = names.get('az', '') or names.get('en', '') or names.get('ru', '')
elif isinstance(names, str):
product_group_name = names
# Обрезаем название группы товаров до 1000 символов
if len(product_group_name) > 1000:
product_group_name = product_group_name[:1000]
# НОВОЕ: Поиск EQM Code по коду группы товаров - возвращаем name для Link поля
eqm_code = None
if product_group_code:
try:
frappe.log_error(f"[EQM DEBUG] Searching for product_group_code: '{product_group_code}'", "EQM Code Search")
# Ищем запись и получаем и name, и eqm_name
eqm_record = frappe.db.sql("""
SELECT name, eqm_name
FROM `tabEQM Codes`
WHERE eqm_name LIKE %s
LIMIT 1
""", (f"{product_group_code} -%",), as_dict=True)
frappe.log_error(f"[EQM DEBUG] SQL search result: {eqm_record}", "EQM Code Search")
if eqm_record:
# ИСПРАВЛЕНИЕ: Сохраняем name (ID записи) для Link поля, а не eqm_name
eqm_code = eqm_record[0].get('name') # Изменено с eqm_name на name
frappe.log_error(f"[EQM DEBUG] Found EQM Code ID: '{eqm_code}' (description: '{eqm_record[0].get('eqm_name')}') for product group '{product_group_code}' in item {item_name}", "EQM Code Assignment")
else:
frappe.log_error(f"[EQM DEBUG] EQM Code not found for product group '{product_group_code}' in item {item_name}", "EQM Code Not Found")
except Exception as e:
frappe.log_error(f"[EQM DEBUG] Error searching EQM Code for '{product_group_code}': {str(e)}", "EQM Code Search Error")
# Логируем финальное значение # Обрезаем до 140 символов
frappe.log_error(f"[EQM DEBUG] Final eqm_code ID to save: '{eqm_code}' for item {item_name}", "EQM Code Final") if len(item_name) > 140:
item_name = item_name[:140]
if item_name in processed_items:
continue
processed_items.add(item_name)
# Проверяем существование
if frappe.db.exists('E-Taxes Item', item_name):
stats['items_skipped'] += 1
continue
try:
# Создаем товар
item_code = item.get('itemId', '') or f"CODE-{serial_number}"
# Обрабатываем группу товаров
product_group = item.get('productGroup', {})
product_group_code = product_group.get('code', '') if product_group else ''
product_group_type = product_group.get('type', '') if product_group else ''
# Получаем название группы товаров
product_group_name = ''
if product_group and product_group.get('name'):
names = product_group.get('name', {})
if isinstance(names, dict):
product_group_name = names.get('az', '') or names.get('en', '') or names.get('ru', '')
elif isinstance(names, str):
product_group_name = names
# Обрезаем название группы товаров до 1000 символов
if len(product_group_name) > 1000:
product_group_name = product_group_name[:1000]
# НОВОЕ: Поиск EQM Code по коду группы товаров - возвращаем name для Link поля
eqm_code = None
if product_group_code:
try:
frappe.log_error(f"[EQM DEBUG] Searching for product_group_code: '{product_group_code}'", "EQM Code Search")
# Ищем запись и получаем и name, и eqm_name
eqm_record = frappe.db.sql("""
SELECT name, eqm_name
FROM `tabEQM Codes`
WHERE eqm_name LIKE %s
LIMIT 1
""", (f"{product_group_code} -%",), as_dict=True)
frappe.log_error(f"[EQM DEBUG] SQL search result: {eqm_record}", "EQM Code Search")
if eqm_record:
# ИСПРАВЛЕНИЕ: Сохраняем name (ID записи) для Link поля, а не eqm_name
eqm_code = eqm_record[0].get('name') # Изменено с eqm_name на name
frappe.log_error(f"[EQM DEBUG] Found EQM Code ID: '{eqm_code}' (description: '{eqm_record[0].get('eqm_name')}') for product group '{product_group_code}' in item {item_name}", "EQM Code Assignment")
else:
frappe.log_error(f"[EQM DEBUG] EQM Code not found for product group '{product_group_code}' in item {item_name}", "EQM Code Not Found")
except Exception as e:
frappe.log_error(f"[EQM DEBUG] Error searching EQM Code for '{product_group_code}': {str(e)}", "EQM Code Search Error")
# Логируем финальное значение
frappe.log_error(f"[EQM DEBUG] Final eqm_code ID to save: '{eqm_code}' for item {item_name}", "EQM Code Final")
# Логируем финальное значение
frappe.log_error(f"[EQM DEBUG] Final eqm_code value to save: '{eqm_code}' for item {item_name}", "EQM Code Final")
# Определяем, является ли товар услугой
is_service = 1 if product_group_type == 'service' else 0
# Определяем источники
is_from_purchase = 1 if source_type == 'purchase' else 0
is_from_sales = 1 if source_type == 'sales' else 0
# Создаем документ с EQM кодом
doc_data = {
'doctype': 'E-Taxes Item',
'etaxes_item_name': item_name,
'etaxes_item_code': item_code,
'etaxes_unit': item.get('unit', ''),
'etaxes_price': item.get('pricePerUnit', 0),
'etaxes_product_group_code': product_group_code,
'etaxes_product_group_name': product_group_name,
'etaxes_product_group_type': product_group_type,
'is_service_item': is_service,
'source_invoice': serial_number,
'is_from_purchase': is_from_purchase,
'is_from_sales': is_from_sales,
'status': 'New'
}
# НОВОЕ: Добавляем EQM код если найден
if eqm_code:
doc_data['eqm_code'] = eqm_code
doc = frappe.get_doc(doc_data)
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
stats['items_created'] += 1
except frappe.DuplicateEntryError:
stats['items_skipped'] += 1
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Item {item_name}: {str(e)}", "Create Item Error")
# Логируем финальное значение
frappe.log_error(f"[EQM DEBUG] Final eqm_code value to save: '{eqm_code}' for item {item_name}", "EQM Code Final")
# Определяем, является ли товар услугой
is_service = 1 if product_group_type == 'service' else 0
# Определяем источники
is_from_purchase = 1 if source_type == 'purchase' else 0
is_from_sales = 1 if source_type == 'sales' else 0
# Создаем документ с EQM кодом
doc_data = {
'doctype': 'E-Taxes Item',
'etaxes_item_name': item_name,
'etaxes_item_code': item_code,
'etaxes_unit': item.get('unit', ''),
'etaxes_price': item.get('pricePerUnit', 0),
'etaxes_product_group_code': product_group_code,
'etaxes_product_group_name': product_group_name,
'etaxes_product_group_type': product_group_type,
'is_service_item': is_service,
'source_invoice': serial_number,
'is_from_purchase': is_from_purchase,
'is_from_sales': is_from_sales,
'status': 'New'
}
# НОВОЕ: Добавляем EQM код если найден
if eqm_code:
doc_data['eqm_code'] = eqm_code
doc = frappe.get_doc(doc_data)
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
stats['items_created'] += 1
except frappe.DuplicateEntryError:
stats['items_skipped'] += 1
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Item {item_name}: {str(e)}", "Create Item Error")
# === СОЗДАНИЕ ЕДИНИЦ === # === СОЗДАНИЕ ЕДИНИЦ ===
processed_units = set() if int(load_units):
for item in items: processed_units = set()
unit_name = item.get('unit', '').strip() for item in items:
if not unit_name or unit_name in processed_units: unit_name = item.get('unit', '').strip()
continue if not unit_name or unit_name in processed_units:
continue
processed_units.add(unit_name)
processed_units.add(unit_name)
# Проверяем существование
if frappe.db.exists('E-Taxes Unit', unit_name): # Проверяем существование
stats['units_skipped'] += 1 if frappe.db.exists('E-Taxes Unit', unit_name):
continue stats['units_skipped'] += 1
continue
try:
source_type_text = 'Purchase' if source_type == 'purchase' else 'Sales' try:
source_type_text = 'Purchase' if source_type == 'purchase' else 'Sales'
doc = frappe.get_doc({
'doctype': 'E-Taxes Unit', doc = frappe.get_doc({
'etaxes_unit_name': unit_name, 'doctype': 'E-Taxes Unit',
'etaxes_unit_code': unit_name, 'etaxes_unit_name': unit_name,
'source_invoice': serial_number, 'etaxes_unit_code': unit_name,
'source_type': source_type_text, 'source_invoice': serial_number,
'status': 'New' 'source_type': source_type_text,
}) 'status': 'New'
doc.insert(ignore_permissions=True, ignore_if_duplicate=True) })
stats['units_created'] += 1 doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
stats['units_created'] += 1
except frappe.DuplicateEntryError:
stats['units_skipped'] += 1 except frappe.DuplicateEntryError:
except Exception as e: stats['units_skipped'] += 1
frappe.log_error(f"Error creating E-Taxes Unit {unit_name}: {str(e)}", "Create Unit Error") except Exception as e:
frappe.log_error(f"Error creating E-Taxes Unit {unit_name}: {str(e)}", "Create Unit Error")
# === СОЗДАНИЕ КЛИЕНТОВ (RECEIVER) === # === СОЗДАНИЕ КЛИЕНТОВ (RECEIVER) ===
if receiver: if int(load_customers) and receiver:
receiver_name = (receiver.get('name') or '').strip() receiver_name = (receiver.get('name') or '').strip()
receiver_tin = (receiver.get('tin') or '').strip() receiver_tin = (receiver.get('tin') or '').strip()
receiver_address = (receiver.get('address') or '').strip() receiver_address = (receiver.get('address') or '').strip()
receiver_name = ' '.join(receiver_name.split()) # Убираем лишние пробелы receiver_name = ' '.join(receiver_name.split()) # Убираем лишние пробелы
if receiver_name and receiver_tin: if receiver_name and receiver_tin:
if not frappe.db.exists('E-Taxes Customers', receiver_name): if not frappe.db.exists('E-Taxes Customers', receiver_name):
try: try:
@ -4004,24 +4014,24 @@ def create_reference_data_from_single_invoice(invoice_details, source_type):
}) })
doc.insert(ignore_permissions=True, ignore_if_duplicate=True) doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
stats['customers_created'] += 1 stats['customers_created'] += 1
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
stats['customers_skipped'] += 1 stats['customers_skipped'] += 1
except Exception as e: except Exception as e:
frappe.log_error(f"Error creating E-Taxes Customers {receiver_name}: {str(e)}", "Create Customer Error") frappe.log_error(f"Error creating E-Taxes Customers {receiver_name}: {str(e)}", "Create Customer Error")
else: else:
stats['customers_skipped'] += 1 stats['customers_skipped'] += 1
# === СОЗДАНИЕ ПОСТАВЩИКОВ/КЛИЕНТОВ (SENDER) === # === СОЗДАНИЕ ПОСТАВЩИКОВ/КЛИЕНТОВ (SENDER) ===
if sender: if sender:
sender_name = (sender.get('name') or '').strip() sender_name = (sender.get('name') or '').strip()
sender_tin = (sender.get('tin') or '').strip() sender_tin = (sender.get('tin') or '').strip()
sender_address = (sender.get('address') or '').strip() sender_address = (sender.get('address') or '').strip()
sender_name = ' '.join(sender_name.split()) # Убираем лишние пробелы sender_name = ' '.join(sender_name.split()) # Убираем лишние пробелы
if sender_name and sender_tin: if sender_name and sender_tin:
if source_type == 'purchase': if source_type == 'purchase' and int(load_suppliers):
# Sender = Supplier для purchase инвойсов # Sender = Supplier для purchase инвойсов
if not frappe.db.exists('E-Taxes Suppliers', sender_name): if not frappe.db.exists('E-Taxes Suppliers', sender_name):
try: try:
@ -4036,14 +4046,14 @@ def create_reference_data_from_single_invoice(invoice_details, source_type):
}) })
doc.insert(ignore_permissions=True, ignore_if_duplicate=True) doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
stats['suppliers_created'] += 1 stats['suppliers_created'] += 1
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
stats['suppliers_skipped'] += 1 stats['suppliers_skipped'] += 1
except Exception as e: except Exception as e:
frappe.log_error(f"Error creating E-Taxes Suppliers {sender_name}: {str(e)}", "Create Supplier Error") frappe.log_error(f"Error creating E-Taxes Suppliers {sender_name}: {str(e)}", "Create Supplier Error")
else: else:
stats['suppliers_skipped'] += 1 stats['suppliers_skipped'] += 1
else: elif source_type != 'purchase' and int(load_customers):
# Sender = Customer для sales инвойсов # Sender = Customer для sales инвойсов
if not frappe.db.exists('E-Taxes Customers', sender_name): if not frappe.db.exists('E-Taxes Customers', sender_name):
try: try:
@ -4058,7 +4068,7 @@ def create_reference_data_from_single_invoice(invoice_details, source_type):
}) })
doc.insert(ignore_permissions=True, ignore_if_duplicate=True) doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
stats['customers_created'] += 1 stats['customers_created'] += 1
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
stats['customers_skipped'] += 1 stats['customers_skipped'] += 1
except Exception as e: except Exception as e:

View File

@ -983,7 +983,7 @@ function start_reference_data_loading(frm, values) {
start_staged_invoice_loading(frm, values); start_staged_invoice_loading(frm, values);
} }
function process_invoices_for_reference_data(invoices, token, accumulated_data, current_index = 0, frm) { function process_invoices_for_reference_data(invoices, token, accumulated_data, current_index = 0, frm, load_flags = null) {
// Инициализация при первом вызове // Инициализация при первом вызове
if (current_index === 0) { if (current_index === 0) {
window.cancelReferenceLoading = false; window.cancelReferenceLoading = false;
@ -1050,7 +1050,11 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
args: { args: {
'token': token, 'token': token,
'invoice_id': invoice_id, 'invoice_id': invoice_id,
'source_type': source_type 'source_type': source_type,
'load_items': load_flags ? load_flags.load_items : 1,
'load_units': load_flags ? load_flags.load_units : 1,
'load_customers': load_flags ? load_flags.load_customers : 1,
'load_suppliers': load_flags ? load_flags.load_suppliers : 1
}, },
callback: function(r) { callback: function(r) {
if (r.message) { if (r.message) {
@ -1071,7 +1075,7 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
token = token_r.message.token; token = token_r.message.token;
setTimeout(function() { setTimeout(function() {
if (!window.cancelReferenceLoading) { if (!window.cancelReferenceLoading) {
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index, frm); process_invoices_for_reference_data(invoices, token, accumulated_data, current_index, frm, load_flags);
} }
}, 100); }, 100);
} else { } else {
@ -1083,7 +1087,7 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
}); });
return; // Выходим, чтобы не продолжать со старым токеном return; // Выходим, чтобы не продолжать со старым токеном
} }
// Обработка успешного результата // Обработка успешного результата
if (r.message.success && r.message.stats) { if (r.message.success && r.message.stats) {
const stats = r.message.stats; const stats = r.message.stats;
@ -1097,14 +1101,14 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
accumulated_data.suppliers_skipped += stats.suppliers_skipped || 0; accumulated_data.suppliers_skipped += stats.suppliers_skipped || 0;
} }
} }
// Переходим к следующему инвойсу // Переходим к следующему инвойсу
setTimeout(function() { setTimeout(function() {
if (!window.cancelReferenceLoading) { if (!window.cancelReferenceLoading) {
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm); process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm, load_flags);
} }
}, 50); }, 50);
}, },
error: function(xhr, status, error) { error: function(xhr, status, error) {
// При ошибке сети пробуем обновить токен // При ошибке сети пробуем обновить токен
@ -1117,14 +1121,14 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
token = token_r.message.token; token = token_r.message.token;
setTimeout(function() { setTimeout(function() {
if (!window.cancelReferenceLoading) { if (!window.cancelReferenceLoading) {
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index, frm); process_invoices_for_reference_data(invoices, token, accumulated_data, current_index, frm, load_flags);
} }
}, 100); }, 100);
} else { } else {
// Переходим к следующему инвойсу // Переходим к следующему инвойсу
setTimeout(function() { setTimeout(function() {
if (!window.cancelReferenceLoading) { if (!window.cancelReferenceLoading) {
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm); process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm, load_flags);
} }
}, 100); }, 100);
} }
@ -1134,7 +1138,7 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
// При других ошибках переходим к следующему инвойсу // При других ошибках переходим к следующему инвойсу
setTimeout(function() { setTimeout(function() {
if (!window.cancelReferenceLoading) { if (!window.cancelReferenceLoading) {
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm); process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm, load_flags);
} }
}, 100); }, 100);
} }
@ -2174,7 +2178,11 @@ function start_staged_invoice_loading(frm, values) {
inbox_completed: false, inbox_completed: false,
outbox_completed: false, outbox_completed: false,
token: null, token: null,
last_update_count: 0 // Для отслеживания когда показывать обновление last_update_count: 0, // Для отслеживания когда показывать обновление
load_items: values.load_items ? 1 : 0,
load_units: values.load_units ? 1 : 0,
load_customers: values.load_customers ? 1 : 0,
load_suppliers: values.load_suppliers ? 1 : 0
}; };
window.cancelReferenceLoading = false; window.cancelReferenceLoading = false;
@ -2344,7 +2352,12 @@ function check_loading_completion(loading_state, frm) {
suppliers_created: 0, suppliers_created: 0,
suppliers_skipped: 0, suppliers_skipped: 0,
total_invoices: loading_state.all_invoices.length total_invoices: loading_state.all_invoices.length
}, 0, frm); }, 0, frm, {
load_items: loading_state.load_items,
load_units: loading_state.load_units,
load_customers: loading_state.load_customers,
load_suppliers: loading_state.load_suppliers
});
}, 2000); }, 2000);
} }
} }