From 77148fe352bab5607057edb3c65fa0f6c0cd8bd2 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 4 Mar 2020 12:05:54 -0500 Subject: [PATCH 1/3] :sparkles: Invoice Ninja Node and Trigger --- .../InvoiceNinjaCloudApi.credentials.ts | 17 + .../invoiceNinjaServerApi.credentials.ts | 17 + .../nodes/InvoiceNinja/ClientDescription.ts | 434 +++++ .../nodes/InvoiceNinja/ClientInterface.ts | 30 + .../nodes/InvoiceNinja/ExpenseDescription.ts | 396 +++++ .../nodes/InvoiceNinja/ExpenseInterface.ts | 19 + .../nodes/InvoiceNinja/GenericFunctions.ts | 80 + .../nodes/InvoiceNinja/ISOCountryCodes.ts | 1581 +++++++++++++++++ .../nodes/InvoiceNinja/InvoiceDescription.ts | 481 +++++ .../nodes/InvoiceNinja/InvoiceNinja.node.ts | 823 +++++++++ .../nodes/InvoiceNinja/InvoiceNinja.png | Bin 0 -> 4699 bytes .../InvoiceNinja/InvoiceNinjaTrigger.node.ts | 171 ++ .../nodes/InvoiceNinja/PaymentDescription.ts | 408 +++++ .../nodes/InvoiceNinja/PaymentInterface.ts | 7 + .../nodes/InvoiceNinja/QuoteDescription.ts | 481 +++++ .../nodes/InvoiceNinja/QuoteInterface.ts | 37 + .../nodes/InvoiceNinja/TaskDescription.ts | 298 ++++ .../nodes/InvoiceNinja/TaskInterface.ts | 8 + .../nodes/InvoiceNinja/invoiceInterface.ts | 36 + packages/nodes-base/package.json | 4 + 20 files changed, 5328 insertions(+) create mode 100644 packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts create mode 100644 packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/ExpenseInterface.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.png create mode 100644 packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/PaymentInterface.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/QuoteInterface.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/TaskInterface.ts create mode 100644 packages/nodes-base/nodes/InvoiceNinja/invoiceInterface.ts diff --git a/packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts b/packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts new file mode 100644 index 0000000000..f9ecdad0e4 --- /dev/null +++ b/packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class InvoiceNinjaCloudApi implements ICredentialType { + name = 'invoiceNinjaCloudApi'; + displayName = 'Invoice Ninja API'; + properties = [ + { + displayName: 'API Token', + name: 'apiToken', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts b/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts new file mode 100644 index 0000000000..4ba8f2a222 --- /dev/null +++ b/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class InvoiceNinjaServerApi implements ICredentialType { + name = 'invoiceNinjaServerApi'; + displayName = 'Invoice Ninja API'; + properties = [ + { + displayName: 'API Token', + name: 'apiToken', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts new file mode 100644 index 0000000000..e10c751183 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts @@ -0,0 +1,434 @@ +import { INodeProperties } from "n8n-workflow"; + +export const clientOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'client', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new client', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a client', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all clients', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a client', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const clientFields = [ +/* -------------------------------------------------------------------------- */ +/* client:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'client', + ], + }, + }, + options: [ + { + displayName: 'Client Name', + name: 'clientName', + type: 'string', + default: '', + }, + { + displayName: 'ID Number', + name: 'idNumber', + type: 'string', + default: '', + }, + { + displayName: 'Private Notes', + name: 'privateNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'VAT Number', + name: 'vatNumber', + type: 'string', + default: '', + }, + { + displayName: 'Work Phone', + name: 'workPhone', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'website', + type: 'string', + default: '', + }, + ] + }, + { + displayName: 'Billing Address', + name: 'billingAddressUi', + placeholder: 'Add Billing Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'billingAddressValue', + displayName: 'Billing Address', + values: [ + { + displayName: 'Street Address', + name: 'streetAddress', + type: 'string', + default: '', + }, + { + displayName: 'Apt/Suite', + name: 'aptSuite', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country Code', + name: 'countryCode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCountryCodes', + }, + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Contacts', + name: 'contactsUi', + placeholder: 'Add Contact', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'contacstValues', + displayName: 'Contact', + values: [ + { + displayName: 'First Name', + name: 'firstName', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'phone', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Shipping Address', + name: 'shippingAddressUi', + placeholder: 'Add Shipping Address', + type: 'fixedCollection', + typeOptions: { + multipleValues: false, + }, + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'shippingAddressValue', + displayName: 'Shipping Address', + values: [ + { + displayName: 'Street Address', + name: 'streetAddress', + type: 'string', + default: '', + }, + { + displayName: 'Apt/Suite', + name: 'aptSuite', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'city', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'state', + type: 'string', + default: '', + }, + { + displayName: 'Postal Code', + name: 'postalCode', + type: 'string', + default: '', + }, + { + displayName: 'Country Code', + name: 'countryCode', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCountryCodes', + }, + default: '', + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* client:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Client ID', + name: 'clientId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'delete', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* client:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Client ID', + name: 'clientId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'get', + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'client', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Invoices', + value: 'invoices', + }, + ], + default: 'invoices', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* client:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'client', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'client', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Invoices', + value: 'invoices', + }, + ], + default: 'invoices', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts new file mode 100644 index 0000000000..998ad2138a --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts @@ -0,0 +1,30 @@ +import { IDataObject } from "n8n-workflow"; + +export interface IContact { + first_name?: string; + last_name?: string; + email?: string; + phone?: string; +} + +export interface IClient { + contacts?: IContact[], + name?: string; + address1?: string; + address2?: string; + city?: string; + state?: string; + postal_code?: string; + country_id?: number; + shipping_address1?: string; + shipping_address2?: string; + shipping_city?: string; + shipping_state?: string; + shipping_postal_code?: string; + shipping_country_id?: number; + work_phone?: string; + private_notes?: string; + website?: string; + vat_number?: string; + id_number?: string; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts new file mode 100644 index 0000000000..39e1c61bb1 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts @@ -0,0 +1,396 @@ +import { INodeProperties } from "n8n-workflow"; + +export const expenseOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'expense', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new expense', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a expense', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all expenses', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a expense', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const expenseFields = [ +/* -------------------------------------------------------------------------- */ +/* expense:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'expense', + ], + }, + }, + options: [ + { + displayName: 'Billable', + name: 'billable', + type: 'boolean', + default: false, + }, + { + displayName: 'Client', + name: 'client', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getClients', + }, + default: '', + }, + { + displayName: 'Custom Value 1', + name: 'customValue1', + type: 'string', + default: '', + }, + { + displayName: 'Custom Value 2', + name: 'customValue2', + type: 'string', + default: '', + }, + { + displayName: 'Category', + name: 'category', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getExpenseCategories', + }, + default: '', + }, + { + displayName: 'Expense Date', + name: 'expenseDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Payment Date', + name: 'paymentDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Payment Type', + name: 'paymentType', + type: 'options', + options: [ + { + name: 'Apply Credit', + value: 1, + }, + { + name: 'Bank Transfer', + value: 2, + }, + { + name: 'Cash', + value: 3, + }, + { + name: 'Debit', + value: 4, + }, + { + name: 'ACH', + value: 5, + }, + { + name: 'Visa Card', + value: 6, + }, + { + name: 'MasterCard', + value: 7, + }, + { + name: 'American Express', + value: 8, + }, + { + name: 'Discover Card', + value: 9, + }, + { + name: 'Diners Card', + value: 10, + }, + { + name: 'EuroCard', + value: 11, + }, + { + name: 'Nova', + value: 12, + }, + { + name: 'Credit Card Other', + value: 13, + }, + { + name: 'Paypal', + value: 14, + }, + { + name: 'Google Wallet', + value: 15, + }, + { + name: 'Check', + value: 16, + }, + { + name: 'Carte Blanche', + value: 17, + }, + { + name: 'UnionPay', + value: 18, + }, + { + name: 'JCB', + value: 19, + }, + { + name: 'Laser', + value: 20, + }, + { + name: 'Maestro', + value: 21, + }, + { + name: 'Solo', + value: 22, + }, + { + name: 'Solo', + value: 22, + }, + { + name: 'Swich', + value: 23, + }, + { + name: 'Swich', + value: 23, + }, + { + name: 'iZettle', + value: 24, + }, + { + name: 'Swish', + value: 25, + }, + { + name: 'Venmo', + value: 26, + }, + { + name: 'Money Order', + value: 27, + }, + { + name: 'Alipay', + value: 28, + }, + { + name: 'Sofort', + value: 29, + }, + { + name: 'SEPA', + value: 30, + }, + { + name: 'GoCardless', + value: 31, + }, + { + name: 'Bitcoin', + value: 32, + }, + ], + default: 1, + }, + { + displayName: 'Private Notes', + name: 'privateNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Public Notes', + name: 'publicNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Tax Name 1', + name: 'taxName1', + type: 'string', + default: '', + }, + { + displayName: 'Tax Name 2', + name: 'taxName2', + type: 'string', + default: '', + }, + { + displayName: 'Tax Rate 1', + name: 'taxRate1', + type: 'number', + default: 0, + }, + { + displayName: 'Tax Rate 2', + name: 'taxRate2', + type: 'number', + default: 0, + }, + { + displayName: 'Transaction Reference', + name: 'transactionReference', + type: 'string', + default: '', + }, + { + displayName: 'Vendor', + name: 'vendor', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getVendors', + }, + default: '', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* expense:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Expense ID', + name: 'expenseId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'expense', + ], + operation: [ + 'delete', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* expense:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Expense ID', + name: 'expenseId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'expense', + ], + operation: [ + 'get', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* expense:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'expense', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'expense', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/ExpenseInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/ExpenseInterface.ts new file mode 100644 index 0000000000..f49e1b42b4 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/ExpenseInterface.ts @@ -0,0 +1,19 @@ +export interface IExpense { + amount?: number; + client_id?: number; + custom_value1?: string; + custom_value2?: string; + expense_category_id?: number; + expense_date?: string; + payment_date?: string; + payment_type_id?: number; + private_notes?: string; + public_notes?: string; + should_be_invoiced?: boolean; + tax_name1?: string; + tax_name2?: string; + tax_rate1?: number; + tax_rate2?: number; + transaction_reference?: string; + vendor_id?: number; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts b/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts new file mode 100644 index 0000000000..2eeea9f596 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts @@ -0,0 +1,80 @@ +import { OptionsWithUri } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +import { get } from 'lodash'; + +export async function invoiceninjaApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any + let token; + let endpoint; + const cloudCredentials = this.getCredentials('invoiceNinjaCloudApi'); + const serverCredentials = this.getCredentials('invoiceNinjaServerApi'); + if (cloudCredentials === undefined && serverCredentials === undefined) { + throw new Error('No credentials got returned!'); + } + if (cloudCredentials !== undefined) { + endpoint = 'https://app.invoiceninja.com'; + token = cloudCredentials!.apiToken; + } else { + endpoint = serverCredentials!.domain; + token = serverCredentials!.apiToken; + } + const options: OptionsWithUri = { + headers: { + Accept: 'application/json', + 'X-Ninja-Token': token, + }, + method, + qs: query, + uri: uri || `${endpoint}/api/v1${resource}`, + body, + json: true + }; + try { + return await this.helpers.request!(options); + } catch (error) { + if (error.response && error.response.body && error.response.body.errors) { + // Try to return the error prettier + const errorMessages = Object.keys(error.response.body.errors).map(errorName => { + return (error.response.body.errors[errorName] as [string]).join(''); + }); + throw new Error(`Invoice Ninja error response [${error.statusCode}]: ${errorMessages.join(' | ')}`); + } + + throw error; + } +} + +export async function invoiceninjaApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + let uri; + query.per_page = 100; + + do { + responseData = await invoiceninjaApiRequest.call(this, method, endpoint, body, query, uri); + let next = get(responseData, 'meta.pagination.links.next') as string | undefined; + if (next) { + uri = next; + } + returnData.push.apply(returnData, responseData[propertyName]); + } while ( + responseData.meta !== undefined && + responseData.meta.pagination && + responseData.meta.pagination.links && + responseData.meta.pagination.links.next + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts b/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts new file mode 100644 index 0000000000..3948ee6285 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/ISOCountryCodes.ts @@ -0,0 +1,1581 @@ +import { IDataObject } from "n8n-workflow"; + +export const countryCodes = [ + { + "name": "Afghanistan", + "alpha2": "AF", + "alpha3": "AFG", + "numeric": "004" + }, + { + "name": "Åland Islands", + "alpha2": "AX", + "alpha3": "ALA", + "numeric": "248", + "altName": "Aland Islands" + }, + { + "name": "Albania", + "alpha2": "AL", + "alpha3": "ALB", + "numeric": "008" + }, + { + "name": "Algeria", + "alpha2": "DZ", + "alpha3": "DZA", + "numeric": "012" + }, + { + "name": "American Samoa", + "alpha2": "AS", + "alpha3": "ASM", + "numeric": "016" + }, + { + "name": "Andorra", + "alpha2": "AD", + "alpha3": "AND", + "numeric": "020" + }, + { + "name": "Angola", + "alpha2": "AO", + "alpha3": "AGO", + "numeric": "024" + }, + { + "name": "Anguilla", + "alpha2": "AI", + "alpha3": "AIA", + "numeric": "660" + }, + { + "name": "Antarctica", + "alpha2": "AQ", + "alpha3": "ATA", + "numeric": "010" + }, + { + "name": "Antigua and Barbuda", + "alpha2": "AG", + "alpha3": "ATG", + "numeric": "028" + }, + { + "name": "Argentina", + "alpha2": "AR", + "alpha3": "ARG", + "numeric": "032" + }, + { + "name": "Armenia", + "alpha2": "AM", + "alpha3": "ARM", + "numeric": "051" + }, + { + "name": "Aruba", + "alpha2": "AW", + "alpha3": "ABW", + "numeric": "533" + }, + { + "name": "Australia", + "alpha2": "AU", + "alpha3": "AUS", + "numeric": "036" + }, + { + "name": "Austria", + "alpha2": "AT", + "alpha3": "AUT", + "numeric": "040" + }, + { + "name": "Azerbaijan", + "alpha2": "AZ", + "alpha3": "AZE", + "numeric": "031" + }, + { + "name": "Bahamas (the)", + "alpha2": "BS", + "alpha3": "BHS", + "numeric": "044", + "altName": "Bahamas" + }, + { + "name": "Bahrain", + "alpha2": "BH", + "alpha3": "BHR", + "numeric": "048" + }, + { + "name": "Bangladesh", + "alpha2": "BD", + "alpha3": "BGD", + "numeric": "050" + }, + { + "name": "Barbados", + "alpha2": "BB", + "alpha3": "BRB", + "numeric": "052" + }, + { + "name": "Belarus", + "alpha2": "BY", + "alpha3": "BLR", + "numeric": "112" + }, + { + "name": "Belgium", + "alpha2": "BE", + "alpha3": "BEL", + "numeric": "056" + }, + { + "name": "Belize", + "alpha2": "BZ", + "alpha3": "BLZ", + "numeric": "084" + }, + { + "name": "Benin", + "alpha2": "BJ", + "alpha3": "BEN", + "numeric": "204" + }, + { + "name": "Bermuda", + "alpha2": "BM", + "alpha3": "BMU", + "numeric": "060" + }, + { + "name": "Bhutan", + "alpha2": "BT", + "alpha3": "BTN", + "numeric": "064" + }, + { + "name": "Bolivia (Plurinational State of)", + "alpha2": "BO", + "alpha3": "BOL", + "numeric": "068", + "altName": "Bolivia" + }, + { + "name": "Bonaire, Sint Eustatius and Saba", + "alpha2": "BQ", + "alpha3": "BES", + "numeric": "535" + }, + { + "name": "Bosnia and Herzegovina", + "alpha2": "BA", + "alpha3": "BIH", + "numeric": "070" + }, + { + "name": "Botswana", + "alpha2": "BW", + "alpha3": "BWA", + "numeric": "072" + }, + { + "name": "Bouvet Island", + "alpha2": "BV", + "alpha3": "BVT", + "numeric": "074" + }, + { + "name": "Brazil", + "alpha2": "BR", + "alpha3": "BRA", + "numeric": "076" + }, + { + "name": "British Indian Ocean Territory (the)", + "alpha2": "IO", + "alpha3": "IOT", + "numeric": "086", + "altName": "British Indian Ocean Territory" + }, + { + "name": "Brunei Darussalam", + "alpha2": "BN", + "alpha3": "BRN", + "numeric": "096", + "shortName": "Brunei" + }, + { + "name": "Bulgaria", + "alpha2": "BG", + "alpha3": "BGR", + "numeric": "100" + }, + { + "name": "Burkina Faso", + "alpha2": "BF", + "alpha3": "BFA", + "numeric": "854" + }, + { + "name": "Burundi", + "alpha2": "BI", + "alpha3": "BDI", + "numeric": "108" + }, + { + "name": "Cabo Verde", + "alpha2": "CV", + "alpha3": "CPV", + "numeric": "132", + "altName": "Cape Verde" + }, + { + "name": "Cambodia", + "alpha2": "KH", + "alpha3": "KHM", + "numeric": "116" + }, + { + "name": "Cameroon", + "alpha2": "CM", + "alpha3": "CMR", + "numeric": "120" + }, + { + "name": "Canada", + "alpha2": "CA", + "alpha3": "CAN", + "numeric": "124" + }, + { + "name": "Cayman Islands (the)", + "alpha2": "KY", + "alpha3": "CYM", + "numeric": "136", + "altName": "Cayman Islands" + }, + { + "name": "Central African Republic (the)", + "alpha2": "CF", + "alpha3": "CAF", + "numeric": "140", + "altName": "Central African Republic" + }, + { + "name": "Chad", + "alpha2": "TD", + "alpha3": "TCD", + "numeric": "148" + }, + { + "name": "Chile", + "alpha2": "CL", + "alpha3": "CHL", + "numeric": "152" + }, + { + "name": "China", + "alpha2": "CN", + "alpha3": "CHN", + "numeric": "156" + }, + { + "name": "Christmas Island", + "alpha2": "CX", + "alpha3": "CXR", + "numeric": "162" + }, + { + "name": "Cocos (Keeling) Islands (the)", + "alpha2": "CC", + "alpha3": "CCK", + "numeric": "166", + "altName": "Cocos (Keeling) Islands", + "shortName": "Cocos Islands" + }, + { + "name": "Colombia", + "alpha2": "CO", + "alpha3": "COL", + "numeric": "170" + }, + { + "name": "Comoros (the)", + "alpha2": "KM", + "alpha3": "COM", + "numeric": "174", + "altName": "Comoros" + }, + { + "name": "Congo (the Democratic Republic of the)", + "alpha2": "CD", + "alpha3": "COD", + "numeric": "180", + "altName": "Congo, (Kinshasa)", + "shortName": "Democratic Republic of the Congo" + }, + { + "name": "Congo (the)", + "alpha2": "CG", + "alpha3": "COG", + "numeric": "178", + "altName": "Congo (Brazzaville)", + "shortName": "Republic of the Congo" + }, + { + "name": "Cook Islands (the)", + "alpha2": "CK", + "alpha3": "COK", + "numeric": "184", + "altName": "Cook Islands" + }, + { + "name": "Costa Rica", + "alpha2": "CR", + "alpha3": "CRI", + "numeric": "188" + }, + { + "name": "Côte d'Ivoire", + "alpha2": "CI", + "alpha3": "CIV", + "numeric": "384", + "shortName": "Ivory Coast" + }, + { + "name": "Croatia", + "alpha2": "HR", + "alpha3": "HRV", + "numeric": "191" + }, + { + "name": "Cuba", + "alpha2": "CU", + "alpha3": "CUB", + "numeric": "192" + }, + { + "name": "Curaçao", + "alpha2": "CW", + "alpha3": "CUW", + "numeric": "531", + "shortName": "Curacao" + }, + { + "name": "Cyprus", + "alpha2": "CY", + "alpha3": "CYP", + "numeric": "196" + }, + { + "name": "Czechia", + "alpha2": "CZ", + "alpha3": "CZE", + "numeric": "203", + "altName": "Czech Republic" + }, + { + "name": "Denmark", + "alpha2": "DK", + "alpha3": "DNK", + "numeric": "208" + }, + { + "name": "Djibouti", + "alpha2": "DJ", + "alpha3": "DJI", + "numeric": "262" + }, + { + "name": "Dominica", + "alpha2": "DM", + "alpha3": "DMA", + "numeric": "212" + }, + { + "name": "Dominican Republic (the)", + "alpha2": "DO", + "alpha3": "DOM", + "numeric": "214", + "altName": "Dominican Republic" + }, + { + "name": "Ecuador", + "alpha2": "EC", + "alpha3": "ECU", + "numeric": "218" + }, + { + "name": "Egypt", + "alpha2": "EG", + "alpha3": "EGY", + "numeric": "818" + }, + { + "name": "El Salvador", + "alpha2": "SV", + "alpha3": "SLV", + "numeric": "222" + }, + { + "name": "Equatorial Guinea", + "alpha2": "GQ", + "alpha3": "GNQ", + "numeric": "226" + }, + { + "name": "Eritrea", + "alpha2": "ER", + "alpha3": "ERI", + "numeric": "232" + }, + { + "name": "Estonia", + "alpha2": "EE", + "alpha3": "EST", + "numeric": "233" + }, + { + "name": "Ethiopia", + "alpha2": "ET", + "alpha3": "ETH", + "numeric": "231" + }, + { + "name": "Falkland Islands (the) [Malvinas]", + "alpha2": "FK", + "alpha3": "FLK", + "numeric": "238", + "altName": "Falkland Islands (Malvinas)", + "shortName": "Falkland Islands" + }, + { + "name": "Faroe Islands (the)", + "alpha2": "FO", + "alpha3": "FRO", + "numeric": "234", + "altName": "Faroe Islands" + }, + { + "name": "Fiji", + "alpha2": "FJ", + "alpha3": "FJI", + "numeric": "242" + }, + { + "name": "Finland", + "alpha2": "FI", + "alpha3": "FIN", + "numeric": "246" + }, + { + "name": "France", + "alpha2": "FR", + "alpha3": "FRA", + "numeric": "250" + }, + { + "name": "French Guiana", + "alpha2": "GF", + "alpha3": "GUF", + "numeric": "254" + }, + { + "name": "French Polynesia", + "alpha2": "PF", + "alpha3": "PYF", + "numeric": "258" + }, + { + "name": "French Southern Territories (the)", + "alpha2": "TF", + "alpha3": "ATF", + "numeric": "260", + "altName": "French Southern Territories" + }, + { + "name": "Gabon", + "alpha2": "GA", + "alpha3": "GAB", + "numeric": "266" + }, + { + "name": "Gambia (the)", + "alpha2": "GM", + "alpha3": "GMB", + "numeric": "270", + "altName": "Gambia" + }, + { + "name": "Georgia", + "alpha2": "GE", + "alpha3": "GEO", + "numeric": "268" + }, + { + "name": "Germany", + "alpha2": "DE", + "alpha3": "DEU", + "numeric": "276" + }, + { + "name": "Ghana", + "alpha2": "GH", + "alpha3": "GHA", + "numeric": "288" + }, + { + "name": "Gibraltar", + "alpha2": "GI", + "alpha3": "GIB", + "numeric": "292" + }, + { + "name": "Greece", + "alpha2": "GR", + "alpha3": "GRC", + "numeric": "300" + }, + { + "name": "Greenland", + "alpha2": "GL", + "alpha3": "GRL", + "numeric": "304" + }, + { + "name": "Grenada", + "alpha2": "GD", + "alpha3": "GRD", + "numeric": "308" + }, + { + "name": "Guadeloupe", + "alpha2": "GP", + "alpha3": "GLP", + "numeric": "312" + }, + { + "name": "Guam", + "alpha2": "GU", + "alpha3": "GUM", + "numeric": "316" + }, + { + "name": "Guatemala", + "alpha2": "GT", + "alpha3": "GTM", + "numeric": "320" + }, + { + "name": "Guernsey", + "alpha2": "GG", + "alpha3": "GGY", + "numeric": "831" + }, + { + "name": "Guinea", + "alpha2": "GN", + "alpha3": "GIN", + "numeric": "324" + }, + { + "name": "Guinea-Bissau", + "alpha2": "GW", + "alpha3": "GNB", + "numeric": "624" + }, + { + "name": "Guyana", + "alpha2": "GY", + "alpha3": "GUY", + "numeric": "328" + }, + { + "name": "Haiti", + "alpha2": "HT", + "alpha3": "HTI", + "numeric": "332" + }, + { + "name": "Heard Island and McDonald Islands", + "alpha2": "HM", + "alpha3": "HMD", + "numeric": "334", + "altName": "Heard and Mcdonald Islands" + }, + { + "name": "Holy See (the)", + "alpha2": "VA", + "alpha3": "VAT", + "numeric": "336", + "altName": "Holy See (Vatican City State)", + "shortName": "Vatican" + }, + { + "name": "Honduras", + "alpha2": "HN", + "alpha3": "HND", + "numeric": "340" + }, + { + "name": "Hong Kong", + "alpha2": "HK", + "alpha3": "HKG", + "numeric": "344", + "altName": "Hong Kong, SAR China" + }, + { + "name": "Hungary", + "alpha2": "HU", + "alpha3": "HUN", + "numeric": "348" + }, + { + "name": "Iceland", + "alpha2": "IS", + "alpha3": "ISL", + "numeric": "352" + }, + { + "name": "India", + "alpha2": "IN", + "alpha3": "IND", + "numeric": "356" + }, + { + "name": "Indonesia", + "alpha2": "ID", + "alpha3": "IDN", + "numeric": "360" + }, + { + "name": "Iran (Islamic Republic of)", + "alpha2": "IR", + "alpha3": "IRN", + "numeric": "364", + "altName": "Iran, Islamic Republic of", + "shortName": "Iran" + }, + { + "name": "Iraq", + "alpha2": "IQ", + "alpha3": "IRQ", + "numeric": "368" + }, + { + "name": "Ireland", + "alpha2": "IE", + "alpha3": "IRL", + "numeric": "372" + }, + { + "name": "Isle of Man", + "alpha2": "IM", + "alpha3": "IMN", + "numeric": "833" + }, + { + "name": "Israel", + "alpha2": "IL", + "alpha3": "ISR", + "numeric": "376" + }, + { + "name": "Italy", + "alpha2": "IT", + "alpha3": "ITA", + "numeric": "380" + }, + { + "name": "Jamaica", + "alpha2": "JM", + "alpha3": "JAM", + "numeric": "388" + }, + { + "name": "Japan", + "alpha2": "JP", + "alpha3": "JPN", + "numeric": "392" + }, + { + "name": "Jersey", + "alpha2": "JE", + "alpha3": "JEY", + "numeric": "832" + }, + { + "name": "Jordan", + "alpha2": "JO", + "alpha3": "JOR", + "numeric": "400" + }, + { + "name": "Kazakhstan", + "alpha2": "KZ", + "alpha3": "KAZ", + "numeric": "398" + }, + { + "name": "Kenya", + "alpha2": "KE", + "alpha3": "KEN", + "numeric": "404" + }, + { + "name": "Kiribati", + "alpha2": "KI", + "alpha3": "KIR", + "numeric": "296" + }, + { + "name": "Korea (the Democratic People's Republic of)", + "alpha2": "KP", + "alpha3": "PRK", + "numeric": "408", + "altName": "Korea (North)", + "shortName": "North Korea" + }, + { + "name": "Korea (the Republic of)", + "alpha2": "KR", + "alpha3": "KOR", + "numeric": "410", + "altName": "Korea (South)", + "shortName": "South Korea" + }, + { + "name": "Kuwait", + "alpha2": "KW", + "alpha3": "KWT", + "numeric": "414" + }, + { + "name": "Kyrgyzstan", + "alpha2": "KG", + "alpha3": "KGZ", + "numeric": "417" + }, + { + "name": "Lao People's Democratic Republic (the)", + "alpha2": "LA", + "alpha3": "LAO", + "numeric": "418", + "altName": "Lao PDR", + "shortName": "Laos" + }, + { + "name": "Latvia", + "alpha2": "LV", + "alpha3": "LVA", + "numeric": "428" + }, + { + "name": "Lebanon", + "alpha2": "LB", + "alpha3": "LBN", + "numeric": "422" + }, + { + "name": "Lesotho", + "alpha2": "LS", + "alpha3": "LSO", + "numeric": "426" + }, + { + "name": "Liberia", + "alpha2": "LR", + "alpha3": "LBR", + "numeric": "430" + }, + { + "name": "Libya", + "alpha2": "LY", + "alpha3": "LBY", + "numeric": "434" + }, + { + "name": "Liechtenstein", + "alpha2": "LI", + "alpha3": "LIE", + "numeric": "438" + }, + { + "name": "Lithuania", + "alpha2": "LT", + "alpha3": "LTU", + "numeric": "440" + }, + { + "name": "Luxembourg", + "alpha2": "LU", + "alpha3": "LUX", + "numeric": "442" + }, + { + "name": "Macao", + "alpha2": "MO", + "alpha3": "MAC", + "numeric": "446", + "altName": "Macao, SAR China", + "shortName": "Macau" + }, + { + "name": "Macedonia (the former Yugoslav Republic of)", + "alpha2": "MK", + "alpha3": "MKD", + "numeric": "807", + "altName": "Macedonia, Republic of", + "shortName": "Macedonia" + }, + { + "name": "Madagascar", + "alpha2": "MG", + "alpha3": "MDG", + "numeric": "450" + }, + { + "name": "Malawi", + "alpha2": "MW", + "alpha3": "MWI", + "numeric": "454" + }, + { + "name": "Malaysia", + "alpha2": "MY", + "alpha3": "MYS", + "numeric": "458" + }, + { + "name": "Maldives", + "alpha2": "MV", + "alpha3": "MDV", + "numeric": "462" + }, + { + "name": "Mali", + "alpha2": "ML", + "alpha3": "MLI", + "numeric": "466" + }, + { + "name": "Malta", + "alpha2": "MT", + "alpha3": "MLT", + "numeric": "470" + }, + { + "name": "Marshall Islands (the)", + "alpha2": "MH", + "alpha3": "MHL", + "numeric": "584", + "altName": "Marshall Islands" + }, + { + "name": "Martinique", + "alpha2": "MQ", + "alpha3": "MTQ", + "numeric": "474" + }, + { + "name": "Mauritania", + "alpha2": "MR", + "alpha3": "MRT", + "numeric": "478" + }, + { + "name": "Mauritius", + "alpha2": "MU", + "alpha3": "MUS", + "numeric": "480" + }, + { + "name": "Mayotte", + "alpha2": "YT", + "alpha3": "MYT", + "numeric": "175" + }, + { + "name": "Mexico", + "alpha2": "MX", + "alpha3": "MEX", + "numeric": "484" + }, + { + "name": "Micronesia (Federated States of)", + "alpha2": "FM", + "alpha3": "FSM", + "numeric": "583", + "altName": "Micronesia, Federated States of", + "shortName": "Micronesia" + }, + { + "name": "Moldova (the Republic of)", + "alpha2": "MD", + "alpha3": "MDA", + "numeric": "498", + "altName": "Moldova" + }, + { + "name": "Monaco", + "alpha2": "MC", + "alpha3": "MCO", + "numeric": "492" + }, + { + "name": "Mongolia", + "alpha2": "MN", + "alpha3": "MNG", + "numeric": "496" + }, + { + "name": "Montenegro", + "alpha2": "ME", + "alpha3": "MNE", + "numeric": "499" + }, + { + "name": "Montserrat", + "alpha2": "MS", + "alpha3": "MSR", + "numeric": "500" + }, + { + "name": "Morocco", + "alpha2": "MA", + "alpha3": "MAR", + "numeric": "504" + }, + { + "name": "Mozambique", + "alpha2": "MZ", + "alpha3": "MOZ", + "numeric": "508" + }, + { + "name": "Myanmar", + "alpha2": "MM", + "alpha3": "MMR", + "numeric": "104" + }, + { + "name": "Namibia", + "alpha2": "NA", + "alpha3": "NAM", + "numeric": "516" + }, + { + "name": "Nauru", + "alpha2": "NR", + "alpha3": "NRU", + "numeric": "520" + }, + { + "name": "Nepal", + "alpha2": "NP", + "alpha3": "NPL", + "numeric": "524" + }, + { + "name": "Netherlands (the)", + "alpha2": "NL", + "alpha3": "NLD", + "numeric": "528", + "altName": "Netherlands" + }, + { + "name": "New Caledonia", + "alpha2": "NC", + "alpha3": "NCL", + "numeric": "540" + }, + { + "name": "New Zealand", + "alpha2": "NZ", + "alpha3": "NZL", + "numeric": "554" + }, + { + "name": "Nicaragua", + "alpha2": "NI", + "alpha3": "NIC", + "numeric": "558" + }, + { + "name": "Niger (the)", + "alpha2": "NE", + "alpha3": "NER", + "numeric": "562", + "altName": "Niger" + }, + { + "name": "Nigeria", + "alpha2": "NG", + "alpha3": "NGA", + "numeric": "566" + }, + { + "name": "Niue", + "alpha2": "NU", + "alpha3": "NIU", + "numeric": "570" + }, + { + "name": "Norfolk Island", + "alpha2": "NF", + "alpha3": "NFK", + "numeric": "574" + }, + { + "name": "Northern Mariana Islands (the)", + "alpha2": "MP", + "alpha3": "MNP", + "numeric": "580", + "altName": "Northern Mariana Islands" + }, + { + "name": "Norway", + "alpha2": "NO", + "alpha3": "NOR", + "numeric": "578" + }, + { + "name": "Oman", + "alpha2": "OM", + "alpha3": "OMN", + "numeric": "512" + }, + { + "name": "Pakistan", + "alpha2": "PK", + "alpha3": "PAK", + "numeric": "586" + }, + { + "name": "Palau", + "alpha2": "PW", + "alpha3": "PLW", + "numeric": "585" + }, + { + "name": "Palestine, State of", + "alpha2": "PS", + "alpha3": "PSE", + "numeric": "275", + "altName": "Palestinian Territory", + "shortName": "Palestine" + }, + { + "name": "Panama", + "alpha2": "PA", + "alpha3": "PAN", + "numeric": "591" + }, + { + "name": "Papua New Guinea", + "alpha2": "PG", + "alpha3": "PNG", + "numeric": "598" + }, + { + "name": "Paraguay", + "alpha2": "PY", + "alpha3": "PRY", + "numeric": "600" + }, + { + "name": "Peru", + "alpha2": "PE", + "alpha3": "PER", + "numeric": "604" + }, + { + "name": "Philippines (the)", + "alpha2": "PH", + "alpha3": "PHL", + "numeric": "608", + "altName": "Philippines" + }, + { + "name": "Pitcairn", + "alpha2": "PN", + "alpha3": "PCN", + "numeric": "612" + }, + { + "name": "Poland", + "alpha2": "PL", + "alpha3": "POL", + "numeric": "616" + }, + { + "name": "Portugal", + "alpha2": "PT", + "alpha3": "PRT", + "numeric": "620" + }, + { + "name": "Puerto Rico", + "alpha2": "PR", + "alpha3": "PRI", + "numeric": "630" + }, + { + "name": "Qatar", + "alpha2": "QA", + "alpha3": "QAT", + "numeric": "634" + }, + { + "name": "Réunion", + "alpha2": "RE", + "alpha3": "REU", + "numeric": "638", + "shortName": "Reunion" + }, + { + "name": "Romania", + "alpha2": "RO", + "alpha3": "ROU", + "numeric": "642" + }, + { + "name": "Russian Federation (the)", + "alpha2": "RU", + "alpha3": "RUS", + "numeric": "643", + "altName": "Russian Federation", + "shortName": "Russia" + }, + { + "name": "Rwanda", + "alpha2": "RW", + "alpha3": "RWA", + "numeric": "646" + }, + { + "name": "Saint Barthélemy", + "alpha2": "BL", + "alpha3": "BLM", + "numeric": "652", + "altName": "Saint-Barthélemy", + "shortName": "Saint Barthelemy" + }, + { + "name": "Saint Helena, Ascension and Tristan da Cunha", + "alpha2": "SH", + "alpha3": "SHN", + "numeric": "654", + "altName": "Saint Helena" + }, + { + "name": "Saint Kitts and Nevis", + "alpha2": "KN", + "alpha3": "KNA", + "numeric": "659" + }, + { + "name": "Saint Lucia", + "alpha2": "LC", + "alpha3": "LCA", + "numeric": "662" + }, + { + "name": "Saint Martin (French part)", + "alpha2": "MF", + "alpha3": "MAF", + "numeric": "663", + "altName": "Saint-Martin (French part)", + "shortName": "Saint Martin" + }, + { + "name": "Saint Pierre and Miquelon", + "alpha2": "PM", + "alpha3": "SPM", + "numeric": "666" + }, + { + "name": "Saint Vincent and the Grenadines", + "alpha2": "VC", + "alpha3": "VCT", + "numeric": "670", + "altName": "Saint Vincent and Grenadines" + }, + { + "name": "Samoa", + "alpha2": "WS", + "alpha3": "WSM", + "numeric": "882" + }, + { + "name": "San Marino", + "alpha2": "SM", + "alpha3": "SMR", + "numeric": "674" + }, + { + "name": "Sao Tome and Principe", + "alpha2": "ST", + "alpha3": "STP", + "numeric": "678" + }, + { + "name": "Saudi Arabia", + "alpha2": "SA", + "alpha3": "SAU", + "numeric": "682" + }, + { + "name": "Senegal", + "alpha2": "SN", + "alpha3": "SEN", + "numeric": "686" + }, + { + "name": "Serbia", + "alpha2": "RS", + "alpha3": "SRB", + "numeric": "688" + }, + { + "name": "Seychelles", + "alpha2": "SC", + "alpha3": "SYC", + "numeric": "690" + }, + { + "name": "Sierra Leone", + "alpha2": "SL", + "alpha3": "SLE", + "numeric": "694" + }, + { + "name": "Singapore", + "alpha2": "SG", + "alpha3": "SGP", + "numeric": "702" + }, + { + "name": "Sint Maarten (Dutch part)", + "alpha2": "SX", + "alpha3": "SXM", + "numeric": "534", + "shortName": "Sint Maarten" + }, + { + "name": "Slovakia", + "alpha2": "SK", + "alpha3": "SVK", + "numeric": "703" + }, + { + "name": "Slovenia", + "alpha2": "SI", + "alpha3": "SVN", + "numeric": "705" + }, + { + "name": "Solomon Islands", + "alpha2": "SB", + "alpha3": "SLB", + "numeric": "090" + }, + { + "name": "Somalia", + "alpha2": "SO", + "alpha3": "SOM", + "numeric": "706" + }, + { + "name": "South Africa", + "alpha2": "ZA", + "alpha3": "ZAF", + "numeric": "710" + }, + { + "name": "South Georgia and the South Sandwich Islands", + "alpha2": "GS", + "alpha3": "SGS", + "numeric": "239" + }, + { + "name": "South Sudan", + "alpha2": "SS", + "alpha3": "SSD", + "numeric": "728" + }, + { + "name": "Spain", + "alpha2": "ES", + "alpha3": "ESP", + "numeric": "724" + }, + { + "name": "Sri Lanka", + "alpha2": "LK", + "alpha3": "LKA", + "numeric": "144" + }, + { + "name": "Sudan (the)", + "alpha2": "SD", + "alpha3": "SDN", + "numeric": "729", + "altName": "Sudan" + }, + { + "name": "Suriname", + "alpha2": "SR", + "alpha3": "SUR", + "numeric": "740" + }, + { + "name": "Svalbard and Jan Mayen", + "alpha2": "SJ", + "alpha3": "SJM", + "numeric": "744", + "altName": "Svalbard and Jan Mayen Islands" + }, + { + "name": "Swaziland", + "alpha2": "SZ", + "alpha3": "SWZ", + "numeric": "748" + }, + { + "name": "Sweden", + "alpha2": "SE", + "alpha3": "SWE", + "numeric": "752" + }, + { + "name": "Switzerland", + "alpha2": "CH", + "alpha3": "CHE", + "numeric": "756" + }, + { + "name": "Syrian Arab Republic", + "alpha2": "SY", + "alpha3": "SYR", + "numeric": "760", + "altName": "Syrian Arab Republic (Syria)", + "shortName": "Syria" + }, + { + "name": "Taiwan (Province of China)", + "alpha2": "TW", + "alpha3": "TWN", + "numeric": "158", + "altName": "Taiwan, Republic of China", + "shortName": "Taiwan" + }, + { + "name": "Tajikistan", + "alpha2": "TJ", + "alpha3": "TJK", + "numeric": "762" + }, + { + "name": "Tanzania, United Republic of", + "alpha2": "TZ", + "alpha3": "TZA", + "numeric": "834", + "shortName": "Tanzania" + }, + { + "name": "Thailand", + "alpha2": "TH", + "alpha3": "THA", + "numeric": "764" + }, + { + "name": "Timor-Leste", + "alpha2": "TL", + "alpha3": "TLS", + "numeric": "626", + "shortName": "East Timor" + }, + { + "name": "Togo", + "alpha2": "TG", + "alpha3": "TGO", + "numeric": "768" + }, + { + "name": "Tokelau", + "alpha2": "TK", + "alpha3": "TKL", + "numeric": "772" + }, + { + "name": "Tonga", + "alpha2": "TO", + "alpha3": "TON", + "numeric": "776" + }, + { + "name": "Trinidad and Tobago", + "alpha2": "TT", + "alpha3": "TTO", + "numeric": "780" + }, + { + "name": "Tunisia", + "alpha2": "TN", + "alpha3": "TUN", + "numeric": "788" + }, + { + "name": "Turkey", + "alpha2": "TR", + "alpha3": "TUR", + "numeric": "792" + }, + { + "name": "Turkmenistan", + "alpha2": "TM", + "alpha3": "TKM", + "numeric": "795" + }, + { + "name": "Turks and Caicos Islands (the)", + "alpha2": "TC", + "alpha3": "TCA", + "numeric": "796", + "altName": "Turks and Caicos Islands" + }, + { + "name": "Tuvalu", + "alpha2": "TV", + "alpha3": "TUV", + "numeric": "798" + }, + { + "name": "Uganda", + "alpha2": "UG", + "alpha3": "UGA", + "numeric": "800" + }, + { + "name": "Ukraine", + "alpha2": "UA", + "alpha3": "UKR", + "numeric": "804" + }, + { + "name": "United Arab Emirates (the)", + "alpha2": "AE", + "alpha3": "ARE", + "numeric": "784", + "altName": "United Arab Emirates" + }, + { + "name": "United Kingdom of Great Britain and Northern Ireland (the)", + "alpha2": "GB", + "alpha3": "GBR", + "numeric": "826", + "altName": "United Kingdom" + }, + { + "name": "United States Minor Outlying Islands (the)", + "alpha2": "UM", + "alpha3": "UMI", + "numeric": "581", + "altName": "US Minor Outlying Islands" + }, + { + "name": "United States of America (the)", + "alpha2": "US", + "alpha3": "USA", + "numeric": "840", + "altName": "United States of America", + "shortName": "United States" + }, + { + "name": "Uruguay", + "alpha2": "UY", + "alpha3": "URY", + "numeric": "858" + }, + { + "name": "Uzbekistan", + "alpha2": "UZ", + "alpha3": "UZB", + "numeric": "860" + }, + { + "name": "Vanuatu", + "alpha2": "VU", + "alpha3": "VUT", + "numeric": "548" + }, + { + "name": "Venezuela (Bolivarian Republic of)", + "alpha2": "VE", + "alpha3": "VEN", + "numeric": "862", + "altName": "Venezuela (Bolivarian Republic)", + "shortName": "Venezuela" + }, + { + "name": "Viet Nam", + "alpha2": "VN", + "alpha3": "VNM", + "numeric": "704", + "shortName": "Vietnam" + }, + { + "name": "Virgin Islands (British)", + "alpha2": "VG", + "alpha3": "VGB", + "numeric": "092", + "altName": "British Virgin Islands" + }, + { + "name": "Virgin Islands (U.S.)", + "alpha2": "VI", + "alpha3": "VIR", + "numeric": "850", + "altName": "Virgin Islands, US", + "shortName": "U.S. Virgin Islands" + }, + { + "name": "Wallis and Futuna", + "alpha2": "WF", + "alpha3": "WLF", + "numeric": "876", + "altName": "Wallis and Futuna Islands" + }, + { + "name": "Western Sahara*", + "alpha2": "EH", + "alpha3": "ESH", + "numeric": "732", + "altName": "Western Sahara" + }, + { + "name": "Yemen", + "alpha2": "YE", + "alpha3": "YEM", + "numeric": "887" + }, + { + "name": "Zambia", + "alpha2": "ZM", + "alpha3": "ZMB", + "numeric": "894" + }, + { + "name": "Zimbabwe", + "alpha2": "ZW", + "alpha3": "ZWE", + "numeric": "716" + } + ] as IDataObject[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts new file mode 100644 index 0000000000..de5a17f974 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts @@ -0,0 +1,481 @@ +import { INodeProperties } from "n8n-workflow"; + +export const invoiceOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new invoice', + }, + { + name: 'Email', + value: 'email', + description: 'Email an invoice', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a invoice', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all invoices', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a invoice', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const invoiceFields = [ +/* -------------------------------------------------------------------------- */ +/* invoice:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'invoice', + ], + }, + }, + options: [ + { + displayName: 'Client', + name: 'client', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getClients', + }, + default: '', + }, + { + displayName: 'Auto Bill', + name: 'autoBill', + type: 'boolean', + default: false, + }, + { + displayName: 'Custom Value 1', + name: 'customValue1', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Custom Value 2', + name: 'customValue2', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Discount', + name: 'discount', + type: 'string', + default: '', + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Email Invoice', + name: 'emailInvoice', + type: 'boolean', + default: false, + }, + { + displayName: 'Invoice Date', + name: 'invoiceDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Invoice Number', + name: 'invoiceNumber', + type: 'string', + default: '', + }, + { + displayName: 'Invoice Status', + name: 'invoiceStatus', + type: 'options', + options: [ + { + name: 'Draft', + value: 1, + }, + { + name: 'Sent', + value: 2, + }, + ], + default: 1, + }, + { + displayName: 'Is Amount Discount', + name: 'isAmountDiscount', + type: 'boolean', + default: false, + }, + { + displayName: 'Paid', + name: 'paid', + type: 'number', + default: 0, + }, + { + displayName: 'Partial', + name: 'partial', + type: 'number', + default: 0, + }, + { + displayName: 'Partial Due Date', + name: 'partialDueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Po Number', + name: 'poNumber', + type: 'string', + default: '', + }, + { + displayName: 'Private Notes', + name: 'privateNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Public Notes', + name: 'publicNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Tax Name 1', + name: 'taxName1', + type: 'string', + default: '', + }, + { + displayName: 'Tax Name 2', + name: 'taxName2', + type: 'string', + default: '', + }, + { + displayName: 'Tax Rate 1', + name: 'taxRate1', + type: 'number', + default: 0, + }, + { + displayName: 'Tax Rate 2', + name: 'taxRate2', + type: 'number', + default: 0, + }, + ], + }, + { + displayName: 'Invoice Items', + name: 'invoiceItemsUi', + placeholder: 'Add Invoice Item', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'invoiceItemsValues', + displayName: 'Invoice Item', + values: [ + { + displayName: 'Cost', + name: 'cost', + type: 'number', + default: 0, + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Service', + name: 'service', + typeOptions: { + alwaysOpenEditWindow: true, + }, + type: 'string', + default: '', + }, + { + displayName: 'Hours', + name: 'hours', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Tax Name 1', + name: 'taxName1', + type: 'string', + default: '', + }, + { + displayName: 'Tax Name 2', + name: 'taxName2', + type: 'string', + default: '', + }, + { + displayName: 'Tax Rate 1', + name: 'taxRate1', + type: 'number', + default: 0, + }, + { + displayName: 'Tax Rate 2', + name: 'taxRate2', + type: 'number', + default: 0, + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* invoice:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Invoice ID', + name: 'invoiceId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'delete', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* invoice:email */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Invoice ID', + name: 'invoiceId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'email', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* invoice:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Invoice ID', + name: 'invoiceId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'get', + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'invoice', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* invoice:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'invoice', + ], + }, + }, + options: [ + { + displayName: 'Invoice Number', + name: 'invoiceNumber', + type: 'string', + default: '', + }, + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts new file mode 100644 index 0000000000..5c996e0384 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts @@ -0,0 +1,823 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; +import { + IDataObject, + INodeTypeDescription, + INodeExecutionData, + INodeType, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; +import { + invoiceninjaApiRequest, + invoiceninjaApiRequestAllItems, +} from './GenericFunctions'; +import { + clientOperations, + clientFields +} from './ClientDescription'; +import { + invoiceOperations, + invoiceFields +} from './InvoiceDescription'; +import { + IClient, + IContact, +} from './ClientInterface'; +import { + countryCodes, + } from './ISOCountryCodes'; +import { + IInvoice, + IItem, + } from './invoiceInterface'; +import { + taskOperations, + taskFields + } from './TaskDescription'; + import { + ITask, + } from './TaskInterface'; + import { + paymentOperations, + paymentFields + } from './PaymentDescription'; + import { + IPayment, + } from './PaymentInterface'; + import { + expenseOperations, + expenseFields + } from './ExpenseDescription'; + import { + IExpense, + } from './ExpenseInterface'; + import { + quoteOperations, + quoteFields + } from './QuoteDescription'; + import { + IQuote, + } from './QuoteInterface'; + +export class InvoiceNinja implements INodeType { + description: INodeTypeDescription = { + displayName: 'Invoice Ninja', + name: 'invoiceNinja', + icon: 'file:invoiceNinja.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Invoice Ninja API', + defaults: { + name: 'Invoice Ninja', + color: '#000000', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'invoiceNinjaCloudApi', + required: true, + displayOptions: { + show: { + invoiceNinjaVersion: [ + 'cloud', + ], + }, + }, + }, + { + name: 'invoiceNinjaServerApi', + required: true, + displayOptions: { + show: { + invoiceNinjaVersion: [ + 'server', + ], + }, + }, + }, + ], + properties: [ + { + displayName: 'Version', + name: 'invoiceNinjaVersion', + type: 'options', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Server (Self Hosted)', + value: 'server', + }, + ], + default: 'cloud', + }, + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + { + name: 'Expense', + value: 'expense', + }, + { + name: 'Invoice', + value: 'invoice', + }, + { + name: 'Payment', + value: 'payment', + }, + { + name: 'Quote', + value: 'quote', + }, + { + name: 'Task', + value: 'task', + }, + ], + default: 'client', + description: 'Resource to consume.', + }, + ...clientOperations, + ...clientFields, + ...invoiceOperations, + ...invoiceFields, + ...taskOperations, + ...taskFields, + ...paymentOperations, + ...paymentFields, + ...expenseOperations, + ...expenseFields, + ...quoteOperations, + ...quoteFields, + ], + }; + + methods = { + loadOptions: { + // Get all the available clients to display them to user so that he can + // select them easily + async getClients(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const clients = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/clients'); + for (const client of clients) { + const clientName = client.display_name; + const clientId = client.id; + returnData.push({ + name: clientName, + value: clientId, + }); + } + return returnData; + }, + // Get all the available projects to display them to user so that he can + // select them easily + async getProjects(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const projects = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/projects'); + for (const project of projects) { + const projectName = project.name; + const projectId = project.id; + returnData.push({ + name: projectName, + value: projectId, + }); + } + return returnData; + }, + // Get all the available invoices to display them to user so that he can + // select them easily + async getInvoices(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const invoices = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/invoices'); + for (const invoice of invoices) { + const invoiceName = invoice.invoice_number; + const invoiceId = invoice.id; + returnData.push({ + name: invoiceName, + value: invoiceId, + }); + } + return returnData; + }, + // Get all the available country codes to display them to user so that he can + // select them easily + async getCountryCodes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + for (let i = 0; i < countryCodes.length; i++) { + const countryName = countryCodes[i].name as string; + const countryId = countryCodes[i].numeric as string; + returnData.push({ + name: countryName, + value: countryId, + }); + } + return returnData; + }, + // Get all the available vendors to display them to user so that he can + // select them easily + async getVendors(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const vendors = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/vendors'); + for (const vendor of vendors) { + const vendorName = vendor.name; + const vendorId = vendor.id; + returnData.push({ + name: vendorName, + value: vendorId, + }); + } + return returnData; + }, + // Get all the available expense categories to display them to user so that he can + // select them easily + async getExpenseCategories(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const categories = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/expense_categories'); + for (const category of categories) { + const categoryName = category.name; + const categoryId = category.id; + returnData.push({ + name: categoryName, + value: categoryId, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + let responseData; + const qs: IDataObject = {}; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + //Routes: https://github.com/invoiceninja/invoiceninja/blob/ff455c8ed9fd0c0326956175ecd509efa8bad263/routes/api.php + if (resource === 'client') { + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IClient = {}; + if (additionalFields.clientName) { + body.name = additionalFields.clientName as string; + } + if (additionalFields.clientName) { + body.name = additionalFields.clientName as string; + } + if (additionalFields.idNumber) { + body.id_number = additionalFields.idNumber as string; + } + if (additionalFields.idNumber) { + body.id_number = additionalFields.idNumber as string; + } + if (additionalFields.privateNotes) { + body.private_notes = additionalFields.privateNotes as string; + } + if (additionalFields.vatNumber) { + body.vat_number = additionalFields.vatNumber as string; + } + if (additionalFields.workPhone) { + body.work_phone = additionalFields.workPhone as string; + } + if (additionalFields.website) { + body.website = additionalFields.website as string; + } + const contactsValues = (this.getNodeParameter('contactsUi', i) as IDataObject).contacstValues as IDataObject[]; + if (contactsValues) { + const contacts: IContact[] = []; + for (const contactValue of contactsValues) { + const contact: IContact = { + first_name: contactValue.firstName as string, + last_name: contactValue.lastName as string, + email: contactValue.email as string, + phone: contactValue.phone as string, + } + contacts.push(contact) + } + body.contacts = contacts; + } + const shippingAddressValue = (this.getNodeParameter('shippingAddressUi', i) as IDataObject).shippingAddressValue as IDataObject; + if (shippingAddressValue) { + body.shipping_address1 = shippingAddressValue.streetAddress as string; + body.shipping_address2 = shippingAddressValue.aptSuite as string; + body.shipping_city = shippingAddressValue.city as string; + body.shipping_state = shippingAddressValue.state as string; + body.shipping_postal_code = shippingAddressValue.postalCode as string; + body.shipping_country_id = parseInt(shippingAddressValue.countryCode as string, 10); + } + const billingAddressValue = (this.getNodeParameter('billingAddressUi', i) as IDataObject).billingAddressValue as IDataObject; + if (billingAddressValue) { + body.address1 = billingAddressValue.streetAddress as string; + body.address2 = billingAddressValue.aptSuite as string; + body.city = billingAddressValue.city as string; + body.state = billingAddressValue.state as string; + body.postal_code = billingAddressValue.postalCode as string; + body.country_id = parseInt(billingAddressValue.countryCode as string, 10); + } + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/clients', body); + responseData = responseData.data; + } + if (operation === 'get') { + const clientId = this.getNodeParameter('clientId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + responseData = await invoiceninjaApiRequest.call(this, 'GET', `/clients/${clientId}`, {}, qs); + responseData = responseData.data; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + if (returnAll === true) { + responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/clients', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', 0) as number; + responseData = await invoiceninjaApiRequest.call(this, 'GET', '/clients', {}, qs); + responseData = responseData.data; + } + } + if (operation === 'delete') { + const clientId = this.getNodeParameter('clientId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/clients/${clientId}`); + responseData = responseData.data; + } + } + if (resource === 'invoice') { + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IInvoice = {}; + if (additionalFields.email) { + body.email = additionalFields.email as string; + } + if (additionalFields.client) { + body.client_id = additionalFields.client as number; + } + if (additionalFields.autoBill) { + body.auto_bill = additionalFields.autoBill as boolean; + } + if (additionalFields.customValue1) { + body.custom_value1 = additionalFields.customValue1 as number; + } + if (additionalFields.customValue2) { + body.custom_value2 = additionalFields.customValue2 as number; + } + if (additionalFields.dueDate) { + body.due_date = additionalFields.dueDate as string; + } + if (additionalFields.invoiceDate) { + body.invoice_date = additionalFields.invoiceDate as string; + } + if (additionalFields.invoiceNumber) { + body.invoice_number = additionalFields.invoiceNumber as string; + } + if (additionalFields.invoiceStatus) { + body.invoice_status_id = additionalFields.invoiceStatus as number; + } + if (additionalFields.isAmountDiscount) { + body.is_amount_discount = additionalFields.isAmountDiscount as boolean; + } + if (additionalFields.partial) { + body.partial = additionalFields.partial as number; + } + if (additionalFields.partialDueDate) { + body.partial_due_date = additionalFields.partialDueDate as string; + } + if (additionalFields.poNumber) { + body.po_number = additionalFields.poNumber as string; + } + if (additionalFields.privateNotes) { + body.private_notes = additionalFields.privateNotes as string; + } + if (additionalFields.publicNotes) { + body.public_notes = additionalFields.publicNotes as string; + } + if (additionalFields.taxName1) { + body.tax_name1 = additionalFields.taxName1 as string; + } + if (additionalFields.taxName2) { + body.tax_name2 = additionalFields.taxName2 as string; + } + if (additionalFields.taxtRate1) { + body.tax_rate1 = additionalFields.taxtRate1 as number; + } + if (additionalFields.taxtRate2) { + body.tax_rate2 = additionalFields.taxtRate2 as number; + } + if (additionalFields.discount) { + body.discount = additionalFields.discount as number; + } + if (additionalFields.paid) { + body.paid = additionalFields.paid as number; + } + if (additionalFields.emailInvoice) { + body.email_invoice = additionalFields.emailInvoice as boolean; + } + const invoceItemsValues = (this.getNodeParameter('invoiceItemsUi', i) as IDataObject).invoiceItemsValues as IDataObject[]; + if (invoceItemsValues) { + const items: IItem[] = []; + for (const itemValue of invoceItemsValues) { + const item: IItem = { + cost: itemValue.cost as number, + notes: itemValue.description as string, + product_key: itemValue.service as string, + qty: itemValue.hours as number, + tax_rate1: itemValue.taxRate1 as number, + tax_rate2: itemValue.taxRate2 as number, + tax_name1: itemValue.taxName1 as string, + tax_name2: itemValue.taxName2 as string, + } + items.push(item) + } + body.invoice_items = items; + } + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/invoices', body); + responseData = responseData.data; + } + if (operation === 'email') { + const invoiceId = this.getNodeParameter('invoiceId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/email_invoice', { id: invoiceId }); + } + if (operation === 'get') { + const invoiceId = this.getNodeParameter('invoiceId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + responseData = await invoiceninjaApiRequest.call(this, 'GET', `/invoices/${invoiceId}`, {}, qs); + responseData = responseData.data; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + if (options.invoiceNumber) { + qs.invoice_number = options.invoiceNumber as string; + } + if (returnAll === true) { + responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/invoices', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', 0) as number; + responseData = await invoiceninjaApiRequest.call(this, 'GET', '/invoices', {}, qs); + responseData = responseData.data; + } + } + if (operation === 'delete') { + const invoiceId = this.getNodeParameter('invoiceId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/invoices/${invoiceId}`); + responseData = responseData.data; + } + } + if (resource === 'task') { + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: ITask = {}; + if (additionalFields.client) { + body.client_id = additionalFields.client as number; + } + if (additionalFields.project) { + body.project = additionalFields.project as number; + } + if (additionalFields.customValue1) { + body.custom_value1 = additionalFields.customValue1 as string; + } + if (additionalFields.customValue2) { + body.custom_value2 = additionalFields.customValue2 as string; + } + if (additionalFields.description) { + body.description = additionalFields.description as string; + } + const timeLogsValues = (this.getNodeParameter('timeLogsUi', i) as IDataObject).timeLogsValues as IDataObject[]; + if (timeLogsValues) { + const logs: number[][] = []; + for (const logValue of timeLogsValues) { + let from = 0, to; + if (logValue.startDate) { + from = new Date(logValue.startDate as string).getTime()/1000 as number; + } + if (logValue.endDate) { + to = new Date(logValue.endDate as string).getTime()/1000 as number; + } + if (logValue.duration) { + to = from + (logValue.duration as number * 3600) + } + logs.push([from as number, to as number]); + } + body.time_log = JSON.stringify(logs); + } + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/tasks', body); + responseData = responseData.data; + } + if (operation === 'get') { + const taskId = this.getNodeParameter('taskId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + responseData = await invoiceninjaApiRequest.call(this, 'GET', `/tasks/${taskId}`, {}, qs); + responseData = responseData.data; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + if (returnAll === true) { + responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/tasks', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', 0) as number; + responseData = await invoiceninjaApiRequest.call(this, 'GET', '/tasks', {}, qs); + responseData = responseData.data; + } + } + if (operation === 'delete') { + const taskId = this.getNodeParameter('taskId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/tasks/${taskId}`); + responseData = responseData.data; + } + } + if (resource === 'payment') { + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const invoice = this.getNodeParameter('invoice', i) as number; + const amount = this.getNodeParameter('amount', i) as number; + const body: IPayment = { + invoice_id: invoice, + amount, + }; + if (additionalFields.paymentType) { + body.payment_type_id = additionalFields.paymentType as number; + } + if (additionalFields.transferReference) { + body.transaction_reference = additionalFields.transferReference as string; + } + if (additionalFields.privateNotes) { + body.private_notes = additionalFields.privateNotes as string; + } + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/payments', body); + responseData = responseData.data; + } + if (operation === 'get') { + const paymentId = this.getNodeParameter('paymentId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + responseData = await invoiceninjaApiRequest.call(this, 'GET', `/payments/${paymentId}`, {}, qs); + responseData = responseData.data; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + if (returnAll === true) { + responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/payments', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', 0) as number; + responseData = await invoiceninjaApiRequest.call(this, 'GET', '/payments', {}, qs); + responseData = responseData.data; + } + } + if (operation === 'delete') { + const paymentId = this.getNodeParameter('paymentId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/payments/${paymentId}`); + responseData = responseData.data; + } + } + if (resource === 'expense') { + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IExpense = {}; + if (additionalFields.billable) { + body.should_be_invoiced = additionalFields.billable as boolean; + } + if (additionalFields.client) { + body.client_id = additionalFields.client as number; + } + if (additionalFields.customValue1) { + body.custom_value1 = additionalFields.customValue1 as string; + } + if (additionalFields.customValue2) { + body.custom_value2 = additionalFields.customValue2 as string; + } + if (additionalFields.category) { + body.expense_category_id = additionalFields.category as number; + } + if (additionalFields.expenseDate) { + body.expense_date = additionalFields.expenseDate as string; + } + if (additionalFields.paymentDate) { + body.payment_date = additionalFields.paymentDate as string; + } + if (additionalFields.paymentType) { + body.payment_type_id = additionalFields.paymentType as number; + } + if (additionalFields.publicNotes) { + body.public_notes = additionalFields.publicNotes as string; + } + if (additionalFields.privateNotes) { + body.private_notes = additionalFields.privateNotes as string; + } + if (additionalFields.taxName1) { + body.tax_name1 = additionalFields.taxName1 as string; + } + if (additionalFields.taxName2) { + body.tax_name2 = additionalFields.taxName2 as string; + } + if (additionalFields.taxRate1) { + body.tax_rate1 = additionalFields.taxRate1 as number; + } + if (additionalFields.taxRate2) { + body.tax_rate2 = additionalFields.taxRate2 as number; + } + if (additionalFields.transactionReference) { + body.transaction_reference = additionalFields.transactionReference as string; + } + if (additionalFields.vendor) { + body.vendor_id = additionalFields.vendor as number; + } + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/expenses', body); + responseData = responseData.data; + } + if (operation === 'get') { + const expenseId = this.getNodeParameter('expenseId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'GET', `/expenses/${expenseId}`, {}, qs); + responseData = responseData.data; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + if (returnAll === true) { + responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/payments', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', 0) as number; + responseData = await invoiceninjaApiRequest.call(this, 'GET', '/payments', {}, qs); + responseData = responseData.data; + } + } + if (operation === 'delete') { + const expenseId = this.getNodeParameter('expenseId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/expenses/${expenseId}`); + responseData = responseData.data; + } + } + if (resource === 'quote') { + if (operation === 'create') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const body: IQuote = { + is_quote: true, + }; + if (additionalFields.client) { + body.client_id = additionalFields.client as number; + } + if (additionalFields.email) { + body.email = additionalFields.email as string; + } + if (additionalFields.autoBill) { + body.auto_bill = additionalFields.autoBill as boolean; + } + if (additionalFields.customValue1) { + body.custom_value1 = additionalFields.customValue1 as number; + } + if (additionalFields.customValue2) { + body.custom_value2 = additionalFields.customValue2 as number; + } + if (additionalFields.dueDate) { + body.due_date = additionalFields.dueDate as string; + } + if (additionalFields.quouteDate) { + body.invoice_date = additionalFields.quouteDate as string; + } + if (additionalFields.quoteNumber) { + body.invoice_number = additionalFields.quoteNumber as string; + } + if (additionalFields.invoiceStatus) { + body.invoice_status_id = additionalFields.invoiceStatus as number; + } + if (additionalFields.isAmountDiscount) { + body.is_amount_discount = additionalFields.isAmountDiscount as boolean; + } + if (additionalFields.partial) { + body.partial = additionalFields.partial as number; + } + if (additionalFields.partialDueDate) { + body.partial_due_date = additionalFields.partialDueDate as string; + } + if (additionalFields.poNumber) { + body.po_number = additionalFields.poNumber as string; + } + if (additionalFields.privateNotes) { + body.private_notes = additionalFields.privateNotes as string; + } + if (additionalFields.publicNotes) { + body.public_notes = additionalFields.publicNotes as string; + } + if (additionalFields.taxName1) { + body.tax_name1 = additionalFields.taxName1 as string; + } + if (additionalFields.taxName2) { + body.tax_name2 = additionalFields.taxName2 as string; + } + if (additionalFields.taxtRate1) { + body.tax_rate1 = additionalFields.taxtRate1 as number; + } + if (additionalFields.taxtRate2) { + body.tax_rate2 = additionalFields.taxtRate2 as number; + } + if (additionalFields.discount) { + body.discount = additionalFields.discount as number; + } + if (additionalFields.paid) { + body.paid = additionalFields.paid as number; + } + if (additionalFields.emailQoute) { + body.email_invoice = additionalFields.emailQoute as boolean; + } + const invoceItemsValues = (this.getNodeParameter('invoiceItemsUi', i) as IDataObject).invoiceItemsValues as IDataObject[]; + if (invoceItemsValues) { + const items: IItem[] = []; + for (const itemValue of invoceItemsValues) { + const item: IItem = { + cost: itemValue.cost as number, + notes: itemValue.description as string, + product_key: itemValue.service as string, + qty: itemValue.hours as number, + tax_rate1: itemValue.taxRate1 as number, + tax_rate2: itemValue.taxRate2 as number, + tax_name1: itemValue.taxName1 as string, + tax_name2: itemValue.taxName2 as string, + } + items.push(item) + } + body.invoice_items = items; + } + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/invoices', body); + responseData = responseData.data; + } + if (operation === 'email') { + const quoteId = this.getNodeParameter('quoteId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'POST', '/email_invoice', { id: quoteId }); + } + if (operation === 'get') { + const quoteId = this.getNodeParameter('quoteId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + responseData = await invoiceninjaApiRequest.call(this, 'GET', `/invoices/${quoteId}`, {}, qs); + responseData = responseData.data; + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + if (options.include) { + qs.include = options.include as string; + } + if (options.invoiceNumber) { + qs.invoice_number = options.invoiceNumber as string; + } + if (returnAll === true) { + responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/quotes', {}, qs); + } else { + qs.per_page = this.getNodeParameter('limit', 0) as number; + responseData = await invoiceninjaApiRequest.call(this, 'GET', '/quotes', {}, qs); + responseData = responseData.data; + } + } + if (operation === 'delete') { + const quoteId = this.getNodeParameter('quoteId', i) as string; + responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/invoices/${quoteId}`); + responseData = responseData.data; + } + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.png b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.png new file mode 100644 index 0000000000000000000000000000000000000000..d35e3050c7e7614a300525049ed97b51a7e73340 GIT binary patch literal 4699 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 Yli1bnW98{Le}3WUYa2sqHQ|x}0Zy~9-v9sr literal 0 HcmV?d00001 diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts new file mode 100644 index 0000000000..924b3947dc --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts @@ -0,0 +1,171 @@ +import { + IHookFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + INodeTypeDescription, + INodeType, + IWebhookResponseData, +} from 'n8n-workflow'; + +import { + invoiceninjaApiRequest, +} from './GenericFunctions'; + +export class InvoiceNinjaTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Invoice Ninja Trigger', + name: 'invoiceNinjaTrigger', + icon: 'file:invoiceNinja.png', + group: ['trigger'], + version: 1, + description: 'Starts the workflow when Invoice Ninja events occure.', + defaults: { + name: 'Invoice Ninja Trigger', + color: '#000000', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'invoiceNinjaCloudApi', + required: true, + displayOptions: { + show: { + invoiceNinjaVersion: [ + 'cloud', + ], + }, + }, + }, + { + name: 'invoiceNinjaServerApi', + required: true, + displayOptions: { + show: { + invoiceNinjaVersion: [ + 'server', + ], + }, + }, + }, + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + responseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Version', + name: 'invoiceNinjaVersion', + type: 'options', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Server (Self Hosted)', + value: 'server', + }, + ], + default: 'cloud', + }, + { + displayName: 'Event', + name: 'event', + type: 'options', + options: [ + { + name: 'client.created', + value: 'create_client', + }, + { + name: 'invoce.created', + value: 'create_invoice', + }, + { + name: 'payment.created', + value: 'create_payment', + }, + { + name: 'quote.created', + value: 'create_quote', + }, + { + name: 'vendor.created', + value: 'create_vendor', + }, + ], + default: '', + required: true, + }, + ], + + }; + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + return false; + }, + async create(this: IHookFunctions): Promise { + const webhookUrl = this.getNodeWebhookUrl('default'); + const event = this.getNodeParameter('event') as string; + + const endpoint = '/hooks'; + + const body = { + target_url: webhookUrl, + event, + }; + + const responseData = await invoiceninjaApiRequest.call(this, 'POST', endpoint, body); + + if (responseData.id === undefined) { + // Required data is missing so was not successful + return false; + } + + const webhookData = this.getWorkflowStaticData('node'); + webhookData.webhookId = responseData.id as string; + + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId !== undefined) { + + const endpoint = `/hooks/${webhookData.webhookId}`; + + try { + await invoiceninjaApiRequest.call(this, 'DELETE', endpoint); + } catch (e) { + return false; + } + + // Remove from the static workflow data so that it is clear + // that no webhooks are registred anymore + delete webhookData.webhookId; + } + + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const bodyData = this.getBodyData(); + return { + workflowData: [ + this.helpers.returnJsonArray(bodyData), + ], + }; + } +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts new file mode 100644 index 0000000000..e3e2763f14 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts @@ -0,0 +1,408 @@ +import { INodeProperties } from "n8n-workflow"; + +export const paymentOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'payment', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new payment', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a payment', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all payments', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a payment', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const paymentFields = [ +/* -------------------------------------------------------------------------- */ +/* payment:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Invoice', + name: 'invoice', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getInvoices', + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'payment', + ], + }, + }, + default: '', + }, + { + displayName: 'Amount', + name: 'amount', + type: 'number', + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'payment', + ], + }, + }, + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'payment', + ], + }, + }, + options: [ + { + displayName: 'Payment Type', + name: 'paymentType', + type: 'options', + options: [ + { + name: 'Apply Credit', + value: 1, + }, + { + name: 'Bank Transfer', + value: 2, + }, + { + name: 'Cash', + value: 3, + }, + { + name: 'Debit', + value: 4, + }, + { + name: 'ACH', + value: 5, + }, + { + name: 'Visa Card', + value: 6, + }, + { + name: 'MasterCard', + value: 7, + }, + { + name: 'American Express', + value: 8, + }, + { + name: 'Discover Card', + value: 9, + }, + { + name: 'Diners Card', + value: 10, + }, + { + name: 'EuroCard', + value: 11, + }, + { + name: 'Nova', + value: 12, + }, + { + name: 'Credit Card Other', + value: 13, + }, + { + name: 'Paypal', + value: 14, + }, + { + name: 'Google Wallet', + value: 15, + }, + { + name: 'Check', + value: 16, + }, + { + name: 'Carte Blanche', + value: 17, + }, + { + name: 'UnionPay', + value: 18, + }, + { + name: 'JCB', + value: 19, + }, + { + name: 'Laser', + value: 20, + }, + { + name: 'Maestro', + value: 21, + }, + { + name: 'Solo', + value: 22, + }, + { + name: 'Solo', + value: 22, + }, + { + name: 'Swich', + value: 23, + }, + { + name: 'Swich', + value: 23, + }, + { + name: 'iZettle', + value: 24, + }, + { + name: 'Swish', + value: 25, + }, + { + name: 'Venmo', + value: 26, + }, + { + name: 'Money Order', + value: 27, + }, + { + name: 'Alipay', + value: 28, + }, + { + name: 'Sofort', + value: 29, + }, + { + name: 'SEPA', + value: 30, + }, + { + name: 'GoCardless', + value: 31, + }, + { + name: 'Bitcoin', + value: 32, + }, + ], + default: 1, + }, + { + displayName: 'Transfer Reference', + name: 'transferReference', + type: 'string', + default: '', + }, + { + displayName: 'Private Notes', + name: 'privateNotes', + typeOptions: { + alwaysOpenEditWindow: true, + }, + type: 'string', + default: '', + }, + ], + }, + +/* -------------------------------------------------------------------------- */ +/* payment:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Payment ID', + name: 'paymentId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'payment', + ], + operation: [ + 'delete', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* payment:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Payment ID', + name: 'paymentId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'payment', + ], + operation: [ + 'get', + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'payment', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* payment:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'payment', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'payment', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'payment', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/PaymentInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/PaymentInterface.ts new file mode 100644 index 0000000000..315f3ced5c --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/PaymentInterface.ts @@ -0,0 +1,7 @@ +export interface IPayment { + invoice_id?: number; + amount?: number; + payment_type_id?: number; + transaction_reference?: string; + private_notes?: string; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts new file mode 100644 index 0000000000..5bdf4ab87d --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts @@ -0,0 +1,481 @@ +import { INodeProperties } from "n8n-workflow"; + +export const quoteOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'quote', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new quote', + }, + { + name: 'Email', + value: 'email', + description: 'Email an quote', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a quote', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all quotes', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a quote', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const quoteFields = [ +/* -------------------------------------------------------------------------- */ +/* quote:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'quote', + ], + }, + }, + options: [ + { + displayName: 'Client', + name: 'client', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getClients', + }, + default: '', + }, + { + displayName: 'Auto Bill', + name: 'autoBill', + type: 'boolean', + default: false, + }, + { + displayName: 'Custom Value 1', + name: 'customValue1', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Custom Value 2', + name: 'customValue2', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Discount', + name: 'discount', + type: 'string', + default: '', + }, + { + displayName: 'Due Date', + name: 'dueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + default: '', + }, + { + displayName: 'Email Qoute', + name: 'emailQoute', + type: 'boolean', + default: false, + }, + { + displayName: 'Quote Date', + name: 'quoteDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Quote Number', + name: 'quoteNumber', + type: 'string', + default: '', + }, + { + displayName: 'Qoute Status', + name: 'quoteStatus', + type: 'options', + options: [ + { + name: 'Draft', + value: 1, + }, + { + name: 'Sent', + value: 2, + }, + ], + default: 1, + }, + { + displayName: 'Is Amount Discount', + name: 'isAmountDiscount', + type: 'boolean', + default: false, + }, + { + displayName: 'Paid', + name: 'paid', + type: 'number', + default: 0, + }, + { + displayName: 'Partial', + name: 'partial', + type: 'number', + default: 0, + }, + { + displayName: 'Partial Due Date', + name: 'partialDueDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Po Number', + name: 'poNumber', + type: 'string', + default: '', + }, + { + displayName: 'Private Notes', + name: 'privateNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Public Notes', + name: 'publicNotes', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Tax Name 1', + name: 'taxName1', + type: 'string', + default: '', + }, + { + displayName: 'Tax Name 2', + name: 'taxName2', + type: 'string', + default: '', + }, + { + displayName: 'Tax Rate 1', + name: 'taxRate1', + type: 'number', + default: 0, + }, + { + displayName: 'Tax Rate 2', + name: 'taxRate2', + type: 'number', + default: 0, + }, + ], + }, + { + displayName: 'Invoice Items', + name: 'invoiceItemsUi', + placeholder: 'Add Invoice Item', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'invoiceItemsValues', + displayName: 'Invoice Item', + values: [ + { + displayName: 'Cost', + name: 'cost', + type: 'number', + default: 0, + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Service', + name: 'service', + typeOptions: { + alwaysOpenEditWindow: true, + }, + type: 'string', + default: '', + }, + { + displayName: 'Hours', + name: 'hours', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Tax Name 1', + name: 'taxName1', + type: 'string', + default: '', + }, + { + displayName: 'Tax Name 2', + name: 'taxName2', + type: 'string', + default: '', + }, + { + displayName: 'Tax Rate 1', + name: 'taxRate1', + type: 'number', + default: 0, + }, + { + displayName: 'Tax Rate 2', + name: 'taxRate2', + type: 'number', + default: 0, + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* quote:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Quote ID', + name: 'quoteId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'delete', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* quote:email */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Quote ID', + name: 'quoteId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'email', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* quote:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Qoute ID', + name: 'quoteId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'get', + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'quote', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* quote:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'quote', + ], + }, + }, + options: [ + { + displayName: 'Qoute Number', + name: 'quoteNumber', + type: 'string', + default: '', + }, + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/QuoteInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/QuoteInterface.ts new file mode 100644 index 0000000000..7b053b8acc --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/QuoteInterface.ts @@ -0,0 +1,37 @@ +export interface IItem { + cost?: number; + notes?: string; + product_key?: string; + qty?: number; + tax_rate1?: number; + tax_rate2?: number; + tax_name1?: string; + tax_name2?: string; +} + +export interface IQuote { + auto_bill?: boolean; + client_id?: number; + custom_value1?: number; + custom_value2?: number; + email_invoice?: boolean; + discount?: number; + due_date?: string; + email?: string; + invoice_date?: string; + invoice_items?: IItem[]; + invoice_number?: string; + invoice_status_id?: number; + is_amount_discount?: boolean; + is_quote?: boolean; + paid?: number; + partial?: number; + partial_due_date?: string; + po_number?: string; + private_notes?: string; + public_notes?: string; + tax_name1?: string; + tax_name2?: string; + tax_rate1?: number; + tax_rate2?: number; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts new file mode 100644 index 0000000000..a3c608fc9e --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts @@ -0,0 +1,298 @@ +import { INodeProperties } from "n8n-workflow"; + +export const taskOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'task', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new task', + }, + { + name: 'Get', + value: 'get', + description: 'Get data of a task', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get data of all tasks', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task', + } + ], + default: 'create', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const taskFields = [ +/* -------------------------------------------------------------------------- */ +/* task:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'task', + ], + }, + }, + options: [ + { + displayName: 'Client', + name: 'client', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getClients', + }, + default: '', + }, + { + displayName: 'Custom Value 1', + name: 'customValue1', + type: 'string', + default: '', + }, + { + displayName: 'Custom Value 2', + name: 'customValue2', + type: 'string', + default: '', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + }, + { + displayName: 'Project', + name: 'project', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + }, + ], + }, + { + displayName: 'Time Logs', + name: 'timeLogsUi', + placeholder: 'Add Time Log', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + default: {}, + options: [ + { + name: 'timeLogsValues', + displayName: 'Time Log', + values: [ + { + displayName: 'Start Date', + name: 'startDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'End Date', + name: 'endDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Duration (Hours)', + name: 'duration', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + ], + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* task:delete */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'taskId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'delete', + ], + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* task:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Task ID', + name: 'taskId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'get', + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'task', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, +/* -------------------------------------------------------------------------- */ +/* task:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 60, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'task', + ], + }, + }, + options: [ + { + displayName: 'Include', + name: 'include', + type: 'options', + options: [ + { + name: 'Client', + value: 'client', + }, + ], + default: 'client', + }, + ], + }, + +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/InvoiceNinja/TaskInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/TaskInterface.ts new file mode 100644 index 0000000000..383bed5d94 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/TaskInterface.ts @@ -0,0 +1,8 @@ +export interface ITask { + client_id?: number; + custom_value1?: string; + custom_value2?: string; + description?: string; + project?: number; + time_log?: string; +} diff --git a/packages/nodes-base/nodes/InvoiceNinja/invoiceInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/invoiceInterface.ts new file mode 100644 index 0000000000..ab61e4fd96 --- /dev/null +++ b/packages/nodes-base/nodes/InvoiceNinja/invoiceInterface.ts @@ -0,0 +1,36 @@ +export interface IItem { + cost?: number; + notes?: string; + product_key?: string; + qty?: number; + tax_rate1?: number; + tax_rate2?: number; + tax_name1?: string; + tax_name2?: string; +} + +export interface IInvoice { + auto_bill?: boolean; + client_id?: number; + custom_value1?: number; + custom_value2?: number; + email_invoice?: boolean; + email?: string; + discount?: number; + due_date?: string; + invoice_date?: string; + invoice_items?: IItem[]; + invoice_number?: string; + invoice_status_id?: number; + is_amount_discount?: boolean; + paid?: number; + partial?: number; + partial_due_date?: string; + po_number?: string; + private_notes?: string; + public_notes?: string; + tax_name1?: string; + tax_name2?: string; + tax_rate1?: number; + tax_rate2?: number; +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index c83d7f80ab..96b7592613 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -59,6 +59,8 @@ "dist/credentials/HunterApi.credentials.js", "dist/credentials/Imap.credentials.js", "dist/credentials/IntercomApi.credentials.js", + "dist/credentials/InvoiceNinjaCloudApi.credentials.js", + "dist/credentials/InvoiceNinjaServerApi.credentials.js", "dist/credentials/JiraSoftwareCloudApi.credentials.js", "dist/credentials/JiraSoftwareServerApi.credentials.js", "dist/credentials/JotFormApi.credentials.js", @@ -159,6 +161,8 @@ "dist/nodes/Hunter/Hunter.node.js", "dist/nodes/If.node.js", "dist/nodes/Intercom/Intercom.node.js", + "dist/nodes/InvoiceNinja/InvoiceNinja.node.js", + "dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js", "dist/nodes/Interval.node.js", "dist/nodes/Jira/JiraSoftwareCloud.node.js", "dist/nodes/JotForm/JotFormTrigger.node.js", From 0e9106c563eea9ae70545a3188a03bb396486a95 Mon Sep 17 00:00:00 2001 From: ricardo Date: Fri, 13 Mar 2020 10:05:17 -0400 Subject: [PATCH 2/3] :zap: small fix --- .../credentials/invoiceNinjaServerApi.credentials.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts b/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts index 4ba8f2a222..906b638921 100644 --- a/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts +++ b/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts @@ -7,6 +7,12 @@ export class InvoiceNinjaServerApi implements ICredentialType { name = 'invoiceNinjaServerApi'; displayName = 'Invoice Ninja API'; properties = [ + { + displayName: 'Domain', + name: 'domain', + type: 'string' as NodePropertyTypes, + default: '', + }, { displayName: 'API Token', name: 'apiToken', From d3a2456de9c4a46a0c7dbb211b9aac16b55761f7 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Mon, 16 Mar 2020 23:26:27 +0100 Subject: [PATCH 3/3] :zap: Fix some InvoiceNinja-Node issues --- ...ials.ts => InvoiceNinjaApi.credentials.ts} | 10 +- .../InvoiceNinjaCloudApi.credentials.ts | 17 -- .../nodes/InvoiceNinja/ClientDescription.ts | 10 +- .../nodes/InvoiceNinja/ClientInterface.ts | 2 +- .../nodes/InvoiceNinja/ExpenseDescription.ts | 18 ++- .../nodes/InvoiceNinja/GenericFunctions.ts | 28 ++-- .../nodes/InvoiceNinja/InvoiceDescription.ts | 12 +- .../nodes/InvoiceNinja/InvoiceNinja.node.ts | 151 +++++++----------- .../nodes/InvoiceNinja/InvoiceNinja.png | Bin 4699 -> 0 bytes .../InvoiceNinja/InvoiceNinjaTrigger.node.ts | 52 ++---- .../nodes/InvoiceNinja/PaymentDescription.ts | 10 +- .../nodes/InvoiceNinja/QuoteDescription.ts | 20 +-- .../nodes/InvoiceNinja/TaskDescription.ts | 10 +- .../nodes/InvoiceNinja/invoiceNinja.png | Bin 0 -> 1109 bytes packages/nodes-base/package.json | 3 +- 15 files changed, 129 insertions(+), 214 deletions(-) rename packages/nodes-base/credentials/{invoiceNinjaServerApi.credentials.ts => InvoiceNinjaApi.credentials.ts} (63%) delete mode 100644 packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts delete mode 100644 packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.png create mode 100644 packages/nodes-base/nodes/InvoiceNinja/invoiceNinja.png diff --git a/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts b/packages/nodes-base/credentials/InvoiceNinjaApi.credentials.ts similarity index 63% rename from packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts rename to packages/nodes-base/credentials/InvoiceNinjaApi.credentials.ts index 906b638921..d79c23859e 100644 --- a/packages/nodes-base/credentials/invoiceNinjaServerApi.credentials.ts +++ b/packages/nodes-base/credentials/InvoiceNinjaApi.credentials.ts @@ -3,15 +3,15 @@ import { NodePropertyTypes, } from 'n8n-workflow'; -export class InvoiceNinjaServerApi implements ICredentialType { - name = 'invoiceNinjaServerApi'; +export class InvoiceNinjaApi implements ICredentialType { + name = 'invoiceNinjaApi'; displayName = 'Invoice Ninja API'; properties = [ { - displayName: 'Domain', - name: 'domain', + displayName: 'URL', + name: 'url', type: 'string' as NodePropertyTypes, - default: '', + default: 'https://app.invoiceninja.com', }, { displayName: 'API Token', diff --git a/packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts b/packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts deleted file mode 100644 index f9ecdad0e4..0000000000 --- a/packages/nodes-base/credentials/InvoiceNinjaCloudApi.credentials.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - ICredentialType, - NodePropertyTypes, -} from 'n8n-workflow'; - -export class InvoiceNinjaCloudApi implements ICredentialType { - name = 'invoiceNinjaCloudApi'; - displayName = 'Invoice Ninja API'; - properties = [ - { - displayName: 'API Token', - name: 'apiToken', - type: 'string' as NodePropertyTypes, - default: '', - }, - ]; -} diff --git a/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts index e10c751183..4a9a467d8d 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/ClientDescription.ts @@ -18,6 +18,11 @@ export const clientOperations = [ value: 'create', description: 'Create a new client', }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a client', + }, { name: 'Get', value: 'get', @@ -28,11 +33,6 @@ export const clientOperations = [ value: 'getAll', description: 'Get data of all clients', }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a client', - } ], default: 'create', description: 'The operation to perform.', diff --git a/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts b/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts index 998ad2138a..0590068e9a 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/ClientInterface.ts @@ -8,7 +8,7 @@ export interface IContact { } export interface IClient { - contacts?: IContact[], + contacts?: IContact[]; name?: string; address1?: string; address2?: string; diff --git a/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts index 39e1c61bb1..3bf3f6977e 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/ExpenseDescription.ts @@ -18,21 +18,21 @@ export const expenseOperations = [ value: 'create', description: 'Create a new expense', }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an expense', + }, { name: 'Get', value: 'get', - description: 'Get data of a expense', + description: 'Get data of an expense', }, { name: 'Get All', value: 'getAll', description: 'Get data of all expenses', }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a expense', - } ], default: 'create', description: 'The operation to perform.', @@ -60,6 +60,12 @@ export const expenseFields = [ }, }, options: [ + { + displayName: 'Amount', + name: 'amount', + type: 'number', + default: 0, + }, { displayName: 'Billable', name: 'billable', diff --git a/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts b/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts index 2eeea9f596..9d690807c2 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts @@ -13,29 +13,21 @@ import { import { get } from 'lodash'; -export async function invoiceninjaApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any - let token; - let endpoint; - const cloudCredentials = this.getCredentials('invoiceNinjaCloudApi'); - const serverCredentials = this.getCredentials('invoiceNinjaServerApi'); - if (cloudCredentials === undefined && serverCredentials === undefined) { +export async function invoiceNinjaApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('invoiceNinjaApi'); + if (credentials === undefined) { throw new Error('No credentials got returned!'); } - if (cloudCredentials !== undefined) { - endpoint = 'https://app.invoiceninja.com'; - token = cloudCredentials!.apiToken; - } else { - endpoint = serverCredentials!.domain; - token = serverCredentials!.apiToken; - } + + const baseUrl = credentials!.url || 'https://app.invoiceninja.com'; const options: OptionsWithUri = { headers: { Accept: 'application/json', - 'X-Ninja-Token': token, + 'X-Ninja-Token': credentials.apiToken, }, method, qs: query, - uri: uri || `${endpoint}/api/v1${resource}`, + uri: uri || `${baseUrl}/api/v1${endpoint}`, body, json: true }; @@ -54,7 +46,7 @@ export async function invoiceninjaApiRequest(this: IHookFunctions | IExecuteFunc } } -export async function invoiceninjaApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any +export async function invoiceNinjaApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; @@ -63,8 +55,8 @@ export async function invoiceninjaApiRequestAllItems(this: IExecuteFunctions | I query.per_page = 100; do { - responseData = await invoiceninjaApiRequest.call(this, method, endpoint, body, query, uri); - let next = get(responseData, 'meta.pagination.links.next') as string | undefined; + responseData = await invoiceNinjaApiRequest.call(this, method, endpoint, body, query, uri); + const next = get(responseData, 'meta.pagination.links.next') as string | undefined; if (next) { uri = next; } diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts index de5a17f974..51ace0215c 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceDescription.ts @@ -18,6 +18,11 @@ export const invoiceOperations = [ value: 'create', description: 'Create a new invoice', }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a invoice', + }, { name: 'Email', value: 'email', @@ -33,11 +38,6 @@ export const invoiceOperations = [ value: 'getAll', description: 'Get data of all invoices', }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a invoice', - } ], default: 'create', description: 'The operation to perform.', @@ -175,7 +175,7 @@ export const invoiceFields = [ default: '', }, { - displayName: 'Po Number', + displayName: 'PO Number', name: 'poNumber', type: 'string', default: '', diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts index 5c996e0384..b1be5bdcd0 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts @@ -3,23 +3,23 @@ import { } from 'n8n-core'; import { IDataObject, - INodeTypeDescription, INodeExecutionData, - INodeType, ILoadOptionsFunctions, INodePropertyOptions, + INodeType, + INodeTypeDescription, } from 'n8n-workflow'; import { - invoiceninjaApiRequest, - invoiceninjaApiRequestAllItems, + invoiceNinjaApiRequest, + invoiceNinjaApiRequestAllItems, } from './GenericFunctions'; import { + clientFields, clientOperations, - clientFields } from './ClientDescription'; import { + invoiceFields, invoiceOperations, - invoiceFields } from './InvoiceDescription'; import { IClient, @@ -33,29 +33,29 @@ import { IItem, } from './invoiceInterface'; import { + taskFields, taskOperations, - taskFields } from './TaskDescription'; import { ITask, } from './TaskInterface'; import { + paymentFields, paymentOperations, - paymentFields } from './PaymentDescription'; import { IPayment, } from './PaymentInterface'; import { + expenseFields, expenseOperations, - expenseFields } from './ExpenseDescription'; import { IExpense, } from './ExpenseInterface'; import { + quoteFields, quoteOperations, - quoteFields } from './QuoteDescription'; import { IQuote, @@ -78,45 +78,11 @@ export class InvoiceNinja implements INodeType { outputs: ['main'], credentials: [ { - name: 'invoiceNinjaCloudApi', + name: 'invoiceNinjaApi', required: true, - displayOptions: { - show: { - invoiceNinjaVersion: [ - 'cloud', - ], - }, - }, - }, - { - name: 'invoiceNinjaServerApi', - required: true, - displayOptions: { - show: { - invoiceNinjaVersion: [ - 'server', - ], - }, - }, }, ], properties: [ - { - displayName: 'Version', - name: 'invoiceNinjaVersion', - type: 'options', - options: [ - { - name: 'Cloud', - value: 'cloud', - }, - { - name: 'Server (Self Hosted)', - value: 'server', - }, - ], - default: 'cloud', - }, { displayName: 'Resource', name: 'resource', @@ -171,7 +137,7 @@ export class InvoiceNinja implements INodeType { // select them easily async getClients(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const clients = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/clients'); + const clients = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/clients'); for (const client of clients) { const clientName = client.display_name; const clientId = client.id; @@ -186,7 +152,7 @@ export class InvoiceNinja implements INodeType { // select them easily async getProjects(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const projects = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/projects'); + const projects = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/projects'); for (const project of projects) { const projectName = project.name; const projectId = project.id; @@ -201,7 +167,7 @@ export class InvoiceNinja implements INodeType { // select them easily async getInvoices(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const invoices = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/invoices'); + const invoices = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/invoices'); for (const invoice of invoices) { const invoiceName = invoice.invoice_number; const invoiceId = invoice.id; @@ -230,7 +196,7 @@ export class InvoiceNinja implements INodeType { // select them easily async getVendors(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const vendors = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/vendors'); + const vendors = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/vendors'); for (const vendor of vendors) { const vendorName = vendor.name; const vendorId = vendor.id; @@ -245,7 +211,7 @@ export class InvoiceNinja implements INodeType { // select them easily async getExpenseCategories(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const categories = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/expense_categories'); + const categories = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/expense_categories'); for (const category of categories) { const categoryName = category.name; const categoryId = category.id; @@ -306,8 +272,8 @@ export class InvoiceNinja implements INodeType { last_name: contactValue.lastName as string, email: contactValue.email as string, phone: contactValue.phone as string, - } - contacts.push(contact) + }; + contacts.push(contact); } body.contacts = contacts; } @@ -329,7 +295,7 @@ export class InvoiceNinja implements INodeType { body.postal_code = billingAddressValue.postalCode as string; body.country_id = parseInt(billingAddressValue.countryCode as string, 10); } - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/clients', body); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/clients', body); responseData = responseData.data; } if (operation === 'get') { @@ -338,7 +304,7 @@ export class InvoiceNinja implements INodeType { if (options.include) { qs.include = options.include as string; } - responseData = await invoiceninjaApiRequest.call(this, 'GET', `/clients/${clientId}`, {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', `/clients/${clientId}`, {}, qs); responseData = responseData.data; } if (operation === 'getAll') { @@ -348,16 +314,16 @@ export class InvoiceNinja implements INodeType { qs.include = options.include as string; } if (returnAll === true) { - responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/clients', {}, qs); + responseData = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/clients', {}, qs); } else { qs.per_page = this.getNodeParameter('limit', 0) as number; - responseData = await invoiceninjaApiRequest.call(this, 'GET', '/clients', {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/clients', {}, qs); responseData = responseData.data; } } if (operation === 'delete') { const clientId = this.getNodeParameter('clientId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/clients/${clientId}`); + responseData = await invoiceNinjaApiRequest.call(this, 'DELETE', `/clients/${clientId}`); responseData = responseData.data; } } @@ -444,17 +410,17 @@ export class InvoiceNinja implements INodeType { tax_rate2: itemValue.taxRate2 as number, tax_name1: itemValue.taxName1 as string, tax_name2: itemValue.taxName2 as string, - } - items.push(item) + }; + items.push(item); } body.invoice_items = items; } - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/invoices', body); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/invoices', body); responseData = responseData.data; } if (operation === 'email') { const invoiceId = this.getNodeParameter('invoiceId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/email_invoice', { id: invoiceId }); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/email_invoice', { id: invoiceId }); } if (operation === 'get') { const invoiceId = this.getNodeParameter('invoiceId', i) as string; @@ -462,7 +428,7 @@ export class InvoiceNinja implements INodeType { if (options.include) { qs.include = options.include as string; } - responseData = await invoiceninjaApiRequest.call(this, 'GET', `/invoices/${invoiceId}`, {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', `/invoices/${invoiceId}`, {}, qs); responseData = responseData.data; } if (operation === 'getAll') { @@ -475,16 +441,16 @@ export class InvoiceNinja implements INodeType { qs.invoice_number = options.invoiceNumber as string; } if (returnAll === true) { - responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/invoices', {}, qs); + responseData = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/invoices', {}, qs); } else { qs.per_page = this.getNodeParameter('limit', 0) as number; - responseData = await invoiceninjaApiRequest.call(this, 'GET', '/invoices', {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/invoices', {}, qs); responseData = responseData.data; } } if (operation === 'delete') { const invoiceId = this.getNodeParameter('invoiceId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/invoices/${invoiceId}`); + responseData = await invoiceNinjaApiRequest.call(this, 'DELETE', `/invoices/${invoiceId}`); responseData = responseData.data; } } @@ -519,13 +485,13 @@ export class InvoiceNinja implements INodeType { to = new Date(logValue.endDate as string).getTime()/1000 as number; } if (logValue.duration) { - to = from + (logValue.duration as number * 3600) + to = from + (logValue.duration as number * 3600); } logs.push([from as number, to as number]); } body.time_log = JSON.stringify(logs); } - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/tasks', body); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/tasks', body); responseData = responseData.data; } if (operation === 'get') { @@ -534,7 +500,7 @@ export class InvoiceNinja implements INodeType { if (options.include) { qs.include = options.include as string; } - responseData = await invoiceninjaApiRequest.call(this, 'GET', `/tasks/${taskId}`, {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', `/tasks/${taskId}`, {}, qs); responseData = responseData.data; } if (operation === 'getAll') { @@ -544,16 +510,16 @@ export class InvoiceNinja implements INodeType { qs.include = options.include as string; } if (returnAll === true) { - responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/tasks', {}, qs); + responseData = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/tasks', {}, qs); } else { qs.per_page = this.getNodeParameter('limit', 0) as number; - responseData = await invoiceninjaApiRequest.call(this, 'GET', '/tasks', {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/tasks', {}, qs); responseData = responseData.data; } } if (operation === 'delete') { const taskId = this.getNodeParameter('taskId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/tasks/${taskId}`); + responseData = await invoiceNinjaApiRequest.call(this, 'DELETE', `/tasks/${taskId}`); responseData = responseData.data; } } @@ -575,7 +541,7 @@ export class InvoiceNinja implements INodeType { if (additionalFields.privateNotes) { body.private_notes = additionalFields.privateNotes as string; } - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/payments', body); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/payments', body); responseData = responseData.data; } if (operation === 'get') { @@ -584,7 +550,7 @@ export class InvoiceNinja implements INodeType { if (options.include) { qs.include = options.include as string; } - responseData = await invoiceninjaApiRequest.call(this, 'GET', `/payments/${paymentId}`, {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', `/payments/${paymentId}`, {}, qs); responseData = responseData.data; } if (operation === 'getAll') { @@ -594,16 +560,16 @@ export class InvoiceNinja implements INodeType { qs.include = options.include as string; } if (returnAll === true) { - responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/payments', {}, qs); + responseData = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/payments', {}, qs); } else { qs.per_page = this.getNodeParameter('limit', 0) as number; - responseData = await invoiceninjaApiRequest.call(this, 'GET', '/payments', {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/payments', {}, qs); responseData = responseData.data; } } if (operation === 'delete') { const paymentId = this.getNodeParameter('paymentId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/payments/${paymentId}`); + responseData = await invoiceNinjaApiRequest.call(this, 'DELETE', `/payments/${paymentId}`); responseData = responseData.data; } } @@ -611,6 +577,9 @@ export class InvoiceNinja implements INodeType { if (operation === 'create') { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; const body: IExpense = {}; + if (additionalFields.amount) { + body.amount = additionalFields.amount as number; + } if (additionalFields.billable) { body.should_be_invoiced = additionalFields.billable as boolean; } @@ -659,27 +628,27 @@ export class InvoiceNinja implements INodeType { if (additionalFields.vendor) { body.vendor_id = additionalFields.vendor as number; } - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/expenses', body); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/expenses', body); responseData = responseData.data; } if (operation === 'get') { const expenseId = this.getNodeParameter('expenseId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'GET', `/expenses/${expenseId}`, {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', `/expenses/${expenseId}`, {}, qs); responseData = responseData.data; } if (operation === 'getAll') { const returnAll = this.getNodeParameter('returnAll', 0) as boolean; if (returnAll === true) { - responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/payments', {}, qs); + responseData = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/expenses', {}, qs); } else { qs.per_page = this.getNodeParameter('limit', 0) as number; - responseData = await invoiceninjaApiRequest.call(this, 'GET', '/payments', {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/expenses', {}, qs); responseData = responseData.data; } } if (operation === 'delete') { const expenseId = this.getNodeParameter('expenseId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/expenses/${expenseId}`); + responseData = await invoiceNinjaApiRequest.call(this, 'DELETE', `/expenses/${expenseId}`); responseData = responseData.data; } } @@ -752,8 +721,8 @@ export class InvoiceNinja implements INodeType { if (additionalFields.paid) { body.paid = additionalFields.paid as number; } - if (additionalFields.emailQoute) { - body.email_invoice = additionalFields.emailQoute as boolean; + if (additionalFields.emailQuote) { + body.email_invoice = additionalFields.emailQuote as boolean; } const invoceItemsValues = (this.getNodeParameter('invoiceItemsUi', i) as IDataObject).invoiceItemsValues as IDataObject[]; if (invoceItemsValues) { @@ -768,17 +737,17 @@ export class InvoiceNinja implements INodeType { tax_rate2: itemValue.taxRate2 as number, tax_name1: itemValue.taxName1 as string, tax_name2: itemValue.taxName2 as string, - } - items.push(item) + }; + items.push(item); } body.invoice_items = items; } - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/invoices', body); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/invoices', body); responseData = responseData.data; } if (operation === 'email') { const quoteId = this.getNodeParameter('quoteId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'POST', '/email_invoice', { id: quoteId }); + responseData = await invoiceNinjaApiRequest.call(this, 'POST', '/email_invoice', { id: quoteId }); } if (operation === 'get') { const quoteId = this.getNodeParameter('quoteId', i) as string; @@ -786,7 +755,7 @@ export class InvoiceNinja implements INodeType { if (options.include) { qs.include = options.include as string; } - responseData = await invoiceninjaApiRequest.call(this, 'GET', `/invoices/${quoteId}`, {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', `/invoices/${quoteId}`, {}, qs); responseData = responseData.data; } if (operation === 'getAll') { @@ -799,16 +768,16 @@ export class InvoiceNinja implements INodeType { qs.invoice_number = options.invoiceNumber as string; } if (returnAll === true) { - responseData = await invoiceninjaApiRequestAllItems.call(this, 'data', 'GET', '/quotes', {}, qs); + responseData = await invoiceNinjaApiRequestAllItems.call(this, 'data', 'GET', '/quotes', {}, qs); } else { qs.per_page = this.getNodeParameter('limit', 0) as number; - responseData = await invoiceninjaApiRequest.call(this, 'GET', '/quotes', {}, qs); + responseData = await invoiceNinjaApiRequest.call(this, 'GET', '/quotes', {}, qs); responseData = responseData.data; } } if (operation === 'delete') { const quoteId = this.getNodeParameter('quoteId', i) as string; - responseData = await invoiceninjaApiRequest.call(this, 'DELETE', `/invoices/${quoteId}`); + responseData = await invoiceNinjaApiRequest.call(this, 'DELETE', `/invoices/${quoteId}`); responseData = responseData.data; } } diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.png b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.png deleted file mode 100644 index d35e3050c7e7614a300525049ed97b51a7e73340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4699 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 Yli1bnW98{Le}3WUYa2sqHQ|x}0Zy~9-v9sr diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts index 924b3947dc..f84982c0f1 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.ts @@ -10,7 +10,7 @@ import { } from 'n8n-workflow'; import { - invoiceninjaApiRequest, + invoiceNinjaApiRequest, } from './GenericFunctions'; export class InvoiceNinjaTrigger implements INodeType { @@ -29,26 +29,8 @@ export class InvoiceNinjaTrigger implements INodeType { outputs: ['main'], credentials: [ { - name: 'invoiceNinjaCloudApi', + name: 'invoiceNinjaApi', required: true, - displayOptions: { - show: { - invoiceNinjaVersion: [ - 'cloud', - ], - }, - }, - }, - { - name: 'invoiceNinjaServerApi', - required: true, - displayOptions: { - show: { - invoiceNinjaVersion: [ - 'server', - ], - }, - }, }, ], webhooks: [ @@ -60,45 +42,29 @@ export class InvoiceNinjaTrigger implements INodeType { }, ], properties: [ - { - displayName: 'Version', - name: 'invoiceNinjaVersion', - type: 'options', - options: [ - { - name: 'Cloud', - value: 'cloud', - }, - { - name: 'Server (Self Hosted)', - value: 'server', - }, - ], - default: 'cloud', - }, { displayName: 'Event', name: 'event', type: 'options', options: [ { - name: 'client.created', + name: 'Client Created', value: 'create_client', }, { - name: 'invoce.created', + name: 'Invoice Created', value: 'create_invoice', }, { - name: 'payment.created', + name: 'Payment Created', value: 'create_payment', }, { - name: 'quote.created', + name: 'Quote Created', value: 'create_quote', }, { - name: 'vendor.created', + name: 'Vendor Created', value: 'create_vendor', }, ], @@ -126,7 +92,7 @@ export class InvoiceNinjaTrigger implements INodeType { event, }; - const responseData = await invoiceninjaApiRequest.call(this, 'POST', endpoint, body); + const responseData = await invoiceNinjaApiRequest.call(this, 'POST', endpoint, body); if (responseData.id === undefined) { // Required data is missing so was not successful @@ -145,7 +111,7 @@ export class InvoiceNinjaTrigger implements INodeType { const endpoint = `/hooks/${webhookData.webhookId}`; try { - await invoiceninjaApiRequest.call(this, 'DELETE', endpoint); + await invoiceNinjaApiRequest.call(this, 'DELETE', endpoint); } catch (e) { return false; } diff --git a/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts index e3e2763f14..baa24a495a 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/PaymentDescription.ts @@ -18,6 +18,11 @@ export const paymentOperations = [ value: 'create', description: 'Create a new payment', }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a payment', + }, { name: 'Get', value: 'get', @@ -28,11 +33,6 @@ export const paymentOperations = [ value: 'getAll', description: 'Get data of all payments', }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a payment', - } ], default: 'create', description: 'The operation to perform.', diff --git a/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts index 5bdf4ab87d..142cbcd782 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/QuoteDescription.ts @@ -18,6 +18,11 @@ export const quoteOperations = [ value: 'create', description: 'Create a new quote', }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a quote', + }, { name: 'Email', value: 'email', @@ -33,11 +38,6 @@ export const quoteOperations = [ value: 'getAll', description: 'Get data of all quotes', }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a quote', - } ], default: 'create', description: 'The operation to perform.', @@ -117,8 +117,8 @@ export const quoteFields = [ default: '', }, { - displayName: 'Email Qoute', - name: 'emailQoute', + displayName: 'Email Quote', + name: 'emailQuote', type: 'boolean', default: false, }, @@ -135,7 +135,7 @@ export const quoteFields = [ default: '', }, { - displayName: 'Qoute Status', + displayName: 'Quote Status', name: 'quoteStatus', type: 'options', options: [ @@ -351,7 +351,7 @@ export const quoteFields = [ /* quote:get */ /* -------------------------------------------------------------------------- */ { - displayName: 'Qoute ID', + displayName: 'Quote ID', name: 'quoteId', type: 'string', required: true, @@ -459,7 +459,7 @@ export const quoteFields = [ }, options: [ { - displayName: 'Qoute Number', + displayName: 'Quote Number', name: 'quoteNumber', type: 'string', default: '', diff --git a/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts b/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts index a3c608fc9e..f03ffb9bb6 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/TaskDescription.ts @@ -18,6 +18,11 @@ export const taskOperations = [ value: 'create', description: 'Create a new task', }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task', + }, { name: 'Get', value: 'get', @@ -28,11 +33,6 @@ export const taskOperations = [ value: 'getAll', description: 'Get data of all tasks', }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a task', - } ], default: 'create', description: 'The operation to perform.', diff --git a/packages/nodes-base/nodes/InvoiceNinja/invoiceNinja.png b/packages/nodes-base/nodes/InvoiceNinja/invoiceNinja.png new file mode 100644 index 0000000000000000000000000000000000000000..50c5ac69ca294601d53c210b16683f5f1ed8c08b GIT binary patch literal 1109 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw3=&b&bYNg$%n0xaaRqX@n3(Y(PG%N7vQ(G) z|NlSGNtVVYJsq8_3=ASXd=Kv5XJ=qswS4*eckdQ2Txe!!_~ZNc-8*+qoisT#D9F;( zjE9}$?wz~m&YoSnYSpai(D$ScT7N=l1MhzSS^^74NE z{Q3H|YexIEHu}e|s&p__l$>fx>nv zttGxQCmHdm=;rM;Fy5Bmv4Hao?d+T-{&-)U8z&E zyml`(+!H!WJndkyu(#OKnpHRD*w-8To?cS5)`NZ4oyp1HeHw1O(7L_qID_t0tHbM< zI-gxrSu5tT%}_b9oMEx=uGsmkYp#UsVVR+sar+*tQIrDz>>|02j~(+Ej-1$>Cb*ts zWrWI`&Kqo96^G_cI^W^myW@+Pfb(0gk1|45+|#3N4ozOBD7)1u#vjc1Is(uUl?95 zaK3R=nZ@S%(H#L&oHZ;?cM7MuYm~9XZ)j?8|7EacdcZT0GA-qJMyJd*FB0eMo}~56 z)@-}-#6RzvEN*H)PU1+7V)A-8KceEXDc{dOSDRj|NSsg0DN~DkaagbY@{614AJVlWCUj{?arvDzw3Tdi|rvL1>%V*EmP?eKny0@vZ z