fixed a bunch of bugs
This commit is contained in:
parent
6e4c8020b0
commit
56b24bc644
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
// Добавляем обработчик для чекбокса "выбрать все"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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'));
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue