I am working on a Node script that creates an XML file using data it has pulled from the Magento api. This xml file has a set structure that I have to replicate. I’ve been having issues getting it set correctly. The XML file currently looks like this:
<Header>
<element>
<BilltoAddress1></BilltoAddress1>
<BilltoAddress2></BilltoAddress2>
<BilltoAddress3></BilltoAddress3>
(I cut out the rest for this example)
</element>
</Header>
<LineItems>
<Lines>
<ProductSpecs></ProductSpecs>
<LineNumber>10</LineNumber>
<OrderNumber></OrderNumber>
<QuantityOrdered></QuantityOrdered>
<StockOrdered></StockOrdered>
<UnitPrice></UnitPrice>
<Options>
<OrderNumber></OrderNumber>
<LineNumber>10</LineNumber>
<BOMDetSeqNum></BOMDetSeqNum>
<OptionNumChosen></OptionNumChosen>
<OptionQty></OptionQty>
<OptionPrice></OptionPrice>
</Options>
</Lines>
<Lines>
<ProductSpecs></ProductSpecs>
<LineNumber>20</LineNumber>
<OrderNumber></OrderNumber>
<QuantityOrdered></QuantityOrdered>
<StockOrdered></StockOrdered>
<UnitPrice></UnitPrice>
<Options>
<OrderNumber></OrderNumber>
<LineNumber>20</LineNumber>
<BOMDetSeqNum></BOMDetSeqNum>
<OptionNumChosen></OptionNumChosen>
<OptionQty></OptionQty>
<OptionPrice></OptionPrice>
</Options>
</Lines>
</LineItems>
but it should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Header>
<element>
<BilltoAddress1></BilltoAddress1>
<BilltoAddress2></BilltoAddress2>
<BilltoAddress3></BilltoAddress3>
</element>
</Header>
<LineItems>
<Lines>
<ProductSpecs></ProductSpecs>
<LineNumber>10</LineNumber>
<OrderNumber></OrderNumber>
<QuantityOrdered></QuantityOrdered>
<StockOrdered></StockOrdered>
<UnitPrice></UnitPrice>
</Lines>
<Options>
<OrderNumber></OrderNumber>
<LineNumber>10</LineNumber>
<BOMDetSeqNum></BOMDetSeqNum>
<OptionNumChosen></OptionNumChosen>
<OptionQty></OptionQty>
<OptionPrice></OptionPrice>
</Options>
<Lines>
<ProductSpecs></ProductSpecs>
<LineNumber>20</LineNumber>
<OrderNumber></OrderNumber>
<QuantityOrdered></QuantityOrdered>
<StockOrdered></StockOrdered>
<UnitPrice></UnitPrice>
</Lines>
<Options>
<OrderNumber></OrderNumber>
<LineNumber>20</LineNumber>
<BOMDetSeqNum></BOMDetSeqNum>
<OptionNumChosen></OptionNumChosen>
<OptionQty></OptionQty>
<OptionPrice></OptionPrice>
</Options>
</LineItems>
</root>
Here is my code,
const fs = require("fs");
function transformOrderData(orders, southwareId, optionsData) {
const transformedOrders = orders.map((order) => {
const customerNumber = order.customer_is_guest === 1 ? 99999 : southwareId || "";
let orderDate = new Date(order.created_at);
let formattedOrderDate = `${('0' + (orderDate.getMonth() + 1)).slice(-2)}/${('0' + orderDate.getDate()).slice(-2)}/${orderDate.getFullYear().toString().substr(-2)}`;
let billingStreet = (order.billing_address && Array.isArray(order.billing_address.street)) ? order.billing_address.street : [];
let shippingAddress = order.extension_attributes.shipping_assignments[0].shipping.address;
let shippingStreet = (shippingAddress && Array.isArray(shippingAddress.street)) ? shippingAddress.street : [];
function formatSku(sku) {
const parts = sku.split('-');
if (parts.length === 1 || parts.length === 2) {
return sku.toUpperCase();
}
return parts[0].toUpperCase();
}
const transformedLineItems = order.items.map((item, itemIndex) => {
const itemLineNumber = (itemIndex + 1) * 10;
const stockOrdered = formatSku(item.sku);
const itemOptions = optionsData.filter(option => {
const isBomMatch = option.ss_bom === stockOrdered;
const isLineNumberMatch = parseInt(option.LineNumber, 10) === itemLineNumber;
return isBomMatch && isLineNumberMatch;
}).map(option => ({
OrderNumber: order.increment_id || "",
LineNumber: itemLineNumber,
BOMDetSeqNum: option.ss_sequence_number || '',
OptionNumChosen: option.ss_option_sequence_number || '',
OptionQty: option.ss_choice_qty || '0',
OptionPrice: option.ss_choice_prc || '0',
}));
return {
ProductSpecs: (item.name || "").toUpperCase(),
LineNumber: itemLineNumber,
OrderNumber: (order.increment_id || ""),
QuantityOrdered: item.qty_ordered || 0,
StockOrdered: stockOrdered,
UnitPrice: item.price_incl_tax || 0,
Options: itemOptions
};
});
return {
Header: {
element: {
BilltoAddress1: (billingStreet[0] || "").toUpperCase(),
BilltoAddress2: (billingStreet[1] || "").toUpperCase(),
BilltoAddress3: (billingStreet[2] || "").toUpperCase(),
BilltoCity: (order.billing_address ? order.billing_address.city : "").toUpperCase(),
BilltoName: `${order.customer_firstname} ${order.customer_lastname}`.toUpperCase(),
BilltoState: (order.billing_address ? order.billing_address.region_code : ""),
BilltoZip: (order.billing_address ? order.billing_address.postcode : ""),
Comment: "",
CustomerNumber: customerNumber,
MagentoID: (order.customer_id || ""),
EmailAddress: (order.customer_email || "").toUpperCase(),
OrderDate: formattedOrderDate,
OrderNumber: (order.increment_id || ""),
OrderStatus: (order.status || ""),
PONumber: (order.increment_id || ""),
ShipViaCode: "",
ShiptoAddress1: (shippingStreet[0] || "").toUpperCase(),
ShiptoAddress2: (shippingStreet[1] || "").toUpperCase(),
ShiptoAddress3: (shippingStreet[2] || "").toUpperCase(),
ShiptoCity: (shippingAddress ? shippingAddress.city : "").toUpperCase(),
ShiptoContactName: `${order.customer_firstname} ${order.customer_lastname}`.toUpperCase(),
ShiptoName: `${order.customer_firstname} ${order.customer_lastname}`.toUpperCase(),
ShiptoPhone: (shippingAddress ? shippingAddress.telephone : ""),
ShiptoState: (shippingAddress ? shippingAddress.region_code : ""),
ShiptoZipCode: (shippingAddress ? shippingAddress.postcode : ""),
TotNonTaxableItems: "",
TotTaxableItems: "",
TotalAllPayment: order.grand_total,
TotalFreightChgs: order.shipping_amount,
TotalOtherCharges: "",
TotalPrice: order.grand_total,
HDRComment: "",
MaskedCardNo: (order.payment.cc_last4 || ""),
CardExp: `${order.payment.cc_exp_month}/${order.payment.cc_exp_year}`,
ChargeAmount: (order.base_total_paid || 0) + (order.tax_amount || 0),
CardAuthNo: (order.payment.cc_approval || ""),
CardTransid: (order.payment.transaction_id || ""),
SalesTaxAmount: order.tax_amount,
DiscountAmount: order.discount_amount,
FreightChargeAmount: order.shipping_amount,
FileName: `${order.increment_id}.xml`,
},
},
LineItems: {
Lines: transformedLineItems
}
};
});
console.log("Transformed Orders:", JSON.stringify(transformedOrders, null, 2));
return transformedOrders;
}
module.exports = {
transformOrderData
};
I’m facing a problem with the structure of my XML output. The issue is with the Options element, which gets nested within the Lines element, whereas it should be at the same hierarchical level as Lines.
Each Line element represents an item from our store, and the associated Options (fetched from a separate data source) should directly follow the corresponding item. Both Line and Options use LineNumber for organization, meaning an item with LineNumber 10 should align with Options having the same LineNumber. However, the current output nests Options within Lines, which is incorrect. I attempted a regex-based solution, but it exacerbated the issue. I’ve included examples of the current output and the desired structure. I am open to running the finalized data through another formatting function if necessary. Any suggestions on how to rectify this XML formatting would be greatly appreciated.