fixed invoices date
This commit is contained in:
parent
f0c19854fe
commit
6df36695db
|
|
@ -55,6 +55,7 @@ def get_invoices(token, filters=None):
|
|||
if key in payload and value: # Update only existing keys and non-empty values
|
||||
payload[key] = value
|
||||
except Exception as e:
|
||||
frappe.log_error(f"[GET_INVOICES] Error parsing filters: {str(e)}", "Filter Parse Error")
|
||||
return
|
||||
|
||||
headers = {
|
||||
|
|
@ -68,6 +69,7 @@ def get_invoices(token, filters=None):
|
|||
response = requests.post(url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
if response.status_code == 500:
|
||||
frappe.log_error(f"[GET_INVOICES] Server error 500", "E-Taxes Server Error")
|
||||
return {
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
|
|
@ -76,23 +78,47 @@ def get_invoices(token, filters=None):
|
|||
|
||||
# Check response status to handle 401 error
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
frappe.log_error(f"[GET_INVOICES] Unauthorized 401 - token expired", "E-Taxes Auth Error")
|
||||
|
||||
# Получаем свежий токен из Asan Login
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if asan_login_settings.get('found'):
|
||||
fresh_token = asan_login_settings['main_token']
|
||||
headers["x-authorization"] = f"Bearer {fresh_token}"
|
||||
|
||||
frappe.log_error(f"[GET_INVOICES] Retrying with fresh token", "E-Taxes Auth Retry")
|
||||
|
||||
# Повторяем запрос с новым токеном
|
||||
response = requests.post(url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
if response.status_code == 401:
|
||||
frappe.log_error(f"[GET_INVOICES] Still unauthorized after token refresh", "E-Taxes Auth Failed")
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
else:
|
||||
frappe.log_error(f"[GET_INVOICES] No Asan Login settings found for token refresh", "E-Taxes Auth Error")
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
frappe.log_error(f"[GET_INVOICES] HTTP error: {e.response.status_code} - {str(e)}", "E-Taxes HTTP Error")
|
||||
|
||||
# Check if the error is 401 Unauthorized
|
||||
if e.response.status_code == 401:
|
||||
frappe.log_error(f"[GET_INVOICES] HTTPError 401 Unauthorized", "E-Taxes Auth Error")
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
elif e.response.status_code == 500:
|
||||
frappe.log_error(f"[GET_INVOICES] HTTPError 500 Server Error", "E-Taxes Server Error")
|
||||
return {
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
|
|
@ -102,6 +128,7 @@ def get_invoices(token, filters=None):
|
|||
frappe.log_error(f"HTTP error in get_invoices: {str(e)}", "API Error")
|
||||
return {"error": "http_error", "message": "An unknown error occurred, please try again in a few minutes."}
|
||||
except Exception as e:
|
||||
frappe.log_error(f"[GET_INVOICES] Unexpected error: {str(e)}\n{frappe.get_traceback()}", "E-Taxes Error")
|
||||
frappe.log_error(f"Error getting invoices: {str(e)}", "API Error")
|
||||
return {"error": "unknown_error", "message": "An unknown error occurred, please try again in a few minutes."}
|
||||
|
||||
|
|
@ -124,6 +151,7 @@ def get_invoice_details(token, invoice_id):
|
|||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 500:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] Server error 500 for invoice {invoice_id}", "E-Taxes Server Error")
|
||||
return {
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
|
|
@ -132,23 +160,47 @@ def get_invoice_details(token, invoice_id):
|
|||
|
||||
# Check for 401 error
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] Unauthorized 401 for invoice {invoice_id}", "E-Taxes Auth Error")
|
||||
|
||||
# Получаем свежий токен из Asan Login
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if asan_login_settings.get('found'):
|
||||
fresh_token = asan_login_settings['main_token']
|
||||
headers["x-authorization"] = f"Bearer {fresh_token}"
|
||||
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] Retrying with fresh token for invoice {invoice_id}", "E-Taxes Auth Retry")
|
||||
|
||||
# Повторяем запрос с новым токеном
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 401:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] Still unauthorized after token refresh for invoice {invoice_id}", "E-Taxes Auth Failed")
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
else:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] No Asan Login settings found for token refresh", "E-Taxes Auth Error")
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] HTTP error for invoice {invoice_id}: {e.response.status_code} - {str(e)}", "E-Taxes HTTP Error")
|
||||
|
||||
# Check if the error is 401 Unauthorized
|
||||
if e.response.status_code == 401:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] HTTPError 401 Unauthorized for invoice {invoice_id}", "E-Taxes Auth Error")
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
elif e.response.status_code == 500:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] HTTPError 500 Server Error for invoice {invoice_id}", "E-Taxes Server Error")
|
||||
return {
|
||||
"error": "server_error",
|
||||
"message": "Service temporarily unavailable. Please try again in a few minutes.",
|
||||
|
|
@ -158,6 +210,7 @@ def get_invoice_details(token, invoice_id):
|
|||
frappe.log_error(f"HTTP error in get_invoice_details: {str(e)}", "API Error")
|
||||
return {"error": "http_error", "message": "An unknown error occurred, please try again in a few minutes."}
|
||||
except Exception as e:
|
||||
frappe.log_error(f"[GET_INVOICE_DETAILS] Unexpected error for invoice {invoice_id}: {str(e)}\n{frappe.get_traceback()}", "E-Taxes Error")
|
||||
frappe.log_error(f"Error getting invoice details: {str(e)}", "API Error")
|
||||
return {"error": "unknown_error", "message": "An unknown error occurred, please try again in a few minutes."}
|
||||
|
||||
|
|
@ -814,18 +867,50 @@ def create_purchase_invoice_from_order(purchase_order_name):
|
|||
# Импортируем стандартную функцию из модуля Purchase Order
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice
|
||||
|
||||
# Получаем Purchase Order ДО создания invoice
|
||||
po_doc = frappe.get_doc('Purchase Order', purchase_order_name)
|
||||
|
||||
# ИСПРАВЛЕНО: Правильная обработка дат
|
||||
if po_doc.transaction_date:
|
||||
posting_date = po_doc.transaction_date
|
||||
# Конвертируем в datetime.date если это строка
|
||||
if isinstance(posting_date, str):
|
||||
posting_date = frappe.utils.getdate(posting_date)
|
||||
else:
|
||||
posting_date = frappe.utils.getdate(frappe.utils.nowdate())
|
||||
|
||||
frappe.log_error(f"[DATE DEBUG] PO transaction_date: {po_doc.transaction_date}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] PI posting_date to set: {posting_date}", "Date Debug")
|
||||
|
||||
# Создаем Purchase Invoice из Purchase Order
|
||||
pi_doc = make_purchase_invoice(purchase_order_name)
|
||||
|
||||
# Устанавливаем даты
|
||||
pi_doc.posting_date = frappe.utils.nowdate()
|
||||
pi_doc.due_date = frappe.utils.add_days(frappe.utils.nowdate(), 30) # 30 дней для оплаты
|
||||
# ДОБАВЛЕНО: Логируем дату сразу после создания
|
||||
frappe.log_error(f"[DATE DEBUG] PI posting_date after make_purchase_invoice: {pi_doc.posting_date}", "Date Debug")
|
||||
|
||||
# ИСПРАВЛЕНО: Устанавливаем дату и флаг posting_time ДО других операций
|
||||
pi_doc.posting_date = posting_date
|
||||
pi_doc.set_posting_time = 1 # Важно! Разрешаем установку даты в прошлом
|
||||
|
||||
# ИСПРАВЛЕНО: Убеждаемся что due_date всегда больше posting_date
|
||||
due_date = frappe.utils.add_days(posting_date, 30)
|
||||
|
||||
# Дополнительная проверка: если due_date в прошлом, используем текущую дату + 30 дней
|
||||
today = frappe.utils.getdate(frappe.utils.nowdate())
|
||||
if due_date < today:
|
||||
due_date = frappe.utils.add_days(today, 30)
|
||||
frappe.log_error(f"[DATE DEBUG] Due date was in past, adjusted to: {due_date}", "Date Debug")
|
||||
|
||||
pi_doc.due_date = due_date
|
||||
|
||||
frappe.log_error(f"[DATE DEBUG] PI posting_date before insert: {pi_doc.posting_date}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] PI set_posting_time: {pi_doc.set_posting_time}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] PI due_date before insert: {pi_doc.due_date}", "Date Debug")
|
||||
|
||||
# ДОБАВЛЕНО: Устанавливаем галочку is_taxes_doc для Purchase Invoice
|
||||
pi_doc.is_taxes_doc = 1
|
||||
|
||||
# ДОБАВЛЕНО: Копируем налоги из Purchase Order
|
||||
po_doc = frappe.get_doc('Purchase Order', purchase_order_name)
|
||||
if po_doc.taxes:
|
||||
pi_doc.taxes = []
|
||||
for tax_row in po_doc.taxes:
|
||||
|
|
@ -841,6 +926,25 @@ def create_purchase_invoice_from_order(purchase_order_name):
|
|||
# Сохраняем документ
|
||||
pi_doc.insert(ignore_permissions=True)
|
||||
|
||||
# ДОБАВЛЕНО: Логируем дату после insert
|
||||
frappe.log_error(f"[DATE DEBUG] PI posting_date after insert: {pi_doc.posting_date}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] PI due_date after insert: {pi_doc.due_date}", "Date Debug")
|
||||
|
||||
# ДОБАВЛЕНО: Если дата все еще неправильная, принудительно устанавливаем
|
||||
if pi_doc.posting_date != posting_date:
|
||||
frappe.log_error(f"[DATE DEBUG] Date mismatch detected! Expected: {posting_date}, Got: {pi_doc.posting_date}", "Date Debug")
|
||||
|
||||
# Принудительно устанавливаем дату через DB
|
||||
frappe.db.set_value('Purchase Invoice', pi_doc.name, {
|
||||
'posting_date': posting_date,
|
||||
'set_posting_time': 1
|
||||
})
|
||||
frappe.db.commit()
|
||||
|
||||
# Перезагружаем документ
|
||||
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
|
||||
frappe.log_error(f"[DATE DEBUG] PI posting_date after DB update: {pi_doc.posting_date}", "Date Debug")
|
||||
|
||||
frappe.log_error(f"Purchase Invoice {pi_doc.name} created from Purchase Order {purchase_order_name}", "Purchase Invoice Creation")
|
||||
|
||||
# Возвращаем имя созданного Purchase Invoice
|
||||
|
|
@ -861,6 +965,11 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
|
|||
if isinstance(invoice_data, str):
|
||||
invoice_data = json.loads(invoice_data)
|
||||
|
||||
# ДОБАВЛЕНО: Логируем входящие данные инвойса для отладки дат
|
||||
frappe.log_error(f"[DATE DEBUG] Invoice data keys: {list(invoice_data.keys())}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] createdAt field: {invoice_data.get('createdAt')}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] createdAt type: {type(invoice_data.get('createdAt'))}", "Date Debug")
|
||||
|
||||
# Get active settings
|
||||
settings = get_active_settings()
|
||||
if not settings:
|
||||
|
|
@ -909,6 +1018,57 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
|
|||
'message': 'Default warehouse not found. Please specify warehouse in E-Taxes settings.'
|
||||
}
|
||||
|
||||
# ИСПРАВЛЕНО: Определяем дату ДО создания Purchase Order
|
||||
invoice_date = None
|
||||
created_at_raw = invoice_data.get("createdAt")
|
||||
|
||||
frappe.log_error(f"[DATE DEBUG] Raw createdAt value: '{created_at_raw}'", "Date Debug")
|
||||
|
||||
if created_at_raw:
|
||||
try:
|
||||
# Попробуем разные форматы даты
|
||||
if isinstance(created_at_raw, str):
|
||||
# Если это строка, попробуем разные форматы
|
||||
frappe.log_error(f"[DATE DEBUG] Trying to parse string date: '{created_at_raw}'", "Date Debug")
|
||||
|
||||
# Попробуем стандартный парсинг ERPNext
|
||||
invoice_date = frappe.utils.getdate(created_at_raw)
|
||||
frappe.log_error(f"[DATE DEBUG] Successfully parsed date: {invoice_date}", "Date Debug")
|
||||
|
||||
elif hasattr(created_at_raw, 'date'):
|
||||
# Если это datetime объект
|
||||
invoice_date = created_at_raw.date()
|
||||
frappe.log_error(f"[DATE DEBUG] Extracted date from datetime: {invoice_date}", "Date Debug")
|
||||
else:
|
||||
# Попробуем привести к строке и парсить
|
||||
date_str = str(created_at_raw)
|
||||
frappe.log_error(f"[DATE DEBUG] Converting to string and parsing: '{date_str}'", "Date Debug")
|
||||
invoice_date = frappe.utils.getdate(date_str)
|
||||
frappe.log_error(f"[DATE DEBUG] Successfully parsed converted date: {invoice_date}", "Date Debug")
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"[DATE DEBUG] Error parsing createdAt '{created_at_raw}': {str(e)}", "Date Debug")
|
||||
invoice_date = frappe.utils.today()
|
||||
frappe.log_error(f"[DATE DEBUG] Using fallback date: {invoice_date}", "Date Debug")
|
||||
else:
|
||||
invoice_date = frappe.utils.today()
|
||||
frappe.log_error(f"[DATE DEBUG] No createdAt found, using today: {invoice_date}", "Date Debug")
|
||||
|
||||
# ИСПРАВЛЕНО: Определяем date_to_use ДО создания Purchase Order
|
||||
# ПРИОРИТЕТ: 1) дата из E-taxes, 2) schedule_date, 3) сегодня
|
||||
date_to_use = None
|
||||
if invoice_date:
|
||||
date_to_use = invoice_date
|
||||
frappe.log_error(f"[DATE DEBUG] Using invoice_date from E-taxes for date_to_use: {date_to_use}", "Date Debug")
|
||||
elif schedule_date:
|
||||
date_to_use = frappe.utils.getdate(schedule_date)
|
||||
frappe.log_error(f"[DATE DEBUG] No E-taxes date, using provided schedule_date: {date_to_use}", "Date Debug")
|
||||
else:
|
||||
date_to_use = frappe.utils.today()
|
||||
frappe.log_error(f"[DATE DEBUG] No dates available, using today for date_to_use: {date_to_use}", "Date Debug")
|
||||
|
||||
frappe.log_error(f"[DATE DEBUG] Final date_to_use: {date_to_use}", "Date Debug")
|
||||
|
||||
# Get or create Purchase Order
|
||||
if purchase_order_name:
|
||||
po = frappe.get_doc('Purchase Order', purchase_order_name)
|
||||
|
|
@ -935,15 +1095,13 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
|
|||
'message': f'No mapping found for supplier: {sender.get("name", "")}'
|
||||
}
|
||||
|
||||
# Set dates
|
||||
po.transaction_date = frappe.utils.today()
|
||||
frappe.log_error(f"[DATE DEBUG] Final invoice_date for PO: {invoice_date}", "Date Debug")
|
||||
|
||||
if schedule_date:
|
||||
po.schedule_date = schedule_date
|
||||
elif invoice_data.get("creationDate"):
|
||||
po.schedule_date = frappe.utils.getdate(invoice_data.get("creationDate"))
|
||||
else:
|
||||
po.schedule_date = frappe.utils.today()
|
||||
po.transaction_date = invoice_date
|
||||
po.schedule_date = date_to_use
|
||||
|
||||
frappe.log_error(f"[DATE DEBUG] PO transaction_date set to: {po.transaction_date}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] PO schedule_date set to: {po.schedule_date}", "Date Debug")
|
||||
|
||||
po.company = frappe.defaults.get_user_default('Company')
|
||||
|
||||
|
|
@ -955,14 +1113,6 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
|
|||
if purchase_order_name and invoice_data.get("items"):
|
||||
po.items = []
|
||||
|
||||
date_to_use = None
|
||||
if schedule_date:
|
||||
date_to_use = schedule_date
|
||||
elif invoice_data.get("creationDate"):
|
||||
date_to_use = frappe.utils.getdate(invoice_data.get("creationDate"))
|
||||
else:
|
||||
date_to_use = frappe.utils.today()
|
||||
|
||||
# Variable for tracking added items
|
||||
added_items_count = 0
|
||||
unmatched_items = []
|
||||
|
|
@ -1050,9 +1200,11 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
|
|||
po_item.rate = item.get("pricePerUnit", 0)
|
||||
po_item.amount = item.get("cost", 0)
|
||||
po_item.uom = mapped_uom
|
||||
po_item.schedule_date = date_to_use
|
||||
po_item.schedule_date = date_to_use # ИСПРАВЛЕНО: Используем правильную дату
|
||||
po_item.warehouse = default_warehouse
|
||||
|
||||
frappe.log_error(f"[DATE DEBUG] Item {po_item.item_code} schedule_date set to: {po_item.schedule_date}", "Date Debug")
|
||||
|
||||
po.append("items", po_item)
|
||||
added_items_count += 1
|
||||
|
||||
|
|
@ -1114,19 +1266,32 @@ def import_invoice_with_mapping(invoice_data, purchase_order_name=None, schedule
|
|||
except Exception as e:
|
||||
frappe.log_error(f"Error adding ƏDV tax: {str(e)}", "Purchase Order Tax Error")
|
||||
|
||||
# ДОБАВЛЕНО: Логируем финальные даты перед сохранением
|
||||
frappe.log_error(f"[DATE DEBUG] Final PO transaction_date: {po.transaction_date}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] Final PO schedule_date: {po.schedule_date}", "Date Debug")
|
||||
|
||||
# Save document
|
||||
po.save()
|
||||
|
||||
# Additional check that all schedule_date and warehouse are set
|
||||
# ИСПРАВЛЕНО: Убедимся что все schedule_date установлены правильно
|
||||
for item in po.items:
|
||||
if not item.schedule_date:
|
||||
item.schedule_date = date_to_use
|
||||
frappe.log_error(f"[DATE DEBUG] Fixed missing schedule_date for item {item.item_code}: {item.schedule_date}", "Date Debug")
|
||||
if not item.warehouse:
|
||||
item.warehouse = default_warehouse
|
||||
|
||||
# Save again to ensure changes are applied
|
||||
po.save()
|
||||
|
||||
# ДОБАВЛЕНО: Логируем даты после сохранения
|
||||
frappe.log_error(f"[DATE DEBUG] After save PO transaction_date: {po.transaction_date}", "Date Debug")
|
||||
frappe.log_error(f"[DATE DEBUG] After save PO schedule_date: {po.schedule_date}", "Date Debug")
|
||||
|
||||
# Логируем schedule_date каждого item после сохранения
|
||||
for item in po.items:
|
||||
frappe.log_error(f"[DATE DEBUG] After save item {item.item_code} schedule_date: {item.schedule_date}", "Date Debug")
|
||||
|
||||
# ИЗМЕНЕНИЕ: Создаем E-Taxes Purchase и устанавливаем связь ДО submit'а
|
||||
invoice_id = invoice_data.get('id', '')
|
||||
serial_number = invoice_data.get('serialNumber', '')
|
||||
|
|
@ -3576,106 +3741,6 @@ def get_reference_data_lists(data_type, limit=100, offset=0):
|
|||
except Exception as e:
|
||||
frappe.log_error(f"Error getting reference data list for {data_type}: {str(e)}\n{frappe.get_traceback()}", "Reference Data List Error")
|
||||
return {'success': False, 'message': str(e)}
|
||||
@frappe.whitelist()
|
||||
def load_reference_data_from_invoices(date_from, date_to, max_count=200, offset=0):
|
||||
"""Загружает только список инвойсов, без обработки"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
# Получаем настройки токена
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if not asan_login_settings.get('found'):
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'No Asan Login settings found'
|
||||
}
|
||||
|
||||
token = asan_login_settings['main_token']
|
||||
|
||||
# Преобразуем параметры
|
||||
try:
|
||||
max_count = int(max_count)
|
||||
except (ValueError, TypeError):
|
||||
max_count = 200
|
||||
|
||||
try:
|
||||
offset = int(offset)
|
||||
except (ValueError, TypeError):
|
||||
offset = 0
|
||||
|
||||
# Настройки фильтров
|
||||
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"]
|
||||
}
|
||||
|
||||
# Получаем список инвойсов из inbox
|
||||
response_inbox = get_invoices(token, json.dumps(filters))
|
||||
|
||||
if 'error' in response_inbox:
|
||||
return {
|
||||
'success': False,
|
||||
'error': response_inbox.get('error'),
|
||||
'message': response_inbox.get('message', 'Failed to retrieve invoices')
|
||||
}
|
||||
|
||||
invoices_inbox = response_inbox.get('data', []) or response_inbox.get('invoices', [])
|
||||
|
||||
for invoice in invoices_inbox:
|
||||
invoice['_source'] = 'inbox'
|
||||
|
||||
all_invoices = invoices_inbox
|
||||
|
||||
# Пытаемся получить инвойсы из outbox
|
||||
try:
|
||||
from .sales_api import get_sales_invoices
|
||||
response_outbox = get_sales_invoices(token, json.dumps(filters))
|
||||
|
||||
if 'error' not in response_outbox:
|
||||
invoices_outbox = response_outbox.get('data', []) or response_outbox.get('invoices', [])
|
||||
|
||||
for invoice in invoices_outbox:
|
||||
invoice['_source'] = 'outbox'
|
||||
|
||||
all_invoices.extend(invoices_outbox)
|
||||
|
||||
except (ImportError, Exception) as e:
|
||||
frappe.log_error(f"Could not load outbox invoices: {str(e)}", "Load Reference Data Warning")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'invoices': all_invoices,
|
||||
'token': token,
|
||||
'total': len(all_invoices),
|
||||
'inbox_count': len(invoices_inbox),
|
||||
'outbox_count': len(all_invoices) - len(invoices_inbox)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error in load_reference_data_from_invoices: {str(e)}\n{frappe.get_traceback()}", "Load Reference Data Error")
|
||||
return {
|
||||
'success': False,
|
||||
'message': "An unknown error occurred, please try again in a few minutes."
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def process_single_invoice_for_reference_data(token, invoice_id, source_type='purchase'):
|
||||
|
|
@ -3686,6 +3751,32 @@ def process_single_invoice_for_reference_data(token, invoice_id, source_type='pu
|
|||
# Получаем детали инвойса
|
||||
invoice_details = get_invoice_details(token, invoice_id)
|
||||
|
||||
# Проверяем на ошибку unauthorized и пробуем обновить токен
|
||||
if isinstance(invoice_details, dict) and invoice_details.get('error') == 'unauthorized':
|
||||
# Пробуем получить новый токен из Asan Login
|
||||
token_result = refresh_token_from_asan_login()
|
||||
if token_result.get('success'):
|
||||
new_token = token_result.get('token')
|
||||
# Повторяем запрос с новым токеном
|
||||
invoice_details = get_invoice_details(new_token, invoice_id)
|
||||
|
||||
# Если все еще ошибка - возвращаем с обновленным токеном для клиента
|
||||
if isinstance(invoice_details, dict) and 'error' in invoice_details:
|
||||
return {
|
||||
'success': False,
|
||||
'error': invoice_details.get('error'),
|
||||
'message': invoice_details.get('message', 'Failed to get invoice details'),
|
||||
'new_token': new_token # Передаем новый токен клиенту
|
||||
}
|
||||
else:
|
||||
# Не удалось обновить токен
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'unauthorized',
|
||||
'message': 'Authentication required. Please login again.'
|
||||
}
|
||||
|
||||
# Если другая ошибка (не unauthorized)
|
||||
if isinstance(invoice_details, dict) and 'error' in invoice_details:
|
||||
return {
|
||||
'success': False,
|
||||
|
|
@ -3707,7 +3798,7 @@ def process_single_invoice_for_reference_data(token, invoice_id, source_type='pu
|
|||
|
||||
@frappe.whitelist()
|
||||
def create_reference_data_from_single_invoice(invoice_details, source_type):
|
||||
"""Создает все 4 типа доктайпов из одного инвойса"""
|
||||
"""Создает все 4 типа доктайпов из одного инвойса - оптимизированная версия"""
|
||||
try:
|
||||
stats = {
|
||||
'items_created': 0,
|
||||
|
|
@ -3928,3 +4019,221 @@ def create_reference_data_from_single_invoice(invoice_details, source_type):
|
|||
'message': str(e)
|
||||
}
|
||||
|
||||
# ===== НОВЫЕ PYTHON ФУНКЦИИ =====
|
||||
|
||||
@frappe.whitelist()
|
||||
def load_single_invoice_batch(date_from, date_to, offset=0, max_count=200):
|
||||
"""Загружает одну порцию инвойсов из inbox"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
# Получаем настройки токена
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if not asan_login_settings.get('found'):
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'No Asan Login settings found'
|
||||
}
|
||||
|
||||
token = asan_login_settings['main_token']
|
||||
|
||||
# Преобразуем параметры
|
||||
try:
|
||||
max_count = int(max_count)
|
||||
if max_count != 200: # Принудительно устанавливаем 200
|
||||
max_count = 200
|
||||
except (ValueError, TypeError):
|
||||
max_count = 200
|
||||
|
||||
try:
|
||||
offset = int(offset)
|
||||
except (ValueError, TypeError):
|
||||
offset = 0
|
||||
|
||||
# Настройки фильтров
|
||||
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"]
|
||||
}
|
||||
|
||||
# Получаем порцию инвойсов из inbox
|
||||
response = get_invoices(token, json.dumps(filters))
|
||||
|
||||
if 'error' in response:
|
||||
return {
|
||||
'success': False,
|
||||
'error': response.get('error'),
|
||||
'message': response.get('message', 'Failed to retrieve invoices')
|
||||
}
|
||||
|
||||
invoices = response.get('data', []) or response.get('invoices', [])
|
||||
hasMore = response.get('hasMore', False)
|
||||
total = response.get('total', 0)
|
||||
|
||||
# Дополнительная проверка hasMore
|
||||
if not hasMore and total > 0:
|
||||
hasMore = (offset + max_count) < total
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'invoices': invoices,
|
||||
'hasMore': hasMore,
|
||||
'token': token,
|
||||
'offset': offset,
|
||||
'total': total,
|
||||
'loaded': len(invoices)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error in load_single_invoice_batch: {str(e)}\n{frappe.get_traceback()}", "Load Invoice Batch Error")
|
||||
return {
|
||||
'success': False,
|
||||
'message': "An unknown error occurred, please try again in a few minutes."
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def load_single_sales_invoice_batch(date_from, date_to, offset=0, max_count=200):
|
||||
"""Загружает одну порцию инвойсов из outbox (sales)"""
|
||||
record_etaxes_activity()
|
||||
|
||||
try:
|
||||
# Получаем настройки токена
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if not asan_login_settings.get('found'):
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'No Asan Login settings found'
|
||||
}
|
||||
|
||||
token = asan_login_settings['main_token']
|
||||
|
||||
# Преобразуем параметры
|
||||
try:
|
||||
max_count = int(max_count)
|
||||
if max_count != 200: # Принудительно устанавливаем 200
|
||||
max_count = 200
|
||||
except (ValueError, TypeError):
|
||||
max_count = 200
|
||||
|
||||
try:
|
||||
offset = int(offset)
|
||||
except (ValueError, TypeError):
|
||||
offset = 0
|
||||
|
||||
# Настройки фильтров
|
||||
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"]
|
||||
}
|
||||
|
||||
# Получаем порцию инвойсов из outbox
|
||||
try:
|
||||
from .sales_api import get_sales_invoices
|
||||
response = get_sales_invoices(token, json.dumps(filters))
|
||||
|
||||
if 'error' in response:
|
||||
return {
|
||||
'success': False,
|
||||
'error': response.get('error'),
|
||||
'message': response.get('message', 'Failed to retrieve sales invoices')
|
||||
}
|
||||
|
||||
invoices = response.get('data', []) or response.get('invoices', [])
|
||||
hasMore = response.get('hasMore', False)
|
||||
total = response.get('total', 0)
|
||||
|
||||
# Дополнительная проверка hasMore
|
||||
if not hasMore and total > 0:
|
||||
hasMore = (offset + max_count) < total
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'invoices': invoices,
|
||||
'hasMore': hasMore,
|
||||
'token': token,
|
||||
'offset': offset,
|
||||
'total': total,
|
||||
'loaded': len(invoices)
|
||||
}
|
||||
|
||||
except ImportError:
|
||||
# Sales API недоступен
|
||||
return {
|
||||
'success': True,
|
||||
'invoices': [],
|
||||
'hasMore': False,
|
||||
'token': token,
|
||||
'offset': offset,
|
||||
'total': 0,
|
||||
'loaded': 0
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error in load_single_sales_invoice_batch: {str(e)}\n{frappe.get_traceback()}", "Load Sales Invoice Batch Error")
|
||||
return {
|
||||
'success': False,
|
||||
'message': "An unknown error occurred, please try again in a few minutes."
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def refresh_token_from_asan_login():
|
||||
"""Обновляет токен из Asan Login"""
|
||||
try:
|
||||
asan_login = frappe.get_all('Asan Login',
|
||||
filters={'is_default': 1},
|
||||
fields=['name', 'main_token'],
|
||||
limit=1)
|
||||
|
||||
if asan_login and asan_login[0].main_token:
|
||||
return {
|
||||
'success': True,
|
||||
'token': asan_login[0].main_token
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'success': False,
|
||||
'message': 'No valid token found in Asan Login'
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
}
|
||||
|
|
@ -21,11 +21,11 @@ DEFAULT_HEADERS = {
|
|||
"Pragma": "no-cache",
|
||||
"Referer": "https://new.e-taxes.gov.az/eportal/az/login/asan"
|
||||
}
|
||||
ACTIVITY_TIMEOUT_MINUTES = 10 # Настраиваемый параметр времени неактивности
|
||||
ACTIVITY_TIMEOUT_MINUTES = 10000000000000000 # Настраиваемый параметр времени неактивности
|
||||
|
||||
@frappe.whitelist()
|
||||
def record_etaxes_activity(asan_login_name=None):
|
||||
"""Записывает время последней активности с e-taxes с задержкой 2 секунды"""
|
||||
"""Записывает время последней активности с e-taxes напрямую"""
|
||||
try:
|
||||
# Если имя не указано, получаем дефолтный профиль
|
||||
if not asan_login_name:
|
||||
|
|
@ -41,23 +41,17 @@ def record_etaxes_activity(asan_login_name=None):
|
|||
|
||||
asan_login_name = default_settings[0].name
|
||||
|
||||
# Запускаем отложенную задачу для обновления поля last_activity_time
|
||||
frappe.enqueue(
|
||||
'invoice_az.auth.update_activity_time',
|
||||
asan_login_name=asan_login_name,
|
||||
queue='short',
|
||||
timeout=300
|
||||
)
|
||||
# Обновляем поле last_activity_time напрямую через БД
|
||||
frappe.db.set_value("Asan Login", asan_login_name, "last_activity_time", now_datetime(), update_modified=False)
|
||||
frappe.db.commit()
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error recording activity: {str(e)}", "Activity Recording Error")
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_activity_time(asan_login_name):
|
||||
"""Выполняет фактическое обновление поля last_activity_time с задержкой"""
|
||||
"""Выполняет фактическое обновление поля last_activity_time - упрощенная версия"""
|
||||
try:
|
||||
# Добавляем задержку для избежания конфликтов
|
||||
time.sleep(2)
|
||||
|
||||
# Обновляем поле last_activity_time напрямую через БД
|
||||
frappe.db.set_value("Asan Login", asan_login_name, "last_activity_time", now_datetime(), update_modified=False)
|
||||
frappe.db.commit()
|
||||
|
|
@ -98,7 +92,7 @@ def check_recent_activity(asan_login_name=None):
|
|||
return False # В случае ошибки считаем, что активности не было
|
||||
|
||||
@frappe.whitelist()
|
||||
def renew_token(asan_login_name=None, retry_count=0, force=False):
|
||||
def renew_token(asan_login_name=None, retry_count=0):
|
||||
"""Renews the main token with optimized performance and error handling"""
|
||||
try:
|
||||
# Получаем документ Asan Login
|
||||
|
|
@ -117,7 +111,7 @@ def renew_token(asan_login_name=None, retry_count=0, force=False):
|
|||
|
||||
asan_login_name = default_settings[0].name
|
||||
|
||||
# Проверяем наличие активности, если force=False
|
||||
# Проверяем наличие активности
|
||||
if not check_recent_activity(asan_login_name):
|
||||
frappe.logger().info(f"Token renewal skipped due to inactivity for {asan_login_name}")
|
||||
return {"success": True, "message": "Token renewal skipped due to inactivity"}
|
||||
|
|
|
|||
|
|
@ -961,8 +961,14 @@ function show_load_dialog_with_summary(frm, summary) {
|
|||
if (values.load_suppliers) selected_types.push(__('Suppliers'));
|
||||
if (values.load_units) selected_types.push(__('Units'));
|
||||
|
||||
// УЛУЧШЕНО: Более подробное сообщение о процессе
|
||||
frappe.confirm(
|
||||
__('This will load {0} from E-Taxes for the period {1} to {2}.<br><br><strong>This operation may take several minutes depending on the number of invoices.</strong><br><br>Continue?',
|
||||
__('This will load {0} from E-Taxes for the period {1} to {2}.<br><br>' +
|
||||
'<strong>Process:</strong><br>' +
|
||||
'1. First, all invoices will be fetched (may take 1-2 minutes)<br>' +
|
||||
'2. Then each invoice will be processed for reference data<br><br>' +
|
||||
'<strong>This operation may take several minutes depending on the number of invoices.</strong><br><br>' +
|
||||
'Continue?',
|
||||
[selected_types.join(', '), values.date_from, values.date_to]),
|
||||
function() {
|
||||
d.hide();
|
||||
|
|
@ -976,50 +982,8 @@ function show_load_dialog_with_summary(frm, summary) {
|
|||
}
|
||||
|
||||
function start_reference_data_loading(frm, values) {
|
||||
// frappe.show_progress(__('Loading'), 0, 100, __('Loading invoices...'));
|
||||
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.load_reference_data_from_invoices',
|
||||
args: {
|
||||
date_from: moment(values.date_from).format("DD-MM-YYYY 00:00"),
|
||||
date_to: moment(values.date_to).format("DD-MM-YYYY 23:59")
|
||||
},
|
||||
callback: function(r) {
|
||||
if (!r.message || !r.message.success) {
|
||||
frappe.hide_progress();
|
||||
frappe.msgprint(__('Failed to load invoices: ') + (r.message?.message || 'Unknown error'));
|
||||
return;
|
||||
}
|
||||
|
||||
const invoices = r.message.invoices || [];
|
||||
const token = r.message.token;
|
||||
|
||||
if (invoices.length === 0) {
|
||||
frappe.hide_progress();
|
||||
frappe.msgprint(__('No invoices found for the selected period.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Начинаем обработку инвойсов
|
||||
process_invoices_for_reference_data(invoices, token, {
|
||||
items_created: 0,
|
||||
items_skipped: 0,
|
||||
units_created: 0,
|
||||
units_skipped: 0,
|
||||
customers_created: 0,
|
||||
customers_skipped: 0,
|
||||
suppliers_created: 0,
|
||||
suppliers_skipped: 0,
|
||||
total_invoices: invoices.length,
|
||||
inbox_count: r.message.inbox_count,
|
||||
outbox_count: r.message.outbox_count
|
||||
}, 0, frm);
|
||||
},
|
||||
error: function(r) {
|
||||
frappe.hide_progress();
|
||||
frappe.msgprint(__('Error loading invoices: ') + (r.message || 'Unknown error'));
|
||||
}
|
||||
});
|
||||
// ИЗМЕНЕНО: Теперь используем поэтапную загрузку вместо одного большого запроса
|
||||
start_staged_invoice_loading(frm, values);
|
||||
}
|
||||
|
||||
function process_invoices_for_reference_data(invoices, token, accumulated_data, current_index = 0, frm) {
|
||||
|
|
@ -1027,6 +991,7 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
|
|||
if (current_index === 0) {
|
||||
window.cancelReferenceLoading = false;
|
||||
|
||||
// Показываем прогресс-бар для обработки инвойсов
|
||||
frappe.show_progress(__('Processing Reference Data'), 0, invoices.length,
|
||||
__('Processing {0} invoices...', [invoices.length]), null, true);
|
||||
}
|
||||
|
|
@ -1091,17 +1056,49 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
|
|||
'source_type': source_type
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message && r.message.success && r.message.stats) {
|
||||
// Обновляем накопленные данные
|
||||
const stats = r.message.stats;
|
||||
accumulated_data.items_created += stats.items_created || 0;
|
||||
accumulated_data.items_skipped += stats.items_skipped || 0;
|
||||
accumulated_data.units_created += stats.units_created || 0;
|
||||
accumulated_data.units_skipped += stats.units_skipped || 0;
|
||||
accumulated_data.customers_created += stats.customers_created || 0;
|
||||
accumulated_data.customers_skipped += stats.customers_skipped || 0;
|
||||
accumulated_data.suppliers_created += stats.suppliers_created || 0;
|
||||
accumulated_data.suppliers_skipped += stats.suppliers_skipped || 0;
|
||||
if (r.message) {
|
||||
// Проверяем, есть ли новый токен в ответе
|
||||
if (r.message.new_token) {
|
||||
// Обновляем токен для последующих запросов
|
||||
token = r.message.new_token;
|
||||
}
|
||||
|
||||
// Если ошибка unauthorized и нет нового токена - пробуем обновить токен
|
||||
if (r.message.error === 'unauthorized' && !r.message.new_token) {
|
||||
// Получаем новый токен из Asan Login
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.refresh_token_from_asan_login',
|
||||
callback: function(token_r) {
|
||||
if (token_r.message && token_r.message.success) {
|
||||
// Обновляем токен и повторяем текущий инвойс
|
||||
token = token_r.message.token;
|
||||
setTimeout(function() {
|
||||
if (!window.cancelReferenceLoading) {
|
||||
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index, frm);
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
// Не удалось обновить токен - останавливаем обработку
|
||||
frappe.hide_progress();
|
||||
frappe.msgprint(__('Authentication expired. Please re-authenticate and try again.'));
|
||||
}
|
||||
}
|
||||
});
|
||||
return; // Выходим, чтобы не продолжать со старым токеном
|
||||
}
|
||||
|
||||
// Обработка успешного результата
|
||||
if (r.message.success && r.message.stats) {
|
||||
const stats = r.message.stats;
|
||||
accumulated_data.items_created += stats.items_created || 0;
|
||||
accumulated_data.items_skipped += stats.items_skipped || 0;
|
||||
accumulated_data.units_created += stats.units_created || 0;
|
||||
accumulated_data.units_skipped += stats.units_skipped || 0;
|
||||
accumulated_data.customers_created += stats.customers_created || 0;
|
||||
accumulated_data.customers_skipped += stats.customers_skipped || 0;
|
||||
accumulated_data.suppliers_created += stats.suppliers_created || 0;
|
||||
accumulated_data.suppliers_skipped += stats.suppliers_skipped || 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Переходим к следующему инвойсу
|
||||
|
|
@ -1113,12 +1110,37 @@ function process_invoices_for_reference_data(invoices, token, accumulated_data,
|
|||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// При ошибке переходим к следующему инвойсу
|
||||
setTimeout(function() {
|
||||
if (!window.cancelReferenceLoading) {
|
||||
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm);
|
||||
}
|
||||
}, 100);
|
||||
// При ошибке сети пробуем обновить токен
|
||||
if (xhr.status === 401) {
|
||||
frappe.call({
|
||||
method: 'invoice_az.api.refresh_token_from_asan_login',
|
||||
callback: function(token_r) {
|
||||
if (token_r.message && token_r.message.success) {
|
||||
// Обновляем токен и повторяем текущий инвойс
|
||||
token = token_r.message.token;
|
||||
setTimeout(function() {
|
||||
if (!window.cancelReferenceLoading) {
|
||||
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index, frm);
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
// Переходим к следующему инвойсу
|
||||
setTimeout(function() {
|
||||
if (!window.cancelReferenceLoading) {
|
||||
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// При других ошибках переходим к следующему инвойсу
|
||||
setTimeout(function() {
|
||||
if (!window.cancelReferenceLoading) {
|
||||
process_invoices_for_reference_data(invoices, token, accumulated_data, current_index + 1, frm);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -2140,3 +2162,192 @@ function complete_authentication_settings(asan_login_name, callback) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ===== НОВЫЕ JAVASCRIPT ФУНКЦИИ =====
|
||||
|
||||
// НОВАЯ ФУНКЦИЯ: Поэтапная загрузка инвойсов
|
||||
function start_staged_invoice_loading(frm, values) {
|
||||
// Инициализируем данные для поэтапной загрузки
|
||||
const loading_state = {
|
||||
date_from: moment(values.date_from).format("DD-MM-YYYY 00:00"),
|
||||
date_to: moment(values.date_to).format("DD-MM-YYYY 23:59"),
|
||||
all_invoices: [],
|
||||
current_offset: 0,
|
||||
total_loaded: 0,
|
||||
inbox_completed: false,
|
||||
outbox_completed: false,
|
||||
token: null,
|
||||
last_update_count: 0 // Для отслеживания когда показывать обновление
|
||||
};
|
||||
|
||||
window.cancelReferenceLoading = false;
|
||||
|
||||
// ВОЗВРАЩАЕМ: Модальное окно в центре экрана
|
||||
const loading_dialog = frappe.msgprint({
|
||||
title: __('Loading Data'),
|
||||
message: __('Fetching invoices from E-Taxes, please wait...'),
|
||||
indicator: 'blue'
|
||||
});
|
||||
|
||||
// Сохраняем ссылку на диалог в состоянии
|
||||
loading_state.loading_dialog = loading_dialog;
|
||||
|
||||
// Начинаем загрузку inbox
|
||||
load_invoice_batch(loading_state, 'inbox', frm);
|
||||
}
|
||||
|
||||
// НОВАЯ ФУНКЦИЯ: Загрузка одной порции инвойсов
|
||||
function load_invoice_batch(loading_state, source, frm) {
|
||||
if (window.cancelReferenceLoading) {
|
||||
// Закрываем диалог при отмене
|
||||
if (loading_state.loading_dialog && loading_state.loading_dialog.hide) {
|
||||
loading_state.loading_dialog.hide();
|
||||
}
|
||||
frappe.show_alert({
|
||||
message: __('Loading cancelled'),
|
||||
indicator: 'red'
|
||||
}, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
const method = source === 'inbox' ?
|
||||
'invoice_az.api.load_single_invoice_batch' :
|
||||
'invoice_az.api.load_single_sales_invoice_batch';
|
||||
|
||||
frappe.call({
|
||||
method: method,
|
||||
args: {
|
||||
date_from: loading_state.date_from,
|
||||
date_to: loading_state.date_to,
|
||||
offset: loading_state.current_offset
|
||||
},
|
||||
callback: function(r) {
|
||||
if (!r.message || !r.message.success) {
|
||||
if (source === 'inbox') {
|
||||
// Если inbox не удался - закрываем диалог и показываем ошибку
|
||||
if (loading_state.loading_dialog && loading_state.loading_dialog.hide) {
|
||||
loading_state.loading_dialog.hide();
|
||||
}
|
||||
frappe.msgprint(__('Failed to load invoices: ') + (r.message?.message || 'Unknown error'));
|
||||
return;
|
||||
} else {
|
||||
// Если outbox не удался - продолжаем без него
|
||||
loading_state.outbox_completed = true;
|
||||
check_loading_completion(loading_state, frm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const batch_invoices = r.message.invoices || [];
|
||||
const token = r.message.token;
|
||||
const hasMore = r.message.hasMore || false;
|
||||
|
||||
// Сохраняем токен при первом запросе
|
||||
if (!loading_state.token) {
|
||||
loading_state.token = token;
|
||||
}
|
||||
|
||||
// Помечаем источник для каждого инвойса
|
||||
batch_invoices.forEach(invoice => {
|
||||
invoice._source = source;
|
||||
});
|
||||
|
||||
// Добавляем к общему списку
|
||||
loading_state.all_invoices = loading_state.all_invoices.concat(batch_invoices);
|
||||
loading_state.total_loaded += batch_invoices.length;
|
||||
|
||||
// ИЗМЕНЕНО: Обновляем сообщение каждые 200 инвойсов или при смене источника
|
||||
const should_update = (
|
||||
Math.floor(loading_state.total_loaded / 200) > Math.floor(loading_state.last_update_count / 200) ||
|
||||
(source === 'outbox' && loading_state.last_update_count === 0) // Первое сообщение для outbox
|
||||
);
|
||||
|
||||
if (should_update && loading_state.loading_dialog && loading_state.loading_dialog.$wrapper) {
|
||||
const progress_text = source === 'inbox' ?
|
||||
__('Finding purchase invoices: {0} found...', [loading_state.total_loaded]) :
|
||||
__('Finding sales invoices: {0} total found...', [loading_state.total_loaded]);
|
||||
|
||||
// Пробуем обновить текст в модальном окне
|
||||
try {
|
||||
const $wrapper = loading_state.loading_dialog.$wrapper;
|
||||
const $modal_body = $wrapper.find('.modal-body');
|
||||
if ($modal_body.length > 0) {
|
||||
$modal_body.html(`<p>${progress_text}</p>`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Could not update modal text:', e);
|
||||
}
|
||||
|
||||
loading_state.last_update_count = loading_state.total_loaded;
|
||||
}
|
||||
|
||||
if (hasMore && batch_invoices.length > 0) {
|
||||
// Есть еще данные - загружаем следующую порцию
|
||||
loading_state.current_offset += 200;
|
||||
setTimeout(() => load_invoice_batch(loading_state, source, frm), 100);
|
||||
} else {
|
||||
// Завершили загрузку из этого источника
|
||||
if (source === 'inbox') {
|
||||
loading_state.inbox_completed = true;
|
||||
// Начинаем загрузку outbox
|
||||
loading_state.current_offset = 0;
|
||||
loading_state.last_update_count = 0; // Сбрасываем счетчик для outbox
|
||||
setTimeout(() => load_invoice_batch(loading_state, 'outbox', frm), 100);
|
||||
} else {
|
||||
loading_state.outbox_completed = true;
|
||||
check_loading_completion(loading_state, frm);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(r) {
|
||||
if (source === 'inbox') {
|
||||
// Закрываем диалог при ошибке
|
||||
if (loading_state.loading_dialog && loading_state.loading_dialog.hide) {
|
||||
loading_state.loading_dialog.hide();
|
||||
}
|
||||
frappe.msgprint(__('Error loading invoices: ') + (r.message || 'Network error'));
|
||||
} else {
|
||||
// Ошибка outbox - продолжаем без него
|
||||
loading_state.outbox_completed = true;
|
||||
check_loading_completion(loading_state, frm);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// НОВАЯ ФУНКЦИЯ: Проверка завершения загрузки
|
||||
function check_loading_completion(loading_state, frm) {
|
||||
if (loading_state.inbox_completed && loading_state.outbox_completed) {
|
||||
// ИСПРАВЛЕНО: Закрываем диалог загрузки
|
||||
if (loading_state.loading_dialog && loading_state.loading_dialog.hide) {
|
||||
loading_state.loading_dialog.hide();
|
||||
}
|
||||
|
||||
if (loading_state.all_invoices.length === 0) {
|
||||
frappe.msgprint(__('No invoices found for the selected period.'));
|
||||
return;
|
||||
}
|
||||
|
||||
// ИЗМЕНЕНО: Показываем результат загрузки
|
||||
frappe.show_alert({
|
||||
message: __('Found {0} invoices. Starting processing...', [loading_state.all_invoices.length]),
|
||||
indicator: 'green'
|
||||
}, 3); // Показываем на 3 секунды
|
||||
|
||||
// Небольшая задержка и начинаем обработку
|
||||
setTimeout(() => {
|
||||
process_invoices_for_reference_data(loading_state.all_invoices, loading_state.token, {
|
||||
items_created: 0,
|
||||
items_skipped: 0,
|
||||
units_created: 0,
|
||||
units_skipped: 0,
|
||||
customers_created: 0,
|
||||
customers_skipped: 0,
|
||||
suppliers_created: 0,
|
||||
suppliers_skipped: 0,
|
||||
total_invoices: loading_state.all_invoices.length
|
||||
}, 0, frm);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,10 +88,26 @@ def get_sales_invoices(token, filters=None):
|
|||
|
||||
# Check for 401 error
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
# Получаем свежий токен из Asan Login
|
||||
from invoice_az.auth import get_default_asan_login
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if asan_login_settings.get('found'):
|
||||
fresh_token = asan_login_settings['main_token']
|
||||
headers["x-authorization"] = f"Bearer {fresh_token}"
|
||||
|
||||
# Повторяем запрос с новым токеном
|
||||
response = requests.post(url, json=payload, headers=headers)
|
||||
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
|
|
@ -165,10 +181,26 @@ def get_sales_invoice_details(token, invoice_id):
|
|||
|
||||
# Check for 401 error
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
# Получаем свежий токен из Asan Login
|
||||
from invoice_az.auth import get_default_asan_login
|
||||
asan_login_settings = get_default_asan_login()
|
||||
if asan_login_settings.get('found'):
|
||||
fresh_token = asan_login_settings['main_token']
|
||||
headers["x-authorization"] = f"Bearer {fresh_token}"
|
||||
|
||||
# Повторяем запрос с новым токеном
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 401:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"error": "unauthorized",
|
||||
"message": "Authentication required. Please login again."
|
||||
}
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
|
|
@ -226,6 +258,12 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
if isinstance(invoice_data, str):
|
||||
invoice_data = json.loads(invoice_data)
|
||||
|
||||
# ДОБАВЛЕНО: Логируем входящие данные инвойса для отладки дат
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Invoice data keys: {list(invoice_data.keys())}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] createdAt field: {invoice_data.get('createdAt')}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] createdAt type: {type(invoice_data.get('createdAt'))}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] schedule_date parameter: {schedule_date}", "Sales Date Debug")
|
||||
|
||||
# Get active settings
|
||||
settings = get_active_settings()
|
||||
if not settings:
|
||||
|
|
@ -274,6 +312,57 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
'message': 'Default warehouse not found. Please specify warehouse in E-Taxes settings.'
|
||||
}
|
||||
|
||||
# ИСПРАВЛЕНО: Определяем дату ДО создания Sales Order
|
||||
invoice_date = None
|
||||
created_at_raw = invoice_data.get("createdAt")
|
||||
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Raw createdAt value: '{created_at_raw}'", "Sales Date Debug")
|
||||
|
||||
if created_at_raw:
|
||||
try:
|
||||
# Попробуем разные форматы даты
|
||||
if isinstance(created_at_raw, str):
|
||||
# Если это строка, попробуем разные форматы
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Trying to parse string date: '{created_at_raw}'", "Sales Date Debug")
|
||||
|
||||
# Попробуем стандартный парсинг ERPNext
|
||||
invoice_date = frappe.utils.getdate(created_at_raw)
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Successfully parsed date: {invoice_date}", "Sales Date Debug")
|
||||
|
||||
elif hasattr(created_at_raw, 'date'):
|
||||
# Если это datetime объект
|
||||
invoice_date = created_at_raw.date()
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Extracted date from datetime: {invoice_date}", "Sales Date Debug")
|
||||
else:
|
||||
# Попробуем привести к строке и парсить
|
||||
date_str = str(created_at_raw)
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Converting to string and parsing: '{date_str}'", "Sales Date Debug")
|
||||
invoice_date = frappe.utils.getdate(date_str)
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Successfully parsed converted date: {invoice_date}", "Sales Date Debug")
|
||||
|
||||
except Exception as e:
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Error parsing createdAt '{created_at_raw}': {str(e)}", "Sales Date Debug")
|
||||
invoice_date = frappe.utils.today()
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Using fallback date: {invoice_date}", "Sales Date Debug")
|
||||
else:
|
||||
invoice_date = frappe.utils.today()
|
||||
frappe.log_error(f"[SALES DATE DEBUG] No createdAt found, using today: {invoice_date}", "Sales Date Debug")
|
||||
|
||||
# ИСПРАВЛЕНО: Определяем date_to_use с ПРИОРИТЕТОМ даты из E-taxes
|
||||
# ПРИОРИТЕТ: 1) дата из E-taxes, 2) schedule_date, 3) сегодня
|
||||
date_to_use = None
|
||||
if invoice_date:
|
||||
date_to_use = invoice_date
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Using invoice_date from E-taxes for date_to_use: {date_to_use}", "Sales Date Debug")
|
||||
elif schedule_date:
|
||||
date_to_use = frappe.utils.getdate(schedule_date)
|
||||
frappe.log_error(f"[SALES DATE DEBUG] No E-taxes date, using provided schedule_date: {date_to_use}", "Sales Date Debug")
|
||||
else:
|
||||
date_to_use = frappe.utils.today()
|
||||
frappe.log_error(f"[SALES DATE DEBUG] No dates available, using today for date_to_use: {date_to_use}", "Sales Date Debug")
|
||||
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Final date_to_use: {date_to_use}", "Sales Date Debug")
|
||||
|
||||
# ИЗМЕНЕНО: Создаем Sales Order сначала (аналогично Purchase Order логике)
|
||||
if sales_order_name:
|
||||
so = frappe.get_doc('Sales Order', sales_order_name)
|
||||
|
|
@ -300,15 +389,13 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
'message': f'No mapping found for customer: {receiver.get("name", "")}'
|
||||
}
|
||||
|
||||
# Set dates
|
||||
so.transaction_date = frappe.utils.today()
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Final invoice_date for SO: {invoice_date}", "Sales Date Debug")
|
||||
|
||||
if schedule_date:
|
||||
so.delivery_date = schedule_date
|
||||
elif invoice_data.get("creationDate"):
|
||||
so.delivery_date = frappe.utils.getdate(invoice_data.get("creationDate"))
|
||||
else:
|
||||
so.delivery_date = frappe.utils.today()
|
||||
so.transaction_date = invoice_date
|
||||
so.delivery_date = date_to_use
|
||||
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SO transaction_date set to: {so.transaction_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SO delivery_date set to: {so.delivery_date}", "Sales Date Debug")
|
||||
|
||||
so.company = frappe.defaults.get_user_default('Company')
|
||||
|
||||
|
|
@ -320,14 +407,6 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
if sales_order_name and invoice_data.get("items"):
|
||||
so.items = []
|
||||
|
||||
date_to_use = None
|
||||
if schedule_date:
|
||||
date_to_use = schedule_date
|
||||
elif invoice_data.get("creationDate"):
|
||||
date_to_use = frappe.utils.getdate(invoice_data.get("creationDate"))
|
||||
else:
|
||||
date_to_use = frappe.utils.today()
|
||||
|
||||
# Variable for tracking added items
|
||||
added_items_count = 0
|
||||
unmatched_items = []
|
||||
|
|
@ -416,9 +495,11 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
so_item.rate = item.get("pricePerUnit", 0)
|
||||
so_item.amount = item.get("cost", 0)
|
||||
so_item.uom = mapped_uom
|
||||
so_item.delivery_date = date_to_use # ИЗМЕНЕНО: delivery_date вместо schedule_date
|
||||
so_item.delivery_date = date_to_use # ИСПРАВЛЕНО: Используем правильную дату
|
||||
so_item.warehouse = default_warehouse
|
||||
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Item {so_item.item_code} delivery_date set to: {so_item.delivery_date}", "Sales Date Debug")
|
||||
|
||||
so.append("items", so_item)
|
||||
added_items_count += 1
|
||||
|
||||
|
|
@ -480,19 +561,32 @@ def import_sales_invoice_with_mapping(invoice_data, sales_order_name=None, sched
|
|||
except Exception as e:
|
||||
frappe.log_error(f"Error adding ƏDV tax: {str(e)}", "Sales Order Tax Error")
|
||||
|
||||
# ДОБАВЛЕНО: Логируем финальные даты перед сохранением
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Final SO transaction_date: {so.transaction_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Final SO delivery_date: {so.delivery_date}", "Sales Date Debug")
|
||||
|
||||
# Save document
|
||||
so.save()
|
||||
|
||||
# Additional check that all delivery_date and warehouse are set
|
||||
# ИСПРАВЛЕНО: Убедимся что все delivery_date установлены правильно
|
||||
for item in so.items:
|
||||
if not item.delivery_date:
|
||||
item.delivery_date = date_to_use
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Fixed missing delivery_date for item {item.item_code}: {item.delivery_date}", "Sales Date Debug")
|
||||
if not item.warehouse:
|
||||
item.warehouse = default_warehouse
|
||||
|
||||
# Save again to ensure changes are applied
|
||||
so.save()
|
||||
|
||||
# ДОБАВЛЕНО: Логируем даты после сохранения
|
||||
frappe.log_error(f"[SALES DATE DEBUG] After save SO transaction_date: {so.transaction_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] After save SO delivery_date: {so.delivery_date}", "Sales Date Debug")
|
||||
|
||||
# Логируем delivery_date каждого item после сохранения
|
||||
for item in so.items:
|
||||
frappe.log_error(f"[SALES DATE DEBUG] After save item {item.item_code} delivery_date: {item.delivery_date}", "Sales Date Debug")
|
||||
|
||||
# ИЗМЕНЕНИЕ: Создаем E-Taxes Sales и устанавливаем связь ДО submit'а
|
||||
invoice_id = invoice_data.get('id', '')
|
||||
serial_number = invoice_data.get('serialNumber', '')
|
||||
|
|
@ -562,18 +656,43 @@ def create_sales_invoice_from_order(sales_order_name):
|
|||
# Импортируем стандартную функцию из модуля Sales Order
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
|
||||
|
||||
# Получаем Sales Order ДО создания invoice
|
||||
so_doc = frappe.get_doc('Sales Order', sales_order_name)
|
||||
|
||||
# ИСПРАВЛЕНО: Правильная обработка дат
|
||||
if so_doc.transaction_date:
|
||||
posting_date = so_doc.transaction_date
|
||||
# Конвертируем в datetime.date если это строка
|
||||
if isinstance(posting_date, str):
|
||||
posting_date = frappe.utils.getdate(posting_date)
|
||||
else:
|
||||
posting_date = frappe.utils.getdate(frappe.utils.nowdate())
|
||||
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SO transaction_date: {so_doc.transaction_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI posting_date to set: {posting_date}", "Sales Date Debug")
|
||||
|
||||
# Создаем Sales Invoice из Sales Order
|
||||
si_doc = make_sales_invoice(sales_order_name)
|
||||
|
||||
# Устанавливаем даты
|
||||
si_doc.posting_date = frappe.utils.nowdate()
|
||||
si_doc.due_date = frappe.utils.add_days(frappe.utils.nowdate(), 30)
|
||||
# ДОБАВЛЕНО: Логируем дату сразу после создания
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI posting_date after make_sales_invoice: {si_doc.posting_date}", "Sales Date Debug")
|
||||
|
||||
# ИСПРАВЛЕНО: Устанавливаем дату и флаг posting_time ДО других операций
|
||||
si_doc.posting_date = posting_date
|
||||
si_doc.set_posting_time = 1 # Важно! Разрешаем установку даты в прошлом
|
||||
|
||||
# ИСПРАВЛЕНО: due_date всегда основана на posting_date из E-taxes (без коррекции на "сегодня")
|
||||
due_date = frappe.utils.add_days(posting_date, 30)
|
||||
si_doc.due_date = due_date
|
||||
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI posting_date before insert: {si_doc.posting_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI due_date before insert (E-taxes based): {si_doc.due_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI set_posting_time: {si_doc.set_posting_time}", "Sales Date Debug")
|
||||
|
||||
# ДОБАВЛЕНО: Устанавливаем галочку is_taxes_doc для Sales Invoice
|
||||
si_doc.is_taxes_doc = 1
|
||||
|
||||
# ДОБАВЛЕНО: Копируем налоги из Sales Order
|
||||
so_doc = frappe.get_doc('Sales Order', sales_order_name)
|
||||
if so_doc.taxes:
|
||||
si_doc.taxes = []
|
||||
for tax_row in so_doc.taxes:
|
||||
|
|
@ -588,6 +707,30 @@ def create_sales_invoice_from_order(sales_order_name):
|
|||
# Сохраняем документ
|
||||
si_doc.insert(ignore_permissions=True)
|
||||
|
||||
# ДОБАВЛЕНО: Логируем дату после insert
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI posting_date after insert: {si_doc.posting_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI due_date after insert: {si_doc.due_date}", "Sales Date Debug")
|
||||
|
||||
# ДОБАВЛЕНО: Если даты все еще неправильные, принудительно устанавливаем
|
||||
expected_due_date = frappe.utils.add_days(posting_date, 30)
|
||||
if si_doc.posting_date != posting_date or si_doc.due_date != expected_due_date:
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Date mismatch detected!", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Expected posting_date: {posting_date}, Got: {si_doc.posting_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] Expected due_date: {expected_due_date}, Got: {si_doc.due_date}", "Sales Date Debug")
|
||||
|
||||
# Принудительно устанавливаем даты через DB
|
||||
frappe.db.set_value('Sales Invoice', si_doc.name, {
|
||||
'posting_date': posting_date,
|
||||
'due_date': expected_due_date,
|
||||
'set_posting_time': 1
|
||||
})
|
||||
frappe.db.commit()
|
||||
|
||||
# Перезагружаем документ
|
||||
si_doc = frappe.get_doc('Sales Invoice', si_doc.name)
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI posting_date after DB update: {si_doc.posting_date}", "Sales Date Debug")
|
||||
frappe.log_error(f"[SALES DATE DEBUG] SI due_date after DB update: {si_doc.due_date}", "Sales Date Debug")
|
||||
|
||||
frappe.log_error(f"Sales Invoice {si_doc.name} created from Sales Order {sales_order_name}", "Sales Invoice Creation")
|
||||
|
||||
# Возвращаем имя созданного Sales Invoice
|
||||
|
|
|
|||
Loading…
Reference in New Issue