* refactor: handle exceptions when updating addresses * refactor: fold common statements in a loop
		
			
				
	
	
		
			226 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
 | |
| from __future__ import unicode_literals
 | |
| import frappe, base64, hashlib, hmac, json
 | |
| from frappe import _
 | |
| 
 | |
| def verify_request():
 | |
| 	woocommerce_settings = frappe.get_doc("Woocommerce Settings")
 | |
| 	sig = base64.b64encode(
 | |
| 		hmac.new(
 | |
| 			woocommerce_settings.secret.encode('utf8'),
 | |
| 			frappe.request.data,
 | |
| 			hashlib.sha256
 | |
| 		).digest()
 | |
| 	)
 | |
| 
 | |
| 	if frappe.request.data and \
 | |
| 		frappe.get_request_header("X-Wc-Webhook-Signature") and \
 | |
| 		not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
 | |
| 			frappe.throw(_("Unverified Webhook Data"))
 | |
| 	frappe.set_user(woocommerce_settings.creation_user)
 | |
| 
 | |
| @frappe.whitelist(allow_guest=True)
 | |
| def order(*args, **kwargs):
 | |
| 	try:
 | |
| 		_order(*args, **kwargs)
 | |
| 	except Exception:
 | |
| 		error_message = frappe.get_traceback()+"\n\n Request Data: \n"+json.loads(frappe.request.data).__str__()
 | |
| 		frappe.log_error(error_message, "WooCommerce Error")
 | |
| 		raise
 | |
| 
 | |
| def _order(*args, **kwargs):
 | |
| 	woocommerce_settings = frappe.get_doc("Woocommerce Settings")
 | |
| 	if frappe.flags.woocomm_test_order_data:
 | |
| 		order = frappe.flags.woocomm_test_order_data
 | |
| 		event = "created"
 | |
| 
 | |
| 	elif frappe.request and frappe.request.data:
 | |
| 		verify_request()
 | |
| 		try:
 | |
| 			order = json.loads(frappe.request.data)
 | |
| 		except ValueError:
 | |
| 			#woocommerce returns 'webhook_id=value' for the first request which is not JSON
 | |
| 			order = frappe.request.data
 | |
| 		event = frappe.get_request_header("X-Wc-Webhook-Event")
 | |
| 
 | |
| 	else:
 | |
| 		return "success"
 | |
| 
 | |
| 	if event == "created":
 | |
| 		sys_lang = frappe.get_single("System Settings").language or 'en'
 | |
| 		raw_billing_data = order.get("billing")
 | |
| 		raw_shipping_data = order.get("shipping")
 | |
| 		customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name")
 | |
| 		link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name)
 | |
| 		link_items(order.get("line_items"), woocommerce_settings, sys_lang)
 | |
| 		create_sales_order(order, woocommerce_settings, customer_name, sys_lang)
 | |
| 
 | |
| def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name):
 | |
| 	customer_woo_com_email = raw_billing_data.get("email")
 | |
| 	customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email})
 | |
| 	if not customer_exists:
 | |
| 		# Create Customer
 | |
| 		customer = frappe.new_doc("Customer")
 | |
| 	else:
 | |
| 		# Edit Customer
 | |
| 		customer = frappe.get_doc("Customer", {"woocommerce_email": customer_woo_com_email})
 | |
| 		old_name = customer.customer_name
 | |
| 
 | |
| 	customer.customer_name = customer_name
 | |
| 	customer.woocommerce_email = customer_woo_com_email
 | |
| 	customer.flags.ignore_mandatory = True
 | |
| 	customer.save()
 | |
| 
 | |
| 	if customer_exists:
 | |
| 		frappe.rename_doc("Customer", old_name, customer_name)
 | |
| 		for address_type in ("Billing", "Shipping",):
 | |
| 			try:
 | |
| 				address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type})
 | |
| 				rename_address(address, customer)
 | |
| 			except (
 | |
| 				frappe.DoesNotExistError,
 | |
| 				frappe.DuplicateEntryError,
 | |
| 				frappe.ValidationError,
 | |
| 			):
 | |
| 				pass
 | |
| 	else:
 | |
| 		create_address(raw_billing_data, customer, "Billing")
 | |
| 		create_address(raw_shipping_data, customer, "Shipping")
 | |
| 		create_contact(raw_billing_data, customer)
 | |
| 
 | |
| def create_contact(data, customer):
 | |
| 	email = data.get("email", None)
 | |
| 	phone = data.get("phone", None)
 | |
| 
 | |
| 	if not email and not phone:
 | |
| 		return
 | |
| 
 | |
| 	contact = frappe.new_doc("Contact")
 | |
| 	contact.first_name = data.get("first_name")
 | |
| 	contact.last_name = data.get("last_name")
 | |
| 	contact.is_primary_contact = 1
 | |
| 	contact.is_billing_contact = 1
 | |
| 
 | |
| 	if phone:
 | |
| 		contact.add_phone(phone, is_primary_mobile_no=1, is_primary_phone=1)
 | |
| 
 | |
| 	if email:
 | |
| 		contact.add_email(email, is_primary=1)
 | |
| 
 | |
| 	contact.append("links", {
 | |
| 		"link_doctype": "Customer",
 | |
| 		"link_name": customer.name
 | |
| 	})
 | |
| 
 | |
| 	contact.flags.ignore_mandatory = True
 | |
| 	contact.save()
 | |
| 
 | |
| def create_address(raw_data, customer, address_type):
 | |
| 	address = frappe.new_doc("Address")
 | |
| 
 | |
| 	address.address_line1 = raw_data.get("address_1", "Not Provided")
 | |
| 	address.address_line2 = raw_data.get("address_2", "Not Provided")
 | |
| 	address.city = raw_data.get("city", "Not Provided")
 | |
| 	address.woocommerce_email = customer.woocommerce_email
 | |
| 	address.address_type = address_type
 | |
| 	address.country = frappe.get_value("Country", {"code": raw_data.get("country", "IN").lower()})
 | |
| 	address.state = raw_data.get("state")
 | |
| 	address.pincode = raw_data.get("postcode")
 | |
| 	address.phone = raw_data.get("phone")
 | |
| 	address.email_id = customer.woocommerce_email
 | |
| 	address.append("links", {
 | |
| 		"link_doctype": "Customer",
 | |
| 		"link_name": customer.name
 | |
| 	})
 | |
| 
 | |
| 	address.flags.ignore_mandatory = True
 | |
| 	address.save()
 | |
| 
 | |
| def rename_address(address, customer):
 | |
| 	old_address_title = address.name
 | |
| 	new_address_title = customer.name + "-" + address.address_type
 | |
| 	address.address_title = customer.customer_name
 | |
| 	address.save()
 | |
| 
 | |
| 	frappe.rename_doc("Address", old_address_title, new_address_title)
 | |
| 
 | |
| def link_items(items_list, woocommerce_settings, sys_lang):
 | |
| 	for item_data in items_list:
 | |
| 		item_woo_com_id = item_data.get("product_id")
 | |
| 
 | |
| 		if frappe.get_value("Item", {"woocommerce_id": item_woo_com_id}):
 | |
| 			#Edit Item
 | |
| 			item = frappe.get_doc("Item", {"woocommerce_id": item_woo_com_id})
 | |
| 		else:
 | |
| 			#Create Item
 | |
| 			item = frappe.new_doc("Item")
 | |
| 
 | |
| 		item.item_name = item_data.get("name")
 | |
| 		item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id"))
 | |
| 		item.woocommerce_id = item_data.get("product_id")
 | |
| 		item.item_group = _("WooCommerce Products", sys_lang)
 | |
| 		item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
 | |
| 		item.flags.ignore_mandatory = True
 | |
| 		item.save()
 | |
| 
 | |
| def create_sales_order(order, woocommerce_settings, customer_name, sys_lang):
 | |
| 	new_sales_order = frappe.new_doc("Sales Order")
 | |
| 	new_sales_order.customer = customer_name
 | |
| 
 | |
| 	new_sales_order.po_no = new_sales_order.woocommerce_id = order.get("id")
 | |
| 	new_sales_order.naming_series = woocommerce_settings.sales_order_series or "SO-WOO-"
 | |
| 
 | |
| 	created_date = order.get("date_created").split("T")
 | |
| 	new_sales_order.transaction_date = created_date[0]
 | |
| 	delivery_after = woocommerce_settings.delivery_after_days or 7
 | |
| 	new_sales_order.delivery_date = frappe.utils.add_days(created_date[0], delivery_after)
 | |
| 
 | |
| 	new_sales_order.company = woocommerce_settings.company
 | |
| 
 | |
| 	set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang)
 | |
| 	new_sales_order.flags.ignore_mandatory = True
 | |
| 	new_sales_order.insert()
 | |
| 	new_sales_order.submit()
 | |
| 
 | |
| 	frappe.db.commit()
 | |
| 
 | |
| def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang):
 | |
| 	company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr')
 | |
| 
 | |
| 	default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr)
 | |
| 	if not frappe.db.exists("Warehouse", default_warehouse) \
 | |
| 		and not woocommerce_settings.warehouse:
 | |
| 		frappe.throw(_("Please set Warehouse in Woocommerce Settings"))
 | |
| 
 | |
| 	for item in order.get("line_items"):
 | |
| 		woocomm_item_id = item.get("product_id")
 | |
| 		found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id})
 | |
| 
 | |
| 		ordered_items_tax = item.get("total_tax")
 | |
| 
 | |
| 		new_sales_order.append("items",{
 | |
| 			"item_code": found_item.item_code,
 | |
| 			"item_name": found_item.item_name,
 | |
| 			"description": found_item.item_name,
 | |
| 			"delivery_date": new_sales_order.delivery_date,
 | |
| 			"uom": woocommerce_settings.uom or _("Nos", sys_lang),
 | |
| 			"qty": item.get("quantity"),
 | |
| 			"rate": item.get("price"),
 | |
| 			"warehouse": woocommerce_settings.warehouse or default_warehouse
 | |
| 			})
 | |
| 
 | |
| 		add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
 | |
| 
 | |
| 	# shipping_details = order.get("shipping_lines") # used for detailed order
 | |
| 
 | |
| 	add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account)
 | |
| 	add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account)
 | |
| 
 | |
| def add_tax_details(sales_order, price, desc, tax_account_head):
 | |
| 	sales_order.append("taxes", {
 | |
| 		"charge_type":"Actual",
 | |
| 		"account_head": tax_account_head,
 | |
| 		"tax_amount": price,
 | |
| 		"description": desc
 | |
| 	})
 |