From 1c251330038d3916b385f029d914039281f85161 Mon Sep 17 00:00:00 2001 From: Thorsten Schmitz Date: Thu, 9 Jul 2026 12:00:11 +0200 Subject: [PATCH] Correct export of Delivery Unit EON_CRM_CANCELLATION (2 files, replaces incomplete initial export) --- .../crm/cancellation/meta/grantPrivileges.sql | 2 +- .../crm/cancellation/v1/cancelinvoice.xsjs | 1219 ++++++++++++++++- 2 files changed, 1214 insertions(+), 7 deletions(-) diff --git a/uniorg/eon/crm/cancellation/meta/grantPrivileges.sql b/uniorg/eon/crm/cancellation/meta/grantPrivileges.sql index 2c60213..b8792f5 100644 --- a/uniorg/eon/crm/cancellation/meta/grantPrivileges.sql +++ b/uniorg/eon/crm/cancellation/meta/grantPrivileges.sql @@ -1,4 +1,4 @@ -GRANT SELECT, INSERT, UPDATE ON "INTEGRATION_DATA"."UO_DOCUMENTS_RAWCALL" TO MSD_CON; +GRANT SELECT, INSERT, UPDATE ON "INTEGRATION_DATA"."UO_DOCUMENTS_RAWCALL" TO MSD_CON; GRANT UPDATE, DELETE ON "A19753_EON_P01"."@UO_SPOOL_QUEUE" TO MSD_CON; GRANT UPDATE ON "A19753_EON_P01"."@UO_SPOOL_TASKS" TO MSD_CON; GRANT UPDATE ON "A19753_EON_P01"."@EON_FEATURES" TO MSD_CON; diff --git a/uniorg/eon/crm/cancellation/v1/cancelinvoice.xsjs b/uniorg/eon/crm/cancellation/v1/cancelinvoice.xsjs index cda1721..c7efbca 100644 --- a/uniorg/eon/crm/cancellation/v1/cancelinvoice.xsjs +++ b/uniorg/eon/crm/cancellation/v1/cancelinvoice.xsjs @@ -1,4 +1,4 @@ -var slib = $.import("uniorg.ServiceLayer", "B1SLLogic"); +var slib = $.import("uniorg.ServiceLayer", "B1SLLogic"); var datetimelib = $.import("uniorg.libs", "datetime"); var logginglib = $.import("uniorg.libs", "uologging"); var slDest = $.import("uniorg.destinations", "uodestlib"); @@ -11,9 +11,9 @@ var outStatus = "success"; var myDebug = {}; var companyid = 0; -var UserName = '; -var UserPass = '; -var UserComp = '; +var UserName = ''; +var UserPass = ''; +var UserComp = ''; var SESSIONID = ""; var NODEID = ""; @@ -26,5 +26,1212 @@ var serviceVersion = '1.9'; var objecttype = "13C"; var schemaName = "A19753_EON_P01"; -// Main cancelWorkflow function and supporting functions would be here -// This is a complex service for canceling invoices with project management +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(); \ No newline at end of file