n8n-nodes-sevdesk-v2
Version:
n8n community node for SevDesk API v2 integration with 24 production-ready workflow templates. Direct API access without external dependencies - simplified, secure, and optimized for German accounting automation (n8n 1.101.1 compatible).
968 lines (967 loc) • 44.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SevDeskResourceManager = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const ResourceRegistry_1 = require("./ResourceRegistry");
const ResourceHandlerFactory_1 = require("./factories/ResourceHandlerFactory");
const BatchOperationHandler_1 = require("./batch/BatchOperationHandler");
const ResourceDependencyResolver_1 = require("./dependencies/ResourceDependencyResolver");
const RateLimiter_1 = require("./utils/RateLimiter");
const SevDeskErrors_1 = require("./errors/SevDeskErrors");
class SevDeskResourceManager {
constructor(executeFunctions) {
this.executeFunctions = executeFunctions;
this.batchHandler = new BatchOperationHandler_1.BatchOperationHandler(executeFunctions);
this.initializeBatchHandlers();
ResourceDependencyResolver_1.ResourceDependencyResolver.initialize();
}
initializeBatchHandlers() {
try {
const supportedResources = ['contact', 'invoice', 'voucher', 'order'];
supportedResources.forEach(resource => {
if (ResourceHandlerFactory_1.ResourceHandlerFactory.isResourceSupported(resource)) {
const handler = ResourceHandlerFactory_1.ResourceHandlerFactory.createHandler(resource, this.executeFunctions);
this.batchHandler.registerResourceHandler(resource, handler);
}
});
this.logApiInteraction('info', 'Batch handlers initialized successfully', {
registeredResources: supportedResources.filter(r => ResourceHandlerFactory_1.ResourceHandlerFactory.isResourceSupported(r))
});
}
catch (error) {
this.logApiInteraction('error', 'Failed to initialize batch handlers', { error: error.message });
}
}
async executeBatchOperations(batchRequest) {
this.logApiInteraction('info', 'Starting batch operations', {
operationCount: batchRequest.operations.length,
options: batchRequest.options
});
try {
if (!BatchOperationHandler_1.BatchOperationUtils.validateOperationCompatibility(batchRequest.operations)) {
throw new Error('Batch contains conflicting operations that cannot be executed together');
}
const result = await this.batchHandler.executeBatch(batchRequest);
this.logApiInteraction('info', 'Batch operations completed', {
summary: result.summary,
success: result.success
});
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logApiInteraction('error', `Batch operations failed: ${errorMessage}`);
throw error;
}
}
createBatchFromInputData(itemIndex) {
try {
const batchConfig = this.executeFunctions.getNodeParameter('batchConfig', itemIndex, {});
const operations = [];
if (batchConfig.operations && Array.isArray(batchConfig.operations)) {
batchConfig.operations.forEach((op) => {
operations.push(BatchOperationHandler_1.BatchOperationHandler.createBatchOperation(op.resource, op.operation, op.data, op.id, op.parameters));
});
}
const options = {
continueOnError: batchConfig.continueOnError !== false,
maxConcurrency: batchConfig.maxConcurrency || 5,
timeout: batchConfig.timeout || 30000
};
return BatchOperationHandler_1.BatchOperationHandler.createBatchRequest(operations, options);
}
catch (error) {
this.logApiInteraction('error', 'Failed to create batch from input data', { error: error.message });
throw new Error(`Failed to create batch operations: ${error.message}`);
}
}
sanitizeLogData(data) {
if (!data || typeof data !== 'object') {
return data;
}
const sensitiveKeys = [
'authorization', 'auth', 'token', 'apikey', 'api_key', 'password', 'passwd', 'pwd',
'secret', 'key', 'credential', 'credentials', 'bearer', 'x-api-key',
'cookie', 'session', 'sessionid', 'session_id', 'csrf', 'csrf_token',
'private_key', 'privatekey', 'client_secret', 'clientsecret'
];
const sanitized = Array.isArray(data) ? [] : {};
for (const [key, value] of Object.entries(data)) {
const lowerKey = key.toLowerCase();
const isSensitive = sensitiveKeys.some(sensitiveKey => lowerKey.includes(sensitiveKey));
if (isSensitive) {
if (typeof value === 'string' && value.length > 0) {
sanitized[key] = '[REDACTED]';
}
else {
sanitized[key] = '[REDACTED]';
}
}
else if (typeof value === 'object' && value !== null) {
sanitized[key] = this.sanitizeLogData(value);
}
else {
sanitized[key] = value;
}
}
return sanitized;
}
logApiInteraction(level, message, data) {
const sanitizedData = data ? this.sanitizeLogData(data) : undefined;
const logData = {
timestamp: new Date().toISOString(),
resource: this.executeFunctions.getNodeParameter('resource', 0, ''),
operation: this.executeFunctions.getNodeParameter('operation', 0, ''),
message,
...(sanitizedData && { data: sanitizedData })
};
console[level](`[SevDesk API] ${JSON.stringify(logData)}`);
}
async makeRateLimitedRequest(requestOptions) {
let retryCount = 0;
const maxRetries = RateLimiter_1.sevDeskRateLimiter.shouldRetry(0) ? 3 : 0;
const secureRequestOptions = {
...requestOptions,
rejectUnauthorized: true,
secureProtocol: 'TLSv1_2_method',
checkServerIdentity: true,
timeout: 30000,
headers: {
...requestOptions.headers,
'User-Agent': 'n8n-sevdesk-node/1.0.0',
'Connection': 'close',
},
};
while (retryCount <= maxRetries) {
try {
await RateLimiter_1.sevDeskRateLimiter.waitForSlot();
const logHeaders = { ...secureRequestOptions.headers };
if (logHeaders.Authorization) {
logHeaders.Authorization = '[REDACTED]';
}
this.logApiInteraction('info', `Making API request: ${secureRequestOptions.method} ${secureRequestOptions.url}`, {
retryCount,
rateLimitStatus: RateLimiter_1.sevDeskRateLimiter.getStatus(),
headers: logHeaders,
});
const response = await this.executeFunctions.helpers.httpRequest(secureRequestOptions);
this.logApiInteraction('info', `API request successful: ${requestOptions.method} ${requestOptions.url}`, {
retryCount,
responseSize: JSON.stringify(response).length,
});
return response;
}
catch (error) {
if (error.httpCode === 429 || error.statusCode === 429) {
retryCount++;
const retryAfter = error.response?.headers?.['retry-after'] ||
error.headers?.['retry-after'];
if (RateLimiter_1.sevDeskRateLimiter.shouldRetry(retryCount - 1)) {
const delay = RateLimiter_1.sevDeskRateLimiter.calculateRetryDelay(retryCount - 1, retryAfter);
this.logApiInteraction('warn', `Rate limit hit, retrying in ${delay}ms`, {
retryCount,
retryAfter,
url: requestOptions.url,
});
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
else {
this.logApiInteraction('error', 'Rate limit exceeded, max retries reached', {
retryCount,
url: requestOptions.url,
});
throw new SevDeskErrors_1.SevDeskRateLimitError('Rate limit exceeded and maximum retries reached', retryAfter, error);
}
}
if (retryCount < maxRetries && this.isRetryableError(error)) {
retryCount++;
const delay = RateLimiter_1.sevDeskRateLimiter.calculateRetryDelay(retryCount - 1);
this.logApiInteraction('warn', `Retryable error occurred, retrying in ${delay}ms`, {
retryCount,
error: error.message,
url: requestOptions.url,
});
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
this.logApiInteraction('error', `API request failed: ${error.message}`, {
retryCount,
url: requestOptions.url,
error: error.message,
});
if (error.httpCode || error.statusCode) {
throw SevDeskErrors_1.SevDeskErrorFactory.createError(error.httpCode || error.statusCode, error.response?.data || error.data || { message: error.message }, error);
}
throw error;
}
}
}
isRetryableError(error) {
if (error.code === 'ECONNRESET' || error.code === 'ENOTFOUND' || error.code === 'ETIMEDOUT') {
return true;
}
const statusCode = error.httpCode || error.statusCode;
if (statusCode >= 500 && statusCode < 600) {
return true;
}
if (statusCode === 408 || statusCode === 502 || statusCode === 503 || statusCode === 504) {
return true;
}
return false;
}
async executeResourceOperation(resource, operation, itemIndex) {
this.logApiInteraction('info', `Starting operation`, { resource, operation, itemIndex });
try {
if (!ResourceHandlerFactory_1.ResourceHandlerFactory.isResourceSupported(resource)) {
return await this.executeLegacyOperation(resource, operation, itemIndex);
}
const handler = ResourceHandlerFactory_1.ResourceHandlerFactory.createHandler(resource, this.executeFunctions);
this.logApiInteraction('info', `Created handler for resource: ${resource}`);
const result = await handler.execute(operation, itemIndex);
this.logApiInteraction('info', `Operation completed successfully`, {
resource,
operation,
hasResult: !!result
});
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logApiInteraction('error', `Operation failed: ${errorMessage}`, {
resource,
operation,
error: errorMessage
});
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Failed to execute ${resource} ${operation}: ${errorMessage}`,
description: "Please check your parameters and try again.",
});
}
}
async executeLegacyOperation(resource, operation, itemIndex) {
this.logApiInteraction('info', `Using legacy handler for resource: ${resource}`);
if (!ResourceRegistry_1.ResourceRegistry.isResourceSupported(resource)) {
const error = `Unsupported resource: ${resource}`;
this.logApiInteraction('error', error);
throw new Error(error);
}
const handlerMethodName = ResourceRegistry_1.ResourceRegistry.getResourceHandler(resource);
this.logApiInteraction('info', `Using legacy handler method: ${handlerMethodName}`);
const handlerMethod = this[handlerMethodName];
if (typeof handlerMethod !== 'function') {
const error = `Handler method ${handlerMethodName} not found for resource ${resource}`;
this.logApiInteraction('error', error);
throw new Error(error);
}
if (handlerMethodName === 'handleGenericOperation') {
return await handlerMethod.call(this, resource, operation, itemIndex);
}
else {
return await handlerMethod.call(this, operation, itemIndex);
}
}
async handleContactOperation(operation, itemIndex) {
const credentials = await this.executeFunctions.getCredentials('sevDeskApi');
const baseURL = `https://my.sevdesk.de/api/${credentials.apiVersion}`;
let requestOptions = {
headers: {
'Authorization': credentials.apiKey,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
url: '',
method: 'GET',
};
switch (operation) {
case 'create':
const createData = this.executeFunctions.getNodeParameter('additionalFields', itemIndex, {});
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Contact`,
body: createData,
};
break;
case 'get':
const contactId = this.executeFunctions.getNodeParameter('contactId', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Contact/${contactId}`,
};
break;
case 'getMany':
const queryParams = this.executeFunctions.getNodeParameter('additionalFields', itemIndex, {});
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Contact`,
qs: queryParams,
};
break;
case 'delete':
const deleteContactId = this.executeFunctions.getNodeParameter('contactId', itemIndex);
requestOptions = {
...requestOptions,
method: 'DELETE',
url: `${baseURL}/Contact/${deleteContactId}`,
};
break;
case 'checkCustomerNumberAvailability':
const customerNumber = this.executeFunctions.getNodeParameter('customerNumber', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Contact/Mapper/checkCustomerNumberAvailability`,
qs: { customerNumber },
};
break;
case 'findByCustomFieldValue':
const customFieldSetting = this.executeFunctions.getNodeParameter('customFieldSetting', itemIndex);
const value = this.executeFunctions.getNodeParameter('value', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Contact/Factory/findContactByCustomFieldValue`,
qs: {
'customFieldSetting[id]': customFieldSetting.id,
'customFieldSetting[objectName]': customFieldSetting.objectName,
value,
},
};
break;
default:
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Unknown contact operation: ${operation}`,
description: 'Please check the operation parameter.',
});
}
try {
const response = await this.makeRateLimitedRequest(requestOptions);
const responseData = response.objects || response;
return {
json: responseData,
pairedItem: {
item: itemIndex,
},
};
}
catch (error) {
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Contact operation failed: ${error.message}`,
description: `Failed to execute ${operation} operation on contact resource.`,
});
}
}
async handleInvoiceOperation(operation, itemIndex) {
const credentials = await this.executeFunctions.getCredentials('sevDeskApi');
const baseURL = `https://my.sevdesk.de/api/${credentials.apiVersion}`;
let requestOptions = {
headers: {
'Authorization': credentials.apiKey,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
url: '',
method: 'GET',
};
switch (operation) {
case 'get':
const invoiceId = this.executeFunctions.getNodeParameter('invoiceId', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Invoice/${invoiceId}`,
};
break;
case 'getMany':
const filters = this.executeFunctions.getNodeParameter('filters', itemIndex, {});
const queryParams = {};
if (filters.status)
queryParams.status = filters.status;
if (filters.invoiceNumber)
queryParams.invoiceNumber = filters.invoiceNumber;
if (filters.startDate)
queryParams.startDate = filters.startDate;
if (filters.endDate)
queryParams.endDate = filters.endDate;
if (filters.contactId) {
queryParams['contact[id]'] = filters.contactId;
queryParams['contact[objectName]'] = 'Contact';
}
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Invoice`,
qs: queryParams,
};
break;
case 'createOrUpdate':
const invoiceData = this.executeFunctions.getNodeParameter('invoice', itemIndex);
const invoicePosSave = this.executeFunctions.getNodeParameter('invoicePosSave', itemIndex, {});
if (invoiceData.contactId) {
invoiceData.contact = { id: invoiceData.contactId, objectName: 'Contact' };
delete invoiceData.contactId;
}
if (invoiceData.contactPersonId) {
invoiceData.contactPerson = { id: invoiceData.contactPersonId, objectName: 'SevUser' };
delete invoiceData.contactPersonId;
}
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Invoice/Factory/saveInvoice`,
body: {
invoice: invoiceData,
invoicePosSave: invoicePosSave.values || [],
},
};
break;
case 'bookAmount':
const bookInvoiceId = this.executeFunctions.getNodeParameter('invoiceId', itemIndex);
const amount = this.executeFunctions.getNodeParameter('amount', itemIndex);
const date = this.executeFunctions.getNodeParameter('date', itemIndex);
const paymentType = this.executeFunctions.getNodeParameter('paymentType', itemIndex);
const checkAccountId = this.executeFunctions.getNodeParameter('checkAccountId', itemIndex);
const createFeed = this.executeFunctions.getNodeParameter('createFeed', itemIndex, false);
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Invoice/${bookInvoiceId}/bookAmount`,
body: {
amount,
date,
type: paymentType,
checkAccount: { id: checkAccountId, objectName: 'CheckAccount' },
createFeed,
},
};
break;
case 'cancel':
const cancelInvoiceId = this.executeFunctions.getNodeParameter('invoiceId', itemIndex);
const cancellationText = this.executeFunctions.getNodeParameter('cancellationText', itemIndex, '');
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Invoice/${cancelInvoiceId}/cancelInvoice`,
qs: { cancellationText },
};
break;
case 'downloadPdf':
const pdfInvoiceId = this.executeFunctions.getNodeParameter('invoiceId', itemIndex);
const download = this.executeFunctions.getNodeParameter('download', itemIndex, true);
const preventive = this.executeFunctions.getNodeParameter('preventive', itemIndex, false);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Invoice/${pdfInvoiceId}/getPdf`,
qs: { download, preventive },
};
break;
case 'enshrine':
const enshrineInvoiceId = this.executeFunctions.getNodeParameter('invoiceId', itemIndex);
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Invoice/${enshrineInvoiceId}/enshrine`,
};
break;
case 'createFromOrder':
const orderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
const creationType = this.executeFunctions.getNodeParameter('creationType', itemIndex);
const orderAmount = this.executeFunctions.getNodeParameter('orderAmount', itemIndex);
const partialType = this.executeFunctions.getNodeParameter('partialType', itemIndex, '');
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Invoice/Factory/createInvoiceFromOrder`,
body: {
order: { id: orderId, objectName: 'Order' },
type: creationType,
amount: orderAmount,
partialType,
},
};
break;
default:
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Unknown invoice operation: ${operation}`,
description: 'Please check the operation parameter.',
});
}
try {
const response = await this.makeRateLimitedRequest(requestOptions);
let responseData = response;
if (response.objects) {
responseData = response.objects;
}
return {
json: responseData,
pairedItem: {
item: itemIndex,
},
};
}
catch (error) {
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Invoice operation failed: ${error.message}`,
description: `Failed to execute ${operation} operation on invoice resource.`,
});
}
}
async handleVoucherOperation(operation, itemIndex) {
const credentials = await this.executeFunctions.getCredentials('sevDeskApi');
const baseURL = `https://my.sevdesk.de/api/${credentials.apiVersion}`;
let requestOptions = {
headers: {
'Authorization': credentials.apiKey,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
url: '',
method: 'GET',
};
switch (operation) {
case 'get':
const voucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Voucher/${voucherId}`,
};
break;
case 'getMany':
const filters = this.executeFunctions.getNodeParameter('filters', itemIndex, {});
const queryParams = {};
if (filters.status)
queryParams.status = filters.status;
if (filters.creditDebit)
queryParams.creditDebit = filters.creditDebit;
if (filters.description)
queryParams.description = filters.description;
if (filters.startDate)
queryParams.startDate = filters.startDate;
if (filters.endDate)
queryParams.endDate = filters.endDate;
if (filters.contactId) {
queryParams['contact[id]'] = filters.contactId;
queryParams['contact[objectName]'] = 'Contact';
}
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Voucher`,
qs: queryParams,
};
break;
case 'create':
const voucherData = this.executeFunctions.getNodeParameter('voucher', itemIndex);
const voucherPosSave = this.executeFunctions.getNodeParameter('voucherPosSave', itemIndex, []);
const fileName = this.executeFunctions.getNodeParameter('fileName', itemIndex, '');
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Voucher/Factory/saveVoucher`,
body: {
voucher: voucherData,
voucherPosSave,
voucherPosDelete: null,
filename: fileName,
},
};
break;
case 'update':
const updateVoucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
const updateData = this.executeFunctions.getNodeParameter('updateFields', itemIndex, {});
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Voucher/${updateVoucherId}`,
body: updateData,
};
break;
case 'delete':
const deleteVoucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
requestOptions = {
...requestOptions,
method: 'DELETE',
url: `${baseURL}/Voucher/${deleteVoucherId}`,
};
break;
case 'book':
const bookVoucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
const amount = this.executeFunctions.getNodeParameter('amount', itemIndex);
const date = this.executeFunctions.getNodeParameter('date', itemIndex);
const paymentType = this.executeFunctions.getNodeParameter('paymentType', itemIndex);
const checkAccountId = this.executeFunctions.getNodeParameter('checkAccountId', itemIndex);
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Voucher/${bookVoucherId}/bookAmount`,
body: {
amount,
date,
type: paymentType,
checkAccount: { id: checkAccountId, objectName: 'CheckAccount' },
},
};
break;
case 'enshrine':
const enshrineVoucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Voucher/${enshrineVoucherId}/enshrine`,
};
break;
case 'resetToDraft':
const draftVoucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Voucher/${draftVoucherId}/resetToDraft`,
};
break;
case 'resetToOpen':
const openVoucherId = this.executeFunctions.getNodeParameter('voucherId', itemIndex);
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Voucher/${openVoucherId}/resetToOpen`,
};
break;
case 'uploadFile':
const inputData = this.executeFunctions.getInputData();
const fileData = inputData[itemIndex]?.binary?.data;
if (!fileData) {
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: 'No binary data found for file upload',
description: 'Please provide binary data for the file upload operation.',
});
}
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Voucher/Factory/uploadTempFile`,
body: {
data: fileData,
},
};
break;
default:
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Unknown voucher operation: ${operation}`,
description: 'Please check the operation parameter.',
});
}
try {
const response = await this.makeRateLimitedRequest(requestOptions);
let responseData = response;
if (response.objects) {
responseData = response.objects;
}
return {
json: responseData,
pairedItem: {
item: itemIndex,
},
};
}
catch (error) {
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Voucher operation failed: ${error.message}`,
description: `Failed to execute ${operation} operation on voucher resource.`,
});
}
}
async handleOrderOperation(operation, itemIndex) {
const credentials = await this.executeFunctions.getCredentials('sevDeskApi');
const baseURL = `https://my.sevdesk.de/api/${credentials.apiVersion}`;
let requestOptions = {
headers: {
'Authorization': credentials.apiKey,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
url: '',
method: 'GET',
};
switch (operation) {
case 'get':
const orderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Order/${orderId}`,
};
break;
case 'getMany':
const filters = this.executeFunctions.getNodeParameter('filters', itemIndex, {});
const queryParams = {};
if (filters.status)
queryParams.status = filters.status;
if (filters.orderNumber)
queryParams.orderNumber = filters.orderNumber;
if (filters.startDate)
queryParams.startDate = filters.startDate;
if (filters.endDate)
queryParams.endDate = filters.endDate;
if (filters.contactId) {
queryParams['contact[id]'] = filters.contactId;
queryParams['contact[objectName]'] = 'Contact';
}
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Order`,
qs: queryParams,
};
break;
case 'create':
const orderData = this.executeFunctions.getNodeParameter('order', itemIndex);
const orderPosSave = this.executeFunctions.getNodeParameter('orderPosSave', itemIndex, []);
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Order/Factory/saveOrder`,
body: {
order: orderData,
orderPosSave,
},
};
break;
case 'update':
const updateOrderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
const contactId = this.executeFunctions.getNodeParameter('contactId', itemIndex, '');
const contactPersonId = this.executeFunctions.getNodeParameter('contactPersonId', itemIndex, '');
const updateBody = {};
if (contactId)
updateBody.contact = contactId;
if (contactPersonId)
updateBody.contactPerson = contactPersonId;
requestOptions = {
...requestOptions,
method: 'PUT',
url: `${baseURL}/Order/${updateOrderId}`,
body: updateBody,
};
break;
case 'delete':
const deleteOrderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
requestOptions = {
...requestOptions,
method: 'DELETE',
url: `${baseURL}/Order/${deleteOrderId}`,
};
break;
case 'createInvoice':
const invoiceOrderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
const partSum = this.executeFunctions.getNodeParameter('partSum', itemIndex, false);
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Order/Factory/createInvoice`,
body: {
order: { id: invoiceOrderId, objectName: 'Order' },
'part-sum': partSum,
},
};
break;
case 'downloadPdf':
const pdfOrderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
requestOptions = {
...requestOptions,
method: 'GET',
url: `${baseURL}/Order/${pdfOrderId}/getPdf`,
};
break;
case 'sendViaEmail':
const emailOrderId = this.executeFunctions.getNodeParameter('orderId', itemIndex);
const email = this.executeFunctions.getNodeParameter('email', itemIndex);
const subject = this.executeFunctions.getNodeParameter('subject', itemIndex, '');
const text = this.executeFunctions.getNodeParameter('text', itemIndex, '');
requestOptions = {
...requestOptions,
method: 'POST',
url: `${baseURL}/Order/${emailOrderId}/sendViaEmail`,
body: {
to: email,
subject,
text,
},
};
break;
default:
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Unknown order operation: ${operation}`,
description: 'Please check the operation parameter.',
});
}
try {
const response = await this.makeRateLimitedRequest(requestOptions);
let responseData = response;
if (response.objects) {
responseData = response.objects;
}
return {
json: responseData,
pairedItem: {
item: itemIndex,
},
};
}
catch (error) {
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Order operation failed: ${error.message}`,
description: `Failed to execute ${operation} operation on order resource.`,
});
}
}
async handleCreditNoteOperation(operation, itemIndex) {
const result = {
resource: "creditNote",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleCategoryOperation(operation, itemIndex) {
const result = {
resource: "category",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleCheckAccountOperation(operation, itemIndex) {
const result = {
resource: "checkAccount",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handlePartOperation(operation, itemIndex) {
const result = {
resource: "part",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleTagOperation(operation, itemIndex) {
const result = {
resource: "tag",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleReportOperation(operation, itemIndex) {
const result = {
resource: "report",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleExportOperation(operation, itemIndex) {
const result = {
resource: "export",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleBasicsOperation(operation, itemIndex) {
const result = {
resource: "basics",
operation,
itemIndex,
timestamp: new Date().toISOString(),
};
return { json: result };
}
async handleBatchOperation(operation, itemIndex) {
this.logApiInteraction('info', `Starting batch operation: ${operation}`, { itemIndex });
try {
switch (operation) {
case 'executeBatch':
const jsonBatchConfig = this.executeFunctions.getNodeParameter('jsonBatchConfig', itemIndex, null);
let batchRequest;
if (jsonBatchConfig && jsonBatchConfig.trim() !== '') {
try {
const parsedConfig = JSON.parse(jsonBatchConfig);
batchRequest = BatchOperationHandler_1.BatchOperationHandler.createBatchRequest(parsedConfig.operations || [], parsedConfig.options || {});
}
catch (parseError) {
throw new Error(`Invalid JSON batch configuration: ${parseError.message}`);
}
}
else {
batchRequest = this.createBatchFromInputData(itemIndex);
}
const batchResult = await this.executeBatchOperations(batchRequest);
const processingOptions = this.executeFunctions.getNodeParameter('processingOptions', itemIndex, {});
const returnDetailedResults = processingOptions.returnDetailedResults !== false;
if (returnDetailedResults) {
return {
json: {
success: batchResult.success,
summary: batchResult.summary,
results: batchResult.results
},
pairedItem: {
item: itemIndex,
},
};
}
else {
const successfulData = batchResult.results
.filter(result => result.success)
.map(result => result.data)
.filter(data => data !== null && data !== undefined);
return {
json: {
success: batchResult.success,
summary: batchResult.summary,
data: successfulData
},
pairedItem: {
item: itemIndex,
},
};
}
default:
throw new Error(`Unknown batch operation: ${operation}`);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logApiInteraction('error', `Batch operation failed: ${errorMessage}`, { operation, itemIndex });
throw new n8n_workflow_1.NodeApiError(this.executeFunctions.getNode(), {
message: `Batch operation failed: ${errorMessage}`,
description: 'Please check your batch configuration and try again.',
});
}
}
async handleGenericOperation(resource, operation, itemIndex) {
const result = {
resource,
operation,
itemIndex,
timestamp: new Date().toISOString(),
note: "Generic operation handler - implement specific logic as needed",
};
return { json: result };
}
}
exports.SevDeskResourceManager = SevDeskResourceManager;