diff --git a/invoice_az/api.py b/invoice_az/api.py index 368cdcc..61f12f7 100644 --- a/invoice_az/api.py +++ b/invoice_az/api.py @@ -669,7 +669,7 @@ def match_similar_items(): @frappe.whitelist() def create_unmapped_items(settings_name): - """Creating items for unmapped elements from E-Taxes settings table""" + """Creating items for unmapped elements from E-Taxes settings table with EQM Code support""" # Записываем активность record_etaxes_activity() @@ -693,12 +693,13 @@ def create_unmapped_items(settings_name): "message": "No unmapped items in table to create" } - # Batch load E-Taxes Items + # Batch load E-Taxes Items с EQM кодом etaxes_items_data = frappe.get_all('E-Taxes Item', filters={'name': ['in', unmapped_etaxes_items]}, fields=['name', 'etaxes_item_name', 'etaxes_item_code', 'etaxes_product_group_code', 'etaxes_product_group_name', - 'is_service_item', 'is_from_purchase', 'is_from_sales']) + 'is_service_item', 'is_from_purchase', 'is_from_sales', + 'eqm_code']) # НОВОЕ: Добавляем eqm_code # Build mapping settings dict mapping_settings = {} @@ -724,6 +725,8 @@ def create_unmapped_items(settings_name): # Check for custom fields once product_group_code_field_exists = frappe.db.exists('Custom Field', {'dt': 'Item', 'fieldname': 'etaxes_product_group_code'}) product_group_name_field_exists = frappe.db.exists('Custom Field', {'dt': 'Item', 'fieldname': 'etaxes_product_group_name'}) + eqm_code_field_exists = frappe.db.exists('Custom Field', {'dt': 'Item', 'fieldname': 'eqm_code'}) # НОВОЕ: Проверяем поле eqm_code + product_group_name_field_length = 140 if product_group_name_field_exists: custom_field = frappe.get_doc('Custom Field', {'dt': 'Item', 'fieldname': 'etaxes_product_group_name'}) @@ -815,6 +818,11 @@ def create_unmapped_items(settings_name): product_group_name = product_group_name[:product_group_name_field_length] item_doc.etaxes_product_group_name = product_group_name + # НОВОЕ: Добавляем EQM код если поле существует и код есть + if eqm_code_field_exists and etaxes_item_data.eqm_code: + item_doc.eqm_code = etaxes_item_data.eqm_code + frappe.log_error(f"Added EQM Code {etaxes_item_data.eqm_code} to item {item_doc.item_name}", "EQM Code Transfer") + # Add tax template tax_template = individual_settings.get('item_tax_template') or settings_doc.default_item_tax_template if tax_template and frappe.db.exists('Item Tax Template', tax_template): @@ -840,6 +848,7 @@ def create_unmapped_items(settings_name): created_count += 1 except Exception as e: + frappe.log_error(f"Error creating item for {etaxes_item_data.etaxes_item_name}: {str(e)}", "Create Item Error") error_count += 1 # Save settings @@ -859,7 +868,7 @@ def create_unmapped_items(settings_name): "success": False, "message": "An unknown error occurred, please try again in a few minutes." } - + @frappe.whitelist() def create_purchase_invoice_from_order(purchase_order_name): """Создает Purchase Invoice из Purchase Order используя стандартную функцию ERPNext""" @@ -2082,7 +2091,7 @@ def refresh_item_mappings_display(settings_name): @frappe.whitelist() def process_single_invoice_for_items(token, invoice_id, source_type='purchase'): - """Processing a single invoice for item extraction with name length handling""" + """Processing a single invoice for item extraction with EQM Code assignment""" # Записываем активность record_etaxes_activity() @@ -2176,6 +2185,39 @@ def process_single_invoice_for_items(token, invoice_id, source_type='purchase'): product_group_name = product_group_name[:1000] frappe.log_error(f"Product group name truncated for item {item_name}: original length {len(product_group.get('name', {}).get('az', ''))}", "Product Group Name Truncation") + # НОВОЕ: Поиск 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 @@ -2192,7 +2234,8 @@ def process_single_invoice_for_items(token, invoice_id, source_type='purchase'): 'source_invoice': serial_number, 'is_from_purchase': is_from_purchase, 'is_from_sales': is_from_sales, - 'status': 'New' + 'status': 'New', + 'eqm_code': eqm_code # НОВОЕ: Добавляем EQM код } # Создаём записи для уникальных товаров @@ -2235,7 +2278,7 @@ def process_single_invoice_for_items(token, invoice_id, source_type='purchase'): 'success': False, 'message': "An unknown error occurred processing this invoice" } - + @frappe.whitelist() def process_single_invoice_for_units(token, invoice_id, source_type='purchase'): """Processing a single invoice for unit extraction with progress support""" @@ -3798,7 +3841,7 @@ def process_single_invoice_for_reference_data(token, invoice_id, source_type='pu @frappe.whitelist() def create_reference_data_from_single_invoice(invoice_details, source_type): - """Создает все 4 типа доктайпов из одного инвойса - оптимизированная версия""" + """Создает все 4 типа доктайпов из одного инвойса с EQM кодами - оптимизированная версия""" try: stats = { 'items_created': 0, @@ -3816,7 +3859,7 @@ def create_reference_data_from_single_invoice(invoice_details, source_type): sender = invoice_details.get('sender', {}) receiver = invoice_details.get('receiver', {}) - # === СОЗДАНИЕ ТОВАРОВ === + # === СОЗДАНИЕ ТОВАРОВ С EQM КОДАМИ === processed_items = set() for item in items: item_name = item.get('productName', '').strip() @@ -3859,6 +3902,38 @@ def create_reference_data_from_single_invoice(invoice_details, source_type): 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 @@ -3866,7 +3941,8 @@ def create_reference_data_from_single_invoice(invoice_details, source_type): is_from_purchase = 1 if source_type == 'purchase' else 0 is_from_sales = 1 if source_type == 'sales' else 0 - doc = frappe.get_doc({ + # Создаем документ с EQM кодом + doc_data = { 'doctype': 'E-Taxes Item', 'etaxes_item_name': item_name, 'etaxes_item_code': item_code, @@ -3880,7 +3956,13 @@ def create_reference_data_from_single_invoice(invoice_details, source_type): '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 @@ -4018,7 +4100,7 @@ def create_reference_data_from_single_invoice(invoice_details, source_type): 'success': False, 'message': str(e) } - + # ===== НОВЫЕ PYTHON ФУНКЦИИ ===== @frappe.whitelist() diff --git a/invoice_az/invoice_az/doctype/e_taxes_item/e_taxes_item.json b/invoice_az/invoice_az/doctype/e_taxes_item/e_taxes_item.json index 732d44b..cce59d1 100644 --- a/invoice_az/invoice_az/doctype/e_taxes_item/e_taxes_item.json +++ b/invoice_az/invoice_az/doctype/e_taxes_item/e_taxes_item.json @@ -9,6 +9,7 @@ "etaxes_item_code", "etaxes_unit", "etaxes_price", + "eqm_code", "product_group_section", "etaxes_product_group_code", "etaxes_product_group_name", @@ -156,11 +157,17 @@ "label": "Tax Template", "options": "Item Tax Template", "read_only": 1 + }, + { + "fieldname": "eqm_code", + "fieldtype": "Link", + "label": "EQM Code", + "options": "EQM Codes" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2025-07-16 20:19:56.319865", + "modified": "2025-09-22 17:32:18.795605", "modified_by": "Administrator", "module": "Invoice Az", "name": "E-Taxes Item", diff --git a/invoice_az/sales_api.py b/invoice_az/sales_api.py index 3189734..daf06a9 100644 --- a/invoice_az/sales_api.py +++ b/invoice_az/sales_api.py @@ -18,13 +18,30 @@ def get_active_settings(): return frappe.get_doc('E-Taxes Settings', settings_list[0].name) -def record_etaxes_activity(): - """Record E-Taxes API activity to optimize token renewal""" +@frappe.whitelist() +def record_etaxes_activity(asan_login_name=None): + """Записывает время последней активности с e-taxes напрямую""" try: - frappe.cache().set_value("etaxes_last_activity", frappe.utils.now(), expires_in_sec=3600) + # Если имя не указано, получаем дефолтный профиль + 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 + + # Обновляем поле last_activity_time напрямую через БД + frappe.db.set_value("Asan Login", asan_login_name, "last_activity_time", now_datetime(), update_modified=False) + frappe.db.commit() + except Exception as e: - # Don't fail the whole operation if activity recording fails - frappe.log_error(f"Error recording E-Taxes activity: {str(e)}", "E-Taxes Activity Recording") + frappe.log_error(f"Error recording activity: {str(e)}", "Activity Recording Error") @frappe.whitelist() def get_sales_invoices(token, filters=None):