n8n-nodes-myob
Version:
Simple n8n node to create sales orders in MYOB Business API with multiple line items
675 lines (674 loc) • 40 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Myob = void 0;
const MyobApi_request_1 = require("../transport/MyobApi.request");
class Myob {
constructor() {
this.description = {
displayName: 'MYOB Sales Order',
name: 'myob',
icon: 'file:myob.png',
group: ['output'],
version: 1,
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
description: 'Create sales orders in MYOB Business API',
defaults: { name: 'MYOB Sales Order' },
credentials: [
{ name: 'myobOAuth2Api', required: true },
],
properties: [
{
displayName: 'Customer UID',
name: 'customerUid',
type: 'string',
default: '',
required: true,
description: 'The unique ID of the customer (GUID format)',
},
{
displayName: 'Order Date',
name: 'orderDate',
type: 'string',
default: '',
required: false,
description: 'Sales order date in ISO format (e.g., 2025-08-24T04:32:01+10:00). If not provided, uses current date in n8n timezone.',
placeholder: '2025-08-24T04:32:01+10:00',
},
{
displayName: 'Tax Inclusive',
name: 'isTaxInclusive',
type: 'boolean',
default: true,
description: 'Whether prices include tax',
},
{
displayName: 'Journal Memo',
name: 'journalMemo',
type: 'string',
default: '',
description: 'Internal memo for the journal entry (optional)',
},
{
displayName: 'Comment',
name: 'comment',
type: 'string',
default: '',
description: 'Comment for the sales order (optional)',
},
{
displayName: 'Purchase Order Number',
name: 'purchaseOrderNumber',
type: 'string',
default: '',
description: 'Purchase order reference number (optional)',
},
{
displayName: 'Shipping Address Line 1',
name: 'shippingLine1',
type: 'string',
default: '',
description: 'First line of shipping address (optional)',
},
{
displayName: 'Shipping Address Line 2',
name: 'shippingLine2',
type: 'string',
default: '',
description: 'Second line of shipping address (optional)',
},
{
displayName: 'Shipping Address Line 3',
name: 'shippingLine3',
type: 'string',
default: '',
description: 'Third line of shipping address (optional)',
},
{
displayName: 'Shipping Address Line 4',
name: 'shippingLine4',
type: 'string',
default: '',
description: 'Fourth line of shipping address (optional)',
},
{
displayName: 'Shipping Address Line 5',
name: 'shippingLine5',
type: 'string',
default: '',
description: 'Fifth line of shipping address (optional)',
},
{
displayName: 'Shipping SKU',
name: 'shippingSku',
type: 'string',
default: '',
description: 'SKU for shipping line item (e.g., P&P1, P&P2) - will be added as last line item if provided',
},
{
displayName: 'Shipping Price',
name: 'shippingPrice',
type: 'number',
default: 0,
description: 'Price for shipping line item - will be added as last line item if Shipping SKU is provided',
},
{
displayName: 'Shipping Description',
name: 'shippingDescription',
type: 'string',
default: '',
description: 'Description for shipping line item (optional - uses MYOB item default if not provided)',
},
{
displayName: 'Input Method',
name: 'inputMethod',
type: 'options',
default: 'ui',
description: 'How to specify the line items',
options: [
{ name: 'UI Form', value: 'ui', description: 'Add line items using the form below' },
{ name: 'JSON Array', value: 'json', description: 'Provide line items as a JSON array (for dynamic data)' },
],
},
{
displayName: 'Line Items',
name: 'lineItems',
type: 'fixedCollection',
typeOptions: { multipleValues: true },
default: [{}],
required: true,
displayOptions: {
show: { inputMethod: ['ui'] }
},
description: 'Line items for the sales order',
options: [
{
name: 'item',
displayName: 'Line Item',
values: [
{ displayName: 'SKU (Item Number)', name: 'sku', type: 'string', default: '', required: true, description: 'The SKU/Item Number - will automatically lookup the MYOB Item UID' },
{ displayName: 'Quantity', name: 'quantity', type: 'number', default: 1, required: true },
{ displayName: 'Unit Price', name: 'unitPrice', type: 'number', default: 0, required: true },
{ displayName: 'Tax Code UID', name: 'taxCodeUid', type: 'string', default: '', description: 'The unique ID of the tax code (optional - uses item default if not specified)' },
{ displayName: 'Description', name: 'description', type: 'string', default: '', description: 'Optional description override (optional - uses item default if not specified)' },
],
},
],
},
{
displayName: 'Default SKU',
name: 'defaultSku',
type: 'string',
default: 'DEFAULT-ITEM',
required: true,
displayOptions: {
show: { inputMethod: ['json'] }
},
description: 'Default SKU to use when line items have no SKU, empty SKU, or when SKU lookup fails',
},
{
displayName: 'Default Tax Code UID',
name: 'defaultTaxCodeUid',
type: 'string',
default: '',
displayOptions: {
show: { inputMethod: ['json'] }
},
description: 'Default Tax Code UID to use for all line items (optional - uses item default if not specified)',
},
{
displayName: 'Line Items Data',
name: 'lineItemsData',
type: 'json',
default: '[]',
required: true,
displayOptions: {
show: { inputMethod: ['json'] }
},
description: 'Raw line items array from Shopify webhook or other source. Will extract sku, quantity, price automatically',
placeholder: 'Paste Shopify line_items array or use expression like {{ $json.line_items }}',
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
// Process all input data as a single sales order
if (items.length === 0) {
throw new Error('No input data received');
}
// Use the first item for parameters, ignore additional items to prevent multiple executions
const i = 0;
try {
// Get credentials to build the base URL
const credentials = await this.getCredentials('myobOAuth2Api');
const baseUrl = `https://api.myob.com/accountright/${credentials.companyFileGuid}`;
const customerUid = this.getNodeParameter('customerUid', i);
const orderDate = this.getNodeParameter('orderDate', i);
const isTaxInclusive = this.getNodeParameter('isTaxInclusive', i);
const journalMemo = this.getNodeParameter('journalMemo', i);
const comment = this.getNodeParameter('comment', i);
const purchaseOrderNumber = this.getNodeParameter('purchaseOrderNumber', i);
const shippingLine1 = this.getNodeParameter('shippingLine1', i);
const shippingLine2 = this.getNodeParameter('shippingLine2', i);
const shippingLine3 = this.getNodeParameter('shippingLine3', i);
const shippingLine4 = this.getNodeParameter('shippingLine4', i);
const shippingLine5 = this.getNodeParameter('shippingLine5', i);
const shippingSku = this.getNodeParameter('shippingSku', i);
const shippingPrice = this.getNodeParameter('shippingPrice', i);
const shippingDescription = this.getNodeParameter('shippingDescription', i);
const inputMethod = this.getNodeParameter('inputMethod', i);
// Clean up purchase order number by removing # symbol
const cleanedPurchaseOrderNumber = purchaseOrderNumber ? purchaseOrderNumber.replace(/^#/, '').trim() : '';
// Validate required fields
if (!customerUid) {
throw new Error('Customer UID is required');
}
// Get line items based on input method
let lineItemsArray = [];
let defaultSku = '';
let defaultTaxCodeUid = '';
if (inputMethod === 'json') {
const rawLineItemsData = this.getNodeParameter('lineItemsData', i);
defaultSku = this.getNodeParameter('defaultSku', i);
defaultTaxCodeUid = this.getNodeParameter('defaultTaxCodeUid', i);
try {
let rawData;
// Handle both string (JSON) and already-parsed object/array data
if (typeof rawLineItemsData === 'string') {
rawData = JSON.parse(rawLineItemsData);
}
else if (Array.isArray(rawLineItemsData)) {
rawData = rawLineItemsData;
}
else if (typeof rawLineItemsData === 'object' && rawLineItemsData !== null) {
// If it's an object but not an array, it might be wrapped
rawData = rawLineItemsData;
}
else {
throw new Error('Line Items Data must be a JSON string or array');
}
if (!Array.isArray(rawData)) {
throw new Error('Line Items Data must be an array');
}
// Transform Shopify/raw data to standardized format
lineItemsArray = rawData.map((shopifyItem) => {
// Preserve original SKU value (including null) for proper fallback logic
// Don't substitute defaultSku here - let the processing logic handle fallbacks
const itemSku = (shopifyItem.sku && shopifyItem.sku.trim() !== '') ? shopifyItem.sku.trim() : null;
// Use current_quantity if available, fallback to quantity, then to 1
const itemQuantity = shopifyItem.current_quantity || shopifyItem.quantity || 1;
// Parse price from string to number
const itemPrice = parseFloat(shopifyItem.price || shopifyItem.unit_price || '0');
// Calculate discount amount from Shopify discount_allocations
let discountAmount = 0;
if (shopifyItem.discount_allocations && Array.isArray(shopifyItem.discount_allocations)) {
discountAmount = shopifyItem.discount_allocations.reduce((total, discount) => {
const discountValue = parseFloat(discount.amount || '0');
return total + discountValue;
}, 0);
}
else if (shopifyItem.total_discount) {
// Fallback to total_discount if discount_allocations not available
discountAmount = parseFloat(shopifyItem.total_discount || '0');
}
// Build description from available fields - prioritize 'name' for Shopify line items
let itemDescription = shopifyItem.name || shopifyItem.title || '';
if (shopifyItem.variant_title && shopifyItem.variant_title.trim() !== '') {
itemDescription = `${itemDescription} - ${shopifyItem.variant_title}`.trim();
}
return {
sku: itemSku,
quantity: itemQuantity,
unitPrice: itemPrice,
discountAmount: discountAmount,
taxCodeUid: defaultTaxCodeUid,
description: itemDescription || shopifyItem.description || ''
};
});
}
catch (error) {
throw new Error(`Invalid Line Items Data: ${error.message}`);
}
}
else {
const lineItemsData = this.getNodeParameter('lineItems', i);
if (!lineItemsData || !lineItemsData.item || lineItemsData.item.length === 0) {
throw new Error('At least one line item is required');
}
lineItemsArray = lineItemsData.item;
}
if (lineItemsArray.length === 0) {
throw new Error('At least one line item is required');
}
// Build lines array with SKU lookup
const lines = [];
for (const item of lineItemsArray) {
let itemUid = '';
let itemDescription = item.description || ''; // Transformed description from Shopify data
// Look up Item UID from SKU with fallback to default SKU
let actualSku = item.sku;
if (item.sku && item.sku.trim() !== '') {
try {
const skuEncoded = encodeURIComponent(`'${item.sku}'`);
const lookupUrl = `/Inventory/Item?$filter=Number eq ${skuEncoded}`;
const lookupResponse = await MyobApi_request_1.myobRequest.call(this, 'GET', lookupUrl, {}, {}, baseUrl);
if (lookupResponse && lookupResponse.Items && lookupResponse.Items.length > 0) {
itemUid = lookupResponse.Items[0].UID;
// Always use MYOB description when SKU matches (prioritize over Shopify)
// Prioritize 'Description' field over 'Name' field from MYOB API
if (lookupResponse.Items[0].Description && lookupResponse.Items[0].Description.trim() !== '') {
itemDescription = lookupResponse.Items[0].Description;
console.log(`Using MYOB Description for SKU '${item.sku}': '${itemDescription}'`);
}
else if (lookupResponse.Items[0].Name && lookupResponse.Items[0].Name.trim() !== '') {
itemDescription = lookupResponse.Items[0].Name;
console.log(`Using MYOB Name (fallback) for SKU '${item.sku}': '${itemDescription}'`);
}
else if (!itemDescription || itemDescription.trim() === '') {
// Only fallback to empty if both MYOB Description, Name, and Shopify descriptions are empty
itemDescription = '';
console.log(`No MYOB Description or Name found for SKU '${item.sku}', using Shopify description: '${item.description || ''}'`);
}
}
else {
// SKU not found, fallback to default SKU
console.log(`SKU '${item.sku}' not found in MYOB, falling back to default SKU '${defaultSku}'`);
actualSku = defaultSku;
if (defaultSku && defaultSku.trim() !== '') {
const defaultSkuEncoded = encodeURIComponent(`'${defaultSku}'`);
const defaultLookupUrl = `/Inventory/Item?$filter=Number eq ${defaultSkuEncoded}`;
const defaultLookupResponse = await MyobApi_request_1.myobRequest.call(this, 'GET', defaultLookupUrl, {}, {}, baseUrl);
if (defaultLookupResponse && defaultLookupResponse.Items && defaultLookupResponse.Items.length > 0) {
itemUid = defaultLookupResponse.Items[0].UID;
// Keep Shopify description when using fallback SKU (only use MYOB for exact SKU matches)
// If Shopify description is empty, only then use the MYOB default SKU description as fallback
if (!itemDescription || itemDescription.trim() === '') {
if (defaultLookupResponse.Items[0].Description && defaultLookupResponse.Items[0].Description.trim() !== '') {
itemDescription = defaultLookupResponse.Items[0].Description;
console.log(`No Shopify description, using default SKU MYOB Description: '${itemDescription}'`);
}
else if (defaultLookupResponse.Items[0].Name && defaultLookupResponse.Items[0].Name.trim() !== '') {
itemDescription = defaultLookupResponse.Items[0].Name;
console.log(`No Shopify description, using default SKU MYOB Name: '${itemDescription}'`);
}
}
else {
console.log(`Using Shopify description for fallback SKU: '${itemDescription}'`);
}
}
else {
throw new Error(`Default SKU '${defaultSku}' not found in MYOB`);
}
}
else {
throw new Error(`SKU '${item.sku}' not found in MYOB and no default SKU provided`);
}
}
}
catch (error) {
// If it's a network/API error (not SKU not found), still try default SKU
if (error.message.includes('not found in MYOB')) {
throw error; // Re-throw our custom not found errors
}
else {
console.log(`Failed to lookup SKU '${item.sku}': ${error.message}, falling back to default SKU '${defaultSku}'`);
actualSku = defaultSku;
if (defaultSku && defaultSku.trim() !== '') {
const defaultSkuEncoded = encodeURIComponent(`'${defaultSku}'`);
const defaultLookupUrl = `/Inventory/Item?$filter=Number eq ${defaultSkuEncoded}`;
const defaultLookupResponse = await MyobApi_request_1.myobRequest.call(this, 'GET', defaultLookupUrl, {}, {}, baseUrl);
if (defaultLookupResponse && defaultLookupResponse.Items && defaultLookupResponse.Items.length > 0) {
itemUid = defaultLookupResponse.Items[0].UID;
// Keep Shopify description when using fallback SKU (only use MYOB for exact SKU matches)
// If Shopify description is empty, only then use the MYOB default SKU description as fallback
if (!itemDescription || itemDescription.trim() === '') {
if (defaultLookupResponse.Items[0].Description && defaultLookupResponse.Items[0].Description.trim() !== '') {
itemDescription = defaultLookupResponse.Items[0].Description;
console.log(`No Shopify description, using default SKU MYOB Description: '${itemDescription}'`);
}
else if (defaultLookupResponse.Items[0].Name && defaultLookupResponse.Items[0].Name.trim() !== '') {
itemDescription = defaultLookupResponse.Items[0].Name;
console.log(`No Shopify description, using default SKU MYOB Name: '${itemDescription}'`);
}
}
else {
console.log(`Using Shopify description for fallback SKU: '${itemDescription}'`);
}
}
else {
throw new Error(`Default SKU '${defaultSku}' not found in MYOB`);
}
}
else {
throw new Error(`Failed to lookup SKU '${item.sku}': ${error.message}, and no default SKU provided`);
}
}
}
}
else {
// No SKU provided, use default SKU
console.log(`No SKU provided for item, using default SKU '${defaultSku}'`);
actualSku = defaultSku;
if (defaultSku && defaultSku.trim() !== '') {
const defaultSkuEncoded = encodeURIComponent(`'${defaultSku}'`);
const defaultLookupUrl = `/Inventory/Item?$filter=Number eq ${defaultSkuEncoded}`;
const defaultLookupResponse = await MyobApi_request_1.myobRequest.call(this, 'GET', defaultLookupUrl, {}, {}, baseUrl);
if (defaultLookupResponse && defaultLookupResponse.Items && defaultLookupResponse.Items.length > 0) {
itemUid = defaultLookupResponse.Items[0].UID;
// When no SKU provided, preserve Shopify description (actual product info)
// Only use default SKU for accounting/pricing, keep meaningful product description
// Always prioritize Shopify description over MYOB when using fallback SKU
if (itemDescription && itemDescription.trim() !== '') {
console.log(`Using Shopify description for empty SKU: '${itemDescription}'`);
}
else {
// Only use MYOB description if Shopify description is truly empty
if (defaultLookupResponse.Items[0].Description && defaultLookupResponse.Items[0].Description.trim() !== '') {
itemDescription = defaultLookupResponse.Items[0].Description;
console.log(`No Shopify description (empty SKU), using default SKU MYOB Description: '${itemDescription}'`);
}
else if (defaultLookupResponse.Items[0].Name && defaultLookupResponse.Items[0].Name.trim() !== '') {
itemDescription = defaultLookupResponse.Items[0].Name;
console.log(`No Shopify description (empty SKU), using default SKU MYOB Name: '${itemDescription}'`);
}
}
}
else {
throw new Error(`Default SKU '${defaultSku}' not found in MYOB`);
}
}
else {
throw new Error('No SKU provided and no default SKU available');
}
}
const lineItem = {
Item: { UID: itemUid },
ShipQuantity: item.quantity,
UnitPrice: item.unitPrice,
};
// Add discount percentage if present (from Shopify discounts)
if (item.discountAmount && item.discountAmount > 0 && item.unitPrice > 0) {
// Convert discount amount to percentage based on total line value (unit price × quantity)
const totalLineValue = item.unitPrice * item.quantity;
const discountPercent = (item.discountAmount / totalLineValue) * 100;
lineItem.DiscountPercent = Math.round(discountPercent * 100) / 100; // Round to 2 decimal places
console.log(`Converted discount amount ${item.discountAmount} to percentage: ${lineItem.DiscountPercent}% (unit price: ${item.unitPrice}, quantity: ${item.quantity}, total line value: ${totalLineValue})`);
}
// Add TaxCode if provided
if (item.taxCodeUid && item.taxCodeUid.trim() !== '') {
lineItem.TaxCode = { UID: item.taxCodeUid };
}
// Add description if available
if (itemDescription && itemDescription.trim() !== '') {
lineItem.Description = itemDescription;
}
lines.push(lineItem);
}
// Add shipping line item if provided
if (shippingSku && shippingSku.trim() !== '') {
let shippingItemUid = '';
let finalShippingDescription = shippingDescription || ''; // Start with manual description as fallback
try {
const skuEncoded = encodeURIComponent(`'${shippingSku}'`);
const lookupUrl = `/Inventory/Item?$filter=Number eq ${skuEncoded}`;
const lookupResponse = await MyobApi_request_1.myobRequest.call(this, 'GET', lookupUrl, {}, {}, baseUrl);
if (lookupResponse && lookupResponse.Items && lookupResponse.Items.length > 0) {
shippingItemUid = lookupResponse.Items[0].UID;
// Always use MYOB description when shipping SKU matches (consistent with line items)
// Prioritize 'Description' field over 'Name' field for shipping items
if (lookupResponse.Items[0].Description && lookupResponse.Items[0].Description.trim() !== '') {
finalShippingDescription = lookupResponse.Items[0].Description;
console.log(`✅ USING MYOB Description for shipping SKU '${shippingSku}': '${finalShippingDescription}'`);
}
else if (lookupResponse.Items[0].Name && lookupResponse.Items[0].Name.trim() !== '') {
// Fallback to Name field if Description is empty
finalShippingDescription = lookupResponse.Items[0].Name;
console.log(`✅ USING MYOB Name (fallback) for shipping SKU '${shippingSku}': '${finalShippingDescription}'`);
}
else {
// Only use manual description if both MYOB Description and Name are empty
console.log(`❌ No MYOB Description or Name found for shipping SKU '${shippingSku}', using manual description: '${shippingDescription || ''}'`);
// finalShippingDescription already set to shippingDescription at initialization
}
const shippingLineItem = {
Item: { UID: shippingItemUid },
ShipQuantity: 1,
UnitPrice: shippingPrice,
};
// Add default tax code if provided
if (defaultTaxCodeUid && defaultTaxCodeUid.trim() !== '') {
shippingLineItem.TaxCode = { UID: defaultTaxCodeUid };
}
// Add description if available
if (finalShippingDescription && finalShippingDescription.trim() !== '') {
shippingLineItem.Description = finalShippingDescription;
}
lines.push(shippingLineItem);
}
else {
throw new Error(`Shipping SKU '${shippingSku}' not found in MYOB`);
}
}
catch (error) {
throw new Error(`Failed to lookup Shipping SKU '${shippingSku}': ${error.message}`);
}
}
// Build shipping address from lines (similar to your example)
let shippingAddress = '';
const newline = '\r\n';
// Add each line to the shipping address, appending newline after each
if (shippingLine1 && shippingLine1.trim() !== '') {
shippingAddress = shippingLine1;
}
if (shippingLine2 && shippingLine2.trim() !== '') {
shippingAddress = shippingAddress + (shippingAddress ? newline : '') + shippingLine2;
}
if (shippingLine3 && shippingLine3.trim() !== '') {
shippingAddress = shippingAddress + (shippingAddress ? newline : '') + shippingLine3;
}
if (shippingLine4 && shippingLine4.trim() !== '') {
shippingAddress = shippingAddress + (shippingAddress ? newline : '') + shippingLine4;
}
if (shippingLine5 && shippingLine5.trim() !== '') {
shippingAddress = shippingAddress + (shippingAddress ? newline : '') + shippingLine5;
}
// Build the payload according to MYOB Business API structure
// Handle order date - use provided date or fallback to current date in n8n timezone
let formattedDate;
if (orderDate && orderDate.trim() !== '') {
// Use provided order date (should be in format like 2025-08-24T04:32:01+10:00)
// Extract the local date part and preserve it, don't convert to UTC
try {
const providedDate = new Date(orderDate.trim());
if (isNaN(providedDate.getTime())) {
throw new Error(`Invalid order date format: ${orderDate}`);
}
// Extract the date/time parts from the original input to preserve local date
// Parse the input to get the local date components
const inputDateMatch = orderDate.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/);
if (inputDateMatch) {
// Use the local date and time from input, format as ISO for MYOB
const [, year, month, day, hour, minute, second] = inputDateMatch;
formattedDate = `${year}-${month}-${day}T${hour}:${minute}:${second}.000Z`;
}
else {
// Fallback: extract date from the Date object but keep it as local date
const year = providedDate.getFullYear();
const month = String(providedDate.getMonth() + 1).padStart(2, '0');
const day = String(providedDate.getDate()).padStart(2, '0');
const hour = String(providedDate.getHours()).padStart(2, '0');
const minute = String(providedDate.getMinutes()).padStart(2, '0');
const second = String(providedDate.getSeconds()).padStart(2, '0');
formattedDate = `${year}-${month}-${day}T${hour}:${minute}:${second}.000Z`;
}
console.log(`Using provided order date: ${orderDate} -> ${formattedDate} (preserved local date/time)`);
}
catch (error) {
throw new Error(`Invalid order date format: ${orderDate}. Expected format: 2025-08-24T04:32:01+10:00`);
}
}
else {
// Fallback to current date in configured n8n timezone
const timezone = this.getTimezone();
const currentDate = new Date();
// Create a date that represents today in the configured timezone
const localDate = new Date(currentDate.toLocaleString("en-US", { timeZone: timezone }));
formattedDate = localDate.toISOString().split('T')[0] + 'T00:00:00.000Z';
console.log(`Using current date in timezone ${timezone}: ${formattedDate}`);
}
const payload = {
OrderType: 'Item',
Customer: { UID: customerUid },
Date: formattedDate,
IsTaxInclusive: isTaxInclusive,
Lines: lines,
};
// Add optional fields if provided
if (journalMemo && journalMemo.trim() !== '') {
payload.JournalMemo = journalMemo;
}
if (comment && comment.trim() !== '') {
payload.Comment = comment;
}
if (cleanedPurchaseOrderNumber && cleanedPurchaseOrderNumber.trim() !== '') {
payload.CustomerPurchaseOrderNumber = cleanedPurchaseOrderNumber;
}
if (shippingAddress && shippingAddress.trim() !== '') {
payload.ShipToAddress = shippingAddress;
}
// Step 1: Create the sales order
const createResponse = await MyobApi_request_1.myobRequest.call(this, 'POST', '/Sale/Order/Item', payload, {}, baseUrl);
// Check if creation was successful
let creationSuccessful = false;
if (createResponse && typeof createResponse === 'object' && 'statusCode' in createResponse) {
creationSuccessful = createResponse.statusCode >= 200 && createResponse.statusCode < 300;
}
else {
// Assume success if we got any response without error
creationSuccessful = true;
}
if (!creationSuccessful) {
throw new Error('Failed to create sales order in MYOB');
}
// Step 2: Look up the created sales order by purchase order number
if (cleanedPurchaseOrderNumber && cleanedPurchaseOrderNumber.trim() !== '') {
try {
const searchCriteria = `'${cleanedPurchaseOrderNumber}'`;
const lookupUrl = `/Sale/Order/Item?$filter=CustomerPurchaseOrderNumber eq ${encodeURIComponent(searchCriteria)}`;
const salesOrderResponse = await MyobApi_request_1.myobRequest.call(this, 'GET', lookupUrl, {}, {}, baseUrl);
if (salesOrderResponse && salesOrderResponse.Items && salesOrderResponse.Items.length > 0) {
// Return the found sales order data
returnData.push({
json: salesOrderResponse.Items[0] // Return the first matching sales order
});
}
else {
// Sales order created but not found in lookup (might be timing issue)
returnData.push({
json: {
success: true,
message: 'Sales order created successfully but not found in immediate lookup',
purchaseOrderNumber: cleanedPurchaseOrderNumber,
note: 'The sales order may take a moment to be available for search. Try searching manually in MYOB.'
}
});
}
}
catch (lookupError) {
// Sales order created but lookup failed
returnData.push({
json: {
success: true,
message: 'Sales order created successfully but lookup failed',
purchaseOrderNumber: cleanedPurchaseOrderNumber,
lookupError: lookupError.message,
note: 'The sales order was created in MYOB but could not be retrieved immediately.'
}
});
}
}
else {
// No purchase order number to search by
returnData.push({
json: {
success: true,
message: 'Sales order created successfully',
note: 'No purchase order number provided, so the created sales order cannot be retrieved automatically.'
}
});
}
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({ json: { error: error.message || error.toString() } });
}
else {
throw error;
}
}
return this.prepareOutputData(returnData);
}
}
exports.Myob = Myob;