diff --git a/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts b/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts index a92b24b8ea..ab0024a763 100644 --- a/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts +++ b/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts @@ -329,9 +329,14 @@ export function processLines(this: IExecuteFunctions, lines: IDataObject[], reso TaxCodeRef: { value: line.TaxCodeRef, }, + Qty: line.Qty, }; + if (line.Qty === undefined) { + delete (line.SalesItemLineDetail as IDataObject).Qty; + } delete line.itemId; delete line.TaxCodeRef; + delete line.Qty; } } }); diff --git a/packages/nodes-base/nodes/QuickBooks/__tests__/GenericFunctions.test.ts b/packages/nodes-base/nodes/QuickBooks/__tests__/GenericFunctions.test.ts new file mode 100644 index 0000000000..9f7039d403 --- /dev/null +++ b/packages/nodes-base/nodes/QuickBooks/__tests__/GenericFunctions.test.ts @@ -0,0 +1,75 @@ +import type { IExecuteFunctions } from 'n8n-workflow'; + +import { processLines } from '../GenericFunctions'; +describe('processLines', () => { + const mockExecuteFunctions: Partial = { + getNodeParameter: jest.fn(), + }; + + test('should process AccountBasedExpenseLineDetail for bill resource', () => { + const lines = [{ DetailType: 'AccountBasedExpenseLineDetail', accountId: '123' }]; + + const result = processLines.call(mockExecuteFunctions as IExecuteFunctions, lines, 'bill'); + + expect(result).toEqual([ + { + DetailType: 'AccountBasedExpenseLineDetail', + AccountBasedExpenseLineDetail: { AccountRef: { value: '123' } }, + }, + ]); + }); + + test('should process ItemBasedExpenseLineDetail for bill resource', () => { + const lines = [{ DetailType: 'ItemBasedExpenseLineDetail', itemId: '456' }]; + + const result = processLines.call(mockExecuteFunctions as IExecuteFunctions, lines, 'bill'); + + expect(result).toEqual([ + { + DetailType: 'ItemBasedExpenseLineDetail', + ItemBasedExpenseLineDetail: { ItemRef: { value: '456' } }, + }, + ]); + }); + + test('should process SalesItemLineDetail for estimate resource', () => { + const lines = [{ DetailType: 'SalesItemLineDetail', itemId: '789', TaxCodeRef: 'TAX1' }]; + + const result = processLines.call(mockExecuteFunctions as IExecuteFunctions, lines, 'estimate'); + + expect(result).toEqual([ + { + DetailType: 'SalesItemLineDetail', + SalesItemLineDetail: { ItemRef: { value: '789' }, TaxCodeRef: { value: 'TAX1' } }, + }, + ]); + }); + + test('should process SalesItemLineDetail for invoice resource with Qty', () => { + const lines = [ + { DetailType: 'SalesItemLineDetail', itemId: '101', TaxCodeRef: 'TAX2', Qty: 10 }, + ]; + + const result = processLines.call(mockExecuteFunctions as IExecuteFunctions, lines, 'invoice'); + + expect(result).toEqual([ + { + DetailType: 'SalesItemLineDetail', + SalesItemLineDetail: { ItemRef: { value: '101' }, TaxCodeRef: { value: 'TAX2' }, Qty: 10 }, + }, + ]); + }); + + test('should process SalesItemLineDetail for invoice resource without Qty', () => { + const lines = [{ DetailType: 'SalesItemLineDetail', itemId: '202', TaxCodeRef: 'TAX3' }]; + + const result = processLines.call(mockExecuteFunctions as IExecuteFunctions, lines, 'invoice'); + + expect(result).toEqual([ + { + DetailType: 'SalesItemLineDetail', + SalesItemLineDetail: { ItemRef: { value: '202' }, TaxCodeRef: { value: 'TAX3' } }, + }, + ]); + }); +}); diff --git a/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts b/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts index 8482828f69..6427fd61d7 100644 --- a/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts +++ b/packages/nodes-base/nodes/QuickBooks/descriptions/Invoice/InvoiceDescription.ts @@ -148,6 +148,13 @@ export const invoiceFields: INodeProperties[] = [ loadOptionsMethod: 'getTaxCodeRefs', }, }, + { + displayName: 'Quantity', + name: 'Qty', + description: 'Number of units of the line item', + type: 'number', + default: 0, + }, ], }, {