mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
⚡ 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:
@@ -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',
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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++) {
|
||||||
@@ -172,7 +188,7 @@ export class QuickBooks implements INodeType {
|
|||||||
// 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') {
|
||||||
|
|
||||||
@@ -902,6 +918,36 @@ 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') {
|
||||||
|
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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[];
|
||||||
@@ -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';
|
||||||
|
|||||||
8
packages/nodes-base/nodes/QuickBooks/types.d.ts
vendored
Normal file
8
packages/nodes-base/nodes/QuickBooks/types.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type QuickBooksOAuth2Credentials = {
|
||||||
|
environment: 'production' | 'sandbox';
|
||||||
|
oauthTokenData: {
|
||||||
|
callbackQueryString: {
|
||||||
|
realmId: string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user