import frappe
import json
import requests
from datetime import datetime
import time
import re

@frappe.whitelist()
def get_active_settings():
    """Getting active E-Taxes settings"""
    settings_list = frappe.get_all('E-Taxes Settings', 
                                  filters={'is_active': 1},
                                  order_by='creation desc',
                                  limit=1)
    
    if not settings_list:
        return None
    
    return frappe.get_doc('E-Taxes Settings', settings_list[0].name)

@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
        
        # Обновляем поле 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:
        frappe.log_error(f"Error recording activity: {str(e)}", "Activity Recording Error")

@frappe.whitelist()
def get_sales_invoices(token, filters=None):
    """Getting list of sales invoices with filtering options"""
    # Записываем активность
    record_etaxes_activity()
    
    url = "https://new.e-taxes.gov.az/api/po/invoice/public/v2/invoice/find.outbox"
    
    # Base request parameters - изменено: только approved и approvedBySystem статусы
    payload = {
        "sortBy": "creationDate",
        "sortAsc": True,
        "statuses": ["approved", "approvedBySystem"],  # Только утвержденные документы
        "types": ["current", "corrected"],
        "kinds": ["defaultInvoice", "agent", "resale", "recycling", "taxCodex163", 
                 "taxCodex177_5", "returnInvoice", "returnByAgent", "returnRecycled", 
                 "exportNoteInvoice", "exciseGoodsTransfer", "advanceInvoice"],
        "serialNumber": None,
        "senderTin": None,
        "senderName": None,
        "productName": None,
        "productCode": None,
        "receiverTin": None,
        "receiverName": None,
        "creationDateFrom": "01-01-2024 00:00",
        "creationDateTo": "31-12-2024 23:59",
        "amountFrom": None,
        "amountTo": None,
        "offset": 0,
        "maxCount": 200,
        "actionOwner": None
    }
    
    # Apply custom filters if they are passed
    if filters:
        try:
            filters_dict = json.loads(filters)
            for key, value in filters_dict.items():
                if key in payload and value:  # Update only existing keys and non-empty values
                    payload[key] = value
        except Exception as e:
            return
        
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Cache-Control": "no-cache",
        "x-authorization": f"Bearer {token}"
    }
    
    try:
        response = requests.post(url, json=payload, headers=headers)
        
        if response.status_code == 500:
            return {
                "error": "server_error",
                "message": "Service temporarily unavailable. Please try again in a few minutes.",
                "status_code": 500
            }

        # Check for 401 error
        if response.status_code == 401:
            # Получаем свежий токен из Asan Login
            from invoice_az.auth import get_default_asan_login
            asan_login_settings = get_default_asan_login()
            if asan_login_settings.get('found'):
                fresh_token = asan_login_settings['main_token']
                headers["x-authorization"] = f"Bearer {fresh_token}"
                
                # Повторяем запрос с новым токеном
                response = requests.post(url, json=payload, headers=headers)
                
                if response.status_code == 401:
                    return {
                        "error": "unauthorized",
                        "message": "Authentication required. Please login again."
                    }
            else:
                return {
                    "error": "unauthorized",
                    "message": "Authentication required. Please login again."
                }
            
        response.raise_for_status()
        
        result = response.json()
        return result
        
    except requests.exceptions.HTTPError as e:
        
        # Check if the error is 401 Unauthorized
        if e.response.status_code == 401:
            return {
                "error": "unauthorized",
                "message": "Authentication required. Please login again."
            }
        elif e.response.status_code == 500:
            return {
                "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_sales_invoices: {str(e)}", "API Error")
        
        return {
            "error": "http_error",
            "message": f"HTTP error {e.response.status_code}: {str(e)}"
        }
    except requests.exceptions.ConnectionError as e:
        frappe.log_error(f"Connection error in get_sales_invoices: {str(e)}", "API Error")
        return {
            "error": "connection_error",
            "message": "Cannot connect to E-Taxes service. Please check your internet connection and try again."
        }
    except requests.exceptions.Timeout as e:
        frappe.log_error(f"Timeout error in get_sales_invoices: {str(e)}", "API Error")
        return {
            "error": "timeout_error",
            "message": "Request timed out. Please try again."
        }
    except Exception as e:
        frappe.log_error(f"Unexpected error in get_sales_invoices: {str(e)}", "API Error")
        return {
            "error": "unexpected_error",
            "message": "An unexpected error occurred. Please try again."
        }

@frappe.whitelist()
def get_sales_invoice_details(token, invoice_id):
    """Getting detailed information about a sales invoice"""
    # Записываем активность
    record_etaxes_activity()
    
    url = f"https://new.e-taxes.gov.az/api/po/invoice/public/v2/invoice/{invoice_id}?sourceSystem=avis"
    
    headers = {
        "Accept": "application/json, text/plain, */*",
        "Content-Type": "application/json",
        "Cache-Control": "no-cache",
        "x-authorization": f"Bearer {token}"
    }
    
    try:
        response = requests.get(url, headers=headers)
        
        if response.status_code == 500:
            return {
                "error": "server_error",
                "message": "Service temporarily unavailable. Please try again in a few minutes.",
                "status_code": 500
            }

        # Check for 401 error
        if response.status_code == 401:
            # Получаем свежий токен из Asan Login
            from invoice_az.auth import get_default_asan_login
            asan_login_settings = get_default_asan_login()
            if asan_login_settings.get('found'):
                fresh_token = asan_login_settings['main_token']
                headers["x-authorization"] = f"Bearer {fresh_token}"
                
                # Повторяем запрос с новым токеном
                response = requests.get(url, headers=headers)
                
                if response.status_code == 401:
                    return {
                        "error": "unauthorized",
                        "message": "Authentication required. Please login again."
                    }
            else:
                return {
                    "error": "unauthorized",
                    "message": "Authentication required. Please login again."
                }
            
        response.raise_for_status()
        
        return response.json()
    except requests.exceptions.HTTPError as e:
        
        # Check if the error is 401 Unauthorized
        if e.response.status_code == 401:
            return {
                "error": "unauthorized",
                "message": "Authentication required. Please login again."
            }
        elif e.response.status_code == 500:
            return {
                "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_sales_invoice_details: {str(e)}", "API Error")
        
        return {
            "error": "http_error",
            "message": f"HTTP error {e.response.status_code}: {str(e)}"
        }
    except requests.exceptions.ConnectionError as e:
        frappe.log_error(f"Connection error in get_sales_invoice_details: {str(e)}", "API Error")
        return {
            "error": "connection_error",
            "message": "Cannot connect to E-Taxes service. Please check your internet connection and try again."
        }
    except requests.exceptions.Timeout as e:
        frappe.log_error(f"Timeout error in get_sales_invoice_details: {str(e)}", "API Error")
        return {
            "error": "timeout_error",
            "message": "Request timed out. Please try again."
        }
    except Exception as e:
        frappe.log_error(f"Unexpected error in get_sales_invoice_details: {str(e)}", "API Error")
        return {
            "error": "unexpected_error",
            "message": "An unexpected error occurred. Please try again."
        }
        
@frappe.whitelist()
def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, schedule_date=None, warehouse=None):
    """Import sales invoice taking into account NEW customer mappings"""
    # Записываем активность
    record_etaxes_activity()
    
    try:
        import re
        
        # Deserialize if necessary
        if isinstance(invoice_data, str):
            invoice_data = json.loads(invoice_data)
        
        # ДОБАВЛЕНО: Логируем входящие данные инвойса для отладки дат
        frappe.log_error(f"[SALES DATE DEBUG] Invoice data keys: {list(invoice_data.keys())}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] createdAt field: {invoice_data.get('createdAt')}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] createdAt type: {type(invoice_data.get('createdAt'))}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] schedule_date parameter: {schedule_date}", "Sales Date Debug")
        
        # Get active settings
        settings = get_active_settings()
        if not settings:
            return {
                'success': False,
                'message': 'No active E-Taxes settings found'
            }
        
        # Create mapping dictionaries
        item_mappings = {}
        for mapping in settings.item_mappings:
            if mapping.etaxes_item_name and mapping.erp_item:
                item_mappings[mapping.etaxes_item_name] = mapping.erp_item
        
        # ИЗМЕНЕНО: Используем customer_mappings вместо party_mappings
        customer_mappings = {}
        for mapping in settings.customer_mappings:
            if mapping.etaxes_customer_name and mapping.erp_customer:
                # ИСПРАВЛЕНИЕ: Обрезаем ключ до 140 символов
                customer_name = mapping.etaxes_customer_name[:140] if len(mapping.etaxes_customer_name) > 140 else mapping.etaxes_customer_name
                key = f"{customer_name.lower()}|{mapping.etaxes_tax_id.lower() if mapping.etaxes_tax_id else ''}"
                customer_mappings[key] = mapping.erp_customer
        
        # Get default warehouse
        default_warehouse = warehouse
        
        if not default_warehouse:
            if hasattr(settings, 'default_warehouse') and settings.default_warehouse:
                default_warehouse = settings.default_warehouse
            else:
                company = frappe.defaults.get_user_default('Company')
                if company:
                    default_warehouse = frappe.db.get_value('Company', company, 'default_warehouse')
                
                if not default_warehouse:
                    warehouses = frappe.get_all('Warehouse', 
                                              filters={'is_group': 0, 'disabled': 0},
                                              fields=['name'],
                                              limit=1)
                    if warehouses:
                        default_warehouse = warehouses[0].name
        
        if not default_warehouse:
            return {
                'success': False,
                'message': 'Default warehouse not found. Please specify warehouse in E-Taxes settings.'
            }
        
        # ИСПРАВЛЕНО: Определяем дату ДО создания Sales Order
        invoice_date = None
        created_at_raw = invoice_data.get("createdAt")
        
        frappe.log_error(f"[SALES DATE DEBUG] Raw createdAt value: '{created_at_raw}'", "Sales Date Debug")
        
        if created_at_raw:
            try:
                # Попробуем разные форматы даты
                if isinstance(created_at_raw, str):
                    # Если это строка, попробуем разные форматы
                    frappe.log_error(f"[SALES DATE DEBUG] Trying to parse string date: '{created_at_raw}'", "Sales Date Debug")
                    
                    # Попробуем стандартный парсинг ERPNext
                    invoice_date = frappe.utils.getdate(created_at_raw)
                    frappe.log_error(f"[SALES DATE DEBUG] Successfully parsed date: {invoice_date}", "Sales Date Debug")
                    
                elif hasattr(created_at_raw, 'date'):
                    # Если это datetime объект
                    invoice_date = created_at_raw.date()
                    frappe.log_error(f"[SALES DATE DEBUG] Extracted date from datetime: {invoice_date}", "Sales Date Debug")
                else:
                    # Попробуем привести к строке и парсить
                    date_str = str(created_at_raw)
                    frappe.log_error(f"[SALES DATE DEBUG] Converting to string and parsing: '{date_str}'", "Sales Date Debug")
                    invoice_date = frappe.utils.getdate(date_str)
                    frappe.log_error(f"[SALES DATE DEBUG] Successfully parsed converted date: {invoice_date}", "Sales Date Debug")
                    
            except Exception as e:
                frappe.log_error(f"[SALES DATE DEBUG] Error parsing createdAt '{created_at_raw}': {str(e)}", "Sales Date Debug")
                invoice_date = frappe.utils.today()
                frappe.log_error(f"[SALES DATE DEBUG] Using fallback date: {invoice_date}", "Sales Date Debug")
        else:
            invoice_date = frappe.utils.today()
            frappe.log_error(f"[SALES DATE DEBUG] No createdAt found, using today: {invoice_date}", "Sales Date Debug")

        # ИСПРАВЛЕНО: Определяем date_to_use с ПРИОРИТЕТОМ даты из E-taxes
        # ПРИОРИТЕТ: 1) дата из E-taxes, 2) schedule_date, 3) сегодня
        date_to_use = None
        if invoice_date:
            date_to_use = invoice_date
            frappe.log_error(f"[SALES DATE DEBUG] Using invoice_date from E-taxes for date_to_use: {date_to_use}", "Sales Date Debug")
        elif schedule_date:
            date_to_use = frappe.utils.getdate(schedule_date)
            frappe.log_error(f"[SALES DATE DEBUG] No E-taxes date, using provided schedule_date: {date_to_use}", "Sales Date Debug")
        else:
            date_to_use = frappe.utils.today()
            frappe.log_error(f"[SALES DATE DEBUG] No dates available, using today for date_to_use: {date_to_use}", "Sales Date Debug")

        frappe.log_error(f"[SALES DATE DEBUG] Final date_to_use: {date_to_use}", "Sales Date Debug")
        
        # ИЗМЕНЕНО: Создаем Sales Order сначала (аналогично Purchase Order логике)
        if sales_order_name:
            so = frappe.get_doc('Sales Order', sales_order_name)
        else:
            so = frappe.new_doc('Sales Order')
            
            # ИЗМЕНЕНО: Set customer используя customer_mappings с обрезкой
            receiver = invoice_data.get('receiver', {})
            receiver_name = receiver.get('name', '')
            # ИСПРАВЛЕНИЕ: Обрезаем название клиента до 140 символов для поиска
            receiver_name_truncated = receiver_name[:140] if len(receiver_name) > 140 else receiver_name
            receiver_key = f"{receiver_name_truncated.lower()}|{receiver.get('tin', '').lower()}"
            
            if receiver_key in customer_mappings and customer_mappings[receiver_key]:
                so.customer = customer_mappings[receiver_key]
            else:
                return {
                    'success': False,
                    'unmatched_parties': [{
                        'name': receiver.get('name', ''),
                        'tin': receiver.get('tin', ''),
                        'type': 'Receiver'
                    }],
                    'message': f'No mapping found for customer: {receiver.get("name", "")}'
                }
            
            frappe.log_error(f"[SALES DATE DEBUG] Final invoice_date for SO: {invoice_date}", "Sales Date Debug")

            so.transaction_date = invoice_date
            so.delivery_date = date_to_use
            
            frappe.log_error(f"[SALES DATE DEBUG] SO transaction_date set to: {so.transaction_date}", "Sales Date Debug")
            frappe.log_error(f"[SALES DATE DEBUG] SO delivery_date set to: {so.delivery_date}", "Sales Date Debug")
                
            so.company = frappe.defaults.get_user_default('Company')
        
        # Add/update additional fields from invoice
        so.title = f"Sales Order {invoice_data.get('serialNumber', '')}"
        so.invoice_serial_number = invoice_data.get("serialNumber") or invoice_data.get("number", "")
        
        # Clear existing items if need to update them completely
        if sales_order_name and invoice_data.get("items"):
            so.items = []
        
        # Variable for tracking added items
        added_items_count = 0
        unmatched_items = []
        unmatched_units = []
        
        # Add items from invoice
        if invoice_data.get("items"):
            for item in invoice_data.get("items", []):
                # Подготавливаем данные товара с очисткой от невидимых символов
                item_name = re.sub(r'\s+', ' ', item.get("productName", "")).strip()
                item_code = item.get("itemId", "") or f"CODE-{invoice_data.get('serialNumber', '')}"
                
                # ИСПРАВЛЕНИЕ: Обрезаем название товара до 140 символов для поиска
                item_name_truncated = item_name[:140] if len(item_name) > 140 else item_name
                
                # Ищем соответствие товара сначала по обрезанному названию
                mapped_item = item_mappings.get(item_name_truncated)

                # Если не найдено, ищем без учета регистра
                if not mapped_item:
                    item_name_lower = item_name_truncated.lower()
                    for mapping_key, mapping_value in item_mappings.items():
                        if mapping_key.lower() == item_name_lower:
                            mapped_item = mapping_value
                            break

                # Если все еще не найдено, ищем с нормализацией азербайджанских символов
                if not mapped_item:
                    normalized_input = normalize_string(item_name_truncated, consider_azeri=True)
                    for mapping_key, mapping_value in item_mappings.items():
                        normalized_key = normalize_string(mapping_key, consider_azeri=True)
                        if normalized_key == normalized_input:
                            mapped_item = mapping_value
                            break
                
                if not mapped_item:
                    unmatched_items.append({
                        'name': item_name,
                        'code': item_code
                    })
                    continue
                
                # Для единиц измерения ищем в E-Taxes Unit
                unit_name = item.get("unit", "")
                mapped_uom = None
                
                if unit_name:
                    try:
                        mapped_uom = frappe.db.get_value('E-Taxes Unit', 
                                                       filters={'etaxes_unit_name': unit_name},
                                                       fieldname='mapped_unit')
                    except Exception:
                        pass
                
                # Если соответствие единицы не найдено, используем UOM из товара или настроек
                if not mapped_uom:
                    if unit_name:
                        unmatched_units.append({
                            'name': unit_name
                        })
                    
                    try:
                        item_doc = frappe.get_doc('Item', mapped_item)
                        mapped_uom = item_doc.stock_uom
                    except:
                        mapped_uom = settings.default_uom if settings.default_uom else "Nos"
                
                # ИЗМЕНЕНО: Add position to Sales Order (не Sales Invoice)
                so_item = frappe.new_doc("Sales Order Item")
                so_item.parent = so.name
                so_item.parenttype = "Sales Order"
                so_item.parentfield = "items"

                so_item.item_code = mapped_item

                # Получаем название и описание из базы данных Item
                try:
                    item_doc = frappe.get_doc('Item', mapped_item)
                    so_item.item_name = item_doc.item_name
                    so_item.description = item_doc.description or item_doc.item_name
                except:
                    so_item.item_name = item.get("productName", "")
                    so_item.description = item.get("productName", "")

                so_item.qty = item.get("quantity", 0)
                so_item.rate = item.get("pricePerUnit", 0)
                so_item.amount = item.get("cost", 0)
                so_item.uom = mapped_uom
                so_item.delivery_date = date_to_use  # ИСПРАВЛЕНО: Используем правильную дату
                so_item.warehouse = default_warehouse
                
                frappe.log_error(f"[SALES DATE DEBUG] Item {so_item.item_code} delivery_date set to: {so_item.delivery_date}", "Sales Date Debug")
                
                so.append("items", so_item)
                added_items_count += 1
        
        # Check if there are unmapped items
        if unmatched_items:
            return {
                'success': False,
                'unmatched_items': unmatched_items,
                'message': f'No mapping found for {len(unmatched_items)} items'
            }
        
        # Check if there are unmapped units
        if unmatched_units:
            return {
                'success': False,
                'unmatched_units': unmatched_units,
                'message': f'No mapping found for {len(unmatched_units)} units'
            }
            
        # Check that there is at least one element in table
        if added_items_count == 0:
            frappe.log_error(
                f"No items were added to Sales Order. Invoice data: {invoice_data}",
                "Import Sales Invoice Error"
            )
            return {
                'success': False,
                'message': 'Could not add any items to sales order'
            }
        
        # ДОБАВЛЕНО: Создание строки ƏDV в Sales Taxes and Charges ТОЛЬКО если есть vat в данных
        try:
            vat_amount = invoice_data.get('vat', 0)
            
            if vat_amount and vat_amount > 0:
                vat_account = frappe.db.get_value('Account', 
                                                filters={'account_number': '521.3'}, 
                                                fieldname='name')
                
                if vat_account:
                    net_total = sum(item.get('cost', 0) for item in invoice_data.get('items', []))
                    vat_rate = (vat_amount / net_total * 100) if net_total > 0 else 0
                    
                    so.append("taxes", {
                        "account_head": vat_account,
                        "charge_type": "On Net Total",
                        "rate": vat_rate,
                        "tax_amount": vat_amount,
                        "description": "ƏDV",
                        "add_deduct_tax": "Add"
                    })
                    
                    frappe.log_error(f"Added ƏDV tax: Amount={vat_amount}, Rate={vat_rate}%", "Sales Order Tax")
                else:
                    frappe.log_error("ƏDV account (521.3) not found", "Sales Order Tax Warning")
            else:
                frappe.log_error("No VAT amount in E-Taxes data, skipping ƏDV tax creation", "Sales Order Tax Info")
                
        except Exception as e:
            frappe.log_error(f"Error adding ƏDV tax: {str(e)}", "Sales Order Tax Error")
        
        # ДОБАВЛЕНО: Логируем финальные даты перед сохранением
        frappe.log_error(f"[SALES DATE DEBUG] Final SO transaction_date: {so.transaction_date}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] Final SO delivery_date: {so.delivery_date}", "Sales Date Debug")
        
        # Save document
        so.save()
        
        # ИСПРАВЛЕНО: Убедимся что все delivery_date установлены правильно
        for item in so.items:
            if not item.delivery_date:
                item.delivery_date = date_to_use
                frappe.log_error(f"[SALES DATE DEBUG] Fixed missing delivery_date for item {item.item_code}: {item.delivery_date}", "Sales Date Debug")
            if not item.warehouse:
                item.warehouse = default_warehouse
        
        # Save again to ensure changes are applied
        so.save()
        
        # ДОБАВЛЕНО: Логируем даты после сохранения
        frappe.log_error(f"[SALES DATE DEBUG] After save SO transaction_date: {so.transaction_date}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] After save SO delivery_date: {so.delivery_date}", "Sales Date Debug")
        
        # Логируем delivery_date каждого item после сохранения
        for item in so.items:
            frappe.log_error(f"[SALES DATE DEBUG] After save item {item.item_code} delivery_date: {item.delivery_date}", "Sales Date Debug")
        
        # ИЗМЕНЕНИЕ: Создаем E-Taxes Sales и устанавливаем связь ДО submit'а
        invoice_id = invoice_data.get('id', '')
        serial_number = invoice_data.get('serialNumber', '')
        receiver_name = invoice_data.get('receiver', {}).get('name', '') if invoice_data.get('receiver') else ''
        total = invoice_data.get('totalAmount', 0) or invoice_data.get('amount', 0)
        
        # Создаем E-Taxes Sales
        etaxes_sales_result = create_etaxes_sales(invoice_id, date_to_use, receiver_name, total)
        if etaxes_sales_result and etaxes_sales_result.get('success'):
            # Устанавливаем поля E-Taxes ДО submit'а
            so.is_taxes_doc = 1
            so.taxes_doc = etaxes_sales_result.get('name')
            # Сохраняем изменения
            so.save()
        
        # ИЗМЕНЕНИЕ: Делаем Submit для Sales Order
        try:
            so.submit()
            frappe.log_error(f"Sales Order {so.name} submitted successfully", "Import Sales Invoice Success")
        except Exception as e:
            frappe.log_error(f"Error submitting Sales Order {so.name}: {str(e)}", "Submit SO Error")
            return {
                'success': False,
                'message': f'Failed to submit Sales Order: {str(e)}'
            }
        
        # ИЗМЕНЕНИЕ: Создаем Sales Invoice из Sales Order
        try:
            si_name = create_sales_invoice_from_order(so.name)
            if si_name:
                # Делаем Submit для Sales Invoice
                si = frappe.get_doc("Sales Invoice", si_name)
                si.submit()
                frappe.log_error(f"Sales Invoice {si_name} created and submitted successfully", "Import Sales Invoice Success")
                
                return {
                    'success': True,
                    'message': 'Sales invoice data imported successfully. Sales Order and Sales Invoice created.',
                    'sales_order': so.name,
                    'sales_invoice': si_name
                }
            else:
                return {
                    'success': True,
                    'message': 'Sales invoice data imported successfully. Sales Order created, but Sales Invoice creation failed.',
                    'sales_order': so.name
                }
        except Exception as e:
            frappe.log_error(f"Error creating or submitting Sales Invoice: {str(e)}", "Submit SI Error")
            return {
                'success': True,
                'message': 'Sales invoice data imported successfully. Sales Order created, but Sales Invoice creation failed.',
                'sales_order': so.name
            }
            
    except Exception as e:
        frappe.log_error(f"Error in import_sales_invoice_with_mapping: {str(e)}\n{frappe.get_traceback()}", "Import Sales Invoice Error")
        return {
            'success': False,
            'message': "An unknown error occurred, please try again in a few minutes."
        }

@frappe.whitelist()
def create_sales_invoice_from_order(sales_order_name):
    """Создает Sales Invoice из Sales Order используя стандартную функцию ERPNext"""
    try:
        # Импортируем стандартную функцию из модуля Sales Order
        from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
        
        # Получаем Sales Order ДО создания invoice
        so_doc = frappe.get_doc('Sales Order', sales_order_name)
        
        # ИСПРАВЛЕНО: Правильная обработка дат
        if so_doc.transaction_date:
            posting_date = so_doc.transaction_date
            # Конвертируем в datetime.date если это строка
            if isinstance(posting_date, str):
                posting_date = frappe.utils.getdate(posting_date)
        else:
            posting_date = frappe.utils.getdate(frappe.utils.nowdate())

        frappe.log_error(f"[SALES DATE DEBUG] SO transaction_date: {so_doc.transaction_date}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] SI posting_date to set: {posting_date}", "Sales Date Debug")

        # Создаем Sales Invoice из Sales Order
        si_doc = make_sales_invoice(sales_order_name)
        
        # ДОБАВЛЕНО: Логируем дату сразу после создания
        frappe.log_error(f"[SALES DATE DEBUG] SI posting_date after make_sales_invoice: {si_doc.posting_date}", "Sales Date Debug")
        
        # ИСПРАВЛЕНО: Устанавливаем дату и флаг posting_time ДО других операций
        si_doc.posting_date = posting_date
        si_doc.set_posting_time = 1  # Важно! Разрешаем установку даты в прошлом
        
        # ИСПРАВЛЕНО: due_date всегда основана на posting_date из E-taxes (без коррекции на "сегодня")
        due_date = frappe.utils.add_days(posting_date, 30)
        si_doc.due_date = due_date
        
        frappe.log_error(f"[SALES DATE DEBUG] SI posting_date before insert: {si_doc.posting_date}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] SI due_date before insert (E-taxes based): {si_doc.due_date}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] SI set_posting_time: {si_doc.set_posting_time}", "Sales Date Debug")
        
        # ДОБАВЛЕНО: Устанавливаем галочку is_taxes_doc для Sales Invoice
        si_doc.is_taxes_doc = 1
        
        # ДОБАВЛЕНО: Копируем налоги из Sales Order
        if so_doc.taxes:
            si_doc.taxes = []
            for tax_row in so_doc.taxes:
                si_doc.append("taxes", {
                    "account_head": tax_row.account_head,
                    "charge_type": tax_row.charge_type,
                    "rate": tax_row.rate,
                    "tax_amount": tax_row.tax_amount,
                    "description": tax_row.description
                })
        
        # Сохраняем документ
        si_doc.insert(ignore_permissions=True)
        
        # ДОБАВЛЕНО: Логируем дату после insert
        frappe.log_error(f"[SALES DATE DEBUG] SI posting_date after insert: {si_doc.posting_date}", "Sales Date Debug")
        frappe.log_error(f"[SALES DATE DEBUG] SI due_date after insert: {si_doc.due_date}", "Sales Date Debug")
        
        # ДОБАВЛЕНО: Если даты все еще неправильные, принудительно устанавливаем
        expected_due_date = frappe.utils.add_days(posting_date, 30)
        if si_doc.posting_date != posting_date or si_doc.due_date != expected_due_date:
            frappe.log_error(f"[SALES DATE DEBUG] Date mismatch detected!", "Sales Date Debug")
            frappe.log_error(f"[SALES DATE DEBUG] Expected posting_date: {posting_date}, Got: {si_doc.posting_date}", "Sales Date Debug")
            frappe.log_error(f"[SALES DATE DEBUG] Expected due_date: {expected_due_date}, Got: {si_doc.due_date}", "Sales Date Debug")
            
            # Принудительно устанавливаем даты через DB
            frappe.db.set_value('Sales Invoice', si_doc.name, {
                'posting_date': posting_date,
                'due_date': expected_due_date,
                'set_posting_time': 1
            })
            frappe.db.commit()
            
            # Перезагружаем документ
            si_doc = frappe.get_doc('Sales Invoice', si_doc.name)
            frappe.log_error(f"[SALES DATE DEBUG] SI posting_date after DB update: {si_doc.posting_date}", "Sales Date Debug")
            frappe.log_error(f"[SALES DATE DEBUG] SI due_date after DB update: {si_doc.due_date}", "Sales Date Debug")
        
        frappe.log_error(f"Sales Invoice {si_doc.name} created from Sales Order {sales_order_name}", "Sales Invoice Creation")
        
        # Возвращаем имя созданного Sales Invoice
        return si_doc.name
        
    except Exception as e:
        frappe.log_error(f"Error creating Sales Invoice from SO {sales_order_name}: {str(e)}\n{frappe.get_traceback()}", "Sales Invoice Creation Error")
        return None
        
@frappe.whitelist()
def create_etaxes_sales(etaxes_id, date, party, total):
    """Creates E-Taxes Sales record for tracking imported sales invoices"""
    # Записываем активность
    record_etaxes_activity()
    
    try:
        # Normalize ID for storage
        etaxes_id = str(etaxes_id).strip() if etaxes_id else ""
        
        # Debug logging
        frappe.logger().info(f"Creating E-Taxes Sales: ID={etaxes_id}, date={date}, party={party}, total={total}")
        
        # Check if record with such etaxes_id already exists
        existing = frappe.db.get_value('E-Taxes Sales', {'etaxes_id': etaxes_id}, 'name')
        if existing:
            # Debug logging
            frappe.logger().info(f"E-Taxes Sales already exists: {existing}")
            
            return {
                'success': True,
                'message': 'Record already exists',
                'name': existing
            }
        
        # Create new record
        etaxes_sales = frappe.new_doc('E-Taxes Sales')
        etaxes_sales.etaxes_id = etaxes_id
        etaxes_sales.date = date
        etaxes_sales.party = party
        etaxes_sales.total = total
        
        etaxes_sales.insert()
        frappe.db.commit()  # Explicit commit
        
        # Debug logging
        frappe.logger().info(f"Created new E-Taxes Sales: {etaxes_sales.name}")
        
        return {
            'success': True,
            'name': etaxes_sales.name
        }
    except Exception as e:
        frappe.log_error(f"Error in create_etaxes_sales: {str(e)}\n{frappe.get_traceback()}", "API Error")
        return {
            'success': False,
            'message': "An unknown error occurred, please try again in a few minutes."
        }

@frappe.whitelist()
def get_etaxes_sales():
    """Gets list of all E-Taxes Sales records"""
    try:
        sales = frappe.get_all('E-Taxes Sales', 
                              fields=['name', 'etaxes_id', 'date', 'party', 'total'])
        
        # Debug logging
        frappe.logger().info(f"Retrieved {len(sales)} E-Taxes Sales records")
        
        return {
            'success': True,
            'sales': sales
        }
    except Exception as e:
        frappe.log_error(f"Error in get_etaxes_sales: {str(e)}", "API Error")
        return {
            'success': False,
            'message': "An unknown error occurred, please try again in a few minutes."
        }        

@frappe.whitelist()
def normalize_string(text, consider_azeri=True):
    """Normalizing string for comparison"""
    import re
    
    if not text:
        return ''
    
    # ДОБАВЛЕНО: Сначала заменяем азербайджанские/турецкие символы ДО приведения к нижнему регистру
    if consider_azeri:
        azeri_replacements = {
            # Заглавные буквы
            'Ə': 'E',
            'Ü': 'U', 
            'Ö': 'O',
            'Ğ': 'G',
            'İ': 'I',
            'Ç': 'C',
            'Ş': 'S',
            # Строчные буквы
            'ə': 'e',
            'ü': 'u',
            'ö': 'o',
            'ğ': 'g',
            'ı': 'i',
            'ç': 'c',
            'ş': 's'
        }
        
        for azeri_char, latin_char in azeri_replacements.items():
            text = text.replace(azeri_char, latin_char)
    
    # Convert to lowercase ПОСЛЕ замены символов
    text = text.lower()
    
    # Remove all special characters and numbers
    text = re.sub(r'[^\w\s]', '', text)
    text = re.sub(r'\d+', '', text)
    
    # Remove extra spaces
    text = ' '.join(text.split())
    
    return text

@frappe.whitelist()
def on_delete_sales_order(doc, method):
    """Deletes related E-Taxes Sales record when Sales Order is deleted"""
    if doc.is_taxes_doc and doc.taxes_doc:
        try:
            frappe.logger().info(f"Deleting E-Taxes Sales {doc.taxes_doc} due to Sales Order {doc.name} deletion")
            
            # Check if E-Taxes Sales record exists
            if frappe.db.exists("E-Taxes Sales", doc.taxes_doc):
                frappe.delete_doc('E-Taxes Sales', doc.taxes_doc, ignore_permissions=True, force=True)
                frappe.db.commit()
                frappe.logger().info(f"Successfully deleted E-Taxes Sales {doc.taxes_doc}")
        except Exception as e:
            frappe.log_error(f"Error deleting E-Taxes Sales {doc.taxes_doc}: {str(e)}\n{frappe.get_traceback()}", 
                           "Sales Order Delete Error")