UNPKG

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
"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;