mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 21:07:28 -08:00
✨ Add Odoo Node (#2601)
* added odoo scaffolding * update getting data from odoo instance * added scaffolding for main loop and request functions * added functions for CRUD opperations * improoved error handling for odooJSONRPCRequest * updated odoo node and fixing nodelinter issues * fixed alpabetical order * fixed types in odoo node * fixing linter errors * fixing linter errors * fixed data shape returned from man loop * updated node input types, added fields list to models * update when custom resource is selected options for fields list will be populated dynamicly * minor fixes * 🔨 fixed credential test, updating CRUD methods * 🔨 added additional fields to crm resource * 🔨 added descriptions, fixed credentials test bug * 🔨 standardize node and descriptions design * 🔨 removed comments * 🔨 added pagination to getAll operation * ⚡ removed leftover function from previous implementation, removed required from optional fields * ⚡ fixed id field, added indication of type and if required to field description, replaced string input in filters to fetched list of fields * 🔨 fetching list of models from odoo, added selection of fields to be returned to predefined models, fixes accordingly to review * ⚡ Small improvements * 🔨 extracted adress fields into collection, changed fields to include in descriptions, minor tweaks * ⚡ Improvements * 🔨 working on review * 🔨 fixed linter errors * 🔨 review wip * 🔨 review wip * 🔨 review wip * ⚡ updated display name for URL in credentials * 🔨 added checks for valid id to delete and update * ⚡ Minor improvements Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
f04e6ac43e
commit
fbdb5eb0fa
41
packages/nodes-base/credentials/OdooApi.credentials.ts
Normal file
41
packages/nodes-base/credentials/OdooApi.credentials.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { ICredentialType, INodeProperties, NodePropertyTypes } from 'n8n-workflow';
|
||||
|
||||
export class OdooApi implements ICredentialType {
|
||||
name = 'odooApi';
|
||||
displayName = 'Odoo API';
|
||||
documentationUrl = 'odoo';
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Site URL',
|
||||
name: 'url',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
placeholder: 'https://my-organization.odoo.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Username',
|
||||
name: 'username',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
placeholder: 'user@email.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Password or API Key',
|
||||
name: 'password',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Database Name',
|
||||
name: 'db',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
436
packages/nodes-base/nodes/Odoo/GenericFunctions.ts
Normal file
436
packages/nodes-base/nodes/Odoo/GenericFunctions.ts
Normal file
|
@ -0,0 +1,436 @@
|
|||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
const serviceJSONRPC = 'object';
|
||||
const methodJSONRPC = 'execute';
|
||||
|
||||
export const mapOperationToJSONRPC = {
|
||||
create: 'create',
|
||||
get: 'read',
|
||||
getAll: 'search_read',
|
||||
update: 'write',
|
||||
delete: 'unlink',
|
||||
};
|
||||
|
||||
export const mapOdooResources: { [key: string]: string } = {
|
||||
contact: 'res.partner',
|
||||
opportunity: 'crm.lead',
|
||||
note: 'note.note',
|
||||
};
|
||||
|
||||
export const mapFilterOperationToJSONRPC = {
|
||||
equal: '=',
|
||||
notEqual: '!=',
|
||||
greaterThen: '>',
|
||||
lesserThen: '<',
|
||||
greaterOrEqual: '>=',
|
||||
lesserOrEqual: '<=',
|
||||
like: 'like',
|
||||
in: 'in',
|
||||
notIn: 'not in',
|
||||
childOf: 'child_of',
|
||||
};
|
||||
|
||||
type FilterOperation =
|
||||
| 'equal'
|
||||
| 'notEqual'
|
||||
| 'greaterThen'
|
||||
| 'lesserThen'
|
||||
| 'greaterOrEqual'
|
||||
| 'lesserOrEqual'
|
||||
| 'like'
|
||||
| 'in'
|
||||
| 'notIn'
|
||||
| 'childOf';
|
||||
|
||||
export interface IOdooFilterOperations {
|
||||
filter: Array<{
|
||||
fieldName: string;
|
||||
operator: string;
|
||||
value: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface IOdooNameValueFields {
|
||||
fields: Array<{
|
||||
fieldName: string;
|
||||
fieldValue: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface IOdooResponceFields {
|
||||
fields: Array<{
|
||||
field: string;
|
||||
fromList?: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
type OdooCRUD = 'create' | 'update' | 'delete' | 'get' | 'getAll';
|
||||
|
||||
export function odooGetDBName (databaseName: string | undefined, url: string) {
|
||||
if (databaseName) return databaseName;
|
||||
const odooURL = new URL(url);
|
||||
const hostname = odooURL.hostname;
|
||||
if (!hostname) return '';
|
||||
return odooURL.hostname.split('.')[0];
|
||||
}
|
||||
|
||||
function processFilters(value: IOdooFilterOperations) {
|
||||
return value.filter?.map((item) => {
|
||||
const operator = item.operator as FilterOperation;
|
||||
item.operator = mapFilterOperationToJSONRPC[operator];
|
||||
return Object.values(item);
|
||||
});
|
||||
}
|
||||
|
||||
export function processNameValueFields(value: IDataObject) {
|
||||
const data = value as unknown as IOdooNameValueFields;
|
||||
return data?.fields?.reduce((acc, record) => {
|
||||
return Object.assign(acc, { [record.fieldName]: record.fieldValue });
|
||||
}, {});
|
||||
}
|
||||
|
||||
// function processResponceFields(value: IDataObject) {
|
||||
// const data = value as unknown as IOdooResponceFields;
|
||||
// return data?.fields?.map((entry) => entry.field);
|
||||
// }
|
||||
|
||||
export async function odooJSONRPCRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
body: IDataObject,
|
||||
url: string,
|
||||
): Promise<IDataObject | IDataObject[]> {
|
||||
try {
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'User-Agent': 'n8n',
|
||||
Connection: 'keep-alive',
|
||||
Accept: '*/*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
body,
|
||||
uri: `${url}/jsonrpc`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
const responce = await this.helpers.request!(options);
|
||||
if (responce.error) {
|
||||
throw new NodeApiError(this.getNode(), responce.error.data, {
|
||||
message: responce.error.data.message,
|
||||
});
|
||||
}
|
||||
return responce.result;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooGetModelFields(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
userID: number,
|
||||
password: string,
|
||||
resource: string,
|
||||
url: string,
|
||||
) {
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: serviceJSONRPC,
|
||||
method: methodJSONRPC,
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
mapOdooResources[resource] || resource,
|
||||
'fields_get',
|
||||
[],
|
||||
['string', 'type', 'help', 'required', 'name'],
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const result = await odooJSONRPCRequest.call(this, body, url);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooCreate(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
userID: number,
|
||||
password: string,
|
||||
resource: string,
|
||||
operation: OdooCRUD,
|
||||
url: string,
|
||||
newItem: IDataObject,
|
||||
) {
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: serviceJSONRPC,
|
||||
method: methodJSONRPC,
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
mapOdooResources[resource] || resource,
|
||||
mapOperationToJSONRPC[operation],
|
||||
newItem || {},
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const result = await odooJSONRPCRequest.call(this, body, url);
|
||||
return { id: result };
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooGet(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
userID: number,
|
||||
password: string,
|
||||
resource: string,
|
||||
operation: OdooCRUD,
|
||||
url: string,
|
||||
itemsID: string,
|
||||
fieldsToReturn?: IDataObject[],
|
||||
) {
|
||||
try {
|
||||
if (!/^\d+$/.test(itemsID) || !parseInt(itemsID, 10)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
status: 'Error',
|
||||
message: `Please specify a valid ID: ${itemsID}`,
|
||||
});
|
||||
}
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: serviceJSONRPC,
|
||||
method: methodJSONRPC,
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
mapOdooResources[resource] || resource,
|
||||
mapOperationToJSONRPC[operation],
|
||||
[+itemsID] || [],
|
||||
fieldsToReturn || [],
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const result = await odooJSONRPCRequest.call(this, body, url);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooGetAll(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
userID: number,
|
||||
password: string,
|
||||
resource: string,
|
||||
operation: OdooCRUD,
|
||||
url: string,
|
||||
filters?: IOdooFilterOperations,
|
||||
fieldsToReturn?: IDataObject[],
|
||||
limit = 0,
|
||||
) {
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: serviceJSONRPC,
|
||||
method: methodJSONRPC,
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
mapOdooResources[resource] || resource,
|
||||
mapOperationToJSONRPC[operation],
|
||||
(filters && processFilters(filters)) || [],
|
||||
fieldsToReturn || [],
|
||||
0, // offset
|
||||
limit,
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const result = await odooJSONRPCRequest.call(this, body, url);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooUpdate(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
userID: number,
|
||||
password: string,
|
||||
resource: string,
|
||||
operation: OdooCRUD,
|
||||
url: string,
|
||||
itemsID: string,
|
||||
fieldsToUpdate: IDataObject,
|
||||
) {
|
||||
try {
|
||||
if (!Object.keys(fieldsToUpdate).length) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
status: 'Error',
|
||||
message: `Please specify at least one field to update`,
|
||||
});
|
||||
}
|
||||
if (!/^\d+$/.test(itemsID) || !parseInt(itemsID, 10)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
status: 'Error',
|
||||
message: `Please specify a valid ID: ${itemsID}`,
|
||||
});
|
||||
}
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: serviceJSONRPC,
|
||||
method: methodJSONRPC,
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
mapOdooResources[resource] || resource,
|
||||
mapOperationToJSONRPC[operation],
|
||||
[+itemsID] || [],
|
||||
fieldsToUpdate,
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
await odooJSONRPCRequest.call(this, body, url);
|
||||
return { id: itemsID };
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooDelete(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
userID: number,
|
||||
password: string,
|
||||
resource: string,
|
||||
operation: OdooCRUD,
|
||||
url: string,
|
||||
itemsID: string,
|
||||
) {
|
||||
if (!/^\d+$/.test(itemsID) || !parseInt(itemsID, 10)) {
|
||||
throw new NodeApiError(this.getNode(), {
|
||||
status: 'Error',
|
||||
message: `Please specify a valid ID: ${itemsID}`,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: serviceJSONRPC,
|
||||
method: methodJSONRPC,
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
mapOdooResources[resource] || resource,
|
||||
mapOperationToJSONRPC[operation],
|
||||
[+itemsID] || [],
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
await odooJSONRPCRequest.call(this, body, url);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooGetUserID(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
db: string,
|
||||
username: string,
|
||||
password: string,
|
||||
url: string,
|
||||
): Promise<number> {
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: 'common',
|
||||
method: 'login',
|
||||
args: [db, username, password],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
const loginResult = await odooJSONRPCRequest.call(this, body, url);
|
||||
return loginResult as unknown as number;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function odooGetServerVersion(
|
||||
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
url: string,
|
||||
) {
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: 'common',
|
||||
method: 'version',
|
||||
args: [],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
const result = await odooJSONRPCRequest.call(this, body, url);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
20
packages/nodes-base/nodes/Odoo/Odoo.node.json
Normal file
20
packages/nodes-base/nodes/Odoo/Odoo.node.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.odoo",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Data & Storage"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/odoo"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.odoo/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
757
packages/nodes-base/nodes/Odoo/Odoo.node.ts
Normal file
757
packages/nodes-base/nodes/Odoo/Odoo.node.ts
Normal file
|
@ -0,0 +1,757 @@
|
|||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import { OptionsWithUri } from 'request';
|
||||
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeCredentialTestResult,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
contactDescription,
|
||||
contactOperations,
|
||||
customResourceDescription,
|
||||
customResourceOperations,
|
||||
noteDescription,
|
||||
noteOperations,
|
||||
opportunityDescription,
|
||||
opportunityOperations,
|
||||
} from './descriptions';
|
||||
|
||||
import {
|
||||
IOdooFilterOperations,
|
||||
odooCreate,
|
||||
odooDelete,
|
||||
odooGet,
|
||||
odooGetAll,
|
||||
odooGetDBName,
|
||||
odooGetModelFields,
|
||||
odooGetUserID,
|
||||
odooJSONRPCRequest,
|
||||
odooUpdate,
|
||||
processNameValueFields,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import { capitalCase } from 'change-case';
|
||||
export class Odoo implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Odoo',
|
||||
name: 'odoo',
|
||||
icon: 'file:odoo.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Consume Odoo API',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
defaults: {
|
||||
name: 'Odoo',
|
||||
color: '#714B67',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'odooApi',
|
||||
required: true,
|
||||
testedBy: 'odooApiTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
default: 'contact',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Contact',
|
||||
value: 'contact',
|
||||
},
|
||||
{
|
||||
name: 'Custom Resource',
|
||||
value: 'custom',
|
||||
},
|
||||
{
|
||||
name: 'Note',
|
||||
value: 'note',
|
||||
},
|
||||
{
|
||||
name: 'Opportunity',
|
||||
value: 'opportunity',
|
||||
},
|
||||
],
|
||||
description: 'The resource to operate on',
|
||||
},
|
||||
|
||||
...customResourceOperations,
|
||||
...customResourceDescription,
|
||||
...opportunityOperations,
|
||||
...opportunityDescription,
|
||||
...contactOperations,
|
||||
...contactDescription,
|
||||
...noteOperations,
|
||||
...noteDescription,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
async getModelFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
let resource;
|
||||
resource = this.getCurrentNodeParameter('resource') as string;
|
||||
if (resource === 'custom') {
|
||||
resource = this.getCurrentNodeParameter('customResource') as string;
|
||||
if (!resource) return [];
|
||||
}
|
||||
|
||||
const credentials = await this.getCredentials('odooApi');
|
||||
const url = credentials?.url as string;
|
||||
const username = credentials?.username as string;
|
||||
const password = credentials?.password as string;
|
||||
const db = odooGetDBName(credentials?.db as string, url);
|
||||
const userID = await odooGetUserID.call(this, db, username, password, url);
|
||||
|
||||
const responce = await odooGetModelFields.call(this, db, userID, password, resource, url);
|
||||
|
||||
const options = Object.values(responce).map((field) => {
|
||||
const optionField = field as { [key: string]: string };
|
||||
return {
|
||||
name: capitalCase(optionField.name),
|
||||
value: optionField.name,
|
||||
// nodelinter-ignore-next-line
|
||||
description: `name: ${optionField?.name}, type: ${optionField?.type} required: ${optionField?.required}`,
|
||||
};
|
||||
});
|
||||
|
||||
return options.sort((a, b) => a.name?.localeCompare(b.name) || 0);
|
||||
},
|
||||
async getModels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const credentials = await this.getCredentials('odooApi');
|
||||
const url = credentials?.url as string;
|
||||
const username = credentials?.username as string;
|
||||
const password = credentials?.password as string;
|
||||
const db = odooGetDBName(credentials?.db as string, url);
|
||||
const userID = await odooGetUserID.call(this, db, username, password, url);
|
||||
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: 'object',
|
||||
method: 'execute',
|
||||
args: [
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
'ir.model',
|
||||
'search_read',
|
||||
[],
|
||||
['name', 'model', 'modules'],
|
||||
],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const responce = (await odooJSONRPCRequest.call(this, body, url)) as IDataObject[];
|
||||
|
||||
const options = responce.map((model) => {
|
||||
return {
|
||||
name: model.name,
|
||||
value: model.model,
|
||||
description: `model: ${model.model}<br> modules: ${model.modules}`,
|
||||
};
|
||||
});
|
||||
return options as INodePropertyOptions[];
|
||||
},
|
||||
async getStates(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const credentials = await this.getCredentials('odooApi');
|
||||
const url = credentials?.url as string;
|
||||
const username = credentials?.username as string;
|
||||
const password = credentials?.password as string;
|
||||
const db = odooGetDBName(credentials?.db as string, url);
|
||||
const userID = await odooGetUserID.call(this, db, username, password, url);
|
||||
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: 'object',
|
||||
method: 'execute',
|
||||
args: [db, userID, password, 'res.country.state', 'search_read', [], ['id', 'name']],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const responce = (await odooJSONRPCRequest.call(this, body, url)) as IDataObject[];
|
||||
|
||||
const options = responce.map((state) => {
|
||||
return {
|
||||
name: state.name as string,
|
||||
value: state.id,
|
||||
};
|
||||
});
|
||||
return options.sort((a, b) => a.name?.localeCompare(b.name) || 0) as INodePropertyOptions[];
|
||||
},
|
||||
async getCountries(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const credentials = await this.getCredentials('odooApi');
|
||||
const url = credentials?.url as string;
|
||||
const username = credentials?.username as string;
|
||||
const password = credentials?.password as string;
|
||||
const db = odooGetDBName(credentials?.db as string, url);
|
||||
const userID = await odooGetUserID.call(this, db, username, password, url);
|
||||
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: 'object',
|
||||
method: 'execute',
|
||||
args: [db, userID, password, 'res.country', 'search_read', [], ['id', 'name']],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const responce = (await odooJSONRPCRequest.call(this, body, url)) as IDataObject[];
|
||||
|
||||
const options = responce.map((country) => {
|
||||
return {
|
||||
name: country.name as string,
|
||||
value: country.id,
|
||||
};
|
||||
});
|
||||
|
||||
return options.sort((a, b) => a.name?.localeCompare(b.name) || 0) as INodePropertyOptions[];
|
||||
},
|
||||
},
|
||||
credentialTest: {
|
||||
async odooApiTest(
|
||||
this: ICredentialTestFunctions,
|
||||
credential: ICredentialsDecrypted,
|
||||
): Promise<INodeCredentialTestResult> {
|
||||
const credentials = credential.data;
|
||||
|
||||
try {
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
method: 'call',
|
||||
params: {
|
||||
service: 'common',
|
||||
method: 'login',
|
||||
args: [odooGetDBName(credentials?.db as string, credentials?.url as string), credentials?.username, credentials?.password],
|
||||
},
|
||||
id: Math.floor(Math.random() * 100),
|
||||
};
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'User-Agent': 'n8n',
|
||||
Connection: 'keep-alive',
|
||||
Accept: '*/*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
body,
|
||||
uri: `${(credentials?.url as string).replace(/\/$/, '')}/jsonrpc`,
|
||||
json: true,
|
||||
};
|
||||
const result = await this.helpers.request!(options);
|
||||
if (result.error || !result.result) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Credentials are not valid`,
|
||||
};
|
||||
} else if (result.error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Credentials are not valid: ${result.error.data.message}`,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Settings are not valid: ${error}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
let items = this.getInputData();
|
||||
items = JSON.parse(JSON.stringify(items));
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
const credentials = await this.getCredentials('odooApi');
|
||||
const url = (credentials?.url as string).replace(/\/$/, '');
|
||||
const username = credentials?.username as string;
|
||||
const password = credentials?.password as string;
|
||||
const db = odooGetDBName(credentials?.db as string, url);
|
||||
const userID = await odooGetUserID.call(this, db, username, password, url);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Main loop
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
if (resource === 'contact') {
|
||||
if (operation === 'create') {
|
||||
let additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (additionalFields.address) {
|
||||
const addressFields = (additionalFields.address as IDataObject).value as IDataObject;
|
||||
if (addressFields) {
|
||||
additionalFields = {
|
||||
...additionalFields,
|
||||
...addressFields,
|
||||
};
|
||||
}
|
||||
delete additionalFields.address;
|
||||
}
|
||||
|
||||
const name = this.getNodeParameter('contactName', i) as string;
|
||||
const fields: IDataObject = {
|
||||
name,
|
||||
...additionalFields,
|
||||
};
|
||||
responseData = await odooCreate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'delete') {
|
||||
const contactId = this.getNodeParameter('contactId', i) as string;
|
||||
responseData = await odooDelete.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
contactId,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'get') {
|
||||
const contactId = this.getNodeParameter('contactId', i) as string;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
responseData = await odooGet.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
contactId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
if (returnAll) {
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
undefined,
|
||||
fields,
|
||||
);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
undefined, // filters, only for custom resource
|
||||
fields,
|
||||
limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const contactId = this.getNodeParameter('contactId', i) as string;
|
||||
let updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
|
||||
if (updateFields.address) {
|
||||
const addressFields = (updateFields.address as IDataObject).value as IDataObject;
|
||||
if (addressFields) {
|
||||
updateFields = {
|
||||
...updateFields,
|
||||
...addressFields,
|
||||
};
|
||||
}
|
||||
delete updateFields.address;
|
||||
}
|
||||
|
||||
responseData = await odooUpdate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
contactId,
|
||||
updateFields,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'custom') {
|
||||
const customResource = this.getNodeParameter('customResource', i) as string;
|
||||
if (operation === 'create') {
|
||||
const fields = this.getNodeParameter('fieldsToCreateOrUpdate', i) as IDataObject;
|
||||
responseData = await odooCreate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
customResource,
|
||||
operation,
|
||||
url,
|
||||
processNameValueFields(fields),
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'delete') {
|
||||
const customResourceId = this.getNodeParameter('customResourceId', i) as string;
|
||||
responseData = await odooDelete.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
customResource,
|
||||
operation,
|
||||
url,
|
||||
customResourceId,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'get') {
|
||||
const customResourceId = this.getNodeParameter('customResourceId', i) as string;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
responseData = await odooGet.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
customResource,
|
||||
operation,
|
||||
url,
|
||||
customResourceId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
const filter = this.getNodeParameter('filterRequest', i) as IOdooFilterOperations;
|
||||
if (returnAll) {
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
customResource,
|
||||
operation,
|
||||
url,
|
||||
filter,
|
||||
fields,
|
||||
);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
customResource,
|
||||
operation,
|
||||
url,
|
||||
filter,
|
||||
fields,
|
||||
limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const customResourceId = this.getNodeParameter('customResourceId', i) as string;
|
||||
const fields = this.getNodeParameter('fieldsToCreateOrUpdate', i) as IDataObject;
|
||||
responseData = await odooUpdate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
customResource,
|
||||
operation,
|
||||
url,
|
||||
customResourceId,
|
||||
processNameValueFields(fields),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'note') {
|
||||
if (operation === 'create') {
|
||||
// const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const memo = this.getNodeParameter('memo', i) as string;
|
||||
const fields: IDataObject = {
|
||||
memo,
|
||||
// ...additionalFields,
|
||||
};
|
||||
responseData = await odooCreate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'delete') {
|
||||
const noteId = this.getNodeParameter('noteId', i) as string;
|
||||
responseData = await odooDelete.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
noteId,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'get') {
|
||||
const noteId = this.getNodeParameter('noteId', i) as string;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
responseData = await odooGet.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
noteId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
if (returnAll) {
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
undefined,
|
||||
fields,
|
||||
);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
undefined, // filters, only for custom resource
|
||||
fields,
|
||||
limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const noteId = this.getNodeParameter('noteId', i) as string;
|
||||
const memo = this.getNodeParameter('memo', i) as string;
|
||||
const fields: IDataObject = {
|
||||
memo,
|
||||
};
|
||||
responseData = await odooUpdate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
noteId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'opportunity') {
|
||||
if (operation === 'create') {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const name = this.getNodeParameter('opportunityName', i) as string;
|
||||
const fields: IDataObject = {
|
||||
name,
|
||||
...additionalFields,
|
||||
};
|
||||
|
||||
responseData = await odooCreate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'delete') {
|
||||
const opportunityId = this.getNodeParameter('opportunityId', i) as string;
|
||||
responseData = await odooDelete.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
opportunityId,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'get') {
|
||||
const opportunityId = this.getNodeParameter('opportunityId', i) as string;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
responseData = await odooGet.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
opportunityId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const fields = options.fieldsList as IDataObject[] || [];
|
||||
if (returnAll) {
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
undefined,
|
||||
fields,
|
||||
);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await odooGetAll.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
undefined, // filters, only for custom resource
|
||||
fields,
|
||||
limit,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const opportunityId = this.getNodeParameter('opportunityId', i) as string;
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
responseData = await odooUpdate.call(
|
||||
this,
|
||||
db,
|
||||
userID,
|
||||
password,
|
||||
resource,
|
||||
operation,
|
||||
url,
|
||||
opportunityId,
|
||||
updateFields,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData);
|
||||
} else if (responseData !== undefined) {
|
||||
returnData.push(responseData);
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const contactOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'create',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new contact',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a contact',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a contact',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all contacts',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a contact',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const contactDescription: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'contactName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Address',
|
||||
name: 'address',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
placeholder: 'Add Address',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'value',
|
||||
displayName: 'Address',
|
||||
values: [
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'city',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country_id',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCountries',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'State',
|
||||
name: 'state_id',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getStates',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Street',
|
||||
name: 'street',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Street 2',
|
||||
name: 'street2',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Zip Code',
|
||||
name: 'zip',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Internal Notes',
|
||||
name: 'comment',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Job Position',
|
||||
name: 'function',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Mobile',
|
||||
name: 'mobile',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Tax ID',
|
||||
name: 'vat',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Website',
|
||||
name: 'website',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Contact ID',
|
||||
name: 'contactId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields To Include',
|
||||
name: 'fieldsList',
|
||||
type: 'multiOptions',
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Contact ID',
|
||||
name: 'contactId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'contact',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Address',
|
||||
name: 'address',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
placeholder: 'Add Address',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'value',
|
||||
displayName: 'Address',
|
||||
values: [
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'city',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country_id',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCountries',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'State',
|
||||
name: 'state_id',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getStates',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Street',
|
||||
name: 'street',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Street 2',
|
||||
name: 'street2',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Zip Code',
|
||||
name: 'zip',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Internal Notes',
|
||||
name: 'comment',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Job Position',
|
||||
name: 'function',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Mobile',
|
||||
name: 'mobile',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Tax ID',
|
||||
name: 'vat',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Website',
|
||||
name: 'website',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,375 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const customResourceOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Custom Resource',
|
||||
name: 'customResource',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModels',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'create',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new item',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete an item',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get an item',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all items',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an item',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const customResourceDescription: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* custom:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fieldsToCreateOrUpdate',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add Field',
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Field Record:',
|
||||
name: 'fields',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field Name',
|
||||
name: 'fieldName',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'New Value',
|
||||
name: 'fieldValue',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* custom:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Custom Resource ID',
|
||||
name: 'customResourceId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* custom:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields To Include',
|
||||
name: 'fieldsList',
|
||||
type: 'multiOptions',
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
loadOptionsDependsOn: [
|
||||
'customResource',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filterRequest',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add Filter',
|
||||
},
|
||||
default: {},
|
||||
description: 'Filter request by applying filters',
|
||||
placeholder: 'Add condition',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'filter',
|
||||
displayName: 'Filter',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'fieldName',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: [
|
||||
'customResource',
|
||||
],
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Operator',
|
||||
name: 'operator',
|
||||
type: 'options',
|
||||
default: 'equal',
|
||||
description: 'Specify an operator',
|
||||
options: [
|
||||
{
|
||||
name: '!=',
|
||||
value: 'notEqual',
|
||||
},
|
||||
{
|
||||
name: '<',
|
||||
value: 'lesserThen',
|
||||
},
|
||||
{
|
||||
name: '=',
|
||||
value: 'equal',
|
||||
},
|
||||
{
|
||||
name: '<=',
|
||||
value: 'lesserOrEqual',
|
||||
},
|
||||
{
|
||||
name: '>',
|
||||
value: 'greaterThen',
|
||||
},
|
||||
{
|
||||
name: '>=',
|
||||
value: 'greaterOrEqual',
|
||||
},
|
||||
{
|
||||
name: ' Child of',
|
||||
value: 'childOf',
|
||||
},
|
||||
{
|
||||
name: 'In',
|
||||
value: 'in',
|
||||
},
|
||||
{
|
||||
name: 'Like',
|
||||
value: 'like',
|
||||
},
|
||||
{
|
||||
name: 'Not In',
|
||||
value: 'notIn',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Specify value for comparison',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* custom:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Custom Resource ID',
|
||||
name: 'customResourceId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'fieldsToCreateOrUpdate',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add Field',
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'custom',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Field Record:',
|
||||
name: 'fields',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field Name',
|
||||
name: 'fieldName',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'New Value',
|
||||
name: 'fieldValue',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
260
packages/nodes-base/nodes/Odoo/descriptions/NoteDescription.ts
Normal file
260
packages/nodes-base/nodes/Odoo/descriptions/NoteDescription.ts
Normal file
|
@ -0,0 +1,260 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const noteOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'create',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new note',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a note',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a note',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all notes',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a note',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const noteDescription: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* note:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Memo',
|
||||
name: 'memo',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// displayName: 'Additional Fields',
|
||||
// name: 'additionalFields',
|
||||
// type: 'collection',
|
||||
// default: {},
|
||||
// placeholder: 'Add Field',
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// operation: [
|
||||
// 'create',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'note',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// options: [
|
||||
// {
|
||||
// displayName: 'Name',
|
||||
// name: 'name',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* note:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Note ID',
|
||||
name: 'noteId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* note:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields To Include',
|
||||
name: 'fieldsList',
|
||||
type: 'multiOptions',
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* note:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Note ID',
|
||||
name: 'noteId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Memo',
|
||||
name: 'memo',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'note',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// displayName: 'Update Fields',
|
||||
// name: 'updateFields',
|
||||
// type: 'collection',
|
||||
// default: {},
|
||||
// placeholder: 'Add Field',
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// operation: [
|
||||
// 'update',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'note',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// options: [
|
||||
// {
|
||||
// displayName: 'Name',
|
||||
// name: 'name',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Memo',
|
||||
// name: 'memo',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
];
|
|
@ -0,0 +1,352 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const opportunityOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'create',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new opportunity',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete an opportunity',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get an opportunity',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all opportunities',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update an opportunity',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const opportunityDescription: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* opportunity:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'opportunityName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email_from',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
// {
|
||||
// displayName: 'Expected Closing Date',
|
||||
// name: 'date_deadline',
|
||||
// type: 'dateTime',
|
||||
// default: '',
|
||||
// },
|
||||
{
|
||||
displayName: 'Expected Revenue',
|
||||
name: 'expected_revenue',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
{
|
||||
displayName: 'Internal Notes',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Priority',
|
||||
name: 'priority',
|
||||
type: 'options',
|
||||
default: '1',
|
||||
options: [
|
||||
{
|
||||
name: '1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: '2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
name: '3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Probability',
|
||||
name: 'probability',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
typeOptions: {
|
||||
maxValue: 100,
|
||||
minValue: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* opportunity:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Opportunity ID',
|
||||
name: 'opportunityId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* opportunity:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 1000,
|
||||
},
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Fields To Include',
|
||||
name: 'fieldsList',
|
||||
type: 'multiOptions',
|
||||
default: [],
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getModelFields',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* opportunity:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Opportunity ID',
|
||||
name: 'opportunityId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'opportunity',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email_from',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
// {
|
||||
// displayName: 'Expected Closing Date',
|
||||
// name: 'date_deadline',
|
||||
// type: 'dateTime',
|
||||
// default: '',
|
||||
// },
|
||||
{
|
||||
displayName: 'Expected Revenue',
|
||||
name: 'expected_revenue',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
{
|
||||
displayName: 'Internal Notes',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Priority',
|
||||
name: 'priority',
|
||||
type: 'options',
|
||||
default: '1',
|
||||
options: [
|
||||
{
|
||||
name: '1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
name: '2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
name: '3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Probability',
|
||||
name: 'probability',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
typeOptions: {
|
||||
maxValue: 100,
|
||||
minValue: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
15
packages/nodes-base/nodes/Odoo/descriptions/index.ts
Normal file
15
packages/nodes-base/nodes/Odoo/descriptions/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { customResourceDescription, customResourceOperations } from './CustomResourceDescription';
|
||||
import { noteDescription, noteOperations } from './NoteDescription';
|
||||
import { contactDescription, contactOperations } from './ContactDescription';
|
||||
import { opportunityDescription, opportunityOperations } from './OpportunityDescription';
|
||||
|
||||
export {
|
||||
customResourceDescription,
|
||||
customResourceOperations,
|
||||
noteDescription,
|
||||
noteOperations,
|
||||
contactDescription,
|
||||
contactOperations,
|
||||
opportunityDescription,
|
||||
opportunityOperations,
|
||||
};
|
1
packages/nodes-base/nodes/Odoo/odoo.svg
Normal file
1
packages/nodes-base/nodes/Odoo/odoo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="191"><circle cx="527.5" cy="118.4" r="72.4" fill="#888"/><path d="M527.5 161.1c23.6 0 42.7-19.1 42.7-42.7s-19.1-42.7-42.7-42.7-42.7 19.1-42.7 42.7 19.1 42.7 42.7 42.7z" fill="#fff"/><circle cx="374" cy="118.4" r="72.4" fill="#888"/><path d="M374 161.1c23.6 0 42.7-19.1 42.7-42.7S397.6 75.7 374 75.7s-42.7 19.1-42.7 42.7 19.1 42.7 42.7 42.7z" fill="#fff"/><path d="M294.9 117.8v.6c0 40-32.4 72.4-72.4 72.4s-72.4-32.4-72.4-72.4S182.5 46 222.5 46c16.4 0 31.5 5.5 43.7 14.6V14.4A14.34 14.34 0 0 1 280.6 0c7.9 0 14.4 6.5 14.4 14.4v102.7c0 .2 0 .5-.1.7z" fill="#888"/><circle cx="222.5" cy="118.4" r="42.7" fill="#fff"/><circle cx="72.4" cy="118.2" r="72.4" fill="#9c5789"/><circle cx="71.7" cy="118.5" r="42.7" fill="#fff"/></svg>
|
After Width: | Height: | Size: 785 B |
|
@ -209,6 +209,7 @@
|
|||
"dist/credentials/NotionOAuth2Api.credentials.js",
|
||||
"dist/credentials/OAuth1Api.credentials.js",
|
||||
"dist/credentials/OAuth2Api.credentials.js",
|
||||
"dist/credentials/OdooApi.credentials.js",
|
||||
"dist/credentials/OneSimpleApi.credentials.js",
|
||||
"dist/credentials/OnfleetApi.credentials.js",
|
||||
"dist/credentials/OpenWeatherMapApi.credentials.js",
|
||||
|
@ -548,6 +549,7 @@
|
|||
"dist/nodes/Onfleet/OnfleetTrigger.node.js",
|
||||
"dist/nodes/Notion/Notion.node.js",
|
||||
"dist/nodes/Notion/NotionTrigger.node.js",
|
||||
"dist/nodes/Odoo/Odoo.node.js",
|
||||
"dist/nodes/OneSimpleApi/OneSimpleApi.node.js",
|
||||
"dist/nodes/OpenThesaurus/OpenThesaurus.node.js",
|
||||
"dist/nodes/OpenWeatherMap/OpenWeatherMap.node.js",
|
||||
|
|
Loading…
Reference in a new issue