Created
April 16, 2025 15:20
-
-
Save robjstanley/7628330cd8c1555bf4b3eec78617e93d to your computer and use it in GitHub Desktop.
Convert Visualsoft Order XML to Matrixify CSV
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import xml.etree.ElementTree as ET | |
import csv | |
import os | |
import re | |
def natural_sort_key(s): | |
return [int(text) if text.isdigit() else text.lower() for text in re.split(r'(\d+)', s)] | |
orders_dir = "orders" | |
xml_files = sorted([os.path.join(orders_dir, file) for file in os.listdir(orders_dir) if file.endswith(".xml")], key=natural_sort_key) | |
headers = [ | |
"Name", "Command", "Send Receipt", "Inventory Behaviour", "Number", "Phone", "Email", "Note", | |
"Tags", "Tags Command", "Cancelled At", "Cancel: Reason", "Cancel: Send Receipt", "Cancel: Refund", | |
"Processed At", "Closed At", "Currency", "Source", "Source Identifier", "Source URL", "Weight Total", | |
"Tax: Included", "Tax: Total", "Payment: Status", "Additional Details", "Customer: Email", | |
"Customer: Phone", "Customer: First Name", "Customer: Last Name", "Customer: Note", "Customer: State", | |
"Customer: Tags", "Billing: First Name", "Billing: Last Name", "Billing: Name", "Billing: Company", | |
"Billing: Phone", "Billing: Address 1", "Billing: Address 2", "Billing: Zip", "Billing: City", | |
"Billing: Province", "Billing: Province Code", "Billing: Country", "Billing: Country Code", | |
"Shipping: First Name", "Shipping: Last Name", "Shipping: Name", "Shipping: Company", "Shipping: Phone", | |
"Shipping: Address 1", "Shipping: Address 2", "Shipping: Zip", "Shipping: City", "Shipping: Province", | |
"Shipping: Province Code", "Shipping: Country", "Shipping: Country Code", "Line: Type", "Line: Command", | |
"Line: Product ID", "Line: Product Handle", "Line: Title", "Line: Name", "Line: Variant ID", | |
"Line: Variant Title", "Line: SKU", "Line: Quantity", "Line: Price", "Line: Discount", "Line: Grams", | |
"Line: Requires Shipping", "Line: Vendor", "Line: Properties", "Line: Gift Card", "Line: Force Gift Card", | |
"Line: Taxable", "Line: Tax 1 Title", "Line: Tax 1 Rate", "Line: Tax 1 Price", "Line: Fulfillment Service", | |
"Transaction: Kind", "Transaction: Processed At", "Transaction: Amount", "Transaction: Currency", | |
"Transaction: Status", "Transaction: Gateway", "Transaction: Force Gateway", "Transaction: Test", | |
"Transaction: Authorization", "Transaction: Parent ID", "Fulfillment: ID", "Fulfillment: Status", | |
"Fulfillment: Processed At", "Fulfillment: Tracking Company", "Fulfillment: Location", | |
"Fulfillment: Shipment Status", "Fulfillment: Tracking Number", "Fulfillment: Tracking URL", | |
"Fulfillment: Send Receipt" | |
] | |
max_orders = 10000 # Matrixify limit | |
order_count = 0 | |
file_count = 1 | |
writer = None | |
# Function to create a new CSV file and writer | |
def create_csv_writer(): | |
global writer, file_count | |
csv_file = f"orders_{file_count}.csv" | |
file_count += 1 | |
file = open(csv_file, mode='w', newline='', encoding='utf-8') | |
writer = csv.DictWriter(file, fieldnames=headers) | |
writer.writeheader() | |
return file | |
file = create_csv_writer() | |
# Iterate over all XML files in the orders directory | |
for xml_file in xml_files: | |
print(f"Processing {xml_file}...") | |
if xml_file.endswith(".xml"): | |
tree = ET.parse(os.path.join(xml_file)) | |
root = tree.getroot() | |
# Iterate over each order in the XML file | |
for order in root.findall(".//web_order"): | |
payment_status = "paid" | |
if order.findtext(".//order_state") == "Order Refunded": | |
payment_status = "refunded" | |
global_row = {key: "" for key in headers} | |
global_row.update({ | |
"Name": order.findtext(".//order_reference", ""), | |
"Command": "REPLACE", | |
"Send Receipt": "false", | |
"Inventory Behaviour": "bypass", | |
"Number": order.findtext(".//order_id", ""), | |
"Phone": "", | |
"Email": order.findtext(".//email_address", ""), | |
"Note": order.findtext(".//order/notes","") + "\n" + order.findtext(".//payment/notes", ""), | |
"Tags": "visualsoft_order,state-" + order.findtext(".//order/order_state","").replace(" ","_").lower() + "," + (f"parent_{ref}" if (ref := order.findtext(".//parent_order/order/order_reference")) else ""), | |
"Tags Command": "MERGE", | |
"Cancelled At": "", | |
"Cancel: Reason": "", | |
"Cancel: Send Receipt": "false", | |
"Cancel: Refund": "false", | |
"Processed At": order.findtext(".//order_date", ""), | |
"Closed At": "", | |
"Currency": order.findtext(".//order_currency", ""), | |
"Source": order.findtext(".//order_type", ""), | |
"Source Identifier": order.findtext(".//order_reference", ""), | |
"Source URL": "", | |
"Weight Total": "", | |
"Tax: Included": "true", | |
"Tax: Total": order.findtext(".//grand_total_vat", ""), | |
"Payment: Status": payment_status, | |
"Additional Details": "", | |
"Customer: Email": order.findtext(".//email_address", ""), | |
"Customer: Phone": "", | |
"Customer: First Name": order.findtext(".//billing_firstname", ""), | |
"Customer: Last Name": order.findtext(".//billing_lastname", ""), | |
"Customer: Note": "", | |
"Customer: State": "", | |
"Customer: Tags": "", | |
"Billing: First Name": order.findtext(".//billing_firstname", ""), | |
"Billing: Last Name": order.findtext(".//billing_lastname", ""), | |
"Billing: Name": order.findtext(".//billing_fullname", ""), | |
"Billing: Company": "", | |
"Billing: Phone": "", | |
"Billing: Address 1": order.findtext(".//billing_address1", ""), | |
"Billing: Address 2": order.findtext(".//billing_address2", ""), | |
"Billing: Zip": order.findtext(".//billing_postcode", ""), | |
"Billing: City": order.findtext(".//billing_town", ""), | |
"Billing: Province": "", | |
"Billing: Province Code": "", | |
"Billing: Country": order.findtext(".//billing_country_name", ""), | |
"Billing: Country Code": "", | |
"Shipping: First Name": order.findtext(".//delivery_firstname", ""), | |
"Shipping: Last Name": order.findtext(".//delivery_lastname", ""), | |
"Shipping: Name": order.findtext(".//delivery_fullname", ""), | |
"Shipping: Company": "", | |
"Shipping: Phone": "", | |
"Shipping: Address 1": order.findtext(".//delivery_address1", ""), | |
"Shipping: Address 2": order.findtext(".//delivery_address2", ""), | |
"Shipping: Zip": order.findtext(".//delivery_postcode", ""), | |
"Shipping: City": order.findtext(".//delivery_town", ""), | |
"Shipping: Province": "", | |
"Shipping: Province Code": "", | |
"Shipping: Country": order.findtext(".//delivery_country_name", ""), | |
"Shipping: Country Code": "", | |
}) | |
for product in order.findall(".//products"): | |
row = global_row.copy() | |
row.update({ | |
"Line: Type": "Line Item", | |
"Line: Command": "DEFAULT", | |
"Line: Product ID": "", | |
"Line: Product Handle": "", | |
"Line: Title": product.findtext(".//title", ""), | |
"Line: Name": product.findtext(".//title", "") + " - " + product.findtext(".//summary", ""), | |
"Line: Variant ID": "", | |
"Line: Variant Title": product.findtext(".//summary", ""), | |
"Line: SKU": product.findtext(".//model", ""), | |
"Line: Quantity": product.findtext(".//quantity", ""), | |
"Line: Price": product.findtext(".//price_inc", ""), | |
"Line: Discount": "", | |
"Line: Grams": product.findtext(".//weight", ""), | |
"Line: Requires Shipping": "", | |
"Line: Vendor": product.findtext(".//manufacturer_name", ""), | |
"Line: Properties": "", | |
"Line: Gift Card": "false", | |
"Line: Force Gift Card": "", | |
"Line: Taxable": "true", | |
"Line: Tax 1 Title": "", | |
"Line: Tax 1 Rate": "", | |
"Line: Tax 1 Price": "", | |
"Line: Fulfillment Service": "manual", | |
}) | |
writer.writerow(row) | |
if float(order.findtext(".//order/discount_inc")) > 0: | |
row = global_row.copy() | |
row.update({ | |
"Line: Type": "Discount", | |
"Line: Command": "DEFAULT", | |
"Line: Title": "fixed_amount", | |
"Line: Name": "Discount", | |
"Line: Price": float(order.findtext(".//order/product_total_inc")) - float(order.findtext(".//order/discount_inc")), | |
"Line: Discount": '-' + order.findtext(".//order/discount_inc"), | |
}) | |
writer.writerow(row) | |
if float(order.findtext(".//order/shipping_total_inc")) > 0: | |
row = global_row.copy() | |
row.update({ | |
"Line: Type": "Shipping Line", | |
"Line: Title": "Shipping " + order.findtext(".//courier_name", ""), | |
"Line: Price": order.findtext(".//order/shipping_total_inc"), | |
}) | |
writer.writerow(row) | |
row = global_row.copy() | |
row.update({ | |
"Line: Type": "Transaction", | |
"Line: Command": "DEFAULT", | |
"Transaction: Kind": "sale", | |
"Transaction: Processed At": "", | |
"Transaction: Amount": order.findtext(".//payment/payment_amount", ""), | |
"Transaction: Currency": order.findtext(".//order_currency", ""), | |
"Transaction: Status": "success", | |
"Transaction: Gateway": "manual (" + order.findtext(".//payment/payment_type", "") + ")", | |
"Transaction: Force Gateway": "", | |
"Transaction: Test": "No", | |
"Transaction: Authorization": order.findtext(".//payment/transaction_reference", ""), | |
"Transaction: Parent ID": "", | |
}) | |
writer.writerow(row) | |
if payment_status == "refunded": | |
row = global_row.copy() | |
row.update({ | |
"Line: Type": "Transaction", | |
"Line: Command": "DEFAULT", | |
"Transaction: Kind": "refund", | |
"Transaction: Processed At": "", | |
"Transaction: Amount": '-' + order.findtext(".//payment/payment_amount", ""), | |
"Transaction: Currency": order.findtext(".//order_currency", ""), | |
"Transaction: Status": "success", | |
"Transaction: Gateway": "manual", | |
"Transaction: Force Gateway": "", | |
"Transaction: Test": "No", | |
"Transaction: Authorization": "", | |
"Transaction: Parent ID": "", | |
}) | |
writer.writerow(row) | |
if order.findtext(".//dispatch_date") != "0000-00-00 00:00:00": | |
row = global_row.copy() | |
row.update({ | |
"Line: Type": "Fulfillment Line", | |
"Line: Command": "DEFAULT", | |
"Fulfillment: ID": "", | |
"Fulfillment: Status": "" if order.findtext(".//dispatch_date") == "0000-00-00 00:00:00" else "success", | |
"Fulfillment: Processed At": "", | |
"Fulfillment: Tracking Company": order.findtext(".//courier_name", ""), | |
"Fulfillment: Location": "", | |
"Fulfillment: Shipment Status": "" if order.findtext(".//dispatch_date") == "0000-00-00 00:00:00" else "delivered", | |
"Fulfillment: Tracking Number": "", | |
"Fulfillment: Tracking URL": "", | |
"Fulfillment: Send Receipt": "false", | |
}) | |
writer.writerow(row) | |
order_count += 1 | |
if order_count % max_orders == 0: | |
file.close() | |
file = create_csv_writer() | |
file.close() | |
print(f"Data has been combined into multiple CSV files successfully.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment