This commit is contained in:
Felipe Martins Diel 2025-03-05 13:57:48 -03:00 committed by GitHub
commit f9ae615a8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 517 additions and 16 deletions

View file

@ -9,7 +9,7 @@ import type {
import { NodeApiError, randomInt } from 'n8n-workflow';
const serviceJSONRPC = 'object';
const methodJSONRPC = 'execute';
const methodJSONRPC = 'execute_kw';
export const mapOperationToJSONRPC = {
create: 'create',
@ -147,7 +147,7 @@ export async function odooGetModelFields(
method: 'call',
params: {
service: serviceJSONRPC,
method: methodJSONRPC,
method: 'execute',
args: [
db,
userID,
@ -177,6 +177,7 @@ export async function odooCreate(
operation: OdooCRUD,
url: string,
newItem: IDataObject,
context?: IDataObject,
) {
try {
const body = {
@ -191,7 +192,8 @@ export async function odooCreate(
password,
mapOdooResources[resource] || resource,
mapOperationToJSONRPC[operation],
newItem || {},
[newItem || {}],
{ context: context || {} },
],
},
id: randomInt(100),
@ -214,6 +216,7 @@ export async function odooGet(
url: string,
itemsID: string,
fieldsToReturn?: IDataObject[],
context?: IDataObject,
) {
try {
if (!/^\d+$/.test(itemsID) || !parseInt(itemsID, 10)) {
@ -235,7 +238,10 @@ export async function odooGet(
mapOdooResources[resource] || resource,
mapOperationToJSONRPC[operation],
itemsID ? [+itemsID] : [],
fieldsToReturn || [],
{
fields: fieldsToReturn || [],
context: context || {},
}
],
},
id: randomInt(100),
@ -259,6 +265,7 @@ export async function odooGetAll(
filters?: IOdooFilterOperations,
fieldsToReturn?: IDataObject[],
limit = 0,
context?: IDataObject,
) {
try {
const body = {
@ -273,10 +280,13 @@ export async function odooGetAll(
password,
mapOdooResources[resource] || resource,
mapOperationToJSONRPC[operation],
(filters && processFilters(filters)) || [],
fieldsToReturn || [],
0, // offset
limit,
[(filters && processFilters(filters)) || []],
{
fields: fieldsToReturn || [],
offset: 0,
limit: limit,
context: context || {},
}
],
},
id: randomInt(100),
@ -299,6 +309,7 @@ export async function odooUpdate(
url: string,
itemsID: string,
fieldsToUpdate: IDataObject,
context?: IDataObject,
) {
try {
if (!Object.keys(fieldsToUpdate).length) {
@ -325,8 +336,8 @@ export async function odooUpdate(
password,
mapOdooResources[resource] || resource,
mapOperationToJSONRPC[operation],
itemsID ? [+itemsID] : [],
fieldsToUpdate,
[itemsID ? [+itemsID] : [], fieldsToUpdate],
{ context: context || {} },
],
},
id: randomInt(100),
@ -348,6 +359,7 @@ export async function odooDelete(
operation: OdooCRUD,
url: string,
itemsID: string,
context?: IDataObject,
) {
if (!/^\d+$/.test(itemsID) || !parseInt(itemsID, 10)) {
throw new NodeApiError(this.getNode(), {
@ -369,6 +381,7 @@ export async function odooDelete(
mapOdooResources[resource] || resource,
mapOperationToJSONRPC[operation],
itemsID ? [+itemsID] : [],
{ context: context || {} },
],
},
id: randomInt(100),

View file

@ -222,6 +222,36 @@ export class Odoo implements INodeType {
return options.sort((a, b) => a.name?.localeCompare(b.name) || 0) as INodePropertyOptions[];
},
async getSupportedLanguages(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.lang', 'search_read', [], ['code', 'name']],
},
id: randomInt(100),
};
const response = (await odooJSONRPCRequest.call(this, body, url)) as IDataObject[];
const options = Object.values(response).map(lang => ({
name: lang.name as string,
value: lang.code as string
}));
return options.sort((a, b) =>
a.name?.localeCompare(b.name) || 0
) as INodePropertyOptions[];
}
},
credentialTest: {
async odooApiTest(
@ -326,6 +356,10 @@ export class Odoo implements INodeType {
name,
...additionalFields,
};
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooCreate.call(
this,
db,
@ -335,11 +369,16 @@ export class Odoo implements INodeType {
operation,
url,
fields,
context,
);
}
if (operation === 'delete') {
const contactId = this.getNodeParameter('contactId', i) as string;
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooDelete.call(
this,
db,
@ -349,6 +388,7 @@ export class Odoo implements INodeType {
operation,
url,
contactId,
context,
);
}
@ -356,6 +396,9 @@ export class Odoo implements INodeType {
const contactId = this.getNodeParameter('contactId', i) as string;
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooGet.call(
this,
db,
@ -366,6 +409,7 @@ export class Odoo implements INodeType {
url,
contactId,
fields,
context,
);
}
@ -373,6 +417,9 @@ export class Odoo implements INodeType {
const returnAll = this.getNodeParameter('returnAll', i);
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
if (returnAll) {
responseData = await odooGetAll.call(
this,
@ -384,6 +431,8 @@ export class Odoo implements INodeType {
url,
undefined,
fields,
undefined,
context,
);
} else {
const limit = this.getNodeParameter('limit', i);
@ -398,6 +447,7 @@ export class Odoo implements INodeType {
undefined, // filters, only for custom resource
fields,
limit,
context,
);
}
}
@ -417,6 +467,10 @@ export class Odoo implements INodeType {
delete updateFields.address;
}
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooUpdate.call(
this,
db,
@ -427,6 +481,7 @@ export class Odoo implements INodeType {
url,
contactId,
updateFields,
context,
);
}
}
@ -435,6 +490,10 @@ export class Odoo implements INodeType {
const customResource = this.getNodeParameter('customResource', i) as string;
if (operation === 'create') {
const fields = this.getNodeParameter('fieldsToCreateOrUpdate', i) as IDataObject;
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooCreate.call(
this,
db,
@ -444,11 +503,16 @@ export class Odoo implements INodeType {
operation,
url,
processNameValueFields(fields),
context,
);
}
if (operation === 'delete') {
const customResourceId = this.getNodeParameter('customResourceId', i) as string;
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooDelete.call(
this,
db,
@ -458,6 +522,7 @@ export class Odoo implements INodeType {
operation,
url,
customResourceId,
context,
);
}
@ -465,6 +530,9 @@ export class Odoo implements INodeType {
const customResourceId = this.getNodeParameter('customResourceId', i) as string;
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooGet.call(
this,
db,
@ -475,14 +543,18 @@ export class Odoo implements INodeType {
url,
customResourceId,
fields,
context,
);
}
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const filter = this.getNodeParameter('filterRequest', i) as IOdooFilterOperations;
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
if (returnAll) {
responseData = await odooGetAll.call(
this,
@ -494,6 +566,8 @@ export class Odoo implements INodeType {
url,
filter,
fields,
undefined,
context,
);
} else {
const limit = this.getNodeParameter('limit', i);
@ -508,6 +582,7 @@ export class Odoo implements INodeType {
filter,
fields,
limit,
context,
);
}
}
@ -515,6 +590,10 @@ export class Odoo implements INodeType {
if (operation === 'update') {
const customResourceId = this.getNodeParameter('customResourceId', i) as string;
const fields = this.getNodeParameter('fieldsToCreateOrUpdate', i) as IDataObject;
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooUpdate.call(
this,
db,
@ -525,6 +604,7 @@ export class Odoo implements INodeType {
url,
customResourceId,
processNameValueFields(fields),
context,
);
}
}
@ -536,7 +616,11 @@ export class Odoo implements INodeType {
const fields: IDataObject = {
memo,
// ...additionalFields,
};
};
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooCreate.call(
this,
db,
@ -546,11 +630,16 @@ export class Odoo implements INodeType {
operation,
url,
fields,
context,
);
}
if (operation === 'delete') {
const noteId = this.getNodeParameter('noteId', i) as string;
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooDelete.call(
this,
db,
@ -560,6 +649,7 @@ export class Odoo implements INodeType {
operation,
url,
noteId,
context,
);
}
@ -567,6 +657,9 @@ export class Odoo implements INodeType {
const noteId = this.getNodeParameter('noteId', i) as string;
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooGet.call(
this,
db,
@ -577,6 +670,7 @@ export class Odoo implements INodeType {
url,
noteId,
fields,
context,
);
}
@ -584,6 +678,9 @@ export class Odoo implements INodeType {
const returnAll = this.getNodeParameter('returnAll', i);
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
if (returnAll) {
responseData = await odooGetAll.call(
this,
@ -595,6 +692,8 @@ export class Odoo implements INodeType {
url,
undefined,
fields,
undefined,
context,
);
} else {
const limit = this.getNodeParameter('limit', i);
@ -609,6 +708,7 @@ export class Odoo implements INodeType {
undefined, // filters, only for custom resource
fields,
limit,
context,
);
}
}
@ -619,6 +719,10 @@ export class Odoo implements INodeType {
const fields: IDataObject = {
memo,
};
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooUpdate.call(
this,
db,
@ -629,6 +733,7 @@ export class Odoo implements INodeType {
url,
noteId,
fields,
context,
);
}
}
@ -641,6 +746,9 @@ export class Odoo implements INodeType {
name,
...additionalFields,
};
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooCreate.call(
this,
@ -651,11 +759,16 @@ export class Odoo implements INodeType {
operation,
url,
fields,
context,
);
}
if (operation === 'delete') {
const opportunityId = this.getNodeParameter('opportunityId', i) as string;
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooDelete.call(
this,
db,
@ -665,6 +778,7 @@ export class Odoo implements INodeType {
operation,
url,
opportunityId,
context,
);
}
@ -672,6 +786,9 @@ export class Odoo implements INodeType {
const opportunityId = this.getNodeParameter('opportunityId', i) as string;
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooGet.call(
this,
db,
@ -682,6 +799,7 @@ export class Odoo implements INodeType {
url,
opportunityId,
fields,
context,
);
}
@ -689,6 +807,9 @@ export class Odoo implements INodeType {
const returnAll = this.getNodeParameter('returnAll', i);
const options = this.getNodeParameter('options', i);
const fields = (options.fieldsList as IDataObject[]) || [];
const language = options.language as string;
const context = language ? { lang: language } : {};
if (returnAll) {
responseData = await odooGetAll.call(
this,
@ -700,6 +821,8 @@ export class Odoo implements INodeType {
url,
undefined,
fields,
undefined,
context,
);
} else {
const limit = this.getNodeParameter('limit', i);
@ -714,6 +837,7 @@ export class Odoo implements INodeType {
undefined, // filters, only for custom resource
fields,
limit,
context,
);
}
}
@ -721,6 +845,10 @@ export class Odoo implements INodeType {
if (operation === 'update') {
const opportunityId = this.getNodeParameter('opportunityId', i) as string;
const updateFields = this.getNodeParameter('updateFields', i);
const options = this.getNodeParameter('options', i);
const language = options.language as string;
const context = language ? { lang: language } : {};
responseData = await odooUpdate.call(
this,
db,
@ -731,6 +859,7 @@ export class Odoo implements INodeType {
url,
opportunityId,
updateFields,
context,
);
}
}

View file

@ -186,6 +186,31 @@ export const contactDescription: INodeProperties[] = [
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['create'],
resource: ['contact'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* contact:get */
@ -245,7 +270,7 @@ export const contactDescription: INodeProperties[] = [
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add Field',
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['getAll', 'get'],
@ -264,6 +289,16 @@ export const contactDescription: INodeProperties[] = [
loadOptionsMethod: 'getModelFields',
},
},
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
@ -411,4 +446,58 @@ export const contactDescription: INodeProperties[] = [
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['update'],
resource: ['contact'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* contact:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['delete'],
resource: ['contact'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
];

View file

@ -109,6 +109,31 @@ export const customResourceDescription: INodeProperties[] = [
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['create'],
resource: ['custom'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* custom:get */
@ -166,7 +191,7 @@ export const customResourceDescription: INodeProperties[] = [
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add Field',
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['getAll', 'get'],
@ -186,6 +211,16 @@ export const customResourceDescription: INodeProperties[] = [
loadOptionsDependsOn: ['customResource'],
},
},
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
{
@ -282,6 +317,7 @@ export const customResourceDescription: INodeProperties[] = [
},
],
},
/* -------------------------------------------------------------------------- */
/* custom:update */
/* -------------------------------------------------------------------------- */
@ -341,4 +377,58 @@ export const customResourceDescription: INodeProperties[] = [
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['update'],
resource: ['custom'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* custom:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['delete'],
resource: ['custom'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
];

View file

@ -89,6 +89,31 @@ export const noteDescription: INodeProperties[] = [
// },
// ],
// },
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['create'],
resource: ['note'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* note:get */
@ -147,7 +172,7 @@ export const noteDescription: INodeProperties[] = [
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add Field',
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['getAll', 'get'],
@ -166,8 +191,19 @@ export const noteDescription: INodeProperties[] = [
loadOptionsMethod: 'getModelFields',
},
},
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* note:update */
/* -------------------------------------------------------------------------- */
@ -228,4 +264,58 @@ export const noteDescription: INodeProperties[] = [
// },
// ],
// },
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['update'],
resource: ['note'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* note:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['delete'],
resource: ['note'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
];

View file

@ -140,6 +140,31 @@ export const opportunityDescription: INodeProperties[] = [
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['create'],
resource: ['opportunity'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* opportunity:get */
@ -198,7 +223,7 @@ export const opportunityDescription: INodeProperties[] = [
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add Field',
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['getAll', 'get'],
@ -217,8 +242,19 @@ export const opportunityDescription: INodeProperties[] = [
loadOptionsMethod: 'getModelFields',
},
},
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* opportunity:update */
/* -------------------------------------------------------------------------- */
@ -316,4 +352,58 @@ export const opportunityDescription: INodeProperties[] = [
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['update'],
resource: ['opportunity'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
/* -------------------------------------------------------------------------- */
/* opportunity:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
default: {},
placeholder: 'Add option',
displayOptions: {
show: {
operation: ['delete'],
resource: ['opportunity'],
},
},
options: [
{
displayName: 'Language',
name: 'language',
type: 'options',
description: 'Choose from the list',
default: '',
typeOptions: {
loadOptionsMethod: 'getSupportedLanguages',
},
},
],
},
];