2021-09-02 11:14:59 +00:00
|
|
|
import base64
|
|
|
|
import hashlib
|
|
|
|
import hmac
|
|
|
|
import json
|
|
|
|
|
|
|
|
import frappe
|
2018-04-03 05:47:03 +00:00
|
|
|
from frappe import _
|
2021-09-02 11:14:59 +00:00
|
|
|
from frappe.utils import cstr
|
|
|
|
|
2018-04-03 05:47:03 +00:00
|
|
|
|
|
|
|
def verify_request():
|
|
|
|
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
|
|
|
|
sig = base64.b64encode(
|
|
|
|
hmac.new(
|
2022-03-28 13:22:46 +00:00
|
|
|
woocommerce_settings.secret.encode("utf8"), frappe.request.data, hashlib.sha256
|
2018-04-03 05:47:03 +00:00
|
|
|
).digest()
|
|
|
|
)
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
if (
|
|
|
|
frappe.request.data
|
|
|
|
and not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode()
|
|
|
|
):
|
|
|
|
frappe.throw(_("Unverified Webhook Data"))
|
2019-04-09 09:50:24 +00:00
|
|
|
frappe.set_user(woocommerce_settings.creation_user)
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2018-04-03 05:47:03 +00:00
|
|
|
@frappe.whitelist(allow_guest=True)
|
2019-06-26 08:11:49 +00:00
|
|
|
def order(*args, **kwargs):
|
|
|
|
try:
|
|
|
|
_order(*args, **kwargs)
|
|
|
|
except Exception:
|
2022-03-28 13:22:46 +00:00
|
|
|
error_message = (
|
|
|
|
frappe.get_traceback() + "\n\n Request Data: \n" + json.loads(frappe.request.data).__str__()
|
|
|
|
)
|
2022-05-02 09:34:26 +00:00
|
|
|
frappe.log_error("WooCommerce Error", error_message)
|
2019-06-26 08:11:49 +00:00
|
|
|
raise
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2019-06-26 08:11:49 +00:00
|
|
|
def _order(*args, **kwargs):
|
2019-04-09 09:50:24 +00:00
|
|
|
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
|
2019-04-08 06:27:15 +00:00
|
|
|
if frappe.flags.woocomm_test_order_data:
|
2019-11-07 12:50:32 +00:00
|
|
|
order = frappe.flags.woocomm_test_order_data
|
2019-04-08 06:27:15 +00:00
|
|
|
event = "created"
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2019-04-08 06:27:15 +00:00
|
|
|
elif frappe.request and frappe.request.data:
|
|
|
|
verify_request()
|
2019-11-07 12:50:32 +00:00
|
|
|
try:
|
|
|
|
order = json.loads(frappe.request.data)
|
|
|
|
except ValueError:
|
2022-03-28 13:22:46 +00:00
|
|
|
# woocommerce returns 'webhook_id=value' for the first request which is not JSON
|
2019-11-07 12:50:32 +00:00
|
|
|
order = frappe.request.data
|
2018-06-14 03:43:39 +00:00
|
|
|
event = frappe.get_request_header("X-Wc-Webhook-Event")
|
2019-04-08 06:27:15 +00:00
|
|
|
|
2018-06-14 03:43:39 +00:00
|
|
|
else:
|
2019-04-08 06:27:15 +00:00
|
|
|
return "success"
|
2018-04-03 05:47:03 +00:00
|
|
|
|
|
|
|
if event == "created":
|
2022-03-28 13:22:46 +00:00
|
|
|
sys_lang = frappe.get_single("System Settings").language or "en"
|
2019-11-07 12:50:32 +00:00
|
|
|
raw_billing_data = order.get("billing")
|
2020-05-08 09:42:08 +00:00
|
|
|
raw_shipping_data = order.get("shipping")
|
2018-04-03 05:47:03 +00:00
|
|
|
customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name")
|
2020-05-08 09:42:08 +00:00
|
|
|
link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name)
|
2020-02-20 06:54:29 +00:00
|
|
|
link_items(order.get("line_items"), woocommerce_settings, sys_lang)
|
|
|
|
create_sales_order(order, woocommerce_settings, customer_name, sys_lang)
|
2019-11-07 12:50:32 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-05-08 09:42:08 +00:00
|
|
|
def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name):
|
2019-11-07 12:50:32 +00:00
|
|
|
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
|
2018-04-03 05:47:03 +00:00
|
|
|
customer = frappe.new_doc("Customer")
|
2019-11-07 12:50:32 +00:00
|
|
|
else:
|
|
|
|
# Edit Customer
|
|
|
|
customer = frappe.get_doc("Customer", {"woocommerce_email": customer_woo_com_email})
|
2018-04-03 05:47:03 +00:00
|
|
|
old_name = customer.customer_name
|
|
|
|
|
2019-11-07 12:50:32 +00:00
|
|
|
customer.customer_name = customer_name
|
|
|
|
customer.woocommerce_email = customer_woo_com_email
|
|
|
|
customer.flags.ignore_mandatory = True
|
2020-05-08 09:42:08 +00:00
|
|
|
customer.save()
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2019-11-07 12:50:32 +00:00
|
|
|
if customer_exists:
|
|
|
|
frappe.rename_doc("Customer", old_name, customer_name)
|
2022-03-28 13:22:46 +00:00
|
|
|
for address_type in (
|
|
|
|
"Billing",
|
|
|
|
"Shipping",
|
|
|
|
):
|
2020-06-18 09:58:55 +00:00
|
|
|
try:
|
2022-03-28 13:22:46 +00:00
|
|
|
address = frappe.get_doc(
|
|
|
|
"Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}
|
|
|
|
)
|
2020-06-18 09:58:55 +00:00
|
|
|
rename_address(address, customer)
|
|
|
|
except (
|
|
|
|
frappe.DoesNotExistError,
|
|
|
|
frappe.DuplicateEntryError,
|
|
|
|
frappe.ValidationError,
|
|
|
|
):
|
|
|
|
pass
|
2020-05-19 06:23:28 +00:00
|
|
|
else:
|
|
|
|
create_address(raw_billing_data, customer, "Billing")
|
|
|
|
create_address(raw_shipping_data, customer, "Shipping")
|
|
|
|
create_contact(raw_billing_data, customer)
|
2020-05-08 09:42:08 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-05-16 13:30:00 +00:00
|
|
|
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)
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
contact.append("links", {"link_doctype": "Customer", "link_name": customer.name})
|
2020-05-16 13:30:00 +00:00
|
|
|
|
|
|
|
contact.flags.ignore_mandatory = True
|
|
|
|
contact.save()
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-05-08 09:42:08 +00:00
|
|
|
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
|
2022-03-28 13:22:46 +00:00
|
|
|
address.append("links", {"link_doctype": "Customer", "link_name": customer.name})
|
2020-05-08 09:42:08 +00:00
|
|
|
|
2019-11-07 12:50:32 +00:00
|
|
|
address.flags.ignore_mandatory = True
|
2020-05-16 13:30:00 +00:00
|
|
|
address.save()
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-05-08 09:42:08 +00:00
|
|
|
def rename_address(address, customer):
|
|
|
|
old_address_title = address.name
|
2020-05-16 14:07:03 +00:00
|
|
|
new_address_title = customer.name + "-" + address.address_type
|
2020-05-08 09:42:08 +00:00
|
|
|
address.address_title = customer.customer_name
|
|
|
|
address.save()
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2020-05-08 09:42:08 +00:00
|
|
|
frappe.rename_doc("Address", old_address_title, new_address_title)
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-02-20 06:54:29 +00:00
|
|
|
def link_items(items_list, woocommerce_settings, sys_lang):
|
2019-11-07 12:50:32 +00:00
|
|
|
for item_data in items_list:
|
2021-05-12 14:12:04 +00:00
|
|
|
item_woo_com_id = cstr(item_data.get("product_id"))
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, "name"):
|
|
|
|
# Create Item
|
2019-11-07 12:50:32 +00:00
|
|
|
item = frappe.new_doc("Item")
|
2021-05-12 14:12:04 +00:00
|
|
|
item.item_code = _("woocommerce - {0}", sys_lang).format(item_woo_com_id)
|
|
|
|
item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
|
|
|
|
item.item_group = _("WooCommerce Products", sys_lang)
|
2020-05-08 09:42:08 +00:00
|
|
|
|
2021-05-12 14:12:04 +00:00
|
|
|
item.item_name = item_data.get("name")
|
|
|
|
item.woocommerce_id = item_woo_com_id
|
|
|
|
item.flags.ignore_mandatory = True
|
|
|
|
item.save()
|
2019-11-07 12:50:32 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-02-20 06:54:29 +00:00
|
|
|
def create_sales_order(order, woocommerce_settings, customer_name, sys_lang):
|
2019-11-07 12:50:32 +00:00
|
|
|
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
|
|
|
|
|
2020-02-20 06:54:29 +00:00
|
|
|
set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang)
|
2019-11-07 12:50:32 +00:00
|
|
|
new_sales_order.flags.ignore_mandatory = True
|
|
|
|
new_sales_order.insert()
|
|
|
|
new_sales_order.submit()
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2019-11-07 12:50:32 +00:00
|
|
|
frappe.db.commit()
|
2018-04-03 05:47:03 +00:00
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
|
2020-02-20 06:54:29 +00:00
|
|
|
def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_lang):
|
2022-03-28 13:22:46 +00:00
|
|
|
company_abbr = frappe.db.get_value("Company", woocommerce_settings.company, "abbr")
|
2019-11-07 12:50:32 +00:00
|
|
|
|
2020-04-25 09:22:05 +00:00
|
|
|
default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr)
|
2022-03-28 13:22:46 +00:00
|
|
|
if not frappe.db.exists("Warehouse", default_warehouse) and not woocommerce_settings.warehouse:
|
2020-04-25 09:22:05 +00:00
|
|
|
frappe.throw(_("Please set Warehouse in Woocommerce Settings"))
|
|
|
|
|
2019-11-07 12:50:32 +00:00
|
|
|
for item in order.get("line_items"):
|
|
|
|
woocomm_item_id = item.get("product_id")
|
2021-05-12 14:12:04 +00:00
|
|
|
found_item = frappe.get_doc("Item", {"woocommerce_id": cstr(woocomm_item_id)})
|
2019-11-07 12:50:32 +00:00
|
|
|
|
|
|
|
ordered_items_tax = item.get("total_tax")
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
new_sales_order.append(
|
|
|
|
"items",
|
|
|
|
{
|
|
|
|
"item_code": found_item.name,
|
|
|
|
"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
|
|
|
|
)
|
2019-11-07 12:50:32 +00:00
|
|
|
|
|
|
|
# shipping_details = order.get("shipping_lines") # used for detailed order
|
|
|
|
|
2022-03-28 13:22:46 +00:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
|
2020-05-08 09:42:08 +00:00
|
|
|
|
2019-11-07 12:50:32 +00:00
|
|
|
def add_tax_details(sales_order, price, desc, tax_account_head):
|
2022-03-28 13:22:46 +00:00
|
|
|
sales_order.append(
|
|
|
|
"taxes",
|
|
|
|
{
|
|
|
|
"charge_type": "Actual",
|
|
|
|
"account_head": tax_account_head,
|
|
|
|
"tax_amount": price,
|
|
|
|
"description": desc,
|
|
|
|
},
|
|
|
|
)
|