fixed bug with sales order not importing
This commit is contained in:
parent
70bf3dd2a3
commit
a198d2a59b
|
|
@ -200,7 +200,7 @@ def get_sales_invoice_details(token, invoice_id):
|
|||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, schedule_date=None):
|
||||
def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, schedule_date=None, warehouse=None):
|
||||
"""Import sales invoice taking into account mappings from E-Taxes Item/Party/Unit mapped fields only"""
|
||||
# Записываем активность
|
||||
record_etaxes_activity()
|
||||
|
|
@ -210,6 +210,57 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
if isinstance(invoice_data, str):
|
||||
invoice_data = json.loads(invoice_data)
|
||||
|
||||
# Get active settings (импортируем функцию из api.py)
|
||||
from invoice_az.api import 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:
|
||||
# mapping.etaxes_item_name содержит name документа E-Taxes Item
|
||||
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:
|
||||
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)
|
||||
|
||||
# Get default warehouse
|
||||
default_warehouse = warehouse
|
||||
|
||||
if not default_warehouse:
|
||||
# If warehouse not specified, try to get from settings
|
||||
if hasattr(settings, 'default_warehouse') and settings.default_warehouse:
|
||||
default_warehouse = settings.default_warehouse
|
||||
else:
|
||||
# If warehouse not specified in settings, try to get default warehouse for company
|
||||
company = frappe.defaults.get_user_default('Company')
|
||||
if company:
|
||||
default_warehouse = frappe.db.get_value('Company', company, 'default_warehouse')
|
||||
|
||||
# If still no warehouse, take first active 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
|
||||
|
||||
# Check warehouse existence
|
||||
if not default_warehouse:
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'Default warehouse not found. Please specify warehouse in E-Taxes settings.'
|
||||
}
|
||||
|
||||
# Get or create Sales Order
|
||||
if sales_order_name:
|
||||
so = frappe.get_doc('Sales Order', sales_order_name)
|
||||
|
|
@ -274,108 +325,291 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
# ДОБАВЛЕНО: Устанавливаем галочку is_taxes_doc
|
||||
so.is_taxes_doc = 1
|
||||
|
||||
# Process items
|
||||
items_data = invoice_data.get('items', [])
|
||||
# Clear existing items if need to update them completely
|
||||
if sales_order_name and invoice_data.get("items"):
|
||||
so.items = []
|
||||
|
||||
date_to_use = None
|
||||
if schedule_date:
|
||||
date_to_use = schedule_date
|
||||
elif invoice_data.get("creationDate"):
|
||||
date_to_use = frappe.utils.getdate(invoice_data.get("creationDate"))
|
||||
else:
|
||||
date_to_use = frappe.utils.today()
|
||||
|
||||
# Variable for tracking added items
|
||||
added_items_count = 0
|
||||
unmatched_items = []
|
||||
unmatched_units = []
|
||||
|
||||
# Process items from invoice - ИСПРАВЛЕНО: правильная обработка структуры данных
|
||||
items_data = invoice_data.get('items', [])
|
||||
|
||||
for item_data in items_data:
|
||||
# Ищем E-Taxes Item через mapped_item
|
||||
item_name = (item_data.get('name') or '').strip()
|
||||
item_code = (item_data.get('code') or '').strip()
|
||||
# ИСПРАВЛЕНО: Используем правильные поля из структуры E-Taxes
|
||||
item_name = item_data.get('productName', '').strip() # Используем productName
|
||||
item_code = item_data.get('itemId', '').strip() # Используем itemId
|
||||
|
||||
mapped_item = None
|
||||
item_found = False
|
||||
# Пропускаем пустые товары
|
||||
if not item_name:
|
||||
unmatched_items.append({
|
||||
'name': 'Item with empty name',
|
||||
'code': item_code
|
||||
})
|
||||
continue
|
||||
|
||||
# Попробуем найти по коду и имени
|
||||
if item_code and item_name:
|
||||
etaxes_item = frappe.db.get_value('E-Taxes Item',
|
||||
filters={
|
||||
'etaxes_item_name': item_name,
|
||||
'etaxes_item_code': item_code
|
||||
},
|
||||
fieldname='mapped_item')
|
||||
if etaxes_item:
|
||||
mapped_item = etaxes_item
|
||||
item_found = True
|
||||
# ИСПРАВЛЕНО: Ищем соответствие товара по productName
|
||||
# Так как autoname = "field:etaxes_item_name", name документа E-Taxes Item = etaxes_item_name
|
||||
# Поэтому проверяем напрямую по item_name в mappings
|
||||
mapped_item = item_mappings.get(item_name)
|
||||
|
||||
# Если не найдено, попробуем только по имени
|
||||
if not item_found and item_name:
|
||||
etaxes_item = frappe.db.get_value('E-Taxes Item',
|
||||
filters={'etaxes_item_name': item_name},
|
||||
fieldname='mapped_item')
|
||||
if etaxes_item:
|
||||
mapped_item = etaxes_item
|
||||
item_found = True
|
||||
|
||||
if not item_found:
|
||||
if not mapped_item:
|
||||
# Если соответствие не найдено, добавляем в список несопоставленных
|
||||
unmatched_items.append({
|
||||
'name': item_name,
|
||||
'code': item_code
|
||||
})
|
||||
continue
|
||||
|
||||
# Ищем UoM через E-Taxes Unit mapped_unit
|
||||
unit_name = (item_data.get('unit', {}).get('name') or '').strip()
|
||||
# Для единиц измерения ищем в E-Taxes Unit
|
||||
unit_name = item_data.get('unit', '').strip() # ИСПРАВЛЕНО: правильное поле
|
||||
mapped_uom = None
|
||||
|
||||
if unit_name:
|
||||
etaxes_unit = frappe.db.get_value('E-Taxes Unit',
|
||||
filters={'etaxes_unit_name': unit_name},
|
||||
fieldname='mapped_unit')
|
||||
if etaxes_unit:
|
||||
mapped_uom = etaxes_unit
|
||||
else:
|
||||
unmatched_units.append({'name': unit_name})
|
||||
# Используем default UoM для товара
|
||||
mapped_uom = frappe.db.get_value('Item', mapped_item, 'stock_uom')
|
||||
# Ищем существующую запись E-Taxes Unit и берем mapped_unit
|
||||
try:
|
||||
mapped_uom = frappe.db.get_value('E-Taxes Unit',
|
||||
filters={'etaxes_unit_name': unit_name},
|
||||
fieldname='mapped_unit')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Add item to sales order
|
||||
item_row = so.append('items', {})
|
||||
item_row.item_code = mapped_item
|
||||
item_row.qty = item_data.get('quantity', 1)
|
||||
item_row.rate = item_data.get('price', 0)
|
||||
item_row.delivery_date = so.delivery_date
|
||||
# Если соответствие единицы не найдено, используем UOM из товара или настроек
|
||||
if not mapped_uom:
|
||||
if unit_name:
|
||||
# Добавляем в список несопоставленных единиц
|
||||
unmatched_units.append({
|
||||
'name': unit_name
|
||||
})
|
||||
|
||||
# Используем UOM из настроек товара или default
|
||||
try:
|
||||
item_doc = frappe.get_doc('Item', mapped_item)
|
||||
mapped_uom = item_doc.stock_uom
|
||||
except:
|
||||
# Fallback на default_uom из настроек
|
||||
mapped_uom = settings.default_uom if settings.default_uom else "Nos"
|
||||
|
||||
if mapped_uom:
|
||||
item_row.uom = mapped_uom
|
||||
# Add position to SO
|
||||
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:
|
||||
# Fallback на название из E-Taxes если не удалось получить из базы
|
||||
so_item.item_name = item_name
|
||||
so_item.description = item_name
|
||||
|
||||
so_item.qty = item_data.get('quantity', 1)
|
||||
so_item.rate = item_data.get('pricePerUnit', 0) # ИСПРАВЛЕНО: правильное поле
|
||||
so_item.amount = item_data.get('cost', 0)
|
||||
so_item.uom = mapped_uom
|
||||
|
||||
# IMPORTANT: Set delivery_date for each row
|
||||
so_item.delivery_date = date_to_use
|
||||
|
||||
# IMPORTANT: Set default warehouse
|
||||
so_item.warehouse = default_warehouse
|
||||
|
||||
so.append("items", so_item)
|
||||
added_items_count += 1
|
||||
|
||||
# Check for unmatched items/units
|
||||
# Check if there are unmapped items
|
||||
if unmatched_items:
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'Item mappings not found for {len(unmatched_items)} items',
|
||||
'unmatched_items': unmatched_items
|
||||
'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,
|
||||
'message': f'Unit mappings not found for {len(unmatched_units)} units',
|
||||
'unmatched_units': unmatched_units
|
||||
'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 SO. Invoice data: {invoice_data}",
|
||||
"Import Sales Invoice Error"
|
||||
)
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'Could not add any items to order'
|
||||
}
|
||||
|
||||
# Save sales order
|
||||
if not sales_order_name:
|
||||
so.insert(ignore_permissions=True)
|
||||
so.submit()
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'Sales Order {so.name} created successfully',
|
||||
'sales_order': so.name
|
||||
}
|
||||
else:
|
||||
# Save document
|
||||
so.save()
|
||||
|
||||
# Additional check that all delivery_date and warehouse are set
|
||||
for item in so.items:
|
||||
if not item.delivery_date:
|
||||
item.delivery_date = date_to_use
|
||||
if not item.warehouse:
|
||||
item.warehouse = default_warehouse
|
||||
|
||||
# Save again to ensure changes are applied
|
||||
so.save()
|
||||
|
||||
# ИЗМЕНЕНИЕ: Создаем 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
|
||||
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:
|
||||
# Если не удалось создать Sales Invoice, всё равно возвращаем успех с Sales Order
|
||||
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")
|
||||
# Если не удалось создать Sales Invoice, всё равно возвращаем успех с Sales Order
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'Sales Order {so.name} updated successfully',
|
||||
'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()}", "Sales Invoice Import Error")
|
||||
frappe.log_error(f"Error in import_sales_invoice_with_mapping: {str(e)}\n{frappe.get_traceback()}", "Import Sales Error")
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'Error importing sales invoice: {str(e)}'
|
||||
'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 Invoice из Sales Order
|
||||
si_doc = make_sales_invoice(sales_order_name)
|
||||
|
||||
# Устанавливаем даты
|
||||
si_doc.posting_date = frappe.utils.nowdate()
|
||||
si_doc.due_date = frappe.utils.add_days(frappe.utils.nowdate(), 30) # 30 дней для оплаты
|
||||
|
||||
# ДОБАВЛЕНО: Устанавливаем галочку is_taxes_doc для Sales Invoice
|
||||
si_doc.is_taxes_doc = 1
|
||||
|
||||
# Сохраняем документ
|
||||
si_doc.insert(ignore_permissions=True)
|
||||
|
||||
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."
|
||||
}
|
||||
Loading…
Reference in New Issue