var slib = $.import("uniorg.ServiceLayer", "B1SLLogic"); var datetimelib = $.import("uniorg.libs", "datetime"); var logginglib = $.import("uniorg.libs", "uologging"); var slDest = $.import("uniorg.destinations", "uodestlib"); var slfwlib = $.import("uniorg.ServiceLayer", "B1SLFramework"); var output = {}; var outMessages = []; var outMsg = {}; var outStatus = "success"; var myDebug = {}; var companyid = 0; var UserName = ''; var UserPass = ''; var UserComp = ''; var SESSIONID = ""; var NODEID = ""; var slLoggedIn = false; var logTableId = 0; var logDocEntry = -1; var serviceName = "crm.cancellation.v1.cancelinvoice.xsjs"; var serviceVersion = '1.9'; var objecttype = "13C"; var schemaName = "A19753_EON_P01"; var projectsReactivated = false; var projectsReverted = false; var followupProjects = { count: 0, rows: [] }; var debugLevel = 0; switch ($.request.parameters.get("debug")) { case "true": case "yes": case "1": debugLevel = 1; break; case "full": case "2": debugLevel = 2; break; } if (debugLevel > 0) { output.requestParams = $.request.parameters; } function updateLogEntry(id, docEntry, b1Obj) { var conn = $.hdb.getConnection(); try { conn.executeUpdate( "UPDATE \"INTEGRATION_DATA\".\"UO_DOCUMENTS_RAWCALL\" SET \"B1_PK\" = ?, \"B1_OBJ\" = ?, \"UPDATE_TS\" = CURRENT_TIMESTAMP WHERE \"ID\" = ?", docEntry, b1Obj, id); conn.commit(); } catch (e) { exceptionHandler(e); return; } } function setOutputMessage() { output.status = outStatus; if (debugLevel > 0) { output.debugInfos = myDebug; } output.messages = outMessages; $.response.contentType = "application/json"; $.response.setBody(JSON.stringify(output)); $.response.status = $.net.http.OK; } function exceptionHandler(ex) { if (outMsg.method) { outMessages.push(outMsg); } outMsg = {}; outMsg.method = "exceptionHandler"; outMsg.status = "error"; outMsg.message = ex.toString(); if (debugLevel > 0) { outMsg.exceptionInfo = {}; outMsg.exceptionInfo.name = ex.name; outMsg.exceptionInfo.message = ex.message; outMsg.exceptionInfo.fileName = ex.fileName; outMsg.exceptionInfo.lineNumber = ex.lineNumber; outMsg.exceptionInfo.columnNumber = ex.columnNumber; outMsg.exceptionInfo.stack = ex.stack; //outMsg.exceptionInfo.source = ex.toSource(); } outMessages.push(outMsg); outStatus = "error"; output.status = outStatus; if (debugLevel > 0) { output.debugInfos = myDebug; } output.messages = outMessages; var errbody = JSON.stringify(output); $.response.contentType = 'application/json'; $.response.setBody(errbody); //$.response.status = $.net.http.INTERNAL_SERVER_ERROR; // Workaround, weil CRM scheinbar Probleme mit anderen Statuscodes hat $.response.status = $.net.http.OK; try { if (slLoggedIn) { slib.SLLogout(SESSIONID, NODEID); } } catch (e) { } } function setConnectionParams() { var idValue = schemaName; var idFieldname = 'SYS_NAME'; var CompanyConfig = slDest.getCompanyCfg(idValue, idFieldname); UserComp = CompanyConfig.COMPANYNAME; UserName = CompanyConfig.USERNAME; UserPass = slDest.readSecureStore(CompanyConfig.ID); if (debugLevel > 1) { myDebug.connectionParams = CompanyConfig; } myDebug.idValue = idValue; myDebug.idFieldname = idFieldname; if (CompanyConfig.COMPANYNAME === undefined) { outMsg = {}; outMsg.method = "getCompany"; outStatus = "error"; outMsg.status = "error"; outMsg.message = "System " + idValue + " is invalid for this endpoint"; outMessages.push(outMsg); } myDebug.CompanyName = UserComp; } function slLogin() { var loginResult = false; var loginInfo = {}; loginInfo.UserName = UserName; loginInfo.Password = UserPass; loginInfo.CompanyDB = UserComp; // SL LOGIN var response = slib.SLLogin(JSON.stringify(loginInfo), null, null); // B1SESSION and ROUTEID cookies returned by Login for (var j in response.cookies) { if (response.cookies[j].name == "B1SESSION") { SESSIONID = response.cookies[j].value; //output.SessionID = SESSIONID; } else if (response.cookies[j].name == "ROUTEID") { NODEID = response.cookies[j].value; //output.NodeID = NODEID; } } var loginResponse; try { loginResponse = JSON.parse(response.body.asString()); } catch (e) { loginResponse = response.body.asString(); } if (debugLevel > 1) { myDebug.loginInfo = loginInfo; } myDebug.loginResponse = loginResponse; if (response.status === 200) { loginResult = true; } else { outMsg = {}; outMsg.method = "login"; outMsg.status = "error"; var slError = myDebug.loginResponse.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.message = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.message = JSON.stringify(myDebug.loginResponse); } outMsg.response = myDebug.loginResponse; //outMsg.message = loginResponse; outMessages.push(outMsg); outStatus = "error"; } return loginResult; } function getDocDates() { var docDate, taxDate; outMsg = {}; outMsg.method = "getDocDates"; outMsg.status = "error"; var conn = $.db.getConnection(); var pstmt; var query = 'with currentPeriod as (select * from "XXREPXX".OFPR where current_date between "F_RefDate" and "T_RefDate"), \ nextPeriod as (select top 1 * from "XXREPXX".OFPR where "F_RefDate" > current_date order by "F_RefDate") \ select case when currentPeriod."PeriodStat"=\'N\' then current_date \ else to_date(nextPeriod."F_RefDate")end "DocDate", current_date as "TaxDate" \ from currentPeriod, nextPeriod'; query = query.replace(/XXREPXX/g, UserComp); pstmt = conn.prepareStatement(query); try { var rs = pstmt.executeQuery(); while (rs.next()) { docDate = rs.getDate(1); taxDate = rs.getDate(2); outMsg.docDate = docDate; outMsg.taxDate = taxDate; outMsg.status = "success"; } rs.close(); } finally { pstmt.close(); conn.close(); if (debugLevel > 0 || outMsg.status === "error") { if (debugLevel > 1) { outMsg.Query = query; } outMessages.push(outMsg); } } return { docDate: docDate, taxDate: taxDate }; } function getDraft(invoiceCrmGuid) { outMsg = {}; outMsg.method = "getDraft"; outMsg.status = "error"; outMsg.invoiceCrmGuid = invoiceCrmGuid; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'select \ T0."U_EON_SRC_GUID", T0."U_EON_SRC_ID", \'ODRF\' as "Table", T0."DocEntry", T0."DocNum", T0."DocStatus", T0."CANCELED", T0."draftKey" \ from ODRF T0 \ where T0."ObjType"=\'13\' \ and T0."U_EON_SRC_SYSTEM"=\'crm\' \ and T0."U_EON_SRC_GUID" = ? \ and not exists ( \ select 1 from OINV T1 WHERE T1."draftKey"=T0."DocEntry" \ ) \ order by T0."DocEntry"', invoiceCrmGuid); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { resultSet.push(iterator.value()); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; outMsg.status = "success"; outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function filterUnprocessedSpoolQueueEntries(docEntry) { outMsg = {}; outMsg.method = "filterSpoolQueue"; outMsg.status = "error"; outMsg.docEntry = docEntry; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var statement = 'update T0 \ set U_UO_STATUS=\'F\', \ U_UO_UPDATEDATE=TO_DATE(CURRENT_TIMESTAMP), \ U_UO_UPDATETIME=HOUR(TO_TIME(CURRENT_TIMESTAMP)) * 100 + MINUTE(TO_TIME(CURRENT_TIMESTAMP)), \ U_UO_LASTERROR=\'Filtered by cancellation process\' \ from "@UO_SPOOL_QUEUE" T0 \ where T0.U_UO_STATUS IN (\'N\',\'E\') and \ T0.U_UO_OBJTYPE=\'13\' and T0.U_UO_DOCENTRY=?'; var affectedRows = conn.executeUpdate(statement, docEntry); conn.commit(); conn.close(); resultData = { count: affectedRows }; outMsg.status = "success"; if (debugLevel > 1) { outMsg.statement = statement; } outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function cancelSpoolTasks(docEntry) { outMsg = {}; outMsg.method = "cancelSpoolTasks"; outMsg.status = "error"; outMsg.docEntry = docEntry; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var statement = 'update t1 \ set U_UO_STATUS=\'C\' \ from "@UO_SPOOL_TASKS" T1 \ where T1.U_UO_STATUS NOT IN (\'I\',\'S\') and T1.U_UO_SPOOLTYPE IN (\'M\',\'P\') and \ exists ( \ select 1 from "@UO_SPOOL" T0 where T0."DocEntry"=T1."DocEntry" and T0.U_UO_OBJTYPE=\'13\' and T0.U_UO_DOCENTRY=? \ )'; var affectedRows = conn.executeUpdate(statement, docEntry); conn.commit(); conn.close(); resultData = { count: affectedRows }; outMsg.status = "success"; if (debugLevel > 1) { outMsg.statement = statement; } outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function getInvoice(invoiceCrmGuid) { outMsg = {}; outMsg.method = "getInvoice"; outMsg.status = "error"; outMsg.invoiceCrmGuid = invoiceCrmGuid; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'select \ T0."U_EON_SRC_GUID", T0."U_EON_SRC_ID", \'OINV\' as "Table", T0."DocEntry", T0."DocNum", T0."DocStatus", T0."CANCELED", T0."draftKey" \ from OINV T0 \ where T0."U_EON_SRC_SYSTEM"=\'crm\' \ and T0."U_EON_SRC_GUID" = ? \ order by T0."DocEntry"', invoiceCrmGuid); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { resultSet.push(iterator.value()); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; outMsg.status = "success"; outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function getCancellationInvoice(baseEntry) { outMsg = {}; outMsg.method = "getCancellationInvoice"; outMsg.status = "error"; outMsg.baseEntry = baseEntry; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'select T0."DocEntry", T0."DocNum", T0."TransId", T0."Comments" \ from OINV T0 \ where T0.CANCELED=\'C\' and exists ( \ select 1 from INV1 T1 where T1."DocEntry"=T0."DocEntry" and T1."BaseType"=T0."ObjType" and T1."BaseEntry"=? \ )', baseEntry); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { resultSet.push(iterator.value()); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; outMsg.status = "success"; outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function checkInactiveProjects(docEntry, docDate) { outMsg = {}; outMsg.method = "checkInactiveProjects"; outMsg.status = "error"; outMsg.docEntry = docEntry; outMsg.docDate = datetimelib.convertToISODate(docDate); var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'select distinct T1."Project", GET_FOLLOWUP_PROJECT(T1."Project") as "FollowupProject" \ from INV1 T1 \ inner join OPRJ T2 ON T1."Project" = T2."PrjCode" \ where T1."DocEntry" = ? and (T2."Active" = \'N\' or T2."ValidTo" < ?)', docEntry, datetimelib.convertToISODate(docDate)); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { resultSet.push(iterator.value()); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; if (resultSet.length > 0) { outMsg.message = "Invoice must be canceled manually in SAP B1 due to inactive projects: " + resultSet.map(function(p) { return p.Project; }).join(", "); } else { outMsg.status = "success"; } outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function getFollowupProjects(docEntry) { outMsg = {}; outMsg.method = "getFollowupProjects"; outMsg.status = "error"; outMsg.docEntry = docEntry; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'with \ oldProjects as ( \ select distinct T2."PrjCode", T2.U_EON_PROFCTR, T2.U_EON_PSP, T2."ValidTo", T2.U_TEMP_VALID_TO \ from OINV T0 \ inner join INV1 T1 on T0."DocEntry"=T1."DocEntry" \ inner join OPRJ T2 on T1."Project" = T2."PrjCode" \ where T0."DocEntry"=? and T2.U_EON_NEWPROJECT!=\'\' \ ), \ followupProjects as ( \ select "PrjCode", GET_FOLLOWUP_PROJECT("PrjCode") as "FollowupProject" \ from oldProjects \ ) \ select T0."PrjCode" as "ProjectOld", T0."ValidTo", T0.U_TEMP_VALID_TO, T0.U_EON_PROFCTR as "ProfitCenterOld", T0.U_EON_PSP as "PspOld", T1."FollowupProject", T2.U_EON_PROFCTR, T2.U_EON_PSP \ from oldProjects T0 \ inner join followupProjects T1 on T0."PrjCode"=T1."PrjCode" \ inner join OPRJ T2 on T1."FollowupProject" = T2."PrjCode"', docEntry); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { resultSet.push(iterator.value()); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; if (resultSet.length > 0) { outMsg.message = "Projects will be replaced: " + resultSet.map(function(p) { return p.Project; }).join(", "); } outMsg.status = "success"; outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function reactivateOldProjects(projects) { /* - service layer update Project - U_TEMP_VALID_TO = ValidTo - ValidTo = '31122099' */ var result = false; var success = true; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "reactivateOldProjects"; outMsg.status = "error"; outMsg.proclist = []; projects.forEach(function(p) { var slBody = { ValidTo: '2099-12-31', U_TEMP_VALID_TO: datetimelib.convertToISODate(p.ValidTo) }; var newIdx = outMsg.proclist.push({ project: p.ProjectOld, slBody: slBody, status: 'error', resultStatusCode: null, resultMessage: '', response: null }) - 1; var responseProjectUpdate = slib.patchDynamicEntity('Projects', p.ProjectOld, JSON.stringify(slBody), SESSIONID, NODEID, false); outMsg.proclist[newIdx].resultStatusCode = responseProjectUpdate.status; if (responseProjectUpdate.status >= 200 && responseProjectUpdate.status < 300) { outMsg.proclist[newIdx].status = 'success'; } else { success = false; outMsg.status = "error"; outStatus = "error"; if (responseProjectUpdate.body) { var responseBodyParsed = JSON.parse(responseProjectUpdate.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.proclist[newIdx].resultMessage = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.proclist[newIdx].resultMessage = responseProjectUpdate.body.asString(); } outMsg.proclist[newIdx].response = responseBodyParsed; } else { outMsg.proclist[newIdx].resultMessage = JSON.stringify(responseProjectUpdate); } } }); if (success) { outMsg.status = "success"; result = true; } if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return result; } function revertOldProjects(projects) { /* - service layer update Project - ValidTo = U_TEMP_VALID_TO - U_TEMP_VALID_TO = NULL */ var result = false; var success = true; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "revertOldProjects"; outMsg.status = "error"; outMsg.proclist = []; projects.forEach(function(p) { var slBody = { ValidTo: p.U_TEMP_VALID_TO === null ? datetimelib.convertToISODate(p.ValidTo) : datetimelib.convertToISODate(p.U_TEMP_VALID_TO), U_TEMP_VALID_TO: null }; var newIdx = outMsg.proclist.push({ project: p.ProjectOld, slBody: slBody, status: 'error', resultStatusCode: null, resultMessage: '', response: null }) - 1; var responseProjectUpdate = slib.patchDynamicEntity('Projects', p.ProjectOld, JSON.stringify(slBody), SESSIONID, NODEID, false); outMsg.proclist[newIdx].resultStatusCode = responseProjectUpdate.status; if (responseProjectUpdate.status >= 200 && responseProjectUpdate.status < 300) { outMsg.proclist[newIdx].status = 'success'; } else { success = false; outMsg.status = "error"; outStatus = "error"; if (responseProjectUpdate.body) { var responseBodyParsed = JSON.parse(responseProjectUpdate.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.proclist[newIdx].resultMessage = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.proclist[newIdx].resultMessage = responseProjectUpdate.body.asString(); } outMsg.proclist[newIdx].response = responseBodyParsed; } else { outMsg.proclist[newIdx].resultMessage = JSON.stringify(responseProjectUpdate); } } }); if (success) { outMsg.status = "success"; result = true; } if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return result; } function updateProjectsInJE(transId, projectMapping) { /* projectMapping = [ { projectOld: "123456", projectNew: "130123456789", profitCenterNew: "PC12345", pspNew: "987654321" } ] */ /* - JE-Update Blockierung deaktivieren - UPDATE "@EON_FEATURES" SET U_EON_ACTIVE='N' WHERE "Code"='EON_TN6' - Service Layer Update JournalEntry Lines - Project => FollowupProject - Evtl. auch U_EON_PROFCTR und U_EON_PSP aus FollowupProject - Ggf. nur bestimmte Positionen (nur mit Betrag <> 0) - JE-Update Blockierung aktivieren - UPDATE "@EON_FEATURES" SET U_EON_ACTIVE='Y' WHERE "Code"='EON_TN6' */ var result = false; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "updateProjectsInJE"; outMsg.status = "error"; outMsg.transId = transId; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var affectedRows = conn.executeUpdate("UPDATE \"@EON_FEATURES\" SET U_EON_ACTIVE='N' WHERE \"Code\"='EON_TN6'"); conn.commit(); if (affectedRows === 1) { outMsg.JEUpdateBlockDeactivated = true; try { var getJE = slib.getDynamicEntity('JournalEntries', transId, SESSIONID, NODEID); if (getJE.status >= 200 && getJE.status < 300) { var oldJE = JSON.parse(getJE.body.asString()); //myDebug.oldJE = oldJE; if (oldJE.JournalEntryLines.findIndex(function(je) { return je.Credit !== 0; }) > -1) { var updJE = { JournalEntryLines: oldJE.JournalEntryLines.map(function(je) { var jeLine = { Line_ID: je.Line_ID }; var prjIdx = projectMapping.findIndex(function(p) { return p.projectCode = je.ProjectCode; }); if (prjIdx > -1) { jeLine.ProjectCode = projectMapping[prjIdx].projectNew; jeLine.U_EON_PROFCTR = projectMapping[prjIdx].profitCenterNew; jeLine.U_EON_PSP = projectMapping[prjIdx].pspNew; } return jeLine; }) }; var jeUpdate = slib.patchDynamicEntity('JournalEntries', transId, JSON.stringify(updJE), SESSIONID, NODEID, false); if (jeUpdate.status >= 200 && jeUpdate.status < 300) { result = true; outMsg.status = "success"; } else { outMsg.status = "error"; outStatus = "error"; if (jeUpdate.body) { var responseBodyParsed = JSON.parse(jeUpdate.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.message = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.message = jeUpdate.body.asString(); } outMsg.response = responseBodyParsed; } else { outMsg.message = JSON.stringify(jeUpdate); } } } else { outMsg.status = "success"; outMsg.message = "no update for zero amount journal entry"; } } else { // JE Get failed outMsg.message = 'failed to retrieve journal entry for cancelled invoice'; outStatus = "error"; } } catch (e) { throw e; } finally { if (outMsg.JEUpdateBlockDeactivated) { conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); affectedRows = conn.executeUpdate("UPDATE \"@EON_FEATURES\" SET U_EON_ACTIVE='Y' WHERE \"Code\"='EON_TN6'"); conn.commit(); if (affectedRows === 1) { outMsg.JEUpdateBlockReactivated = true; } } } } conn.close(); if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return result; } function getInternalReconciliation(docEntry) { outMsg = {}; outMsg.method = "getInternalReconciliation"; outMsg.status = "error"; outMsg.docEntry = docEntry; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'SELECT T0."ReconNum", T0."ReconType", T0."IsSystem" \ FROM OITR T0 \ INNER JOIN ITR1 T1 ON T0."ReconNum" = T1."ReconNum" \ WHERE T1."SrcObjTyp" = \'13\' and T1."SrcObjAbs" = ? and T0."Canceled" = \'N\'', docEntry); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { var oitr = JSON.parse(JSON.stringify(iterator.value())); switch (oitr.ReconType) { case '0': oitr.ReconTypeDescription = 'Manual'; break; case '1': oitr.ReconTypeDescription = 'Automatic'; break; case '2': oitr.ReconTypeDescription = 'Semi-Automatic'; break; case '3': oitr.ReconTypeDescription = 'Payment'; break; case '4': oitr.ReconTypeDescription = 'Credit Memo'; break; case '6': oitr.ReconTypeDescription = 'Zero Value'; break; case '7': oitr.ReconTypeDescription = 'Cancellation'; break; case '8': oitr.ReconTypeDescription = 'BoE'; break; case '9': oitr.ReconTypeDescription = 'Deposit'; break; case '10': oitr.ReconTypeDescription = 'Bank Statement Processing'; break; case '11': oitr.ReconTypeDescription = 'Period Closing'; break; case '12': oitr.ReconTypeDescription = 'Correction Invoice'; break; case '13': oitr.ReconTypeDescription = 'Inventory/Expense Allocation'; break; case '14': oitr.ReconTypeDescription = 'WIP'; break; case '15': oitr.ReconTypeDescription = 'Deferred Tax Interim Account'; break; case '16': oitr.ReconTypeDescription = 'Down Payment Allocation'; break; case '17': oitr.ReconTypeDescription = 'Auto. Conversion Difference'; break; case '18': oitr.ReconTypeDescription = 'Interim Document'; break; case '19': oitr.ReconTypeDescription = 'Withholding Tax Interim Account'; break; default: oitr.ReconTypeDescription = 'Unknown Reconciliation Type'; } resultSet.push(oitr); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; outMsg.status = "success"; outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function getInternalReconciliationCreditLines(reconNum) { outMsg = {}; outMsg.method = "getInternalReconciliationCreditLines"; outMsg.status = "error"; outMsg.reconNum = reconNum; var resultData = {}; var conn = $.hdb.getConnection(); conn.executeUpdate("SET SCHEMA \"" + schemaName + "\""); var queryResult = conn.executeQuery( 'SELECT T0."ReconNum", T0."ReconType", T0."IsSystem", T1."SrcObjTyp", T1."SrcObjAbs" \ FROM OITR T0 \ INNER JOIN ITR1 T1 ON T0."ReconNum" = T1."ReconNum" \ WHERE T0."ReconNum" = ? and T1."IsCredit" = \'C\'', reconNum); var iterator = queryResult.getIterator(); var resultSet = []; while (iterator.next()) { resultSet.push(iterator.value()); } conn.close(); resultData = { count: resultSet.length, rows: resultSet }; outMsg.status = "success"; outMsg.result = resultData; if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } return resultData; } function deleteDraft(docEntry) { logDocEntry = docEntry; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "deleteDraft"; outMsg.status = "error"; outMsg.docEntry = docEntry; var responseDelete = slib.deleteDraft(docEntry, SESSIONID, NODEID); outMsg.statuscode = responseDelete.status; if (responseDelete.status >= 200 && responseDelete.status < 300) { outMsg.status = "success"; } else { outMsg.status = "error"; myDebug.statusDelete = responseDelete.status; myDebug.responseDetele = JSON.stringify(responseDelete); //outMsg.message = responseDelete.body.asString(); //outMsg.message = JSON.parse(responseDelete.body.asString()); outStatus = "error"; } outMessages.push(outMsg); } function cancelManualReconciliation(reconNum) { var result = false; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "cancelManualReconciliation"; outMsg.status = "error"; outMsg.reconNum = reconNum; var responseCancel = slib.cancelInternalReconciliation(reconNum, SESSIONID, NODEID); outMsg.statuscode = responseCancel.status; if (responseCancel.status >= 200 && responseCancel.status < 300) { outMsg.status = "success"; result = true; } else { outMsg.status = "error"; myDebug.statusCancelReconciliation = responseCancel.status; myDebug.responseCancelReconciliation = JSON.stringify(responseCancel); if (responseCancel.body) { var responseBodyParsed = JSON.parse(responseCancel.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.message = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.message = responseCancel.body.asString(); } outMsg.response = responseBodyParsed; } else { outMsg.message = JSON.stringify(responseCancel); } outStatus = "error"; } outMessages.push(outMsg); return result; } function cancelIncomingPayment(docEntry) { var result = false; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "cancelIncomingPayment"; outMsg.status = "error"; outMsg.docEntry = docEntry; var responseCancel = slib.CancelIncomingPaymentByCurrentSystemDate(docEntry, SESSIONID, NODEID); outMsg.statuscode = responseCancel.status; if (responseCancel.status >= 200 && responseCancel.status < 300) { outMsg.status = "success"; result = true; } else { outMsg.status = "error"; myDebug.statusCancelPayment = responseCancel.status; myDebug.responseCancelPayment = JSON.stringify(responseCancel); if (responseCancel.body) { var responseBodyParsed = JSON.parse(responseCancel.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.message = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.message = responseCancel.body.asString(); } outMsg.response = responseBodyParsed; } else { outMsg.message = JSON.stringify(responseCancel); } outStatus = "error"; } outMessages.push(outMsg); return result; } function updateCancellationInvoice(docEntry, body) { if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "updateCancellationInvoice"; outMsg.status = "error"; outMsg.docEntry = docEntry; var responseUpdate = slib.updateInvoice(docEntry, JSON.stringify(body), SESSIONID, NODEID); outMsg.statuscode = responseUpdate.status; if (responseUpdate.status >= 200 && responseUpdate.status < 300) { outMsg.status = "success"; } else { outMsg.status = "error"; myDebug.statusUpdateInvoice = responseUpdate.status; myDebug.responseUpdateInvoice = JSON.stringify(responseUpdate); if (responseUpdate.body) { var responseBodyParsed = JSON.parse(responseUpdate.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.message = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.message = responseUpdate.body.asString(); } outMsg.response = responseBodyParsed; } else { outMsg.message = JSON.stringify(responseUpdate); } outStatus = "error"; } if (debugLevel > 0 || outMsg.status === "error") { outMessages.push(outMsg); } } function cancelInvoice(docEntry, docDate) { logDocEntry = docEntry; if (!slLoggedIn) { slLoggedIn = slLogin(); } outMsg = {}; outMsg.method = "cancelInvoice"; outMsg.status = "error"; outMsg.docEntry = docEntry; //outMsg.cancellationReason = cancellationReason; var slBody = { Document: { DocEntry: docEntry, DocDate: datetimelib.convertToISODate(docDate) } }; /*if (cancellationReason) { slBody.Document.U_EON_Cancellation = cancellationReason; }*/ myDebug.cancelBody = slBody; var responseCancel = slib.cancelInvoice(JSON.stringify(slBody), SESSIONID, NODEID); outMsg.statuscode = responseCancel.status; if (responseCancel.status >= 200 && responseCancel.status < 300) { outMsg.status = "success"; } else { outMsg.status = "error"; myDebug.statusCancel = responseCancel.status; myDebug.responseCancel = JSON.stringify(responseCancel); if (responseCancel.body) { var responseBodyParsed = JSON.parse(responseCancel.body.asString()); var slError = responseBodyParsed.error; if (slError && slError.hasOwnProperty("code") && slError.message && slError.message.value) { outMsg.message = slError.message.value + " (Code: " + slError.code.toString() + ")"; } else { outMsg.message = responseCancel.body.asString(); } outMsg.response = responseBodyParsed; } else { outMsg.message = JSON.stringify(responseCancel); } outStatus = "error"; } outMessages.push(outMsg); } function cancelWorkflow(inObj) { try { var cancelRequest = JSON.parse(inObj); var invoiceCrmGuid = cancelRequest.invoice_crm_guid; // check for open draft invoices and delete them var draftList = getDraft(invoiceCrmGuid); if (draftList.count > 0) { draftList.rows.forEach(function(r) { deleteDraft(r.DocEntry); }); } // check for invoice(s) var invoiceList = getInvoice(invoiceCrmGuid); if (draftList.count === 0 && invoiceList.count === 0) { // Rechnungsbeleg existiert nicht in B1 outMsg = {}; outMsg.method = "checkExist"; outMsg.status = "error"; outMsg.invoiceCrmGuid = invoiceCrmGuid; outMsg.message = "Invoice doesn't exist in SAP B1"; outMessages.push(outMsg); outStatus = "error"; } // check cancellation status of found invoice(s) var invoiceListCanceled = invoiceList.rows.filter(function(r) { return r.CANCELED !== 'N'; }); myDebug.invoiceListCanceled = invoiceListCanceled; // error, if results with cancellation status Y (canceled) or C (cancellation document) exist if (invoiceListCanceled.length > 0 && invoiceListCanceled.length === invoiceList.count) { outMsg = {}; outMsg.method = "checkCanceled"; outMsg.status = "error"; outMsg.message = "Invoice was already canceled before"; outMessages.push(outMsg); outStatus = "error"; } if (invoiceList.count > 0 && outStatus !== "error") { const { docDate, taxDate } = getDocDates(); invoiceList.rows.filter(function(r) { return r.CANCELED === 'N'; }).forEach(function(r) { var spoolCancellationInvoice = true; // controls if the cancellation invoice is relevant for output management // filter spool queue entry if unprocessed var filteredSpoolQueueInvoice = filterUnprocessedSpoolQueueEntries(r.DocEntry); if (filteredSpoolQueueInvoice.count > 0) { spoolCancellationInvoice = false; } // abort processing of spool tasks with type Mail and Print, if possible var canceledSpoolTasks = cancelSpoolTasks(r.DocEntry); if (canceledSpoolTasks.count > 0) { spoolCancellationInvoice = false; } // check if invoice contains inactive projects (if yes, the invoice must be canceled manually due to complex workaround implemented with add-on) /* var inactiveProjects = checkInactiveProjects(r.DocEntry, docDate); if (inactiveProjects.count > 0) { outStatus = "error"; } else {*/ var cancellationAllowed = true; // check for manual or automatic internal reconciliations of invoice var internalReconciliations = getInternalReconciliation(r.DocEntry); try { internalReconciliations.rows.forEach(function(oitr) { switch (oitr.ReconType) { // 13.10.2025: Kein Storno für Rechnungen mit Zahlung oder anderen internen Abstimmungen /* case '0': // Manual reconciliation -> can be canceled if (!cancelManualReconciliation(oitr.ReconNum)) { cancellationAllowed = false; }; break; case '3': // Automatic reconciliation by payment -> get payment entries from ITR1 var internalReconciliationCreditLines = getInternalReconciliationCreditLines(oitr.ReconNum); internalReconciliationCreditLines.rows.forEach(function(itr1) { if (itr1.SrcObjTyp === '24') { // Incoming Payment -> can be canceled if (!cancelIncomingPayment(itr1.SrcObjAbs)) { cancellationAllowed = false; }; } }); break; */ default: // error for other types of internal reconciliation (Zero value invoice, credit memo, ...) outMsg = {}; outMsg.method = "checkInternalReconciliation"; outMsg.status = "error"; outMsg.message = "Cannot cancel invoice due to an existing internal reconciliation: " + oitr.ReconTypeDescription; outMessages.push(outMsg); outStatus = "error"; cancellationAllowed = false; throw new Error('ExitForEachLoop'); } }); } catch (e) { if (!e.message === 'ExitForEachLoop') { throw e; } } if (cancellationAllowed && outStatus !== "error") { // if projects need to be replaced // reactivateProjects(projects) followupProjects = getFollowupProjects(r.DocEntry); var reactivateProjects = followupProjects.rows.filter(function(p) { return p.U_TEMP_VALID_TO === null; }); myDebug.reactivateProjects = reactivateProjects; projectsReactivated = reactivateOldProjects(reactivateProjects); if (outStatus !== "error") { // finally cancel the invoice cancelInvoice(r.DocEntry, docDate); // get DocEntry and DocNum of created cancellation document var cancellationInvoice = getCancellationInvoice(r.DocEntry); cancellationInvoice.rows.forEach(function(ci) { // update outMsg 'cancelInvoice' with DocEntry and DocNum var omIndex = outMessages.findIndex(function(om) { return om.method === 'cancelInvoice' && om.docEntry === r.DocEntry; }); if (omIndex > -1) { outMessages[omIndex].cancellationDocEntry = ci.DocEntry; outMessages[omIndex].cancellationDocNum = ci.DocNum; } if (spoolCancellationInvoice === false) { // no spooling of cancellation document if spooling of original invoice was aborted filterUnprocessedSpoolQueueEntries(ci.DocEntry); } if (cancelRequest.cancellation_reason || cancelRequest.cancellation_remarks) { // update cancellation invoice UDF and remarks, if provided // this subsequent update is a workaround, since the UDF cannot be changed when the cancellation document is created var updateBody = {}; if (cancelRequest.cancellation_reason) { updateBody.U_EON_Cancellation = cancelRequest.cancellation_reason; } if (cancelRequest.cancellation_remarks) { updateBody.Comments = cancelRequest.cancellation_remarks + '\n' + ci.Comments; } updateCancellationInvoice(ci.DocEntry, updateBody); } if (projectsReactivated) { var projectMapping = followupProjects.rows.map(function(fp) { return { projectOld: fp.ProjectOld, projectNew: fp.FollowupProject, profitCenterNew: fp.U_EON_PROFCTR, pspNew: fp.U_EON_PSP }; }); // replace projects in journal entry var journalEntryUpdated = updateProjectsInJE(ci.TransId, projectMapping); // deactivate old projects again projectsReverted = revertOldProjects(followupProjects.rows); } }); } } //} }); } } catch (e) { exceptionHandler(e); } finally { if (projectsReactivated && !projectsReverted) { try { revertOldProjects(followupProjects.rows); } catch (ex) { } } if (slLoggedIn) { try { slib.SLLogout(SESSIONID, NODEID); } catch (ex) { } } } } var content = $.request.body.asString(); try { outMsg = {}; outMsg.method = "logRequest"; outMsg.status = "error"; logTableId = logginglib.logCall2Table(content, "UO_DOCUMENTS_RAWCALL", objecttype, serviceName, true); outMsg.logTableId = logTableId; if (logTableId > 0) { outMsg.status = "success"; } if (debugLevel > 0) { outMessages.push(outMsg); } } catch (e) { outStatus = "error"; exceptionHandler(e); } setConnectionParams(content); if (outStatus !== "error") { cancelWorkflow(content); } updateLogEntry(logTableId, logDocEntry, objecttype); setOutputMessage();