|
|
|
|
@ -2,10 +2,8 @@ import frappe
|
|
|
|
|
import datetime
|
|
|
|
|
from .orders_utills import (check_customer_if_exists,
|
|
|
|
|
create_address_contact,
|
|
|
|
|
get_sps_market_place_order_ID,
|
|
|
|
|
create_sps_sales_order_item_row,
|
|
|
|
|
check_and_create_contact,
|
|
|
|
|
create_customer_if_not_exists,
|
|
|
|
|
get_path_to_skip,
|
|
|
|
|
exclude_skipped_file_paths)
|
|
|
|
|
# from .order_acknowledgement import get_sales_order_pyload
|
|
|
|
|
@ -79,19 +77,10 @@ def enqueue_sps_sync_order(doc:str):
|
|
|
|
|
# """
|
|
|
|
|
|
|
|
|
|
# try:
|
|
|
|
|
# data = retrieve_sps_order_urls(setting_doc_name=doc)
|
|
|
|
|
# frappe.log_error(title="Get SPS Sales Order Polling", message=f"{data}")
|
|
|
|
|
# sales_orders = frappe.get_all("Sales Order", fields = ["name", "custom_sales_order_path"])
|
|
|
|
|
# filtered_sales_orders = {order["custom_sales_order_path"] for order in sales_orders if order["custom_sales_order_path"]}
|
|
|
|
|
# frappe.log_error(
|
|
|
|
|
# title = "Previously Synced Sales Order",
|
|
|
|
|
# message = f"{filtered_sales_orders}"
|
|
|
|
|
# )
|
|
|
|
|
# results = data.get("results", [])
|
|
|
|
|
# access_token = ""
|
|
|
|
|
# failed_requests = []
|
|
|
|
|
# skipped_sales_orders = []
|
|
|
|
|
|
|
|
|
|
# setting_doc = frappe.get_doc("SPS Integration Settings", doc)
|
|
|
|
|
# SPS_URL = setting_doc.get_sales_order_url
|
|
|
|
|
# expires_on = get_datetime(setting_doc.expires_on)
|
|
|
|
|
# current_time = now_datetime()
|
|
|
|
|
# if expires_on <= current_time or (expires_on - current_time).total_seconds() <= 300:
|
|
|
|
|
@ -99,23 +88,19 @@ def enqueue_sps_sync_order(doc:str):
|
|
|
|
|
# access_token = setting_doc.get_password("access_token")
|
|
|
|
|
# else:
|
|
|
|
|
# access_token = setting_doc.get_password("access_token")
|
|
|
|
|
|
|
|
|
|
# order_url_path = retrieve_sps_order_urls(setting_doc_name=doc).get("results", [])
|
|
|
|
|
# headers = {
|
|
|
|
|
# "Authorization": f"Bearer {access_token}",
|
|
|
|
|
# "Content-Type": "application/json"
|
|
|
|
|
# }
|
|
|
|
|
|
|
|
|
|
# for record in results:
|
|
|
|
|
# so_url = record.get("url")
|
|
|
|
|
# path = record.get("path", "").split("/")[-1]
|
|
|
|
|
|
|
|
|
|
# if path in filtered_sales_orders:
|
|
|
|
|
# skipped_sales_orders.append(path)
|
|
|
|
|
# continue
|
|
|
|
|
# if so_url:
|
|
|
|
|
# # Filtered path url for sales order to avoid duplicates
|
|
|
|
|
# filtered_url_path = exclude_skipped_file_paths(paths=[d.get("path").split("/")[-1] for d in order_url_path])
|
|
|
|
|
# for path in filtered_url_path:
|
|
|
|
|
# # path = record.get("path", "").split("/")[-1]
|
|
|
|
|
# url = f"{SPS_URL}{path}"
|
|
|
|
|
# if url:
|
|
|
|
|
# try:
|
|
|
|
|
# response = requests.get(url=so_url, headers=headers)
|
|
|
|
|
|
|
|
|
|
# response = requests.get(url=url, headers=headers)
|
|
|
|
|
# if response.status_code == 200:
|
|
|
|
|
# sales_order = create_sps_sales_order(
|
|
|
|
|
# data=response.json(),
|
|
|
|
|
@ -123,38 +108,26 @@ def enqueue_sps_sync_order(doc:str):
|
|
|
|
|
# path=path
|
|
|
|
|
# )
|
|
|
|
|
# if frappe.db.exists("Sales Order", {"name": sales_order, "docstatus":1}):
|
|
|
|
|
# sales_order_acknowledgement(data=response.json())
|
|
|
|
|
# # Once the sales order sync in ERPNext will ack the sales order
|
|
|
|
|
# # sales_order_acknowledgement(data=response.json())
|
|
|
|
|
# path_to_skip = frappe.new_doc("Paths to Skip")
|
|
|
|
|
# path_to_skip.file_path = path
|
|
|
|
|
# path_to_skip.save()
|
|
|
|
|
# frappe.db.commit()
|
|
|
|
|
# else:
|
|
|
|
|
# failed_requests.append(so_url)
|
|
|
|
|
# failed_requests.append(url)
|
|
|
|
|
# except requests.RequestException as e:
|
|
|
|
|
# failed_requests.append(so_url)
|
|
|
|
|
# frappe.log_error(title="Request Error", message=str(e))
|
|
|
|
|
|
|
|
|
|
# api_info = {
|
|
|
|
|
# "Total Sales Order Payload": len(results),
|
|
|
|
|
# "Failed Requests": failed_requests
|
|
|
|
|
# "Total sales order payload": len(filtered_url_path),
|
|
|
|
|
# "Failed sales order": failed_requests
|
|
|
|
|
# }
|
|
|
|
|
# frappe.log_error(title="Skipped Synced Sales Order", message=f"{skipped_sales_orders}")
|
|
|
|
|
# frappe.log_error(title="API Detail Info", message=f"{api_info}")
|
|
|
|
|
|
|
|
|
|
# except Exception as e:
|
|
|
|
|
# frappe.log_error(title="Enqueuing Error", message=f"{frappe.get_traceback()}")
|
|
|
|
|
|
|
|
|
|
# frappe.log_error(title="Error while sales order sycing", message=f"{frappe.get_traceback()}")
|
|
|
|
|
|
|
|
|
|
@frappe.whitelist(allow_guest=True)
|
|
|
|
|
def get_sps_sales_order(doc: str):
|
|
|
|
|
"""
|
|
|
|
|
Calls the SPS API to retrieve a list of all Sales Order URLs.
|
|
|
|
|
Iterates through the URLs, fetches the corresponding payloads,
|
|
|
|
|
syncs them into ERPNext, and stores the Sales Order URL paths.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
doc (str): The settings document.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
None
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
access_token = ""
|
|
|
|
|
failed_requests = []
|
|
|
|
|
@ -162,48 +135,59 @@ def get_sps_sales_order(doc: str):
|
|
|
|
|
SPS_URL = setting_doc.get_sales_order_url
|
|
|
|
|
expires_on = get_datetime(setting_doc.expires_on)
|
|
|
|
|
current_time = now_datetime()
|
|
|
|
|
|
|
|
|
|
if expires_on <= current_time or (expires_on - current_time).total_seconds() <= 300:
|
|
|
|
|
refresh_access_token(setting_doc_name=doc)
|
|
|
|
|
access_token = setting_doc.get_password("access_token")
|
|
|
|
|
frappe.log_error("SPS Token Refresh", "Token refreshed")
|
|
|
|
|
else:
|
|
|
|
|
access_token = setting_doc.get_password("access_token")
|
|
|
|
|
|
|
|
|
|
order_url_path = retrieve_sps_order_urls(setting_doc_name=doc).get("results", [])
|
|
|
|
|
frappe.log_error("SPS URL Fetch", f"Fetched URLs: {order_url_path}")
|
|
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
|
"Authorization": f"Bearer {access_token}",
|
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
|
}
|
|
|
|
|
# Filtered path url for sales order to avoid duplicates
|
|
|
|
|
|
|
|
|
|
filtered_url_path = exclude_skipped_file_paths(paths=[d.get("path").split("/")[-1] for d in order_url_path])
|
|
|
|
|
frappe.log_error("Filtered Paths", f"{filtered_url_path}")
|
|
|
|
|
|
|
|
|
|
for path in filtered_url_path:
|
|
|
|
|
# path = record.get("path", "").split("/")[-1]
|
|
|
|
|
url = f"{SPS_URL}{path}"
|
|
|
|
|
if url:
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get(url=url, headers=headers)
|
|
|
|
|
if response.status_code == 200:
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get(url=url, headers=headers)
|
|
|
|
|
if response.status_code == 200:
|
|
|
|
|
try:
|
|
|
|
|
sales_order = create_sps_sales_order(
|
|
|
|
|
data=response.json(),
|
|
|
|
|
setting_doc=doc,
|
|
|
|
|
path=path
|
|
|
|
|
)
|
|
|
|
|
if frappe.db.exists("Sales Order", {"name": sales_order, "docstatus":1}):
|
|
|
|
|
# Once the sales order sync in ERPNext will ack the sales order
|
|
|
|
|
# sales_order_acknowledgement(data=response.json())
|
|
|
|
|
if frappe.db.exists("Sales Order", {"name": sales_order, "docstatus": 1}):
|
|
|
|
|
# Order ack
|
|
|
|
|
sales_order_acknowledgement(data=response.json(),path=path)
|
|
|
|
|
path_to_skip = frappe.new_doc("Paths to Skip")
|
|
|
|
|
path_to_skip.file_path = path
|
|
|
|
|
path_to_skip.save()
|
|
|
|
|
frappe.db.commit()
|
|
|
|
|
else:
|
|
|
|
|
failed_requests.append(url)
|
|
|
|
|
except requests.RequestException as e:
|
|
|
|
|
frappe.log_error(title="Request Error", message=str(e))
|
|
|
|
|
except Exception as inner_e:
|
|
|
|
|
frappe.log_error("Error in create_sps_sales_order", frappe.get_traceback())
|
|
|
|
|
else:
|
|
|
|
|
failed_requests.append(url)
|
|
|
|
|
frappe.log_error("Non-200 SPS Response", f"{url} returned {response.status_code}")
|
|
|
|
|
except requests.RequestException as e:
|
|
|
|
|
frappe.log_error(title="Request Error", message=f"{str(e)}\nURL: {url}")
|
|
|
|
|
|
|
|
|
|
api_info = {
|
|
|
|
|
"Total sales order payload": len(filtered_url_path),
|
|
|
|
|
"Failed sales order": failed_requests
|
|
|
|
|
}
|
|
|
|
|
frappe.log_error(title="API Detail Info", message=f"{api_info}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
frappe.log_error(title="Error while sales order sycing", message=f"{frappe.get_traceback()}")
|
|
|
|
|
frappe.log_error(title="Error while sales order syncing", message=frappe.get_traceback())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -333,10 +317,130 @@ def create_sps_sales_order(data: dict, setting_doc: str, path: str) -> None:
|
|
|
|
|
error_msg = frappe.get_traceback()
|
|
|
|
|
frappe.log_error(
|
|
|
|
|
title="Sales Order Creation Error",
|
|
|
|
|
message=f"Marketplace: SPS\n\nTraceback:\n{error_msg}",
|
|
|
|
|
message=f"Marketplace: SPS\n\nTraceback:\n{error_msg}\\Order not synced:{data}",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_sps_sales_order_console(data: dict, setting_doc: str, path: str) -> None:
|
|
|
|
|
print("🔍 Step 1: Fetching order header from payload...")
|
|
|
|
|
order_header = data.get("Header", {}).get("OrderHeader", {})
|
|
|
|
|
po_no = order_header.get("PurchaseOrderNumber")
|
|
|
|
|
consumer_order_number = order_header.get("CustomerOrderNumber")
|
|
|
|
|
po_date = order_header.get("PurchaseOrderDate")
|
|
|
|
|
print(f"➡️ PO No: {po_no}, Customer Order No: {consumer_order_number}, PO Date: {po_date}")
|
|
|
|
|
|
|
|
|
|
print("🔧 Step 2: Fetching SPS Integration Settings...")
|
|
|
|
|
try:
|
|
|
|
|
doc = frappe.get_doc("SPS Integration Settings", setting_doc)
|
|
|
|
|
print(f"✅ Settings fetched for: {setting_doc}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Failed to fetch SPS Integration Settings: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("📅 Step 3: Extracting delivery date from header...")
|
|
|
|
|
try:
|
|
|
|
|
delivery_date = next(
|
|
|
|
|
(date.get("Date") for date in data.get("Header", {}).get("Dates", [])
|
|
|
|
|
if date.get("DateTimeQualifier") == "001"), None
|
|
|
|
|
)
|
|
|
|
|
print(f"➡️ Delivery Date (001): {delivery_date}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Failed to extract delivery date: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("👤 Step 4: Checking/creating customer...")
|
|
|
|
|
try:
|
|
|
|
|
_, customer_name = check_customer_if_exists(data)
|
|
|
|
|
print(f"✅ Customer resolved: {customer_name}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error while checking/creating customer: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("📞 Step 5: Checking/creating contact...")
|
|
|
|
|
try:
|
|
|
|
|
contact_name = check_and_create_contact(data, setting_doc)
|
|
|
|
|
print(f"✅ Contact created/found: {contact_name}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error while creating contact: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("🏠 Step 6: Creating billing and shipping addresses...")
|
|
|
|
|
try:
|
|
|
|
|
billing_address, shipping_address = create_address_contact(data, customer_name)
|
|
|
|
|
print(f"✅ Billing: {billing_address}, Shipping: {shipping_address}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error while creating addresses: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("🛒 Step 7: Validating marketplace...")
|
|
|
|
|
marketplace_name = doc.marketplace
|
|
|
|
|
if not marketplace_name:
|
|
|
|
|
print("❌ No wholesale marketplace configured in settings.")
|
|
|
|
|
frappe.log_error("SPS Order Error", "No wholesale marketplace found.")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print(f"➡️ Marketplace: {marketplace_name}")
|
|
|
|
|
try:
|
|
|
|
|
series_name = frappe.get_value("Marketplace", marketplace_name, "naming_series")
|
|
|
|
|
print(f"✅ Naming Series: {series_name}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Failed to get naming series for marketplace: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("📝 Step 8: Creating new Sales Order document...")
|
|
|
|
|
try:
|
|
|
|
|
sales_order = frappe.new_doc("Sales Order")
|
|
|
|
|
sales_order.naming_series = series_name
|
|
|
|
|
sales_order.customer = customer_name
|
|
|
|
|
sales_order.custom_spss_purchase_order = po_no
|
|
|
|
|
sales_order.po_no = consumer_order_number
|
|
|
|
|
sales_order.transaction_date = datetime.datetime.strptime(po_date, "%Y-%m-%d").date()
|
|
|
|
|
sales_order.marketplace = marketplace_name
|
|
|
|
|
sales_order.delivery_date = frappe.utils.add_to_date(po_date, days=doc.days)
|
|
|
|
|
sales_order.customer_address = billing_address
|
|
|
|
|
sales_order.shipping_address_name = shipping_address
|
|
|
|
|
sales_order.custom_sales_order_path = path
|
|
|
|
|
if contact_name:
|
|
|
|
|
sales_order.contact_person = contact_name
|
|
|
|
|
print("✅ Sales Order initialized.")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Failed to create Sales Order document: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("📦 Step 9: Adding items to Sales Order...")
|
|
|
|
|
try:
|
|
|
|
|
for idx, item in enumerate(data.get("LineItem", []), start=1):
|
|
|
|
|
line_item = item.get("OrderLine", {})
|
|
|
|
|
sku = line_item.get("VendorPartNumber")
|
|
|
|
|
quantity = line_item.get("OrderQty", 0)
|
|
|
|
|
uom = line_item.get("OrderQtyUOM")
|
|
|
|
|
amount = line_item.get("PurchasePrice", 0.0)
|
|
|
|
|
description = next(
|
|
|
|
|
(desc.get("ProductDescription") for desc in item.get("ProductOrItemDescription", [])), ""
|
|
|
|
|
)
|
|
|
|
|
print(f" ➕ Item {idx}: SKU: {sku}, Qty: {quantity}, UOM: {uom}, Price: {amount}")
|
|
|
|
|
item_row = create_sps_sales_order_item_row(sku, quantity, amount, uom, description, setting_doc)
|
|
|
|
|
sales_order.append("items", item_row)
|
|
|
|
|
print("✅ All items added to Sales Order.")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Failed while adding items to Sales Order: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("💾 Step 10: Saving and submitting Sales Order...")
|
|
|
|
|
try:
|
|
|
|
|
sales_order.save()
|
|
|
|
|
sales_order.submit()
|
|
|
|
|
frappe.db.commit()
|
|
|
|
|
print(f"🎉 Sales Order '{sales_order.name}' created and submitted successfully!")
|
|
|
|
|
return sales_order.name
|
|
|
|
|
except Exception as e:
|
|
|
|
|
error_msg = frappe.get_traceback()
|
|
|
|
|
print(f"❌ Error during saving/submitting Sales Order: {e}")
|
|
|
|
|
frappe.log_error(
|
|
|
|
|
title="Sales Order Creation Error",
|
|
|
|
|
message=f"Marketplace: SPS\n\nTraceback:\n{error_msg}\\Order not synced:{data}",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|