fixed a bunch of bugs

This commit is contained in:
Ali 2025-05-26 14:43:08 +04:00
parent 6e4c8020b0
commit 56b24bc644
8 changed files with 805 additions and 490 deletions

View File

@ -1213,8 +1213,8 @@ def get_uom_for_unit(unit_name):
return "Nos"
@frappe.whitelist()
def load_items_from_invoices(date_from, date_to, max_count=200):
"""Loading items from invoices for a period"""
def load_items_from_invoices(date_from, date_to, max_count=200, offset=0):
"""Loading items from invoices for a period with duplicate prevention"""
# Записываем активность
record_etaxes_activity()
@ -1239,7 +1239,7 @@ def load_items_from_invoices(date_from, date_to, max_count=200):
except (ValueError, TypeError):
max_count = 200
# Format filters for the API request - изменено: только approved и approvedBySystem статусы
# Format filters for the API request
filters = {
"actionOwner": None,
"amountFrom": None,
@ -1248,7 +1248,7 @@ def load_items_from_invoices(date_from, date_to, max_count=200):
"creationDateTo": date_to,
"kinds": ["defaultInvoice", "agent", "resale", "recycling", "taxCodex163", "taxCodex177_5", "returnInvoice", "returnByAgent", "returnRecycled", "exportNoteInvoice", "exciseGoodsTransfer", "advanceInvoice"],
"maxCount": max_count,
"offset": 0,
"offset": offset,
"productCode": None,
"productName": None,
"receiverName": None,
@ -1258,7 +1258,7 @@ def load_items_from_invoices(date_from, date_to, max_count=200):
"serialNumber": None,
"sortAsc": True,
"sortBy": "creationDate",
"statuses": ["approved", "approvedBySystem"], # Только утвержденные документы
"statuses": ["approved", "approvedBySystem"],
"types": ["current", "corrected"]
}
@ -1267,14 +1267,24 @@ def load_items_from_invoices(date_from, date_to, max_count=200):
if 'error' in response:
return {
'success': False,
'error': response.get('error'),
'message': response['message'] if 'message' in response else 'Failed to retrieve invoices'
}
# Extract data from response
items_data = response.get('data', []) or response.get('invoices', [])
# Check if more data is available
hasMore = False
if 'hasMore' in response:
hasMore = response.get('hasMore')
elif response.get('total', 0) > offset + max_count:
hasMore = True
# Get details for each invoice
processed_count = 0
unique_items = {} # Используем словарь для предотвращения дубликатов
for invoice in items_data:
invoice_id = invoice.get('id', '')
if invoice_id:
@ -1297,46 +1307,69 @@ def load_items_from_invoices(date_from, date_to, max_count=200):
failed_count += 1
continue
# Check if such an element already exists
if frappe.db.exists('E-Taxes Item', {'etaxes_item_name': item_name}):
# ИСПРАВЛЕНИЕ: Проверяем существование записи с таким именем
if frappe.db.exists('E-Taxes Item', item_name):
skipped_count += 1
continue
# Create new element
try:
doc = frappe.get_doc({
'doctype': 'E-Taxes Item',
'etaxes_item_name': item_name,
'etaxes_item_code': item_code,
'etaxes_unit': item.get('unit', ''),
'etaxes_price': item.get('pricePerUnit', 0),
'source_invoice': serial_number,
'status': 'New'
})
doc.insert(ignore_permissions=True)
created_count += 1
except Exception as e:
failed_count += 1
# Используем имя товара как ключ для предотвращения дубликатов в одной загрузке
if item_name in unique_items:
skipped_count += 1
continue
# Добавляем в словарь уникальных товаров
unique_items[item_name] = {
'etaxes_item_name': item_name,
'etaxes_item_code': item_code,
'etaxes_unit': item.get('unit', ''),
'etaxes_price': item.get('pricePerUnit', 0),
'source_invoice': serial_number,
'status': 'New'
}
processed_count += 1
# Создаём записи для уникальных товаров
for item_name, item_data in unique_items.items():
try:
# Двойная проверка перед созданием (на случай конкурентного доступа)
if frappe.db.exists('E-Taxes Item', item_name):
skipped_count += 1
continue
doc = frappe.get_doc({
'doctype': 'E-Taxes Item',
**item_data
})
doc.insert(ignore_permissions=True)
created_count += 1
except frappe.DuplicateEntryError:
# Если всё-таки произошло дублирование, считаем как пропущенный
skipped_count += 1
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Item {item_name}: {str(e)}", "Load Items Error")
failed_count += 1
return {
'success': True,
'created_count': created_count,
'skipped_count': skipped_count,
'failed_count': failed_count,
'total_invoices': processed_count
'total_invoices': processed_count,
'hasMore': hasMore
}
except Exception as e:
frappe.log_error(f"Error in load_items_from_invoices: {str(e)}\n{frappe.get_traceback()}", "Load Items Error")
return {
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
@frappe.whitelist()
def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="purchase"):
"""Loading parties from invoices for a period"""
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"""
# Записываем активность
record_etaxes_activity()
@ -1361,7 +1394,7 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
except (ValueError, TypeError):
max_count = 200
# Format filters for the API request - изменено: только approved и approvedBySystem статусы
# Format filters for the API request
filters = {
"actionOwner": None,
"amountFrom": None,
@ -1370,7 +1403,7 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
"creationDateTo": date_to,
"kinds": ["defaultInvoice", "agent", "resale", "recycling", "taxCodex163", "taxCodex177_5", "returnInvoice", "returnByAgent", "returnRecycled", "exportNoteInvoice", "exciseGoodsTransfer", "advanceInvoice"],
"maxCount": max_count,
"offset": 0,
"offset": offset,
"productCode": None,
"productName": None,
"receiverName": None,
@ -1380,7 +1413,7 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
"serialNumber": None,
"sortAsc": True,
"sortBy": "creationDate",
"statuses": ["approved", "approvedBySystem"], # Только утвержденные документы
"statuses": ["approved", "approvedBySystem"],
"types": ["current", "corrected"]
}
@ -1389,12 +1422,20 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
if 'error' in response:
return {
'success': False,
'error': response.get('error'),
'message': response['message'] if 'message' in response else 'Failed to retrieve invoices'
}
# Extract data from response
invoices_data = response.get('data', []) or response.get('invoices', [])
# Check if more data is available
hasMore = False
if 'hasMore' in response:
hasMore = response.get('hasMore')
elif response.get('total', 0) > offset + max_count:
hasMore = True
if len(invoices_data) == 0:
return {
'success': True,
@ -1402,11 +1443,13 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
'skipped_count': 0,
'failed_count': 0,
'total_invoices': 0,
'unique_parties': 0,
'hasMore': False,
'message': 'No invoices found for the specified period'
}
# For storing unique parties (suppliers and customers)
unique_parties = {}
unique_parties = {} # Используем словарь для предотвращения дубликатов
# Process each invoice
processed_count = 0
@ -1427,7 +1470,17 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
if not sender_name or not sender_tin:
continue
# Create key for uniqueness check
# ИСПРАВЛЕНИЕ: Проверяем существование записи
existing = frappe.db.exists('E-Taxes Parties', {
'etaxes_party_name': sender_name,
'etaxes_tax_id': sender_tin
})
if existing:
skipped_count += 1
continue
# Create key for uniqueness check (в пределах одной загрузки)
sender_key = f"{sender_tin}|{sender_name}"
if sender_key not in unique_parties:
@ -1453,7 +1506,17 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
if not receiver_name or not receiver_tin:
continue
# Create key for uniqueness check
# ИСПРАВЛЕНИЕ: Проверяем существование записи
existing = frappe.db.exists('E-Taxes Parties', {
'etaxes_party_name': receiver_name,
'etaxes_tax_id': receiver_tin
})
if existing:
skipped_count += 1
continue
# Create key for uniqueness check (в пределах одной загрузки)
receiver_key = f"{receiver_tin}|{receiver_name}"
if receiver_key not in unique_parties:
@ -1467,34 +1530,31 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
'status': 'New'
}
# Add parties to the system
# Создаём записи для уникальных контрагентов
for party_key, party_data in unique_parties.items():
party_name = party_data.get('etaxes_party_name')
party_tin = party_data.get('etaxes_tax_id')
# Check if such party already exists
existing = frappe.db.exists('E-Taxes Parties', {'etaxes_party_name': party_name})
if existing:
skipped_count += 1
continue
# Create new party
try:
# Двойная проверка перед созданием (на случай конкурентного доступа)
existing = frappe.db.exists('E-Taxes Parties', {
'etaxes_party_name': party_data['etaxes_party_name'],
'etaxes_tax_id': party_data['etaxes_tax_id']
})
if existing:
skipped_count += 1
continue
doc = frappe.get_doc({
'doctype': 'E-Taxes Parties',
'etaxes_party_name': party_name,
'etaxes_tax_id': party_tin,
'etaxes_address': party_data.get('etaxes_address', ''),
'etaxes_party_type': party_data.get('etaxes_party_type', 'Sender'),
'erp_party_type': party_data.get('erp_party_type', 'Supplier'),
'source_invoice': party_data.get('source_invoice', ''),
'status': 'New'
**party_data
})
doc.insert(ignore_permissions=True)
created_count += 1
except frappe.DuplicateEntryError:
# Если всё-таки произошло дублирование, считаем как пропущенный
skipped_count += 1
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Party {party_data['etaxes_party_name']}: {str(e)}", "Load Parties Error")
failed_count += 1
return {
@ -1503,10 +1563,12 @@ def load_parties_from_invoices(date_from, date_to, max_count=200, invoice_type="
'skipped_count': skipped_count,
'failed_count': failed_count,
'total_invoices': processed_count,
'unique_parties': len(unique_parties)
'unique_parties': len(unique_parties),
'hasMore': hasMore
}
except Exception as e:
frappe.log_error(f"Error in load_parties_from_invoices: {str(e)}\n{frappe.get_traceback()}", "Load Parties Error")
return {
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
@ -2190,23 +2252,24 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
'message': 'No active E-Taxes settings found'
}
# Create mapping dictionaries with case-insensitive keys
# Create mapping dictionaries
item_mappings = {}
for mapping in settings.item_mappings:
key = f"{mapping.etaxes_item_name.lower()}|{mapping.etaxes_item_code.lower() if mapping.etaxes_item_code else ''}"
item_mappings[key] = mapping.erp_item
# 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:
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)
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)
unit_mappings = {}
for mapping in settings.unit_mappings:
if mapping.etaxes_unit_name and mapping.erp_unit:
# Get the unit name from E-Taxes Unit record
etaxes_unit = frappe.get_doc('E-Taxes Unit', mapping.etaxes_unit_name)
unit_mappings[etaxes_unit.etaxes_unit_name.lower()] = mapping.erp_unit
# mapping.etaxes_unit_name содержит name документа E-Taxes Unit
unit_mappings[mapping.etaxes_unit_name] = mapping.erp_unit
# Get default warehouse
default_warehouse = warehouse
@ -2248,19 +2311,9 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
sender = invoice_data.get('sender', {})
sender_key = f"{sender.get('name', '').lower()}|{sender.get('tin', '').lower()}"
if sender_key in party_mappings:
if sender_key in party_mappings and party_mappings[sender_key][0]:
po.supplier = party_mappings[sender_key][0]
else:
# Add supplier to unmapped list
# First save party information if it doesn't exist yet
if not frappe.db.exists('E-Taxes Parties', {'etaxes_party_name': sender.get('name', ''), 'etaxes_tax_id': sender.get('tin', '')}):
etaxes_party = frappe.new_doc('E-Taxes Parties')
etaxes_party.etaxes_party_name = sender.get('name', '')
etaxes_party.etaxes_tax_id = sender.get('tin', '')
etaxes_party.etaxes_party_type = 'Sender'
etaxes_party.status = 'New'
etaxes_party.insert()
return {
'success': False,
'unmatched_parties': [{
@ -2308,108 +2361,46 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
# Add items from invoice
if invoice_data.get("items"):
for item in invoice_data.get("items", []):
# Prepare key for mapping search (case-insensitive)
# Подготавливаем данные товара
item_name = item.get("productName", "")
item_code = item.get("itemId", "")
item_key = f"{item_name.lower()}|{item_code.lower()}"
# Look for mapping
mapped_item = None
# ИСПРАВЛЕНО: Ищем соответствие товара
# Так как autoname = "field:etaxes_item_name", name документа E-Taxes Item = etaxes_item_name
# Поэтому проверяем напрямую по item_name в mappings
mapped_item = item_mappings.get(item_name)
# First look in mappings
if item_key in item_mappings:
mapped_item = item_mappings[item_key]
else:
# Try to find item by code if specified (case-insensitive)
if item_code:
item_list = frappe.get_all('Item',
filters=[['item_code', 'like', f'%{item_code}%']],
limit=1)
if not item_list:
# Try exact match but case-insensitive
item_list = frappe.db.sql("""
SELECT name FROM tabItem
WHERE LOWER(item_code) = %s
LIMIT 1
""", (item_code.lower(),), as_dict=True)
if item_list:
mapped_item = item_list[0].name if 'name' in item_list[0] else item_list[0]['name']
# If not found by code, look by name (case-insensitive)
if not mapped_item:
item_list = frappe.db.sql("""
SELECT name FROM tabItem
WHERE LOWER(item_name) = %s
LIMIT 1
""", (item_name.lower(),), as_dict=True)
if item_list:
mapped_item = item_list[0]['name']
# If not found by code or name
if not mapped_item:
# Save item information if it doesn't exist yet
if not frappe.db.exists('E-Taxes Item', {'etaxes_item_name': item_name, 'etaxes_item_code': item_code}):
etaxes_item = frappe.new_doc('E-Taxes Item')
etaxes_item.etaxes_item_name = item_name
etaxes_item.etaxes_item_code = item_code
etaxes_item.etaxes_unit = item.get('unit', '')
etaxes_item.etaxes_price = item.get('pricePerUnit', 0)
etaxes_item.status = 'New'
etaxes_item.insert()
# Add to unmapped list
# Если соответствие не найдено, добавляем в список несопоставленных
unmatched_items.append({
'name': item_name,
'code': item_code
})
continue # Move to next element
continue
# Get the UOM from unit mapping or fallback (case-insensitive)
# ИСПРАВЛЕНО: Для единиц измерения
unit_name = item.get("unit", "")
mapped_uom = None
# Check if there's a mapping for this unit (case-insensitive)
if unit_name.lower() in unit_mappings:
mapped_uom = unit_mappings[unit_name.lower()]
else:
# If no direct mapping, check if UOM exists with exact name (case-insensitive)
uom_list = frappe.db.sql("""
SELECT name FROM tabUOM
WHERE LOWER(uom_name) = %s
LIMIT 1
""", (unit_name.lower(),), as_dict=True)
if uom_list:
mapped_uom = uom_list[0]['name']
# Save the unit to E-Taxes Unit if it doesn't exist yet
if unit_name and not frappe.db.exists('E-Taxes Unit', {'etaxes_unit_name': unit_name}):
# Generate a consistent code for the unit
unit_code = "UNIT-" + ''.join(e for e in unit_name if e.isalnum()).upper()
etaxes_unit = frappe.new_doc('E-Taxes Unit')
etaxes_unit.etaxes_unit_name = unit_name
etaxes_unit.etaxes_unit_code = unit_code
etaxes_unit.source_invoice = invoice_data.get('serialNumber', '')
etaxes_unit.status = 'New'
try:
etaxes_unit.insert(ignore_permissions=True)
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Unit: {str(e)}", "Import Invoice Error")
# Add to unmapped list if unit is present
if unit_name:
# Так как autoname = "field:etaxes_unit_name", name документа E-Taxes Unit = etaxes_unit_name
# Поэтому проверяем напрямую по unit_name в mappings
mapped_uom = unit_mappings.get(unit_name)
# Если соответствие единицы не найдено, получаем UOM из товара
if not mapped_uom:
if unit_name:
# Добавляем в список несопоставленных единиц
unmatched_units.append({
'name': unit_name
})
# If no mapping found, use default UOM from item
if not mapped_uom:
# Try to get default UOM from item master
item_doc = frappe.get_doc('Item', mapped_item)
mapped_uom = item_doc.stock_uom
# Используем UOM из настроек товара
try:
item_doc = frappe.get_doc('Item', mapped_item)
mapped_uom = item_doc.stock_uom
except:
mapped_uom = "Nos" # Fallback
# Add position to PO
po_item = frappe.new_doc("Purchase Order Item")
@ -2423,7 +2414,7 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
po_item.qty = item.get("quantity", 0)
po_item.rate = item.get("pricePerUnit", 0)
po_item.amount = item.get("cost", 0)
po_item.uom = mapped_uom # Use the mapped UOM
po_item.uom = mapped_uom
# IMPORTANT: Set schedule_date for each row
po_item.schedule_date = date_to_use
@ -2537,7 +2528,164 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
# Также нужно убрать создание новых записей из других функций:
@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"""
# Записываем активность
record_etaxes_activity()
try:
# Counters for tracking
created_count = 0
skipped_count = 0
failed_count = 0
# Get default settings
asan_login_settings = get_default_asan_login()
if not asan_login_settings.get('found'):
return {
'success': False,
'message': 'No Asan Login settings found'
}
# Ensure max_count is an integer
try:
max_count = int(max_count)
except (ValueError, TypeError):
max_count = 200
# Format filters for the API request
filters = {
"actionOwner": None,
"amountFrom": None,
"amountTo": None,
"creationDateFrom": date_from,
"creationDateTo": date_to,
"kinds": ["defaultInvoice", "agent", "resale", "recycling", "taxCodex163", "taxCodex177_5", "returnInvoice", "returnByAgent", "returnRecycled", "exportNoteInvoice", "exciseGoodsTransfer", "advanceInvoice"],
"maxCount": max_count,
"offset": offset,
"productCode": None,
"productName": None,
"receiverName": None,
"receiverTin": None,
"senderName": None,
"senderTin": None,
"serialNumber": None,
"sortAsc": True,
"sortBy": "creationDate",
"statuses": ["approved", "approvedBySystem"],
"types": ["current", "corrected"]
}
response = get_invoices(asan_login_settings['main_token'], json.dumps(filters))
if 'error' in response:
return {
'success': False,
'error': response.get('error'),
'message': response['message'] if 'message' in response else 'Failed to retrieve invoices'
}
# Extract data from response
items_data = response.get('data', []) or response.get('invoices', [])
# Check if more data is available
hasMore = False
if 'hasMore' in response:
hasMore = response.get('hasMore')
elif response.get('total', 0) > offset + max_count:
hasMore = True
# Get details for each invoice
processed_count = 0
unique_items = {} # Используем словарь для предотвращения дубликатов
for invoice in items_data:
invoice_id = invoice.get('id', '')
if invoice_id:
# Get detailed information about the invoice
invoice_details = get_invoice_details(asan_login_settings['main_token'], invoice_id)
# Check for errors
if isinstance(invoice_details, dict) and 'error' in invoice_details:
continue
serial_number = invoice_details.get('serialNumber', '')
items = invoice_details.get('items', [])
for item in items:
item_name = item.get('productName', '')
item_code = item.get('itemId', '') or f"CODE-{serial_number}-{processed_count}"
# 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
# Добавляем в словарь уникальных товаров
unique_items[item_name] = {
'etaxes_item_name': item_name,
'etaxes_item_code': item_code,
'etaxes_unit': item.get('unit', ''),
'etaxes_price': item.get('pricePerUnit', 0),
'source_invoice': serial_number,
'status': 'New'
}
processed_count += 1
# Создаём записи для уникальных товаров
for item_name, item_data in unique_items.items():
try:
# Двойная проверка перед созданием (на случай конкурентного доступа)
if frappe.db.exists('E-Taxes Item', item_name):
skipped_count += 1
continue
doc = frappe.get_doc({
'doctype': 'E-Taxes Item',
**item_data
})
doc.insert(ignore_permissions=True)
created_count += 1
except frappe.DuplicateEntryError:
# Если всё-таки произошло дублирование, считаем как пропущенный
skipped_count += 1
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Item {item_name}: {str(e)}", "Load Items Error")
failed_count += 1
return {
'success': True,
'created_count': created_count,
'skipped_count': skipped_count,
'failed_count': failed_count,
'total_invoices': processed_count,
'hasMore': hasMore
}
except Exception as e:
frappe.log_error(f"Error in load_items_from_invoices: {str(e)}\n{frappe.get_traceback()}", "Load Items Error")
return {
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
# Helper functions
@frappe.whitelist()
@ -2985,11 +3133,13 @@ def get_unmapped_units():
# 3. Add only those that are not in settings
for unit in etaxes_units:
if unit.name not in existing_mappings:
# Create unit object with default settings
# ПРАВИЛЬНО: возвращаем объект с нужными полями для Link форматера
unit_data = {
'value': unit.name, # ID документа для Link поля
'label': unit.etaxes_unit_name, # Отображаемое имя
'etaxes_unit_name': unit.etaxes_unit_name, # Для link_formatter
'name': unit.name,
'etaxes_unit_name': unit.etaxes_unit_name,
'etaxes_unit_code': unit.etaxes_unit_code,
'etaxes_unit_code': unit.etaxes_unit_code
}
unmapped_units.append(unit_data)
@ -3003,7 +3153,7 @@ def get_unmapped_units():
'success': False,
'message': "An unknown error occurred, please try again in a few minutes."
}
@frappe.whitelist()
def match_similar_units():
"""Matching units by similar names"""
@ -3222,7 +3372,7 @@ def create_unmapped_units(settings_name):
@frappe.whitelist()
def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
"""Loading units from invoices for a period"""
"""Loading units from invoices for a period with duplicate prevention"""
# Записываем активность
record_etaxes_activity()
@ -3247,7 +3397,7 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
except (ValueError, TypeError):
max_count = 200
# Format filters for the API request - изменено: только approved и approvedBySystem статусы
# Format filters for the API request
filters = {
"actionOwner": None,
"amountFrom": None,
@ -3266,7 +3416,7 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
"serialNumber": None,
"sortAsc": True,
"sortBy": "creationDate",
"statuses": ["approved", "approvedBySystem"], # Только утвержденные документы
"statuses": ["approved", "approvedBySystem"],
"types": ["current", "corrected"]
}
@ -3275,6 +3425,7 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
if 'error' in response:
return {
'success': False,
'error': response.get('error'),
'message': response['message'] if 'message' in response else 'Failed to retrieve invoices'
}
@ -3290,7 +3441,7 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
# Get details for each invoice and collect unique units
processed_count = 0
unique_units = {}
unique_units = {} # Используем словарь для предотвращения дубликатов
for invoice in items_data:
invoice_id = invoice.get('id', '')
@ -3312,38 +3463,46 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
if not unit_name:
continue
# Create key for uniqueness check
if unit_name not in unique_units:
# Generate a consistent code for the unit
unit_code = "UNIT-" + ''.join(e for e in unit_name if e.isalnum()).upper()
unique_units[unit_name] = {
'etaxes_unit_name': unit_name,
'etaxes_unit_code': unit_code,
'source_invoice': serial_number
}
# ИСПРАВЛЕНИЕ: Проверяем существование записи с таким именем
if frappe.db.exists('E-Taxes Unit', {'etaxes_unit_name': unit_name}):
skipped_count += 1
continue
# Используем имя единицы как ключ для предотвращения дубликатов в одной загрузке
if unit_name in unique_units:
continue
# Generate a consistent code for the unit
unit_code = "UNIT-" + ''.join(e for e in unit_name if e.isalnum()).upper()
unique_units[unit_name] = {
'etaxes_unit_name': unit_name,
'etaxes_unit_code': unit_code,
'source_invoice': serial_number,
'status': 'New'
}
processed_count += 1
# Now create E-Taxes Unit records for each unique unit
# Создаём записи для уникальных единиц
for unit_name, unit_data in unique_units.items():
# Check if such a unit already exists
if frappe.db.exists('E-Taxes Unit', {'etaxes_unit_name': unit_name}):
skipped_count += 1
continue
# Create new unit
try:
# Двойная проверка перед созданием (на случай конкурентного доступа)
if frappe.db.exists('E-Taxes Unit', {'etaxes_unit_name': unit_name}):
skipped_count += 1
continue
doc = frappe.get_doc({
'doctype': 'E-Taxes Unit',
'etaxes_unit_name': unit_data['etaxes_unit_name'],
'etaxes_unit_code': unit_data['etaxes_unit_code'],
'source_invoice': unit_data['source_invoice'],
'status': 'New'
**unit_data
})
doc.insert(ignore_permissions=True)
created_count += 1
except frappe.DuplicateEntryError:
# Если всё-таки произошло дублирование, считаем как пропущенный
skipped_count += 1
except Exception as e:
frappe.log_error(f"Error creating E-Taxes Unit {unit_name}: {str(e)}", "Load Units Error")
failed_count += 1
return {
@ -3363,6 +3522,7 @@ def load_units_from_invoices(date_from, date_to, max_count=200, offset=0):
'message': "An unknown error occurred, please try again in a few minutes."
}
# Обработчик для обновления статусов сопоставленных элементов
def update_mapped_statuses(doc, method=None):
"""Update mapped statuses in E-Taxes DocTypes when settings are saved"""
@ -3482,4 +3642,107 @@ def update_mapped_statuses(doc, method=None):
except Exception as e:
frappe.log_error(f"Error updating mapped statuses: {str(e)}\n{frappe.get_traceback()}",
"E-Taxes Settings Error")
"E-Taxes Settings Error")
@frappe.whitelist()
def refresh_unit_mappings_display(settings_name):
"""Операция-пустышка для обновления отображения unit mappings"""
try:
# Получаем документ настроек
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
# Подготавливаем данные для обновления отображения на клиенте
updated_mappings = []
for mapping in settings_doc.unit_mappings:
if mapping.etaxes_unit_name:
# Получаем display name для Link поля
display_name = frappe.db.get_value('E-Taxes Unit', mapping.etaxes_unit_name, 'etaxes_unit_name')
updated_mappings.append({
'idx': mapping.idx,
'name': mapping.name,
'etaxes_unit_name_id': mapping.etaxes_unit_name,
'etaxes_unit_name_display': display_name or mapping.etaxes_unit_name
})
return {
'success': True,
'message': 'Display refreshed successfully',
'updated_mappings': updated_mappings
}
except Exception as e:
frappe.log_error(f"Error refreshing display: {str(e)}", "Display Refresh Error")
return {
'success': False,
'message': "An error occurred while refreshing display"
}
@frappe.whitelist()
def refresh_item_mappings_display(settings_name):
"""Операция-пустышка для обновления отображения item mappings"""
try:
# Получаем документ настроек
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
# Подготавливаем данные для обновления отображения на клиенте
updated_mappings = []
for mapping in settings_doc.item_mappings:
if mapping.etaxes_item_name:
# Получаем display name для Link поля
display_name = frappe.db.get_value('E-Taxes Item', mapping.etaxes_item_name, 'etaxes_item_name')
updated_mappings.append({
'idx': mapping.idx,
'name': mapping.name,
'etaxes_item_name_id': mapping.etaxes_item_name,
'etaxes_item_name_display': display_name or mapping.etaxes_item_name
})
return {
'success': True,
'message': 'Display refreshed successfully',
'updated_mappings': updated_mappings
}
except Exception as e:
frappe.log_error(f"Error refreshing display: {str(e)}", "Display Refresh Error")
return {
'success': False,
'message': "An error occurred while refreshing display"
}
@frappe.whitelist()
def refresh_party_mappings_display(settings_name):
"""Операция-пустышка для обновления отображения party mappings"""
try:
# Получаем документ настроек
settings_doc = frappe.get_doc("E-Taxes Settings", settings_name)
# Подготавливаем данные для обновления отображения на клиенте
updated_mappings = []
for mapping in settings_doc.party_mappings:
if mapping.etaxes_party_name:
# Для parties используем etaxes_party_name как отображаемое имя
# так как это текстовое поле, а не Link
updated_mappings.append({
'idx': mapping.idx,
'name': mapping.name,
'etaxes_party_name_display': mapping.etaxes_party_name
})
return {
'success': True,
'message': 'Display refreshed successfully',
'updated_mappings': updated_mappings
}
except Exception as e:
frappe.log_error(f"Error refreshing display: {str(e)}", "Display Refresh Error")
return {
'success': False,
'message': "An error occurred while refreshing display"
}

View File

@ -936,16 +936,16 @@ function load_etaxes_invoices(frm, fromDate, toDate, warehouse, accumulated_invo
});
}
// Функция для отображения диалога с выбором счета-фактуры
// Функция для отображения диалога с выбором счета-фактуры (широкая версия)
function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
// Создаем таблицу с инвойсами
var invoice_table = '<div style="max-height: 500px; overflow-y: auto;"><table class="table table-bordered etaxes-invoices-table">';
var invoice_table = '<div style="max-height: 500px; overflow-y: auto;"><table class="table table-bordered etaxes-invoices-table" style="width: 100%; table-layout: fixed;">';
invoice_table += '<thead><tr>' +
'<th style="width: 30px;"><input type="checkbox" class="select-all-invoices"></th>' +
'<th>' + __('Number') + '</th>' +
'<th>' + __('Date') + '</th>' +
'<th>' + __('Supplier') + '</th>' +
'<th>' + __('Amount') + '</th>' +
'<th style="width: 6%;"><input type="checkbox" class="select-all-invoices"></th>' +
'<th style="width: 24%;">' + __('Number') + '</th>' +
'<th style="width: 16%;">' + __('Date') + '</th>' +
'<th style="width: 38%;">' + __('Supplier') + '</th>' +
'<th style="width: 16%;">' + __('Amount') + '</th>' +
'</tr></thead><tbody>';
invoices.forEach(function(invoice) {
@ -964,10 +964,10 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
invoice_table += '<tr>' +
'<td><input type="checkbox" class="select-invoice" data-id="' + invoice.id + '"></td>' +
'<td>' + serialNumber + '</td>' +
'<td style="word-break: break-word;">' + serialNumber + '</td>' +
'<td>' + creationDate + '</td>' +
'<td>' + senderName + '</td>' +
'<td>' + format_currency(amount) + '</td>' +
'<td style="word-break: break-word;">' + senderName + '</td>' +
'<td style="text-align: right;">' + format_currency(amount) + '</td>' +
'</tr>';
});
@ -978,9 +978,10 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
__('New invoices found: ') + invoices.length +
'</div>';
// Создаем диалог
// Создаем диалог с оптимальной шириной
var d = new frappe.ui.Dialog({
title: __('Select invoice'),
size: 'large', // Используем large вместо extra-large
fields: [
{
fieldname: 'info_html',
@ -1031,6 +1032,18 @@ function show_invoice_selection_dialog(frm, invoices, token, warehouse) {
}
});
// Добавляем оптимизированные CSS стили
d.$wrapper.find('.modal-dialog').css({
'max-width': '80%',
'width': '80%',
'margin': '30px auto'
});
// Оптимизируем контент
d.$wrapper.find('.modal-body').css({
'padding': '15px'
});
d.show();
// Добавляем обработчик для чекбокса "выбрать все"

View File

@ -1,6 +1,6 @@
{
"actions": [],
"autoname": "field:etaxes_item_code",
"autoname": "field:etaxes_item_name",
"creation": "2025-05-01 12:00:00",
"doctype": "DocType",
"engine": "InnoDB",
@ -99,7 +99,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-05-15 14:25:13.578790",
"modified": "2025-05-23 18:34:54.024727",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Item",
@ -130,11 +130,8 @@
"write": 1
}
],
"search_fields": "etaxes_item_name",
"show_title_field_in_link": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "etaxes_item_name",
"track_changes": 1
}

View File

@ -1,105 +1,106 @@
{
"actions": [],
"creation": "2025-05-01 12:00:00",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"etaxes_item_name",
"etaxes_item_code",
"erp_item",
"mapping_type",
"item_settings_section",
"item_group",
"uom",
"is_stock_item",
"is_purchase_item",
"item_tax_template"
],
"fields": [
{
"fieldname": "etaxes_item_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "E-Taxes Item Name",
"options": "E-Taxes Item",
"reqd": 1
},
{
"fieldname": "etaxes_item_code",
"fieldtype": "Data",
"in_list_view": 0,
"hidden": 1,
"in_preview": 0,
"label": "E-Taxes Item Code",
"read_only": 1
},
{
"fieldname": "erp_item",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "System Item",
"options": "Item"
},
{
"default": "Manual",
"fieldname": "mapping_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Mapping Type",
"options": "Manual\nAutomatic"
},
{
"fieldname": "item_settings_section",
"fieldtype": "Section Break",
"label": "Item Settings"
},
{
"fieldname": "item_group",
"fieldtype": "Link",
"label": "Item Group",
"options": "Item Group",
"description": "Overrides default item group setting"
},
{
"fieldname": "uom",
"fieldtype": "Link",
"label": "Unit of Measure",
"options": "UOM",
"description": "Overrides default UOM setting"
},
{
"fieldname": "is_stock_item",
"fieldtype": "Check",
"label": "Is Stock Item",
"description": "Overrides default Is Stock Item setting"
},
{
"fieldname": "is_purchase_item",
"fieldtype": "Check",
"label": "Is Purchase Item",
"description": "Overrides default Is Purchase Item setting"
},
{
"fieldname": "item_tax_template",
"fieldtype": "Link",
"label": "Item Tax Template",
"options": "Item Tax Template",
"description": "Overrides default Item Tax Template setting"
}
],
"istable": 1,
"links": [],
"modified": "2025-05-23 12:44:49.793210",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Item Mapping",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
"actions": [],
"creation": "2025-05-01 12:00:00",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"etaxes_item_name",
"etaxes_item_code",
"erp_item",
"mapping_type",
"item_settings_section",
"item_group",
"uom",
"is_stock_item",
"is_purchase_item",
"item_tax_template"
],
"fields": [
{
"fieldname": "etaxes_item_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "E-Taxes Item Name",
"options": "E-Taxes Item",
"reqd": 1
},
{
"fieldname": "etaxes_item_code",
"fieldtype": "Data",
"hidden": 1,
"label": "E-Taxes Item Code",
"read_only": 1
},
{
"fieldname": "erp_item",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "System Item",
"options": "Item"
},
{
"default": "Manual",
"fieldname": "mapping_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Mapping Type",
"options": "Manual\nAutomatic"
},
{
"fieldname": "item_settings_section",
"fieldtype": "Section Break",
"label": "Item Settings"
},
{
"description": "Overrides default item group setting",
"fieldname": "item_group",
"fieldtype": "Link",
"label": "Item Group",
"options": "Item Group"
},
{
"description": "Overrides default UOM setting",
"fieldname": "uom",
"fieldtype": "Link",
"label": "Unit of Measure",
"options": "UOM"
},
{
"default": "0",
"description": "Overrides default Is Stock Item setting",
"fieldname": "is_stock_item",
"fieldtype": "Check",
"label": "Is Stock Item"
},
{
"default": "0",
"description": "Overrides default Is Purchase Item setting",
"fieldname": "is_purchase_item",
"fieldtype": "Check",
"label": "Is Purchase Item"
},
{
"description": "Overrides default Item Tax Template setting",
"fieldname": "item_tax_template",
"fieldtype": "Link",
"label": "Item Tax Template",
"options": "Item Tax Template"
}
],
"istable": 1,
"links": [],
"modified": "2025-05-23 16:00:32.090354",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Item Mapping",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@ -1,109 +1,110 @@
{
"actions": [],
"creation": "2025-05-01 12:00:00",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"etaxes_party_name",
"etaxes_tax_id",
"party_type",
"erp_party",
"mapping_type",
"party_settings_section",
"supplier_group",
"customer_group",
"territory",
"payment_terms"
],
"fields": [
{
"fieldname": "etaxes_party_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "E-Taxes Party Name",
"options": "E-Taxes Parties",
"reqd": 1
},
{
"fieldname": "etaxes_tax_id",
"fieldtype": "Data",
"in_list_view": 1,
"in_preview": 1,
"label": "Tax ID"
},
{
"fieldname": "party_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Party Type",
"options": "Supplier\nCustomer",
"reqd": 1
},
{
"fieldname": "erp_party",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"in_preview": 1,
"label": "System Party",
"options": "party_type"
},
{
"default": "Manual",
"fieldname": "mapping_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Mapping Type",
"options": "Manual\nAutomatic"
},
{
"fieldname": "party_settings_section",
"fieldtype": "Section Break",
"label": "Party Settings"
},
{
"fieldname": "supplier_group",
"fieldtype": "Link",
"label": "Supplier Group",
"options": "Supplier Group",
"description": "Overrides default Supplier Group setting (applies only when Party Type is Supplier)",
"depends_on": "eval:doc.party_type=='Supplier'"
},
{
"fieldname": "customer_group",
"fieldtype": "Link",
"label": "Customer Group",
"options": "Customer Group",
"description": "Overrides default Customer Group setting (applies only when Party Type is Customer)",
"depends_on": "eval:doc.party_type=='Customer'"
},
{
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"options": "Territory",
"description": "Territory for the business partner (applies mainly for Customer)"
},
{
"fieldname": "payment_terms",
"fieldtype": "Link",
"label": "Payment Terms",
"options": "Payment Terms Template",
"description": "Overrides default Payment Terms setting"
}
],
"istable": 1,
"links": [],
"modified": "2025-05-06 19:20:15.362967",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Party Mapping",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
"actions": [],
"creation": "2025-05-01 12:00:00",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"etaxes_party_name",
"etaxes_tax_id",
"party_type",
"erp_party",
"mapping_type",
"party_settings_section",
"supplier_group",
"customer_group",
"territory",
"payment_terms"
],
"fields": [
{
"fieldname": "etaxes_party_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "E-Taxes Party Name",
"options": "E-Taxes Parties",
"reqd": 1
},
{
"fieldname": "etaxes_tax_id",
"fieldtype": "Data",
"in_list_view": 1,
"in_preview": 1,
"label": "Tax ID"
},
{
"fieldname": "party_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Party Type",
"options": "Supplier\nCustomer",
"reqd": 1
},
{
"fieldname": "erp_party",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"in_preview": 1,
"label": "System Party",
"options": "party_type"
},
{
"default": "Manual",
"fieldname": "mapping_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Mapping Type",
"options": "Manual\nAutomatic"
},
{
"fieldname": "party_settings_section",
"fieldtype": "Section Break",
"label": "Party Settings"
},
{
"depends_on": "eval:doc.party_type=='Supplier'",
"description": "Overrides default Supplier Group setting (applies only when Party Type is Supplier)",
"fieldname": "supplier_group",
"fieldtype": "Link",
"label": "Supplier Group",
"options": "Supplier Group"
},
{
"depends_on": "eval:doc.party_type=='Customer'",
"description": "Overrides default Customer Group setting (applies only when Party Type is Customer)",
"fieldname": "customer_group",
"fieldtype": "Link",
"label": "Customer Group",
"options": "Customer Group"
},
{
"description": "Territory for the business partner (applies mainly for Customer)",
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"options": "Territory"
},
{
"description": "Overrides default Payment Terms setting",
"fieldname": "payment_terms",
"fieldtype": "Link",
"label": "Payment Terms",
"options": "Payment Terms Template"
}
],
"istable": 1,
"links": [],
"modified": "2025-05-23 16:01:02.892751",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Party Mapping",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@ -8,20 +8,20 @@ frappe.form.link_formatters['E-Taxes Unit'] = function(value, doc) {
frappe.ui.form.on('E-Taxes Settings', {
validate: function(frm) {
// Показываем индикатор загрузки при сохранении
frm.page.set_indicator(__('Updating mappings...'), 'blue');
},
// validate: function(frm) {
// // Показываем индикатор загрузки при сохранении
// frm.page.set_indicator(__('Updating mappings...'), 'blue');
// },
after_save: function(frm) {
// Обновляем индикатор после сохранения
frm.page.set_indicator(__('Mappings updated'), 'green');
// Показываем уведомление об успешном обновлении
frappe.show_alert({
message: __('Mappings updated successfully'),
indicator: 'green'
}, 5);
// frappe.show_alert({
// message: __('Mappings updated successfully'),
// indicator: 'green'
// }, 5);
// Снимаем индикатор через 2 секунды
setTimeout(function() {
@ -199,8 +199,8 @@ frappe.ui.form.on('E-Taxes Settings', {
r.message.items.forEach(function(item) {
let row = frm.add_child('item_mappings');
// Используем ID документа (name) вместо отображаемого имени
row.etaxes_item_name = item.name; // Должен быть ID документа
// Используем ID документа (name)
row.etaxes_item_name = item.name;
// Добавляем все значения по умолчанию
if (item.item_group) row.item_group = item.item_group;
@ -212,7 +212,41 @@ frappe.ui.form.on('E-Taxes Settings', {
// Тип сопоставления
row.mapping_type = 'Manual';
});
frm.refresh_field('item_mappings');
// СНАЧАЛА СОХРАНЯЕМ документ с новыми строками
frm.save().then(function() {
// СРАЗУ вызываем операцию для обновления отображения
frappe.call({
method: 'invoice_az.api.refresh_item_mappings_display',
args: {
'settings_name': frm.doc.name
},
callback: function(refresh_r) {
if (refresh_r.message && refresh_r.message.success && refresh_r.message.updated_mappings) {
// Обновляем отображение Link полей на клиенте без перезагрузки
refresh_r.message.updated_mappings.forEach(function(mapping) {
// Находим строку в grid и обновляем отображение
let grid_row = frm.fields_dict['item_mappings'].grid.grid_rows.find(
row => row.doc.name === mapping.name
);
if (grid_row && grid_row.columns.etaxes_item_name) {
// Обновляем отображаемый текст в Link поле
let link_field = grid_row.columns.etaxes_item_name.df;
if (link_field && grid_row.columns.etaxes_item_name.$input) {
// Устанавливаем правильное отображение
grid_row.columns.etaxes_item_name.$input.val(mapping.etaxes_item_name_display);
grid_row.columns.etaxes_item_name.set_value(mapping.etaxes_item_name_id);
}
}
});
}
}
});
});
frappe.show_alert({
message: __('Added {0} unmapped items', [r.message.items.length]),
indicator: 'green'
@ -408,11 +442,19 @@ frappe.ui.form.on('E-Taxes Settings', {
// Тип сопоставления
row.mapping_type = 'Manual';
});
frm.refresh_field('party_mappings');
frappe.show_alert({
message: __('Added {0} unmapped partners', [r.message.parties.length]),
indicator: 'green'
}, 5);
// СНАЧАЛА СОХРАНЯЕМ документ с новыми строками
frm.save().then(function() {
// Для parties обновление отображения не требуется,
// так как etaxes_party_name это обычное текстовое поле
frappe.show_alert({
message: __('Added {0} unmapped partners', [r.message.parties.length]),
indicator: 'green'
}, 5);
});
} else {
frappe.show_alert({
message: __('No unmapped partners'),
@ -554,13 +596,11 @@ frappe.ui.form.on('E-Taxes Settings', {
frappe.confirm(
__('Document contains unsaved changes. Save before performing the operation?'),
function() {
// Если "Да", сохраняем и выполняем действие
frm.save().then(function() {
add_unmapped_units();
});
},
function() {
// Если "Нет", не делаем ничего
frappe.show_alert({
message: __('Operation canceled. Please save the document first.'),
indicator: 'red'
@ -568,7 +608,6 @@ frappe.ui.form.on('E-Taxes Settings', {
}
);
} else {
// Если документ не изменен, просто выполняем действие
add_unmapped_units();
}
@ -582,13 +621,16 @@ frappe.ui.form.on('E-Taxes Settings', {
r.message.units.forEach(function(unit) {
let row = frm.add_child('unit_mappings');
// Используем ID документа (name) вместо отображаемого имени
row.etaxes_unit_name = unit.name; // Должен быть ID документа
// Тип сопоставления
// ПРАВИЛЬНОЕ решение: используем frappe.model.set_value
// который корректно обрабатывает Link поля
frappe.model.set_value(row.doctype, row.name, 'etaxes_unit_name', unit.value);
row.mapping_type = 'Manual';
});
// Обновляем таблицу и сохраняем
frm.refresh_field('unit_mappings');
frm.save();
frappe.show_alert({
message: __('Added {0} unmapped units', [r.message.units.length]),
indicator: 'green'
@ -610,7 +652,7 @@ frappe.ui.form.on('E-Taxes Settings', {
});
}
}, __('Units'));
}
});

View File

@ -1,6 +1,6 @@
{
"actions": [],
"autoname": "field:etaxes_unit_code",
"autoname": "field:etaxes_unit_name",
"creation": "2025-05-21 12:00:00",
"doctype": "DocType",
"engine": "InnoDB",
@ -54,7 +54,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-05-21 14:25:13.578790",
"modified": "2025-05-23 18:34:52.662827",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Unit",
@ -85,11 +85,8 @@
"write": 1
}
],
"search_fields": "etaxes_unit_name",
"show_title_field_in_link": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "etaxes_unit_name",
"track_changes": 1
}

View File

@ -1,51 +1,52 @@
{
"actions": [],
"creation": "2025-05-21 12:00:00",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"etaxes_unit_name",
"erp_unit",
"mapping_type"
],
"fields": [
{
"fieldname": "etaxes_unit_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "E-Taxes Unit Name",
"options": "E-Taxes Unit",
"reqd": 1
},
{
"fieldname": "erp_unit",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "System UOM",
"options": "UOM"
},
{
"default": "Manual",
"fieldname": "mapping_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Mapping Type",
"options": "Manual\nAutomatic"
}
],
"istable": 1,
"links": [],
"modified": "2025-05-21 12:44:49.793210",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Unit Mapping",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
"actions": [],
"creation": "2025-05-21 12:00:00",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"etaxes_unit_name",
"erp_unit",
"mapping_type"
],
"fields": [
{
"fieldname": "etaxes_unit_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "E-Taxes Unit Name",
"options": "E-Taxes Unit",
"reqd": 1
},
{
"fieldname": "erp_unit",
"fieldtype": "Link",
"in_list_view": 1,
"in_preview": 1,
"label": "System UOM",
"options": "UOM"
},
{
"default": "Manual",
"fieldname": "mapping_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_preview": 1,
"label": "Mapping Type",
"options": "Manual\nAutomatic"
}
],
"istable": 1,
"links": [],
"modified": "2025-05-23 16:01:00.029816",
"modified_by": "Administrator",
"module": "Invoice Az",
"name": "E-Taxes Unit Mapping",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}