fixed bugs

This commit is contained in:
Ali 2025-05-27 00:07:49 +04:00
parent 49aad69efb
commit 7b92c6a062
1 changed files with 140 additions and 127 deletions

View File

@ -1369,7 +1369,7 @@ def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
@frappe.whitelist()
def load_parties_from_invoices(date_from, date_to, max_count=200, offset=0, invoice_type="purchase"):
"""Loading parties from invoices for a period with duplicate prevention"""
"""Loading parties from invoices for a period with duplicate prevention and normalization"""
# Записываем активность
record_etaxes_activity()
@ -1459,18 +1459,22 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, offset=0, invo
serial_number = invoice.get('serialNumber', '')
# Processing sender (Sender -> Supplier)
# Processing sender (Sender -> Supplier) с нормализацией
sender = invoice.get('sender', {})
if sender:
sender_name = sender.get('name', '')
sender_tin = sender.get('tin', '')
sender_address = sender.get('address', '')
# ИСПРАВЛЕНО: безопасная обработка None значений
sender_name = (sender.get('name') or '').strip()
sender_tin = (sender.get('tin') or '').strip()
sender_address = (sender.get('address') or '').strip()
# Дополнительная нормализация
sender_name = ' '.join(sender_name.split()) # Убираем лишние пробелы
if not sender_name or not sender_tin:
continue
# ИСПРАВЛЕНИЕ: Проверяем существование записи
# Проверяем существование записи
existing = frappe.db.exists('E-Taxes Parties', {
'etaxes_party_name': sender_name,
'etaxes_tax_id': sender_tin
@ -1499,14 +1503,18 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, offset=0, invo
receiver = invoice.get('receiver', {})
if receiver:
receiver_name = receiver.get('name', '')
receiver_tin = receiver.get('tin', '')
receiver_address = receiver.get('address', '')
# ИСПРАВЛЕНО: безопасная обработка None значений
receiver_name = (receiver.get('name') or '').strip()
receiver_tin = (receiver.get('tin') or '').strip()
receiver_address = (receiver.get('address') or '').strip()
# Дополнительная нормализация
receiver_name = ' '.join(receiver_name.split()) # Убираем лишние пробелы
if not receiver_name or not receiver_tin:
continue
# ИСПРАВЛЕНИЕ: Проверяем существование записи
# Проверяем существование записи
existing = frappe.db.exists('E-Taxes Parties', {
'etaxes_party_name': receiver_name,
'etaxes_tax_id': receiver_tin
@ -1573,7 +1581,7 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, offset=0, invo
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
@frappe.whitelist()
def get_default_settings():
"""Getting active E-Taxes settings"""
@ -2235,7 +2243,7 @@ def create_purchase_invoice_from_order(purchase_order_name):
@frappe.whitelist()
def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule_date=None, warehouse=None):
"""Import invoice taking into account mappings and processing unmapped elements"""
"""Import invoice taking into account mappings from E-Taxes Item/Party/Unit mapped fields only"""
# Записываем активность
record_etaxes_activity()
@ -2244,55 +2252,26 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
if isinstance(invoice_data, str):
invoice_data = json.loads(invoice_data)
# Get active settings
settings = get_active_settings()
if not settings:
return {
'success': False,
'message': 'No active E-Taxes settings found'
}
# Create mapping dictionaries - ИСПРАВЛЕНО: по ID документов
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
party_mappings = {}
for mapping in settings.party_mappings:
if mapping.etaxes_party_name and mapping.erp_party:
# Ключ остается по имени и TIN, но значение - это erp_party
key = f"{mapping.etaxes_party_name.lower()}|{mapping.etaxes_tax_id.lower() if mapping.etaxes_tax_id else ''}"
party_mappings[key] = (mapping.erp_party, mapping.party_type)
unit_mappings = {}
for mapping in settings.unit_mappings:
if mapping.etaxes_unit_name and mapping.erp_unit:
unit_mappings[mapping.etaxes_unit_name] = mapping.erp_unit
# 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
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.'
'message': 'Default warehouse not found. Please specify warehouse.'
}
# Get or create Purchase Order
@ -2301,34 +2280,41 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
else:
po = frappe.new_doc('Purchase Order')
# Set supplier - ИСПРАВЛЕНО: поиск через E-Taxes Parties
# Set supplier - поиск ТОЛЬКО через E-Taxes Parties mapped_party
sender = invoice_data.get('sender', {})
sender_name = sender.get('name', '')
sender_tin = sender.get('tin', '')
sender_name = (sender.get('name') or '').strip() # ИСПРАВЛЕНО: безопасная обработка None
sender_tin = (sender.get('tin') or '').strip() # ИСПРАВЛЕНО: безопасная обработка None
# Ищем E-Taxes Parties документ
# Дополнительная нормализация для имени отправителя
sender_name = ' '.join(sender_name.split()) # Убираем лишние пробелы
# Ищем E-Taxes Parties документ по имени и TIN
supplier_found = False
if sender_name and sender_tin:
etaxes_parties = frappe.get_all('E-Taxes Parties',
filters={
'etaxes_party_name': sender_name,
'etaxes_tax_id': sender_tin
},
fields=['name'],
limit=1)
if etaxes_parties:
etaxes_party_name = etaxes_parties[0].name
# Ищем маппинг для этого документа
for mapping in settings.party_mappings:
if (mapping.etaxes_party_name == sender_name and
mapping.etaxes_tax_id == sender_tin and
mapping.erp_party):
po.supplier = mapping.erp_party
supplier_found = True
break
mapped_supplier = None
if not supplier_found:
if sender_name and sender_tin:
# Ищем точное соответствие по имени и TIN
etaxes_party = frappe.db.get_value('E-Taxes Parties',
filters={
'etaxes_party_name': sender_name,
'etaxes_tax_id': sender_tin
},
fieldname='mapped_party')
if etaxes_party:
mapped_supplier = etaxes_party
supplier_found = True
# Если не найдено по TIN и имени, пробуем только по имени
if not supplier_found and sender_name:
etaxes_party = frappe.db.get_value('E-Taxes Parties',
filters={'etaxes_party_name': sender_name},
fieldname='mapped_party')
if etaxes_party:
mapped_supplier = etaxes_party
supplier_found = True
if not supplier_found or not mapped_supplier:
return {
'success': False,
'unmatched_parties': [{
@ -2339,6 +2325,8 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
'message': f'No mapping found for supplier: {sender_name}'
}
po.supplier = mapped_supplier
# Set dates
po.transaction_date = frappe.utils.today()
@ -2366,62 +2354,71 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
else:
date_to_use = frappe.utils.today()
# Variable for tracking added items
# Variables for tracking added items and unmapped entities
added_items_count = 0
unmatched_items = []
unmatched_units = []
# Add items from invoice
# Get serial number for fallback item codes
serial_number = invoice_data.get("serialNumber") or invoice_data.get("number", "")
# Add items from invoice - используем ТОЛЬКО mapped поля с нормализацией
if invoice_data.get("items"):
for item in invoice_data.get("items", []):
item_name = item.get("productName", "")
item_code = item.get("itemId", "")
# ИСПРАВЛЕНО: безопасная обработка None значений
item_name = (item.get("productName") or '').strip() # Убираем пробелы и \n
# ИСПРАВЛЕНО: Ищем E-Taxes Item по названию товара, затем маппинг по ID документа
# ИСПРАВЛЕНО: безопасная обработка itemId когда оно None
item_id_raw = item.get("itemId")
if item_id_raw is not None:
item_code = str(item_id_raw).strip()
else:
# Если itemId отсутствует, используем serial number инвойса
item_code = serial_number
unit_name = (item.get("unit") or '').strip()
# Дополнительная очистка от невидимых символов
item_name = ' '.join(item_name.split()) # Убираем лишние пробелы
unit_name = ' '.join(unit_name.split()) # Убираем лишние пробелы
# Поиск mapped_item в E-Taxes Item ТОЛЬКО по etaxes_item_name
mapped_item = None
etaxes_items = frappe.get_all('E-Taxes Item',
filters={'etaxes_item_name': item_name},
fields=['name'],
limit=1)
if etaxes_items:
etaxes_item_name = etaxes_items[0].name
mapped_item = item_mappings.get(etaxes_item_name)
if item_name:
mapped_item = frappe.db.get_value('E-Taxes Item',
{'etaxes_item_name': item_name},
'mapped_item')
if not mapped_item:
unmatched_items.append({
'name': item_name,
'code': item_code
'code': item_code,
'original_name': item.get("productName", ""), # Для отладки
'has_item_id': item_id_raw is not None # Дополнительная информация для отладки
})
continue
# ИСПРАВЛЕНО: Для единиц измерения
unit_name = item.get("unit", "")
# Поиск mapped_unit в E-Taxes Unit ТОЛЬКО по etaxes_unit_name
mapped_uom = None
if unit_name:
# Ищем E-Taxes Unit по названию единицы, затем маппинг по ID документа
etaxes_units = frappe.get_all('E-Taxes Unit',
filters={'etaxes_unit_name': unit_name},
fields=['name'],
limit=1)
if etaxes_units:
etaxes_unit_name = etaxes_units[0].name
mapped_uom = unit_mappings.get(etaxes_unit_name)
mapped_uom = frappe.db.get_value('E-Taxes Unit',
{'etaxes_unit_name': unit_name},
'mapped_unit')
if not mapped_uom and unit_name:
unmatched_units.append({
'name': unit_name,
'original_name': item.get("unit", "") # Для отладки
})
continue
# Если единица измерения не указана в инвойсе, используем stock_uom товара
if not mapped_uom:
if unit_name:
unmatched_units.append({
'name': unit_name
})
# Используем UOM из настроек товара
try:
item_doc = frappe.get_doc('Item', mapped_item)
mapped_uom = item_doc.stock_uom
except:
mapped_uom = "Nos"
mapped_uom = "Nos" # Fallback только если единица не указана в инвойсе
# Add position to PO
po_item = frappe.new_doc("Purchase Order Item")
@ -2436,8 +2433,8 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
po_item.item_name = item_doc.item_name
po_item.description = item_doc.description or item_doc.item_name
except:
po_item.item_name = item.get("productName", "")
po_item.description = item.get("productName", "")
po_item.item_name = item_name
po_item.description = item_name
po_item.qty = item.get("quantity", 0)
po_item.rate = item.get("pricePerUnit", 0)
@ -2490,7 +2487,8 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
# Создаем E-Taxes Purchase и устанавливаем связь ДО submit'а
invoice_id = invoice_data.get('id', '')
serial_number = invoice_data.get('serialNumber', '')
sender_name = invoice_data.get('sender', {}).get('name', '') if invoice_data.get('sender') else ''
sender_name = (invoice_data.get('sender', {}).get('name') or '').strip() if invoice_data.get('sender') else ''
sender_name = ' '.join(sender_name.split()) # Нормализация
total = invoice_data.get('totalAmount', 0) or invoice_data.get('amount', 0)
etaxes_purchase_result = create_etaxes_purchase(invoice_id, date_to_use, sender_name, total)
@ -2547,7 +2545,7 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
@frappe.whitelist()
def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
"""Loading items from invoices for a period with duplicate prevention"""
"""Loading items from invoices for a period with duplicate prevention and normalization"""
# Записываем активность
record_etaxes_activity()
@ -2632,21 +2630,34 @@ def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
items = invoice_details.get('items', [])
for item in items:
item_name = item.get('productName', '')
item_code = item.get('itemId', '') or serial_number
# ИСПРАВЛЕНО: безопасная обработка None значений
item_name = (item.get('productName') or '').strip()
# ИСПРАВЛЕНО: безопасная обработка itemId когда оно None
item_id_raw = item.get('itemId')
if item_id_raw is not None:
item_code = str(item_id_raw).strip()
else:
# Если itemId отсутствует, используем serial number инвойса
item_code = serial_number
unit_name = (item.get('unit') or '').strip()
# Дополнительная очистка от невидимых символов
item_name = ' '.join(item_name.split()) # Убираем лишние пробелы
unit_name = ' '.join(unit_name.split()) # Убираем лишние пробелы
# Skip empty items
if not item_name:
failed_count += 1
continue
# ИСПРАВЛЕНО: Проверяем существование записи с таким именем
# Так как autoname = "field:etaxes_item_name", то name документа = etaxes_item_name
# Проверяем существование записи с таким именем
if frappe.db.exists('E-Taxes Item', item_name):
skipped_count += 1
continue
# Используем имя товара как ключ для предотвращения дубликатов в одной загрузке
# Используем нормализованное имя товара как ключ для предотвращения дубликатов в одной загрузке
if item_name in unique_items:
skipped_count += 1
continue
@ -2655,7 +2666,7 @@ def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
unique_items[item_name] = {
'etaxes_item_name': item_name,
'etaxes_item_code': item_code,
'etaxes_unit': item.get('unit', ''),
'etaxes_unit': unit_name,
'etaxes_price': item.get('pricePerUnit', 0),
'source_invoice': serial_number,
'status': 'New'
@ -2699,7 +2710,7 @@ def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
# Helper functions
@frappe.whitelist()
@ -3383,10 +3394,9 @@ def create_unmapped_units(settings_name):
"message": "An unknown error occurred, please try again in a few minutes."
}
@frappe.whitelist()
def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
"""Loading units from invoices for a period with duplicate prevention"""
"""Loading units from invoices for a period with duplicate prevention and normalization"""
# Записываем активность
record_etaxes_activity()
@ -3471,13 +3481,17 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
items = invoice_details.get('items', [])
for item in items:
unit_name = item.get('unit', '')
# ИСПРАВЛЕНО: безопасная обработка None значений
unit_name = (item.get('unit') or '').strip()
# Дополнительная очистка от невидимых символов
unit_name = ' '.join(unit_name.split()) # Убираем лишние пробелы
# Skip empty units
if not unit_name:
continue
# ИСПРАВЛЕНИЕ: Проверяем существование записи с таким именем
# Проверяем существование записи с таким именем
if frappe.db.exists('E-Taxes Unit', {'etaxes_unit_name': unit_name}):
skipped_count += 1
continue
@ -3535,8 +3549,7 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
# Обработчик для обновления статусов сопоставленных элементов
@frappe.whitelist()
def update_mapped_statuses(doc, method=None):