fixed bugs
This commit is contained in:
parent
49aad69efb
commit
7b92c6a062
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in New Issue