Separate scopes and add purchase operations to QuickBooks node (#1859)

* allow qbo to use accounting or payment scopes separately

* added purchase get and getall

* removed irrelevant field options

*  Sort alphabetically

* 🔥 Remove unused file

* ✏️ Fix description casing

*  Add credentials type

* ✏️ Fix resource dividers

* 🔨 Format import

* ✏️ Fix documentation link

*  Refactor credentials

*  Use multiOptions for scopes

* 🔥 Remove payment scope

The payment scope is used only by the QuickBooks Payments API, but this node implements the QuickBooks Online API, which only needs the accounting scope.

* 🚚 Rename node to QuickBooks Online

This reflects the specific API implemented in this node and allows for a future QuickBooks Payments node. Until node versioning is released, only display name changed.

Co-authored-by: Calvin Tan <calvin14@gmail.com>
This commit is contained in:
Iván Ovejero
2021-06-27 13:21:11 +02:00
committed by GitHub
parent 224a26c922
commit 91a5bc3bc4
6 changed files with 206 additions and 22 deletions

View File

@@ -3,11 +3,6 @@ import {
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
const scopes = [
'com.intuit.quickbooks.accounting',
'com.intuit.quickbooks.payment',
];
// https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization // https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization
export class QuickBooksOAuth2Api implements ICredentialType { export class QuickBooksOAuth2Api implements ICredentialType {
@@ -15,7 +10,7 @@ export class QuickBooksOAuth2Api implements ICredentialType {
extends = [ extends = [
'oAuth2Api', 'oAuth2Api',
]; ];
displayName = 'QuickBooks OAuth2 API'; displayName = 'QuickBooks Online OAuth2 API';
documentationUrl = 'quickbooks'; documentationUrl = 'quickbooks';
properties: INodeProperties[] = [ properties: INodeProperties[] = [
{ {
@@ -34,7 +29,7 @@ export class QuickBooksOAuth2Api implements ICredentialType {
displayName: 'Scope', displayName: 'Scope',
name: 'scope', name: 'scope',
type: 'hidden', type: 'hidden',
default: scopes.join(' '), default: 'com.intuit.quickbooks.accounting',
}, },
{ {
displayName: 'Auth URI Query Parameters', displayName: 'Auth URI Query Parameters',

View File

@@ -29,6 +29,10 @@ import {
OptionsWithUri, OptionsWithUri,
} from 'request'; } from 'request';
import {
QuickBooksOAuth2Credentials,
} from './types';
/** /**
* Make an authenticated API request to QuickBooks. * Make an authenticated API request to QuickBooks.
*/ */
@@ -53,7 +57,7 @@ export async function quickBooksApiRequest(
const productionUrl = 'https://quickbooks.api.intuit.com'; const productionUrl = 'https://quickbooks.api.intuit.com';
const sandboxUrl = 'https://sandbox-quickbooks.api.intuit.com'; const sandboxUrl = 'https://sandbox-quickbooks.api.intuit.com';
const credentials = this.getCredentials('quickBooksOAuth2Api') as IDataObject; const credentials = this.getCredentials('quickBooksOAuth2Api') as QuickBooksOAuth2Credentials;
const options: OptionsWithUri = { const options: OptionsWithUri = {
headers: { headers: {

View File

@@ -26,6 +26,8 @@ import {
itemOperations, itemOperations,
paymentFields, paymentFields,
paymentOperations, paymentOperations,
purchaseFields,
purchaseOperations,
vendorFields, vendorFields,
vendorOperations, vendorOperations,
} from './descriptions'; } from './descriptions';
@@ -49,17 +51,21 @@ import {
isEmpty, isEmpty,
} from 'lodash'; } from 'lodash';
import {
QuickBooksOAuth2Credentials,
} from './types';
export class QuickBooks implements INodeType { export class QuickBooks implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
displayName: 'QuickBooks', displayName: 'QuickBooks Online',
name: 'quickbooks', name: 'quickbooks',
icon: 'file:quickbooks.svg', icon: 'file:quickbooks.svg',
group: ['transform'], group: ['transform'],
version: 1, version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume the QuickBooks API', description: 'Consume the QuickBooks Online API',
defaults: { defaults: {
name: 'QuickBooks', name: 'QuickBooks Online',
color: '#2CA01C', color: '#2CA01C',
}, },
inputs: ['main'], inputs: ['main'],
@@ -104,6 +110,10 @@ export class QuickBooks implements INodeType {
name: 'Payment', name: 'Payment',
value: 'payment', value: 'payment',
}, },
{
name: 'Purchase',
value: 'purchase',
},
{ {
name: 'Vendor', name: 'Vendor',
value: 'vendor', value: 'vendor',
@@ -126,6 +136,8 @@ export class QuickBooks implements INodeType {
...itemFields, ...itemFields,
...paymentOperations, ...paymentOperations,
...paymentFields, ...paymentFields,
...purchaseOperations,
...purchaseFields,
...vendorOperations, ...vendorOperations,
...vendorFields, ...vendorFields,
], ],
@@ -145,6 +157,10 @@ export class QuickBooks implements INodeType {
return await loadResource.call(this, 'item'); return await loadResource.call(this, 'item');
}, },
async getPurchases(this: ILoadOptionsFunctions) {
return await loadResource.call(this, 'purchase');
},
async getVendors(this: ILoadOptionsFunctions) { async getVendors(this: ILoadOptionsFunctions) {
return await loadResource.call(this, 'vendor'); return await loadResource.call(this, 'vendor');
}, },
@@ -160,8 +176,8 @@ export class QuickBooks implements INodeType {
let responseData; let responseData;
const returnData: IDataObject[] = []; const returnData: IDataObject[] = [];
const { oauthTokenData } = this.getCredentials('quickBooksOAuth2Api') as IDataObject; const { oauthTokenData } = this.getCredentials('quickBooksOAuth2Api') as QuickBooksOAuth2Credentials;
// @ts-ignore
const companyId = oauthTokenData.callbackQueryString.realmId; const companyId = oauthTokenData.callbackQueryString.realmId;
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
@@ -169,10 +185,10 @@ export class QuickBooks implements INodeType {
if (resource === 'bill') { if (resource === 'bill') {
// ********************************************************************* // *********************************************************************
// bill // bill
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/estimate // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/bill
if (operation === 'create') { if (operation === 'create') {
@@ -288,7 +304,7 @@ export class QuickBooks implements INodeType {
} else if (resource === 'customer') { } else if (resource === 'customer') {
// ********************************************************************* // *********************************************************************
// customer // customer
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/customer // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/customer
@@ -360,7 +376,7 @@ export class QuickBooks implements INodeType {
} else if (resource === 'employee') { } else if (resource === 'employee') {
// ********************************************************************* // *********************************************************************
// employee // employee
// ********************************************************************* // *********************************************************************
if (operation === 'create') { if (operation === 'create') {
@@ -431,7 +447,7 @@ export class QuickBooks implements INodeType {
} else if (resource === 'estimate') { } else if (resource === 'estimate') {
// ********************************************************************* // *********************************************************************
// estimate // estimate
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/estimate // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/estimate
@@ -574,7 +590,7 @@ export class QuickBooks implements INodeType {
} else if (resource === 'invoice') { } else if (resource === 'invoice') {
// ********************************************************************* // *********************************************************************
// invoice // invoice
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/invoice // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/invoice
@@ -733,7 +749,7 @@ export class QuickBooks implements INodeType {
} else if (resource === 'item') { } else if (resource === 'item') {
// ********************************************************************* // *********************************************************************
// item // item
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/item // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/item
@@ -763,7 +779,7 @@ export class QuickBooks implements INodeType {
} else if (resource === 'payment') { } else if (resource === 'payment') {
// ********************************************************************* // *********************************************************************
// payment // payment
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/payment // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/payment
@@ -902,10 +918,40 @@ export class QuickBooks implements INodeType {
} }
} else if (resource === 'purchase') {
// *********************************************************************
// purchase
// *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/purchase
if (operation === 'get') {
// ----------------------------------
// purchase: get
// ----------------------------------
const purchaseId = this.getNodeParameter('purchaseId', i);
const endpoint = `/v3/company/${companyId}/${resource}/${purchaseId}`;
responseData = await quickBooksApiRequest.call(this, 'GET', endpoint, {}, {});
responseData = responseData[capitalCase(resource)];
} else if (operation === 'getAll') {
// ----------------------------------
// purchase: getAll
// ----------------------------------
const endpoint = `/v3/company/${companyId}/query`;
responseData = await handleListing.call(this, i, endpoint, resource);
}
} else if (resource === 'vendor') { } else if (resource === 'vendor') {
// ********************************************************************* // *********************************************************************
// vendor // vendor
// ********************************************************************* // *********************************************************************
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/vendor // https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/vendor
@@ -975,6 +1021,7 @@ export class QuickBooks implements INodeType {
} }
} }
Array.isArray(responseData) Array.isArray(responseData)
? returnData.push(...responseData) ? returnData.push(...responseData)
: returnData.push(responseData); : returnData.push(responseData);

View File

@@ -0,0 +1,129 @@
import {
INodeProperties,
} from 'n8n-workflow';
export const purchaseOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
default: 'get',
description: 'Operation to perform',
options: [
{
name: 'Get',
value: 'get',
},
{
name: 'Get All',
value: 'getAll',
},
],
displayOptions: {
show: {
resource: [
'purchase',
],
},
},
},
] as INodeProperties[];
export const purchaseFields = [
// ----------------------------------
// purchase: get
// ----------------------------------
{
displayName: 'Purchase ID',
name: 'purchaseId',
type: 'string',
required: true,
default: '',
description: 'The ID of the purchase to retrieve.',
displayOptions: {
show: {
resource: [
'purchase',
],
operation: [
'get',
],
},
},
},
// ----------------------------------
// purchase: getAll
// ----------------------------------
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
default: false,
description: 'Return all results.',
displayOptions: {
show: {
resource: [
'purchase',
],
operation: [
'getAll',
],
},
},
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
default: 5,
description: 'The number of results to return.',
typeOptions: {
minValue: 1,
maxValue: 1000,
},
displayOptions: {
show: {
resource: [
'purchase',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
options: [
{
displayName: 'Query',
name: 'query',
type: 'string',
default: '',
placeholder: 'WHERE Metadata.LastUpdatedTime > \'2021-01-01\'',
description: 'The condition for selecting purchases. See the <a href="https://developer.intuit.com/app/developer/qbo/docs/develop/explore-the-quickbooks-online-api/data-queries" target="_blank">guide</a> for supported syntax.',
typeOptions: {
alwaysOpenEditWindow: true,
},
},
],
displayOptions: {
show: {
resource: [
'purchase',
],
operation: [
'getAll',
],
},
},
},
] as INodeProperties[];

View File

@@ -6,3 +6,4 @@ export * from './Invoice/InvoiceDescription';
export * from './Item/ItemDescription'; export * from './Item/ItemDescription';
export * from './Payment/PaymentDescription'; export * from './Payment/PaymentDescription';
export * from './Vendor/VendorDescription'; export * from './Vendor/VendorDescription';
export * from './Purchase/PurchaseDescription';

View File

@@ -0,0 +1,8 @@
export type QuickBooksOAuth2Credentials = {
environment: 'production' | 'sandbox';
oauthTokenData: {
callbackQueryString: {
realmId: string;
}
};
};