UNPKG

n8n-nodes-exact-online-v1

Version:

Exact Online Community node for n8n

296 lines 13.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createReconciliationXml = exports.exactOnlineXmlRequest = exports.toOptionsFromStringArray = exports.toFieldFilterOptions = exports.toFieldSelectOptions = exports.toOptions = exports.toDivisionOptions = exports.getEndpointConfig = exports.getEndpointFieldConfig = exports.getResourceOptions = exports.getFieldType = exports.getServiceOptions = exports.getMandatoryFields = exports.getFields = exports.getAllData = exports.getData = exports.getCurrentDivision = exports.exactOnlineApiRequest = void 0; const ExactOnlineRestApi_json_1 = __importDefault(require("./ExactOnlineRestApi.json")); const ExactOnlineXmlApi_json_1 = __importDefault(require("./ExactOnlineXmlApi.json")); const n8n_workflow_1 = require("n8n-workflow"); let mergedConfigs = null; function getAllConfigs() { if (mergedConfigs) { return mergedConfigs; } const restEndpoints = ExactOnlineRestApi_json_1.default; const xmlEndpoints = ExactOnlineXmlApi_json_1.default; const typedRestEndpoints = restEndpoints.map(config => ({ ...config, apiType: 'rest', })); const typedXmlEndpoints = xmlEndpoints.map(config => ({ ...config, apiType: 'xml', })); mergedConfigs = [...typedRestEndpoints, ...typedXmlEndpoints]; return mergedConfigs; } async function exactOnlineApiRequest(method, uri, body = {}, qs = {}, option = {}, nextPageUrl = '') { var _a; let options = { headers: { 'Content-Type': 'application/json', }, method, body, qs, uri: ``, json: true, }; options.resolveWithFullResponse = true; const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2'); let credentialType; let baseUrl; if (authenticationMethod === 'accessToken') { const credentials = await this.getCredentials('exactOnlineApi'); credentialType = 'exactOnlineApi'; baseUrl = String(credentials.url) || 'https://start.exactonline.nl'; } else { const credentials = await this.getCredentials('exactOnlineApiOAuth2Api'); credentialType = 'exactOnlineApiOAuth2Api'; baseUrl = String(credentials.url) || 'https://start.exactonline.nl'; } options.uri = `${baseUrl}${uri}`; if (nextPageUrl !== '') { options.uri = nextPageUrl; } options = Object.assign({}, options, option); try { if (Object.keys(body).length === 0) { delete options.body; } const oAuth2Options = { includeCredentialsOnRefreshOnBody: true, }; let response; try { response = await this.helpers.requestWithAuthentication.call(this, credentialType, options); } catch (error) { console.warn('[ExactNode] Error: ' + error.httpCode + ' | ' + ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status)); if (error.httpCode && parseInt(error.httpCode, 10) === 429) { console.warn('[ExactNode] Detected 429: waiting 60 seconds.'); await new Promise((resolve) => setTimeout(resolve, 61000)); console.warn('[ExactNode] Waiting done.'); response = await this.helpers.requestWithAuthentication.call(this, credentialType, options); } else { console.warn('[ExactNode] Unknown error', error); throw error; } } return response; } catch (error) { throw new n8n_workflow_1.NodeApiError(this.getNode(), error); } } exports.exactOnlineApiRequest = exactOnlineApiRequest; async function getCurrentDivision() { const responseData = await exactOnlineApiRequest.call(this, 'GET', `/api/v1/current/Me?$select=CurrentDivision`); return responseData.body.d.results[0].CurrentDivision; } exports.getCurrentDivision = getCurrentDivision; async function getData(uri, body = {}, qs = {}, option = {}) { const responseData = await exactOnlineApiRequest.call(this, 'GET', uri, body, qs, option); if (responseData.body.d.results) { return [].concat(responseData.body.d.results); } else { return [].concat(responseData.body.d); } } exports.getData = getData; async function getAllData(uri, limit = 60, body = {}, qs = {}, option = {}, ignoreRateLimit = false) { let returnData = []; let responseData; let nextPageUrl = ''; do { if (nextPageUrl === '') { responseData = await exactOnlineApiRequest.call(this, 'GET', uri, body, qs, option); } else { responseData = await exactOnlineApiRequest.call(this, 'GET', uri, body, {}, option, nextPageUrl); } if (responseData.body.d.results) { returnData = returnData.concat(responseData.body.d.results); } else { returnData = returnData.concat(responseData.body.d); } nextPageUrl = responseData.body.d.__next; if (!ignoreRateLimit && responseData.headers && responseData.headers['x-ratelimit-minutely-remaining'] === "0") { const resetTimestamp = responseData.headers['x-ratelimit-minutely-reset']; if (resetTimestamp && typeof resetTimestamp === 'number') { const waitTime = resetTimestamp - Date.now(); if (waitTime >= 0) { await new Promise((resolve) => setTimeout(resolve, Math.min(waitTime, 60000))); } } else if (resetTimestamp) { console.warn(`[ExactNode Rate Limit] Invalid x-ratelimit-minutely-reset header type: ${typeof resetTimestamp}. Value: ${resetTimestamp}`); } } } while ((limit === 0 || returnData.length < limit) && responseData.body.d.__next); if (limit !== 0) { return returnData.slice(0, limit); } return returnData; } exports.getAllData = getAllData; async function getFields(endpointConfig) { return endpointConfig.fields.map((a) => a.name); } exports.getFields = getFields; async function getMandatoryFields(endpointConfig) { return endpointConfig.fields.filter((x) => x.mandatory === true).map((a) => a.name); } exports.getMandatoryFields = getMandatoryFields; async function getServiceOptions() { const allConfigs = getAllConfigs(); return allConfigs.map((x) => x.service.toLocaleLowerCase()); } exports.getServiceOptions = getServiceOptions; async function getFieldType(endpointConfig, fieldName) { var _a; return (_a = endpointConfig.fields.filter((a) => a.name === fieldName)[0].type) !== null && _a !== void 0 ? _a : 'Edm.String'; } exports.getFieldType = getFieldType; async function getResourceOptions(service) { const allConfigs = getAllConfigs(); return allConfigs.filter((x) => x.service.toLocaleLowerCase() === service).map((x) => x.endpoint); } exports.getResourceOptions = getResourceOptions; async function getEndpointFieldConfig(service, endpoint) { const endpointConfig = await getEndpointConfig.call(this, service, endpoint); return (endpointConfig === null || endpointConfig === void 0 ? void 0 : endpointConfig.fields) || []; } exports.getEndpointFieldConfig = getEndpointFieldConfig; async function getEndpointConfig(service, endpoint) { const allConfigs = getAllConfigs(); return allConfigs.find((x) => x.service.toLocaleLowerCase() === service.toLocaleLowerCase() && x.endpoint === endpoint); } exports.getEndpointConfig = getEndpointConfig; const toDivisionOptions = (items) => items.map(({ Code, CustomerName, Description }) => ({ name: `${CustomerName} : ${Description}`, value: Code, })); exports.toDivisionOptions = toDivisionOptions; const toOptions = (items) => items.map(({ value, name }) => ({ name, value })); exports.toOptions = toOptions; const toFieldSelectOptions = (items) => items.map(({ name }) => ({ name, value: name })); exports.toFieldSelectOptions = toFieldSelectOptions; const toFieldFilterOptions = (items) => items.map(({ name }) => ({ name, value: name })); exports.toFieldFilterOptions = toFieldFilterOptions; const toOptionsFromStringArray = (items) => items.map((x) => ({ name: x.charAt(0).toUpperCase() + x.slice(1), value: x })); exports.toOptionsFromStringArray = toOptionsFromStringArray; async function exactOnlineXmlRequest(division, topic, xmlBody) { var _a; const options = { headers: { 'Content-Type': 'application/xml', }, method: 'POST', body: xmlBody, uri: '', resolveWithFullResponse: true, }; const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2'); let credentialType; let baseUrl; if (authenticationMethod === 'accessToken') { const credentials = await this.getCredentials('exactOnlineApi'); credentialType = 'exactOnlineApi'; baseUrl = String(credentials.url) || 'https://start.exactonline.nl'; } else { const credentials = await this.getCredentials('exactOnlineApiOAuth2Api'); credentialType = 'exactOnlineApiOAuth2Api'; baseUrl = String(credentials.url) || 'https://start.exactonline.nl'; } const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; options.uri = `${cleanBaseUrl}/docs/XMLUpload.aspx?Topic=${topic}&_Division_=${division}`; try { const oAuth2Options = { includeCredentialsOnRefreshOnBody: true, }; let response; try { response = await this.helpers.requestWithAuthentication.call(this, credentialType, options); } catch (error) { console.warn('[ExactNode] Error: ' + error.httpCode + ' | ' + ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status)); if (error.httpCode && parseInt(error.httpCode, 10) === 429) { console.warn('[ExactNode] Detected 429: waiting 60 seconds.'); await new Promise((resolve) => setTimeout(resolve, 61000)); console.warn('[ExactNode] Waiting done.'); response = await this.helpers.requestWithAuthentication.call(this, credentialType, options); } else { console.warn('[ExactNode] Unknown error', error); throw error; } } return response; } catch (error) { throw new n8n_workflow_1.NodeApiError(this.getNode(), error); } } exports.exactOnlineXmlRequest = exactOnlineXmlRequest; function createReconciliationXml(matchSets) { let xml = '<?xml version="1.0" encoding="utf-8"?>\n'; xml += '<eExact xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">\n'; xml += ' <MatchSets>\n'; for (const matchSet of matchSets) { xml += ' <MatchSet>\n'; xml += ` <GLAccount code="${matchSet.GLAccount}"/>\n`; if (matchSet.Account) { xml += ` <Account code="${matchSet.Account}"/>\n`; } xml += ' <MatchLines>\n'; for (const matchLine of matchSet.MatchLines) { const finYearInt = parseInt(String(matchLine.finYear), 10); const finPeriodInt = parseInt(String(matchLine.finPeriod), 10); const entryInt = parseInt(String(matchLine.entry), 10); const amountFloat = parseFloat(String(matchLine.amountDC)); xml += ' <MatchLine '; xml += `finyear="${finYearInt}" `; xml += `finperiod="${finPeriodInt}" `; xml += `journal="${matchLine.journal}" `; xml += `entry="${entryInt}" `; xml += `amountdc="${amountFloat}" `; xml += '/>\n'; } xml += ' </MatchLines>\n'; if (matchSet.WriteOff) { xml += ` <WriteOff type="${matchSet.WriteOff.type}">\n`; if (matchSet.WriteOff.GLAccount) { xml += ` <GLAccount code="${matchSet.WriteOff.GLAccount}"/>\n`; } if (matchSet.WriteOff.Description) { xml += ` <Description>${matchSet.WriteOff.Description}</Description>\n`; } if (matchSet.WriteOff.FinYear) { xml += ` <FinYear>${parseInt(String(matchSet.WriteOff.FinYear), 10)}</FinYear>\n`; } if (matchSet.WriteOff.FinPeriod) { xml += ` <FinPeriod>${parseInt(String(matchSet.WriteOff.FinPeriod), 10)}</FinPeriod>\n`; } if (matchSet.WriteOff.Date) { xml += ` <Date>${matchSet.WriteOff.Date}</Date>\n`; } if (matchSet.WriteOff.VATCorrection !== undefined) { xml += ` <VATCorrection>${matchSet.WriteOff.VATCorrection}</VATCorrection>\n`; } xml += ' </WriteOff>\n'; } xml += ' </MatchSet>\n'; } xml += ' </MatchSets>\n'; xml += '</eExact>'; return xml; } exports.createReconciliationXml = createReconciliationXml; //# sourceMappingURL=GenericFunctions.js.map