From 18bed4e7fa42e290d980fc43fc3e28e98adf4292 Mon Sep 17 00:00:00 2001 From: vrashank Date: Sat, 26 Apr 2025 04:33:37 +0000 Subject: [PATCH] Sales order Ack feature implemented --- .../doctype/acknowledgement_type/__init__.py | 0 .../acknowledgement_type.js | 8 + .../acknowledgement_type.json | 50 ++++ .../acknowledgement_type.py | 9 + .../test_acknowledgement_type.py | 9 + .../sps_integration/order_acknowledgement.py | 97 ++++++-- sps_integration/sps_integration/order_sync.py | 228 +++++++++++++----- .../sps_integration/orders_utills.py | 227 +++++++++-------- 8 files changed, 451 insertions(+), 177 deletions(-) create mode 100644 sps_integration/sps_integration/doctype/acknowledgement_type/__init__.py create mode 100644 sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.js create mode 100644 sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.json create mode 100644 sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.py create mode 100644 sps_integration/sps_integration/doctype/acknowledgement_type/test_acknowledgement_type.py diff --git a/sps_integration/sps_integration/doctype/acknowledgement_type/__init__.py b/sps_integration/sps_integration/doctype/acknowledgement_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.js b/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.js new file mode 100644 index 0000000..11a1a12 --- /dev/null +++ b/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, UnifyXperts and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Acknowledgement Type", { +// refresh(frm) { + +// }, +// }); diff --git a/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.json b/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.json new file mode 100644 index 0000000..378aca7 --- /dev/null +++ b/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:type", + "creation": "2025-04-24 01:59:12.184305", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "type", + "description" + ], + "fields": [ + { + "fieldname": "type", + "fieldtype": "Data", + "label": "Type", + "unique": 1 + }, + { + "fieldname": "description", + "fieldtype": "Data", + "label": "Description" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-04-24 02:03:12.556191", + "modified_by": "Administrator", + "module": "SPS Integration", + "name": "Acknowledgement Type", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.py b/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.py new file mode 100644 index 0000000..ea84155 --- /dev/null +++ b/sps_integration/sps_integration/doctype/acknowledgement_type/acknowledgement_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, UnifyXperts and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class AcknowledgementType(Document): + pass diff --git a/sps_integration/sps_integration/doctype/acknowledgement_type/test_acknowledgement_type.py b/sps_integration/sps_integration/doctype/acknowledgement_type/test_acknowledgement_type.py new file mode 100644 index 0000000..ef352ec --- /dev/null +++ b/sps_integration/sps_integration/doctype/acknowledgement_type/test_acknowledgement_type.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, UnifyXperts and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestAcknowledgementType(FrappeTestCase): + pass diff --git a/sps_integration/sps_integration/order_acknowledgement.py b/sps_integration/sps_integration/order_acknowledgement.py index ada084f..5718ea0 100644 --- a/sps_integration/sps_integration/order_acknowledgement.py +++ b/sps_integration/sps_integration/order_acknowledgement.py @@ -2,8 +2,11 @@ import requests import json import frappe from datetime import datetime, timedelta +from frappe.utils import get_datetime, now_datetime +from .oauth import refresh_access_token +from frappe.utils import now -def format_sales_order_ack(data: dict) -> dict: +def format_sales_order_ack(data: dict, schedule_shipped_date: str, ack_typ: str) -> dict: """ Format sales order acknowledgment for SPS Commerce API. @@ -26,22 +29,29 @@ def format_sales_order_ack(data: dict) -> dict: purchase_order_date = datetime.strptime(purchase_order_date_str, "%Y-%m-%d") acknowledgment_date = (purchase_order_date + timedelta(days=1)).strftime("%Y-%m-%d") except ValueError: - acknowledgment_date = (datetime.utcnow() + timedelta(days=1)).strftime("%Y-%m-%d") - + print("f") + # acknowledgment_date = (datetime.utcnow() + timedelta(days=1)).strftime("%Y-%m-%d") acknowledgement_payload = { "meta": { "transactionType": "855" }, "Header": { "OrderHeader": { - "tradingPartnerId": order_header.get("TradingPartnerId", ""), - "purchaseOrderNumber": order_header.get("PurchaseOrderNumber"), - "acknowledgmentType": "AK", - "acknowledgmentDate": acknowledgment_date, - "purchaseOrderDate": purchase_order_date_str, - "vendor": order_header.get("Vendor") - } + "TradingPartnerId": order_header.get("TradingPartnerId", ""), + "AurchaseOrderNumber": order_header.get("PurchaseOrderNumber"), + "AcknowledgmentType": ack_typ, + "AcknowledgmentDate": acknowledgment_date, + "PurchaseOrderDate": purchase_order_date_str, + "Vendor": order_header.get("Vendor") + }, + }, + "Dates" : [ { + "DateTimeQualifier" : "068", + "Date" : schedule_shipped_date, + # "Time" : "14:22:14" + "Time" : now().split(" ")[1] + } ], "lineItems": [ { "lineSequenceNumber": item.get("OrderLine", {}).get("LineSequenceNumber"), @@ -55,7 +65,7 @@ def format_sales_order_ack(data: dict) -> dict: "ItemStatusCode": "IA", "ItemScheduleQty": item.get("OrderLine", {}).get("OrderQty", 0), "ItemScheduleUOM": item.get("OrderLine", {}).get("OrderQtyUOM", ""), - "ItemScheduleQualifier": "064", + "ItemScheduleQualifier": "068", "ItemScheduleDate": acknowledgment_date } ] @@ -84,7 +94,7 @@ def format_sales_order_ack(data: dict) -> dict: -def sales_order_acknowledgement(data: dict): +def sales_order_acknowledgement(data: dict , path: str): """Send Sales Order Acknowledgment to SPS Commerce API.""" try: doc = frappe.get_doc("SPS Integration Settings", "Develop") @@ -99,13 +109,68 @@ def sales_order_acknowledgement(data: dict): if not formatted_data: return - - response = requests.post(url=doc.order_acknowledgement_url, headers=headers, data=json.dumps(formatted_data)) - + url = f"{doc.order_acknowledgement_url}ACK_" + path.split("_")[1] + response = requests.post(url=url, headers=headers, data=json.dumps(formatted_data)) + print(response) if response.status_code in [200, 201]: frappe.log_error(title="SO Acknowledgment Success", message=f"{response.text}") else: frappe.log_error(title="SO Acknowledgment Failure", message=f"{response.text}") - + return response.json() except Exception as e: frappe.log_error(title="SO Acknowledgment Error", message=str(frappe.get_traceback())) + + +@frappe.whitelist(allow_guest=True) +def submit_order_acknowledgement(path: str, schedule_shipped_date: str, ack_type: str, sales_order: str): + """ + Acknowledge the sales order + args: + path (str): ID for sales order to get payload from sps + schedule_shipped_date (str): Date for ack order + ack_type (str): Ack Type + returns: + Returns the ack sales order ID + """ + try: + doc = frappe.get_doc("SPS Integration Settings", "Develop") + url = f"{doc.get_sales_order_url}/{path}" + expires_on = get_datetime(doc.expires_on) + current_time = now_datetime() + access_token = "" + + if expires_on <= current_time or (expires_on - current_time).total_seconds() <= 300: + refresh_access_token(setting_doc_name=doc) + access_token = doc.get_password("access_token") + frappe.log_error("SPS Token Refresh", "Token refreshed") + else: + access_token = doc.get_password("access_token") + + headers = { + "Authorization": f"Bearer {access_token}", + "Content-Type": "application/json" + } + + response = requests.get(url=url, headers=headers) + if response.status_code == 200: + frappe.log_error(title="Payload for Ord ACK",message=f"{response.json()}") + formatted_data = format_sales_order_ack(response.json(),schedule_shipped_date, ack_type) + frappe.log_error(title="Formatted SO Ack Payload", message=f"{formatted_data}") + # Sales order ack functionality + ack_response = sales_order_acknowledgement(data=formatted_data,path=path) + # ack_response = { + # "path":"/in/ACK_a86e370c-43b9-3db1-b6bd-6289a132448f}-d76e3c43e0024359960669e9855509ce.dat", + # "url":"https://api.spscommerce.com/transactions/v5/data/in/ACK_a86e370c-43b9-3db1-b6bd-6289a132448f}-d76e3c43e0024359960669e9855509ce.dat"} + if ack_response: + ack_path = ack_response.get("path") + if frappe.db.exists("Sales Order", {"name": sales_order, "docstatus": 1}): + so_doc = frappe.get_doc("Sales Order", sales_order) + so_doc.custom_sales_order_ack_path = ack_path + so_doc.save() + frappe.db.commit() + frappe.log_error(title="Sales order ack path", message=f"{ack_path}") + else: + frappe.log_error(title="Payload for Ord ACK error",message=f"{response.json()}") + frappe.log_error(title="Testing functionality order ack", message=f"Order Ack Required Stuff: {path}\\n{schedule_shipped_date}\\n{ack_type}") + except Exception as e: + frappe.log_error(title="Error while ack the order through button", message=f"{str(e)}") \ No newline at end of file diff --git a/sps_integration/sps_integration/order_sync.py b/sps_integration/sps_integration/order_sync.py index 9526ccf..7272264 100644 --- a/sps_integration/sps_integration/order_sync.py +++ b/sps_integration/sps_integration/order_sync.py @@ -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}", + ) + diff --git a/sps_integration/sps_integration/orders_utills.py b/sps_integration/sps_integration/orders_utills.py index 6ebcd59..efa192a 100644 --- a/sps_integration/sps_integration/orders_utills.py +++ b/sps_integration/sps_integration/orders_utills.py @@ -75,25 +75,25 @@ def get_sps_item_code_if_exists(order_uom: str, description: str, sku: str, sett if frappe.db.exists("Item", {"item_code": sku}): return sku - item_doc = frappe.new_doc("Item") - item_doc.update({ - "item_code": sku, - "item_name": description, - "stock_uom": get_uom(order_uom) if order_uom != "EA" and order_uom != None else frappe.get_value("SPS Integration Settings", setting_doc, "default_uom"), - "item_group": frappe.get_value("SPS Integration Settings", setting_doc, "default_item_group"), - "custom_item_shipping_category": frappe.get_value("SPS Integration Settings", setting_doc, "shipping_category") - }) - - try: - item_doc.save() - frappe.db.commit() - except: - frappe.log_error( - title="Item creation", - message=f"{frappe.get_traceback()}" - ) - - return item_doc.name + # item_doc = frappe.new_doc("Item") + # item_doc.update({ + # "item_code": sku, + # "item_name": description, + # "stock_uom": get_uom(order_uom) if order_uom != "EA" and order_uom != None else frappe.get_value("SPS Integration Settings", setting_doc, "default_uom"), + # "item_group": frappe.get_value("SPS Integration Settings", setting_doc, "default_item_group"), + # "custom_item_shipping_category": frappe.get_value("SPS Integration Settings", setting_doc, "shipping_category") + # }) + + # try: + # item_doc.save() + # frappe.db.commit() + # except: + # frappe.log_error( + # title="Item creation", + # message=f"{frappe.get_traceback()}" + # ) + + # return item_doc.name def get_uom(uom: str) -> str: """Get or create UOM.""" @@ -157,111 +157,121 @@ def get_link_row(docname: str, link_name: str) -> Dict: return row +# def check_and_create_contact(data, setting_doc): +# """Check if contacts exist, else create new contacts""" + +# addresses = data.get('Header', {}).get('Address', []) +# contacts = None + + +# for address in addresses: +# if address.get("AddressTypeCode") == "BT" and address.get("Contacts"): +# contacts = address["Contacts"] +# break # Stop once billing contacts are found + +# if not contacts: +# for address in addresses: +# if address.get("AddressTypeCode") == "ST" and address.get("Contacts"): +# contacts = address["Contacts"] +# break # Stop once shipping contacts are found + +# if not contacts: +# return None + +# # Check if customer exists +# customer_exists, customer_name = check_customer_if_exists(data) + +# if customer_exists: +# # Check if the contact email already exists +# email_id = contacts[0].get("PrimaryEmail", "") +# if frappe.db.exists("Contact Email", {"email_id": email_id}): +# contact_name = frappe.get_value( +# "Contact Email", {"email_id": email_id}, "parent" +# ) +# else: +# doc = frappe.new_doc("Contact") +# doc.first_name = customer_name +# if email_id: +# email_row = { +# "email_id": email_id, +# "is_primary": 1 +# } +# doc.append("email_ids", email_row) + +# phone_number = contacts[0].get("PrimaryPhone", "") +# if phone_number: +# num_row = { +# "phone": phone_number, +# "is_primary_phone": 1 +# } +# doc.append("phone_nos", num_row) + +# link_row = get_link_row("Customer", customer_name) +# doc.append("links", link_row) + +# doc.save() +# frappe.db.commit() +# contact_name = doc.name + +# return contact_name + +# Made the fixes related to assignment error of contact_name variable def check_and_create_contact(data, setting_doc): """Check if contacts exist, else create new contacts""" addresses = data.get('Header', {}).get('Address', []) contacts = None - for address in addresses: if address.get("AddressTypeCode") == "BT" and address.get("Contacts"): contacts = address["Contacts"] - break # Stop once billing contacts are found + break if not contacts: for address in addresses: if address.get("AddressTypeCode") == "ST" and address.get("Contacts"): contacts = address["Contacts"] - break # Stop once shipping contacts are found + break - if not contacts: + if not contacts or len(contacts) == 0: return None - # Check if customer exists customer_exists, customer_name = check_customer_if_exists(data) + if not customer_exists: + return None - if customer_exists: - # Check if the contact email already exists - email_id = contacts[0].get("PrimaryEmail", "") - if frappe.db.exists("Contact Email", {"email_id": email_id}): - contact_name = frappe.get_value( - "Contact Email", {"email_id": email_id}, "parent" - ) - else: - doc = frappe.new_doc("Contact") - doc.first_name = customer_name - if email_id: - email_row = { - "email_id": email_id, - "is_primary": 1 - } - doc.append("email_ids", email_row) - - phone_number = contacts[0].get("PrimaryPhone", "") - if phone_number: - num_row = { - "phone": phone_number, - "is_primary_phone": 1 - } - doc.append("phone_nos", num_row) - - link_row = get_link_row("Customer", customer_name) - doc.append("links", link_row) - - doc.save() - frappe.db.commit() - contact_name = doc.name - - return contact_name - - - -def create_customer_if_not_exists(data: dict, setting_doc: str) -> str: - """ - Create new customer if not exists. - - Args: - data (Dict): Payload from webhook - setting_doc (str): setting doc name - - Returns: - customer_name (str): Return customer name after creation of customer. - """ - trading_id = data.get("Header", {}).get("OrderHeader", {}).get("TradingPartnerId") - - addresses = data.get("Header", {}).get("Address", []) - address_names = [address.get("AddressName") for address in addresses] + contact_name = None + first_contact = contacts[0] + email_id = first_contact.get("PrimaryEmail", "") - if frappe.db.exists("Customer", {"custom_trading_partner_id": trading_id}): - customer_name = frappe.get_value( - "Customer", - {"custom_trading_partner_id": trading_id}, - "name" + if frappe.db.exists("Contact Email", {"email_id": email_id}): + contact_name = frappe.get_value( + "Contact Email", {"email_id": email_id}, "parent" ) else: - - doc = frappe.new_doc("Customer") - doc.custom_trading_partner_id = trading_id - doc.customer_name = address_names[0].capitalize() - - doc.customer_type = frappe.get_value( - "SPS Integration Settings", setting_doc, "customer_type" - ) - + doc = frappe.new_doc("Contact") + doc.first_name = customer_name + + if email_id: + doc.append("email_ids", { + "email_id": email_id, + "is_primary": 1 + }) + + phone_number = first_contact.get("PrimaryPhone", "") + if phone_number: + doc.append("phone_nos", { + "phone": phone_number, + "is_primary_phone": 1 + }) + + doc.append("links", get_link_row("Customer", customer_name)) doc.save() frappe.db.commit() + contact_name = doc.name - for address in addresses: - address_type = address.get("AddressTypeCode") - - if address_type == "BT": - doc.customer_primary_contact = create_or_update_address(address, "Billing", doc.name) - - doc.save() - frappe.db.commit() + return contact_name - return doc.name def get_path_to_skip(): """Function to get all paths to skip""" @@ -290,4 +300,23 @@ def exclude_skipped_file_paths(paths: List[str]) -> List[str]: title="Error while excluding skipped file paths", message=frappe.get_traceback() ) - return [] \ No newline at end of file + return [] + + +def get_unsyced_sps_orders_urls(unsyced_path: list) -> list: + """Returns list of sps unsyced sales order's urls""" + syced_sps_orders_path = get_path_to_skip() + cleaned_unsyced_path = [item["path"].split("/")[2] for item in unsyced_path] + unsyced_urls = [] + count = 0 + for path in cleaned_unsyced_path: + if path not in syced_sps_orders_path: + count += 1 + unsyced_urls.append(path) + + print(f"Total unsynced sales orders: {count}") + frappe.log_error( + title="Unsyced SPS SO URLs", + message=f"Total unsyced sales order: {count}\\n Paths:{unsyced_urls}" + ) + \ No newline at end of file