var slib = $.import("uniorg.ServiceLayer", "B1SLLogic");
var slDest = $.import("uniorg.destinations", "uodestlib");
var copybplib = $.import("uniorg.eon.libs", "copybp");
var dateTimeLib = $.import("uniorg.libs", "datetime");

var ParDebug = false;
var ParRecallIdList = "";
var ParRecallMode = false;
var outMessages = [];
var outMsg = {};
var outStatus = "success";
var globalDebug = {};
var UserName = slDest.uniorg_eon_nice_v1_UserName;
var UserPass = slDest.uniorg_eon_nice_v1_UserPass;
var UserComp = slDest.uniorg_eon_nice_v1_UserComp;
var ContractUDOID = "EON_CONTR";

var series_b2c = 68;
var series_b2b = 69;
var series_b2m = 70;
var groupcode_b2c = 100;
var groupcode_b2b = 102;
var groupcode_b2m = 103;
var cmpprivate_b2c = "cPrivate";
var cmpprivate_b2b = "cCompany";
var cmpprivate_b2m = "cCompany";
var service_version = '5.9';
// 03.11.2020 U_ORDER_ID (262)
// last changes:  31.03.2020 eoperate Felder
// Mapping Verträge 19.05.2020
// Mapping evdriver undefined 12.08.2020
//17.08.2020 Parameter können nun auch ohne evdriver gesnedet werden.
//16.09.2020 Erweiterung Aktivierungsgebühr
//Mazda 6 monatige RabattGebühr
//19.01.2021 duplicatebp lib Implementierung bei BFV Verträgen
//06.10.2021 GP_DISCOUNT_MONTH
//22.11.2021 Factor für BFV Verträge in Price Kollektion implementiert
//29.11.2022 EMPEPI1-145: Erweiterung Contract SST/ Prüfung alte Projektnummern
//23.06.2023 EMPEPI1-277: Validierungsprüfung BV ohne Chargepoint Parameter / Paramater ohne valid_from
//18.09.2023 EMPEPI1-316: zeitl. Abgrenzung für AG Flag
//15.12.2023 EMPEPI1-327: Fehlerbehandlung bei leerem valid_from in prices
//05.03.2024 EMPEPI1-347: BMW: PO-No. Zeitscheibensystem
//19.07.2024 BUS-7346: Error message for contract transmission to B1
//19.07.2024 BUS-7579: BMW-CCA-Abrechnung eoperate_valid_to < eoperate_valid_from
//01.08.2024 Implementierung Rechnungsadresse
//13.12.2024 BUS-9963: Bestellnummern am E.ON-Vertrag "einfach" überschreiben
//11.02.2025 BUS-10922: RFID_CARD ohne Text1 und Text2 ablehnen
//03.06.2025 BUS-12296 New Endpoint URLs for CRM interfaces
//10.07.2025 BUS-12570: RFID_CARD ohne Text1 oder ohne Text2 oder Text1=n/a ablehnen
//18.12.2025 PFT-590: MBV - Anpassung Contract API
//08.01.2026 Anpassung MODUL -> MODULE
//28.01.2026 PFT-1233: MBV - Erweiterung von Contract-API und E.ON Vertrag um "rechnungsbegleitende Unterlagen"
//02.02.2026 PFT-1340 / PFT-590: Validierung Project/PSP und ItemCode
//05.03.2026 PFT-1565: Chargepoints ohne Text1 sollen entfernt werden

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 > 1) {
	globalDebug.requestParams = $.request.parameters;
}

/*function setOutputMessage() {

	$.response.contentType = "application/json";
	$.response.setBody(JSON.stringify(output));
	$.response.status = $.net.http.OK;
}*/

function pad(number) {
	var r = String(number);
	if (r.length === 1) {
		r = '0' + r;
	}
	return r;
}

function convertToISODate(dateString) {
	try {
		//var options = { year: 'numeric', month: '2-digit', day: '2-digit' };
		var d = new Date(dateString);
		if (!isNaN(d)) {
			//var convDate = d.toLocaleDateString('de-DE', options);
			var convDate = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
			return convDate;
		} else {
			return dateString;
		}

	} catch (err) {
		return (err);
	}
}

function getProductId(prodID) {
	/*  switch (prodID) {
        case "E.ON Drive Business ohne Grundgebühr":
        prodID='E.ON Drive Business Comfort';
            break;
            
               case "LT_E.ON Drive ElectricFleet E.ON Konzern Ladetarif":
        prodID='LT_E.ON DRIVE ELECTRICFLEET E.ON KONZERN LADETARIF';
            break;
            
               case "LT_E.ON Drive ElectricFleet Ladetarif":
        prodID='E.ON DRIVE ELECTRICFLEET LADETARIF';
            break;
            
               case "BMW Group Corporate Charging CCA Ladetarif":
        prodID='BMW GROUP CORPORATE CHARGING CCA LADETARIF';
            break;
            case "LT_BMW Group Corporate Charging MA Ladetarif":
                prodID='BMW GROUP CORPORATE CHARGING MA LADETARIF';
            break;
            
              default:
              prodID = prodID;
    }*/
	var conn = $.db.getConnection();
	var pstmt;
	var query =
		"SELECT MIN(T1.\"Code\") as \"Code\", T0.\"U_BILLING_MODE\" FROM \"XXREPXX\".\"@EON_PRODUCT\" T0 INNER JOIN \"XXREPXX\".\"@EON_PRODUCT_PARAM\" T1 ON T0.\"Code\"=T1.\"Code\" " +
		"WHERE T1.U_COMPONENT = \'CRM_PRODUCT_ID\' AND UPPER(T1.\"U_ITEM\") = UPPER('" +
		prodID + "') GROUP BY T0.\"U_BILLING_MODE\"";
	query = query.replace(/XXREPXX/g, UserComp);
	pstmt = conn.prepareStatement(query);
	//pstmt.setString(1, prodID);
	var rs = pstmt.executeQuery();
	if (!rs.next()) {
		outMsg = {};
		outMsg.method = "getProductId";
		outMsg.message = "wrong ProductID - not exists in Table Product Param. ProductID: " + prodID;
		if (debugLevel > 0) {
			outMsg.message += " / Query: " + query;
		}
		outMsg.status = "error";
		outStatus = "error";
		//	outMsg.message = e.message;
		outMessages.push(outMsg);
	} else {
		var prodList = [];
		do {
			prodList.push({
				Code: rs.getNString(1),
				BillingMode: rs.getString(2)
			});
		} while (rs.next());
		return prodList;
	}
	rs.close();
	pstmt.close();
	conn.close();
}

function getProductComponents(prodCode, component) {
	var prodComponents = [];
	var conn = $.db.getConnection();
	var pstmt;
	var query =
		"SELECT \"LineId\", \"U_COMPONENT\", \"U_DESCRIPTION\", \"U_RULE\", \"U_ITEM\", \"U_VALID_FROM\", \"U_VALID_TO\", \"U_DATA_TEXT\", \"U_DATA_NUMERIC\", \"U_DATA_TEXT2\", \"U_DATA_NUMERIC2\" FROM \"XXREPXX\".\"@EON_PRODUCT_PARAM\" WHERE \"Code\"= ? AND \"U_COMPONENT\" = ?";
	query = query.replace(/XXREPXX/g, UserComp);
	pstmt = conn.prepareStatement(query);
	pstmt.setNString(1, prodCode);
	pstmt.setNString(2, component);
	globalDebug.getProductComponents = {
		query: query,
		prodCode: prodCode,
		component: component
	};
	var rs = pstmt.executeQuery();
	while (rs.next()) {
		prodComponents.push({
			LineId: rs.getInteger(1),
			U_COMPONENT: rs.getNString(2),
			U_DESCRIPTION: rs.getNString(3),
			U_RULE: rs.getNString(4),
			U_ITEM: rs.getNString(5),
			U_VALID_FROM: rs.getDate(6),
			U_VALID_TO: rs.getDate(7),
			U_DATA_TEXT: rs.getNString(8),
			U_DATA_NUMERIC: rs.getInteger(9),
			U_DATA_TEXT2: rs.getNString(10),
			U_DATA_NUMERIC2: rs.getInteger(11)
		});
	}
	rs.close();
	pstmt.close();
	conn.close();
	return prodComponents;
}

function getCardcodebyCRMID(mycrmid) {
	var conn = $.db.getConnection();
	try {
		var pstmt;
		//var query = "select CARDCODE from TEST.OCRD WHERE U_EON_CRM_ID = " + crmid;
		var query = "SELECT T0.\"CardCode\" FROM " + UserComp + ".\"OCRD\" T0 WHERE T0.\"CardType\" = 'C' AND T0.\"U_EON_ID_CRM\" = '" + mycrmid +
			"'";
		pstmt = conn.prepareStatement(query);
		var rs = pstmt.executeQuery();
		if (!rs.next()) {
			outMsg = {};
			outMsg.method = "getCardCodebyCRMID";
			outMsg.message = "not found. CRM ID: " + mycrmid;
			if (debugLevel > 0) {
				outMsg.message += " / Query: " + query;
			}
			outMsg.status = "error";
			outStatus = "error";
			//	outMsg.message = e.message;
			outMessages.push(outMsg);
		} else {
			return rs.getString(1);
		}
		rs.close();
		pstmt.close();
	} catch (e) {
		$.trace.error(e.message);
		outMsg = {};
		outMsg.method = "getCardCodebyCRMID";
		outMsg.message = "getcardcodebycrmid error: " + query;
		outMsg.status = "error";
		//	outMsg.message = e.message;
		outMessages.push(outMsg);
		throw e;
	} finally {
		conn.close();
	}
	throw new Error("not found. CRM ID: " + mycrmid);
}

function onlyUnique(value, index, array) {
	return array.indexOf(value) === index;
}

function validateProjects(projects) {
	if (projects.findIndex(function(project) {
		return project.startsWith('80');
	}) > -1) {
		throw new Error("Old project numbers starting with '80' are not allowed anymore");
	}
	var missingProjects = [];
	var conn = $.hdb.getConnection();
	projects.forEach(function(project) {
		var rs = conn.executeQuery('SELECT "PrjCode" FROM "' + UserComp +
			'".OPRJ WHERE "PrjCode" = ?', project);
		if (rs.length === 0) {
			missingProjects.push(project);
		}
	});
	conn.close();
	if (missingProjects.length > 0) {
		throw new Error("Invalid project code: " + missingProjects.join());
	}
}

function validateItemCodes(itemCodes) {
	var missingItemCodes = [];
	var conn = $.hdb.getConnection();
	itemCodes.forEach(function(itemCode) {
		var rs = conn.executeQuery('SELECT "ItemCode" FROM "' + UserComp +
			'".OITM WHERE UPPER("ItemCode") = UPPER(?)', itemCode);
		if (rs.length === 0) {
			missingItemCodes.push(itemCode);
		}
	});
	conn.close();
	if (missingItemCodes.length > 0) {
		throw new Error("Invalid item code: " + missingItemCodes.join());
	}
}

function validatePayload(payload) {
	if (payload.hasOwnProperty("product") && payload.product.startsWith('BV_')) {
		// chargepoint compoment mandatory for BV contracts
		if (!(payload.hasOwnProperty("parameters") && payload.parameters instanceof Array && payload.parameters.some(function(p) {
			return p.component === 'CHARGEPOINT';
		}))) {
			throw new Error("Chargepoint is missing");
		}
		if (payload.parameters.some(function(p) {
			return p.component === 'CHARGEPOINT' && (p.text1 === null || p.text1 === '' || p.text1 === 'None');
		})) {
			throw new Error("Empty 'text1' is not allowed for CHARGEPOINT components");
		}
	}
	if (payload.hasOwnProperty("product") && payload.product.startsWith('MBV_')) {
		// chargepoint compoment mandatory for BV contracts
		if (!(payload.hasOwnProperty("parameters") && payload.parameters instanceof Array && payload.parameters.some(function(p) {
			return p.component === 'MODULE';
		}))) {
			throw new Error("Module is missing");
		}
	}
	if (payload.hasOwnProperty("parameters") && payload.parameters instanceof Array) {
		payload.parameters.forEach(function(param) {
			if (param.component === 'MODULE' && !(param.product_b1_articlenumber)) {
				throw new Error("Empty 'product_b1_articlenumber' is not allowed for MODULE components");
			}
			if (param.component === 'MODULE' && !(param.psp)) {
				throw new Error("Empty 'psp' is not allowed for MODULE components");
			}
		});
	}
}

function convertAdrType(adrtype) {
	switch (adrtype) {
		case '1':
		case 'S':
			return 'bo_ShipTo';
		case '2':
		case '3':
		case 'B':
			return 'bo_BillTo';
		default:
			return 'bo_BillTo';
	}
}

function splitcardname(mycardname) {
	var mycardnamenew = [];
	if (mycardname.length > 50) {
		var mycardname1 = mycardname.substring(0, 50);
		var mylastindex1 = mycardname1.lastIndexOf(' ');
		var mylastindex2 = mycardname1.lastIndexOf('-');
		var mylastindex3 = mycardname1.lastIndexOf('&');
		var mylastindex = Math.max(mylastindex1, mylastindex2, mylastindex3);
		mycardname1 = mycardname.substring(0, mylastindex);
		var mycardname2 = mycardname.substring(mylastindex + 1, 100);
		mycardnamenew.push(mycardname1);
		mycardnamenew.push(mycardname2);
	} else {
		mycardnamenew.push(mycardname);
		mycardnamenew.push("");
	}
	return mycardnamenew;
}

function lookupMimeTypes(crmAttachments) {
	var mimeTypeList = [];
	outMsg = {};
	outMsg.method = "lookup MIME Types";
	outMsg.status = 'success';
	var conn = $.hdb.getConnection();
	mimeTypeList = crmAttachments.map(function(a) {
		var rs = conn.executeQuery('SELECT T0."Code", T0."Name" FROM "' + UserComp + '"."@UO_MIME_TYPES" T0 WHERE T0."Name" = ?', a.document_mime_type);
		if (rs.length > 0) {
			return {
				mimeTypeCode: rs[0].Code,
				mimeType: rs[0].Name
			};
		} else {
			outMsg.status = "error";
			outStatus = "error";
			outMsg.message = "MIME Type '" + a.document_mime_type + "' not found";
		}
	});
	conn.close();
	if (debugLevel > 0) {
		outMsg.mimeTypeList = mimeTypeList;
	}
	if (debugLevel > 0 || outMsg.status == "error") {
		outMessages.push(outMsg);
	}
	return mimeTypeList;
}

function buildFilesObj(crmAttachments) {
	var filesObj = [];
	outMsg = {};
	outMsg.method = "build files object";
	outMsg.status = 'success';
	filesObj = crmAttachments.map(function(a) {
		return {
			contentType: a.document_mime_type,
			fileName: $.util.createUuid() + "_" + a.document_name,
			b64Content: a.document_content
		};
	});
	if (debugLevel > 0) {
		outMsg.filesObj = filesObj;
		outMessages.push(outMsg);
	}
	return filesObj;
}

function prepareAttachments(attachments, SESSIONID, NODEID) {
	var attachmentEntry = -1;
	var attachments2Lines = [];
	var returnObj = {
		attachmentEntry: attachmentEntry,
		attachments2Lines: attachments2Lines
	};
	var response;
	var slError;

	var mimeTypeList = lookupMimeTypes(attachments);
	if (outStatus !== "error") {
		// Erstellung des Attachments Objekt inkl. Hochladen der Dateien - POST /b1s/v1/Attachments2
		var filesObj = buildFilesObj(attachments);
		if (outStatus !== "error") {
			outMsg = {};
			outMsg.method = "uploadAttachments";
			outMsg.status = "error";
			response = slib.callServiceLayerFileUpload('/b1s/v1/Attachments2', $.net.http.POST, filesObj, SESSIONID, NODEID);
			outMsg.statuscode = response.status;
			var parsedResponseUpload = response.body ? JSON.parse(response.body.asString()) : {};
			if (response.status >= 200 && response.status < 300) {
				outMsg.status = "success";
				var attachmentObjResponse = parsedResponseUpload;
				// attachmentEntry = AbsoluteEntry aus Response
				if (attachmentObjResponse.AbsoluteEntry > 0) {
					attachmentEntry = attachmentObjResponse.AbsoluteEntry;
					returnObj.attachmentEntry = attachmentEntry;
					outMsg.AttachmentEntry = attachmentEntry;
				}
			} else if (response.exception) {
				outStatus = "error";
				outMsg.status = "error";
				outMsg.message = response.exception;
			} else {
				outStatus = "error";
				outMsg.status = "error";
				slError = parsedResponseUpload.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(parsedResponseUpload);
				}
			}
			if (debugLevel > 0 || outMsg.status == "error") {
				outMessages.push(outMsg);
			}
		}
		// Anreicherung der UDFs - PATCH /b1s/v1/Attachments2
		if (outStatus !== 'error') {
			outMsg = {};
			outMsg.method = "updateAttachments";
			outMsg.status = "error";
			attachments2Lines = attachments.map(function(a, idx) {
				return {
					AbsoluteEntry: attachmentEntry,
					LineNum: idx + 1,
					Override: 'tYES',
					U_EON_EDOC_REL: 'Y',
					U_EON_ORIG_FILE: a.document_name,
					U_EON_MIME_TYPE: mimeTypeList.find(function(mt) {
						return mt.mimeType === a.document_mime_type;
					}).mimeTypeCode,
					U_EON_DOC_GUID: a.document_guid,
					U_EON_TRANS_KEY: a.transmission_key
				};
			});
			returnObj.attachments2Lines = attachments2Lines;
			var attachmentPatchBody = {
				AbsoluteEntry: attachmentEntry,
				Attachments2_Lines: attachments2Lines
			};
			if (debugLevel > 0) {
				outMsg.requestBody = attachmentPatchBody;
			}
			response = slib.patchDynamicEntity('Attachments2', attachmentEntry, JSON.stringify(attachmentPatchBody), SESSIONID, NODEID, false);

			outMsg.statuscode = response.status;
			var parsedResponseUpdate = response.body ? JSON.parse(response.body.asString()) : {};
			if (response.status >= 200 && response.status < 300) {
				outMsg.status = "success";
			} else if (response.exception) {
				outStatus = "error";
				outMsg.status = "error";
				outMsg.message = response.exception;
			} else {
				outStatus = "error";
				outMsg.status = "error";
				slError = parsedResponseUpdate.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(parsedResponseUpdate);
				}
			}
			if (debugLevel > 0 || outMsg.status == "error") {
				outMessages.push(outMsg);
			}
		}
	}
	return returnObj;
}

function buildContractObj(InObj, SBOOBJ, billingMode, attachmentsObj) {
	var isAO = billingMode === 'AO';
	var isCreateMode = Object.keys(SBOOBJ).length === 0;

	var UDOObj = {};
	UDOObj.U_VALID_FROM = isAO ? InObj.implementation_date : InObj.begin_date;
	UDOObj.U_VALID_TO = InObj.end_date;
	UDOObj.U_EXT_ID_CRM = InObj.contract_id;
	UDOObj.U_EXT_ID_PREBILLING = InObj.c_id;
	UDOObj.U_PROJECT = InObj.project;
	UDOObj.U_ORDER_ID = InObj.orderid;

	var productCode = getProductId(InObj.product).find(function(p) {
		return p.BillingMode === billingMode;
	}).Code;
	UDOObj.U_PRODUCT = productCode;
	if (InObj.product_customer_name) {
		UDOObj.U_ORDER_ID = InObj.product_customer_name;
	}
	if (isAO) {
		UDOObj.U_CUSTOMER = getCardcodebyCRMID(InObj.bp_crm) + '-L';
		/*} else if (InObj.ev_drivers != undefined) {
		if (InObj.ev_drivers[0].parameters[0].component === "RFID_CARD") {
			UDOObj.U_CUSTOMER = getCardcodebyCRMID(InObj.bp_crm);
		}*/
	} else {
		UDOObj.U_CUSTOMER = getCardcodebyCRMID(InObj.bp_crm);
	}

	UDOObj.U_SIGNATURE_DATE = (InObj.signature_date === null) ? SBOOBJ.U_SIGNATURE_DATE : InObj.signature_date;

	UDOObj.U_BillToAddress2 = InObj.invoice_address && InObj.invoice_address.person ? splitcardname(InObj.invoice_address.person.company_name)[
		0] : '';
	UDOObj.U_CompanyName2B = InObj.invoice_address && InObj.invoice_address.person ? splitcardname(InObj.invoice_address.person.company_name)[
		1].substring(0, 50) : '';
	UDOObj.U_BillToBuilding = InObj.invoice_address && InObj.invoice_address.person ? InObj.invoice_address.person.street_addon : '';
	UDOObj.U_BillToAddress3 = InObj.invoice_address && InObj.invoice_address.person ? InObj.invoice_address.person.salutation + ' ' + InObj.invoice_address
		.person.name : '';
	UDOObj.U_BillToStreet = InObj.invoice_address && InObj.invoice_address.address ? InObj.invoice_address.address.street_name : '';
	UDOObj.U_BillToStreetNo = InObj.invoice_address && InObj.invoice_address.address ? InObj.invoice_address.address.housenumber : '';
	UDOObj.U_BillToZipCode = InObj.invoice_address && InObj.invoice_address.address ? InObj.invoice_address.address.postalcode : '';
	UDOObj.U_BillToCity = InObj.invoice_address && InObj.invoice_address.address ? InObj.invoice_address.address.city : '';
	UDOObj.U_BillToCountry = InObj.invoice_address && InObj.invoice_address.address ? InObj.invoice_address.address.country : '';

	// get special product components
	var prodComponentsAG = getProductComponents(productCode, "AG").filter(function(comp) {
		var beginDate = dateTimeLib.convertToISODate(InObj.begin_date);
		return comp.U_VALID_FROM === null || dateTimeLib.convertToISODate(comp.U_VALID_FROM) <= beginDate && comp.U_VALID_TO === null ||
			dateTimeLib.convertToISODate(comp.U_VALID_TO) >= beginDate;
	});
	var prodComponentsGP_DISCOUNT_MONTH = getProductComponents(productCode, "GP_DISCOUNT_MONTH");
	var prodComponentsTP_DISCOUNT_MONTH = getProductComponents(productCode, "TP_DISCOUNT_MONTH");
	var prodComponentsBFV_BILLING = getProductComponents(productCode, "BFV_BILLING");
	var prodComponentsINITIAL_BILLING_BLOCK = getProductComponents(productCode, "INITIAL_BILLING_BLOCK");

	prodComponentsINITIAL_BILLING_BLOCK.forEach(function(pc) {
		if (pc.U_RULE === "Flag" && pc.U_DATA_TEXT && pc.U_DATA_TEXT.length <= 2 && isCreateMode) {
			UDOObj.U_BILLING_BLOCK = pc.U_DATA_TEXT;
		}
	});

	// Price Collection

	UDOObj.EON_CONTRACT_PRICECollection = [];

	for (var g in SBOOBJ.EON_CONTRACT_PRICECollection) {
		var myPrice = {};
		// copy existing
		myPrice = SBOOBJ.EON_CONTRACT_PRICECollection[g];

		for (var h in InObj.prices) {

			if (SBOOBJ.EON_CONTRACT_PRICECollection[g].U_ITEM === InObj.prices[h].item) {
				if (!InObj.prices[h].valid_from) {
					throw new Error("Prices: valid_from is missing");
				}
				myPrice.U_VALID_FROM = (InObj.prices[h].valid_from === null) ? SBOOBJ.EON_CONTRACT_PRICECollection[g].U_VALID_FROM : InObj.prices[h].valid_from;
				myPrice.U_VALID_TO = (InObj.prices[h].valid_to === null) ? SBOOBJ.EON_CONTRACT_PRICECollection[g].U_VALID_TO : InObj.prices[h].valid_to;
				myPrice.U_PRICE = (InObj.prices[h].price === null) ? SBOOBJ.EON_CONTRACT_PRICECollection[g].U_PRICE : InObj.prices[h].price;
				myPrice.U_FACTOR = (InObj.prices[h].price === null) ? SBOOBJ.EON_CONTRACT_PRICECollection[g].U_FACTOR : InObj.prices[h].factor;
				break;
			}
		}
		UDOObj.EON_CONTRACT_PRICECollection.push(myPrice);

	}

	// Sonderpreise für Rabattmonate
	prodComponentsGP_DISCOUNT_MONTH.concat(prodComponentsTP_DISCOUNT_MONTH).forEach(function(pc) {
		if (SBOOBJ.EON_CONTRACT_PRICECollection === undefined || SBOOBJ.EON_CONTRACT_PRICECollection.findIndex(function(x) {
			return x.U_ITEM === pc.U_ITEM;
		}) === -1) {
			var connection = $.hdb.getConnection();
			var validToDate = connection.executeQuery('SELECT ADD_MONTHS(?,?) FROM DUMMY', InObj.begin_date, pc.U_DATA_NUMERIC)[0][0];
			connection.close();
			UDOObj.EON_CONTRACT_PRICECollection.push({
				U_ITEM: pc.U_ITEM,
				U_VALID_FROM: InObj.begin_date,
				U_VALID_TO: validToDate,
				U_PRICE: parseFloat(pc.U_DATA_TEXT2.replace(/,/g, '.'))
			});
		}
	});

	// Sonderpreise für kostenfreie Monate BFV BFV_BILLING
	if (InObj.costfree_months && InObj.costfree_months > 0) {
		prodComponentsBFV_BILLING.forEach(function(pc) {
			var connection = $.hdb.getConnection();
			var validToDate = connection.executeQuery('SELECT ADD_MONTHS_LAST(LAST_DAY(ADD_DAYS(?, -1)), ?) FROM DUMMY', InObj.begin_date, InObj.costfree_months)[
				0][0];
			connection.close();
			// update existing special price entry, if existing
			UDOObj.EON_CONTRACT_PRICECollection.filter(function(x) {
				return Boolean(x.U_ITEM) && x.U_ITEM === pc.U_ITEM && x.U_PRICE === 0.0;
			}).forEach(function(pcUpdate) {
				pcUpdate.U_VALID_FROM = InObj.begin_date;
				pcUpdate.U_VALID_TO = dateTimeLib.convertToISODate(validToDate);
			});
			// add new special price entry
			if (UDOObj.EON_CONTRACT_PRICECollection.findIndex(function(x) {
				return x.U_ITEM === pc.U_ITEM;
			}) === -1) {
				UDOObj.EON_CONTRACT_PRICECollection.push({
					U_ITEM: pc.U_ITEM,
					U_VALID_FROM: InObj.begin_date,
					U_VALID_TO: dateTimeLib.convertToISODate(validToDate),
					U_PRICE: 0.0
				});
			}
		});
	}
	// bestehenden Sonderpreis entfernen, falls kostenfreie Monate auf 0 geändert werden
	if (InObj.hasOwnProperty('costfree_months') && InObj.costfree_months === 0 && UDOObj.EON_CONTRACT_PRICECollection.length > 0) {
		prodComponentsBFV_BILLING.forEach(function(pc) {
			for (var x = UDOObj.EON_CONTRACT_PRICECollection.length - 1; x >= 0; x--) {
				if (Boolean(UDOObj.EON_CONTRACT_PRICECollection[x].U_ITEM) && UDOObj.EON_CONTRACT_PRICECollection[x].U_ITEM === pc.U_ITEM && UDOObj.EON_CONTRACT_PRICECollection[
					x].U_PRICE === 0.0) {
					UDOObj.EON_CONTRACT_PRICECollection.splice(x, 1);
				}
			}
		});
	}

	//Suche ob Objekte vorhanden wenn nicht ergänzen

	for (var h in InObj.prices) {
		var myfound = false;
		for (var g in SBOOBJ.EON_CONTRACT_PRICECollection) {
			if (SBOOBJ.EON_CONTRACT_PRICECollection[g].U_ITEM === InObj.prices[h].item) {
				myfound = true;
			}
		}

		if (myfound === false) {
			var myPrice = {};

			if (!InObj.prices[h].valid_from) {
				throw new Error("Prices: valid_from is missing");
			}

			myPrice.U_VALID_FROM = InObj.prices[h].valid_from;
			myPrice.U_VALID_TO = InObj.prices[h].valid_to;
			myPrice.U_PRICE = InObj.prices[h].price;
			myPrice.U_ITEM = InObj.prices[h].item;
			myPrice.U_FACTOR = InObj.prices[h].factor;

			UDOObj.EON_CONTRACT_PRICECollection.push(myPrice);
		}
	}

	//EV Drivers neu 4.2.2019
	// EVDRIVERS Collection

	UDOObj.EON_CONTRACT_EVDRVCollection = [];

	for (var g in SBOOBJ.EON_CONTRACT_EVDRVCollection) {
		var myEVDRV = {};
		// copy existing			
		myEVDRV = SBOOBJ.EON_CONTRACT_EVDRVCollection[g];

		for (var h in InObj.ev_drivers) {

			if (myEVDRV.U_EV_DRIVERID === InObj.ev_drivers[h].ev_driverid) {
				//	UDOObj.U_EXT_ID_PREBILLING = InObj.ev_drivers[h].c_id;
				myEVDRV.U_EV_DRIVER = InObj.ev_drivers[h].ev_driver;
				myEVDRV.U_NICE_ID = InObj.ev_drivers[h].c_id;
				myEVDRV.U_FIRSTNAME = (InObj.ev_drivers[h].firstname === null) ? myEVDRV.U_FIRSTNAME : InObj.ev_drivers[h].firstname;
				myEVDRV.U_LASTNAME = (InObj.ev_drivers[h].lastname === null) ? myEVDRV.U_LASTNAME : InObj.ev_drivers[h].lastname;
				//				myParam.U_EMAIL = (InObj.ev_drivers[h].email === null) ? myEVDRV.U_EMAIL : InObj.ev_drivers[h].email;
				myEVDRV.U_LICENSE_PLATE = (InObj.ev_drivers[h].license_plate === null) ? myEVDRV.U_LICENSE_PLATE : InObj.ev_drivers[h].license_plate;
				myEVDRV.U_VIRTA_ID = (InObj.ev_drivers[h].virta_id === null) ? myEVDRV.U_VIRTA_ID : InObj.ev_drivers[h].virta_id;
				myEVDRV.U_VIRTA_VALID_FROM = (InObj.ev_drivers[h].virta_valid_from === null) ? myEVDRV.U_VIRTA_VALID_FROM : InObj.ev_drivers[h].virta_valid_from;
				myEVDRV.U_VIRTA_VALID_TO = (InObj.ev_drivers[h].virta_valid_to === null) ? myEVDRV.U_VIRTA_VALID_TO : InObj.ev_drivers[h].virta_valid_to;

				myEVDRV.U_NICE_VALID_FROM = (InObj.ev_drivers[h].nice_valid_from === null) ? myEVDRV.U_NICE_VALID_FROM : InObj.ev_drivers[h].nice_valid_from;
				myEVDRV.U_NICE_VALID_TO = (InObj.ev_drivers[h].nice_valid_to === null) ? myEVDRV.U_NICE_VALID_TO : InObj.ev_drivers[h].nice_valid_to;
				//neu 31.03.2020 eoperate Felder
				myEVDRV.U_EOPERATE_ID = (InObj.ev_drivers[h].eoperate_ID === null) ? myEVDRV.U_EOPERATE_ID : InObj.ev_drivers[h].eoperate_ID;
				myEVDRV.U_EOPERATE_VALID_FROM = (InObj.ev_drivers[h].eoperate_valid_from === null) ? myEVDRV.U_EOPERATE_VALID_FROM : InObj.ev_drivers[
					h]
					.eoperate_valid_from;
				myEVDRV.U_EOPERATE_VALID_TO = (InObj.ev_drivers[h].eoperate_valid_to === null) ? myEVDRV.U_EOPERATE_VALID_TO :
					InObj.ev_drivers[h].eoperate_valid_to >= InObj.ev_drivers[h].eoperate_valid_from ? InObj.ev_drivers[h].eoperate_valid_to : null;

				break;
			}
		}

		UDOObj.EON_CONTRACT_EVDRVCollection.push(myEVDRV);

	}

	//Suche ob Objekte vorhanden wenn nicht ergänzen

	for (var h in InObj.ev_drivers) {
		var myfound = false;
		for (var g in SBOOBJ.EON_CONTRACT_EVDRVCollection) {
			if (SBOOBJ.EON_CONTRACT_EVDRVCollection[g].U_EV_DRIVERID === InObj.ev_drivers[h].ev_driverid) {
				myfound = true;
			}
		}

		if (myfound === false) {
			var myEVDRV = {};
			//	UDOObj.U_EXT_ID_PREBILLING = InObj.ev_drivers[h].c_id;
			myEVDRV.U_EV_DRIVER = InObj.ev_drivers[h].ev_driver;
			myEVDRV.U_NICE_ID = InObj.ev_drivers[h].c_id;
			myEVDRV.U_EV_DRIVERID = InObj.ev_drivers[h].ev_driverid;
			myEVDRV.U_FIRSTNAME = InObj.ev_drivers[h].firstname;
			myEVDRV.U_LASTNAME = InObj.ev_drivers[h].lastname;
			//	myParam.U_EMAIL =  InObj.ev_drivers[h].email;
			myEVDRV.U_LICENSE_PLATE = InObj.ev_drivers[h].license_plate;
			myEVDRV.U_VIRTA_ID = InObj.ev_drivers[h].virta_id;
			myEVDRV.U_VIRTA_VALID_TO = InObj.ev_drivers[h].virta_valid_to;
			myEVDRV.U_VIRTA_VALID_FROM = InObj.ev_drivers[h].virta_valid_from;
			myEVDRV.U_NICE_VALID_FROM = InObj.ev_drivers[h].nice_valid_from;
			myEVDRV.U_NICE_VALID_TO = InObj.ev_drivers[h].nice_valid_to;
			//neu 31.03.2020 eoperate Felder
			myEVDRV.U_EOPERATE_ID = InObj.ev_drivers[h].eoperate_ID;
			myEVDRV.U_EOPERATE_VALID_FROM = InObj.ev_drivers[h].eoperate_valid_from;
			myEVDRV.U_EOPERATE_VALID_TO = InObj.ev_drivers[h].eoperate_valid_to >= InObj.ev_drivers[h].eoperate_valid_from ? InObj.ev_drivers[h].eoperate_valid_to :
				null;

			UDOObj.EON_CONTRACT_EVDRVCollection.push(myEVDRV);
		}

	}

	// Param Collection

	UDOObj.EON_CONTRACT_PARAMCollection = [];

	//LOOP durch existierende Parameter
	loopParamsB1: for (var i in SBOOBJ.EON_CONTRACT_PARAMCollection) {
		var myParam2 = {};
		// copy existing			
		myParam2 = SBOOBJ.EON_CONTRACT_PARAMCollection[i];
		// Abgleich mit Parameters aus ev_drivers.parameters
		loopEVDriversInObj: for (var h in InObj.ev_drivers) {
			loopParamsInObj: for (var j in InObj.ev_drivers[h].parameters) {
				if (!(myParam2.U_COMPONENT === 'MODULE') && myParam2.U_DATA_TEXT === InObj.ev_drivers[h].parameters[j].text1) {
					//myParam.DocEntry=SBOOBJ.EON_CONTRACT_PARAMCollection[g].DocEntry;
					//myParam.LineId=SBOOBJ.EON_CONTRACT_PARAMCollection[g].LineId;
					//myParam.VisOrder=SBOOBJ.EON_CONTRACT_PARAMCollection[g].VisOrder;
					//myParam.Object=SBOOBJ.EON_CONTRACT_PARAMCollection[g].Object;
					myParam2.U_COMPONENT = (InObj.ev_drivers[h].parameters[j].component === null) ? myParam2.U_COMPONENT :
						InObj.ev_drivers[h].parameters[j].component;
					//myParam.U_DESCRIPTION=SBOOBJ.EON_CONTRACT_PARAMCollection[g].U_DESCRIPTION;
					//myParam.U_RULE=SBOOBJ.EON_CONTRACT_PARAMCollection[g].U_RULE;
					myParam2.U_DATA_TEXT = (InObj.ev_drivers[h].parameters[j].text1 === null) ? myParam2.U_DATA_TEXT :
						InObj.ev_drivers[h].parameters[j].text1;
					myParam2.U_DATA_NUMERIC = (InObj.ev_drivers[h].parameters[j].numeric1 === null) ? myParam2.U_DATA_NUMERIC :
						InObj.ev_drivers[h].parameters[j].numeric1;
					myParam2.U_DATA_TEXT2 = (InObj.ev_drivers[h].parameters[j].text2 === null) ? myParam2.U_DATA_TEXT2 :
						InObj.ev_drivers[h].parameters[j].text2;
					myParam2.U_DATE_NUMERIC2 = (InObj.ev_drivers[h].parameters[j].numeric2 === null) ? myParam2.U_DATE_NUMERIC2 :
						InObj.ev_drivers[h].parameters[j].numeric2;
					myParam2.U_VALID_FROM = (InObj.ev_drivers[h].parameters[j].valid_from === null) ? myParam2.U_VALID_FROM :
						InObj.ev_drivers[h].parameters[j].valid_from;
					myParam2.U_VALID_TO = (InObj.ev_drivers[h].parameters[j].valid_to === null) ? myParam2.U_VALID_TO :
						InObj.ev_drivers[h].parameters[j].valid_to;
					//myParam.U_ITEM=SBOOBJ.EON_CONTRACT_PARAMCollection[g].U_ITEM;
					myParam2.U_DATA_TEXT3 = (InObj.ev_drivers[h].ev_driverid === null) ? myParam2.U_DATA_TEXT3 : InObj.ev_drivers[h].ev_driverid;
					break loopEVDriversInObj;
				}
			}
		}
		// Abgleich mit Parametern aus Kopfebene
		for (var j in InObj.parameters) {
			if (!(myParam2.U_COMPONENT === 'MODULE') && myParam2.U_DATA_TEXT === InObj.parameters[j].text1 || myParam2.U_COMPONENT === 'MODULE' &&
				myParam2.U_CONTRACT_ITEM_UID === InObj.parameters[j].contract_item_uid) {
				myParam2.U_COMPONENT = (InObj.parameters[j].component === null) ? myParam2.U_COMPONENT :
					InObj.parameters[j].component;
				myParam2.U_RULE = "data";
				//myParam.U_DESCRIPTION=SBOOBJ.EON_CONTRACT_PARAMCollection[g].U_DESCRIPTION;
				//myParam.U_RULE=SBOOBJ.EON_CONTRACT_PARAMCollection[g].U_RULE;
				myParam2.U_DATA_TEXT = (InObj.parameters[j].text1 === null) ? myParam2.U_DATA_TEXT :
					InObj.parameters[j].text1;
				myParam2.U_DATA_NUMERIC = (InObj.parameters[j].numeric1 === null) ? myParam2.U_DATA_NUMERIC :
					InObj.parameters[j].numeric1;
				myParam2.U_DATA_TEXT2 = (InObj.parameters[j].text2 === null) ? myParam2.U_DATA_TEXT2 :
					InObj.parameters[j].text2;
				myParam2.U_DATA_NUMERIC2 = (InObj.parameters[j].numeric2 === null) ? myParam2.U_DATA_NUMERIC2 :
					InObj.parameters[j].numeric2;
				myParam2.U_VALID_FROM = (InObj.parameters[j].valid_from === null) ? myParam2.U_VALID_FROM :
					InObj.parameters[j].valid_from;
				myParam2.U_VALID_TO = (InObj.parameters[j].valid_to === null) ? myParam2.U_VALID_TO :
					InObj.parameters[j].valid_to;
				if (InObj.parameters[j].valid_to && !InObj.parameters[j].valid_from) {
					throw new Error("Parameter with valid_to but without valid_from is not allowed");
				}
				myParam2.U_DATA_PRICE1 = (InObj.parameters[j].price1 === null) ? myParam2.U_DATA_PRICE1 :
					InObj.parameters[j].price1;
				//myParam.U_ITEM=SBOOBJ.EON_CONTRACT_PARAMCollection[g].U_ITEM;
				myParam2.U_DATA_TEXT3 = (InObj.parameters[j].text3 === null) ? myParam2.U_DATA_TEXT3 :
					InObj.parameters[j].text3;
				myParam2.U_ITEM = (InObj.parameters[j].product_b1_articlenumber === null) ? myParam2.U_ITEM :
					InObj.parameters[j].product_b1_articlenumber;
				myParam2.U_PROJECT = (InObj.parameters[j].psp === null) ? myParam2.U_PROJECT :
					InObj.parameters[j].psp;
				myParam2.U_CONTRACT_ITEM_UID = (InObj.parameters[j].contract_item_uid === null) ? myParam2.U_CONTRACT_ITEM_UID :
					InObj.parameters[j].contract_item_uid;
			}
		}
		
		if ((!(myParam2.U_COMPONENT === 'MODULE') || InObj.parameters.findIndex(function(p) {
			return myParam2.U_COMPONENT === 'MODULE' && p.component === 'MODULE' && p.contract_item_uid === myParam2.U_CONTRACT_ITEM_UID;
		}) > -1) && !(myParam2.U_COMPONENT === 'CHARGEPOINT' && (myParam2.U_DATA_TEXT === null || myParam2.U_DATA_TEXT === '' || myParam2.U_DATA_TEXT === 'None'))) {
			// bestehende Vertragsparameter wieder übernehmen, außer es handelt sich um ein MODUL, welches nicht mehr aus dem CRM übermittelt wird
			// auch bestehende Chargepoints ohne Text1 sollen entfernt werden
			UDOObj.EON_CONTRACT_PARAMCollection.push(myParam2);
		}
	}

	// neue Aktivierungsgebühr hinzufügen
	if (prodComponentsAG.length > 0 && (SBOOBJ.EON_CONTRACT_PARAMCollection === undefined || SBOOBJ.EON_CONTRACT_PARAMCollection.findIndex(
		function(x) {
			return x.U_COMPONENT === "AG";
		}) === -1)) {
		UDOObj.EON_CONTRACT_PARAMCollection.push({
			U_COMPONENT: prodComponentsAG[0].U_COMPONENT,
			U_DESCRIPTION: prodComponentsAG[0].U_DESCRIPTION,
			U_RULE: "const1",
			U_ITEM: prodComponentsAG[0].U_ITEM,
			U_VALID_FROM: InObj.begin_date,
			U_VALID_TO: InObj.begin_date
		});
	}

	// neue Rabattmonate hinzufügen
	prodComponentsTP_DISCOUNT_MONTH.forEach(function(pc) {
		if (SBOOBJ.EON_CONTRACT_PARAMCollection === undefined || SBOOBJ.EON_CONTRACT_PARAMCollection.findIndex(function(x) {
			return x.U_COMPONENT === pc.U_DATA_TEXT;
		}) === -1) {
			var connection = $.hdb.getConnection();
			var validToDate = connection.executeQuery('SELECT ADD_MONTHS(?,?) FROM DUMMY', InObj.begin_date, pc.U_DATA_NUMERIC)[0][0];
			connection.close();
			UDOObj.EON_CONTRACT_PARAMCollection.push({
				U_COMPONENT: pc.U_DATA_TEXT,
				U_DESCRIPTION: pc.U_DESCRIPTION,
				U_RULE: "QxP",
				U_ITEM: pc.U_ITEM,
				U_VALID_FROM: InObj.begin_date,
				U_VALID_TO: validToDate
			});
		}
	});

	// neue Grundpreis Rabattmonate hinzufügen
	prodComponentsGP_DISCOUNT_MONTH.forEach(function(pc) {
		if (SBOOBJ.EON_CONTRACT_PARAMCollection === undefined || SBOOBJ.EON_CONTRACT_PARAMCollection.findIndex(function(x) {
			return x.U_COMPONENT === pc.U_DATA_TEXT;
		}) === -1) {
			var connection = $.hdb.getConnection();
			var validToDate = connection.executeQuery('SELECT ADD_MONTHS(?,?) FROM DUMMY', InObj.begin_date, pc.U_DATA_NUMERIC)[0][0];
			connection.close();
			UDOObj.EON_CONTRACT_PARAMCollection.push({
				U_COMPONENT: pc.U_DATA_TEXT,
				U_DESCRIPTION: pc.U_DESCRIPTION,
				U_RULE: "const",
				U_ITEM: pc.U_ITEM,
				U_VALID_FROM: InObj.begin_date,
				U_VALID_TO: validToDate
			});
		}
	});

	// neue Parameter aus ev_drivers.parameters hinzufügen
	for (var h in InObj.ev_drivers) {
		for (var k in InObj.ev_drivers[h].parameters) {
			var myfound2 = false;
			for (var l in SBOOBJ.EON_CONTRACT_PARAMCollection) {
				if (!(InObj.ev_drivers[h].parameters[k].component === 'MODULE') && SBOOBJ.EON_CONTRACT_PARAMCollection[l].U_DATA_TEXT === InObj.ev_drivers[
					h].parameters[k].text1) {
					myfound2 = true;
				}
			}

			if (myfound2 === false) {
				var myParam3 = {};

				if (InObj.ev_drivers[h].parameters[k].component === 'RFID_CARD' && (!InObj.ev_drivers[h].parameters[k].text1 || InObj.ev_drivers[h].parameters[
					k].text1 === 'n/a' || !InObj.ev_drivers[h].parameters[k].text2)) {
					throw new Error("Parameter RFID_CARD with invalid text1 or text2 is not allowed");
				}
				myParam3.U_COMPONENT = InObj.ev_drivers[h].parameters[k].component;
				//myParam.U_DESCRIPTION="";
				myParam3.U_RULE = "data";
				myParam3.U_DATA_TEXT = InObj.ev_drivers[h].parameters[k].text1;
				myParam3.U_DATA_NUMERIC = InObj.ev_drivers[h].parameters[k].numeric1;
				myParam3.U_DATA_TEXT2 = InObj.ev_drivers[h].parameters[k].text2;
				myParam3.U_DATE_NUMERIC2 = InObj.ev_drivers[h].parameters[k].numeric2;
				myParam3.U_VALID_FROM = InObj.ev_drivers[h].parameters[k].valid_from;
				myParam3.U_VALID_TO = InObj.ev_drivers[h].parameters[k].valid_to;
				myParam3.U_DATA_TEXT3 = InObj.ev_drivers[h].ev_driverid;
				//myParam.U_ITEM="";

				UDOObj.EON_CONTRACT_PARAMCollection.push(myParam3);
			}
		}
	}
	// neue Parameter aus Kopfebene hinzufügen
	for (var k in InObj.parameters) {
		var myfound3 = false;
		for (var l in SBOOBJ.EON_CONTRACT_PARAMCollection) {
			if (!(InObj.parameters[k].component === 'MODULE') && SBOOBJ.EON_CONTRACT_PARAMCollection[l].U_DATA_TEXT === InObj.parameters[k].text1 ||
				InObj.parameters[k].component === 'MODULE' && SBOOBJ.EON_CONTRACT_PARAMCollection[l].U_CONTRACT_ITEM_UID === InObj.parameters[k].contract_item_uid
			) {
				myfound3 = true;
			}
		}
		if (myfound3 === false) {
			var myParam4 = {};
			if (InObj.parameters[k].component === 'RFID_CARD' && (!InObj.parameters[k].text1 || InObj.parameters[k].text1 === 'n/a' || !InObj.parameters[
				k].text2)) {
				throw new Error("Parameter RFID_CARD with invalid text1 or text2 is not allowed");
			}
			myParam4.U_COMPONENT = InObj.parameters[k].component;
			//myParam.U_DESCRIPTION="";
			myParam4.U_RULE = "data";
			myParam4.U_DATA_TEXT = InObj.parameters[k].text1;
			myParam4.U_DATA_NUMERIC = InObj.parameters[k].numeric1;
			myParam4.U_DATA_TEXT2 = InObj.parameters[k].text2;
			myParam4.U_DATE_NUMERIC2 = InObj.parameters[k].numeric2;
			myParam4.U_VALID_FROM = InObj.parameters[k].valid_from;
			myParam4.U_VALID_TO = InObj.parameters[k].valid_to;
			if (InObj.parameters[k].valid_to && !InObj.parameters[k].valid_from) {
				throw new Error("Parameter with valid_to but without valid_from is not allowed");
			}
			myParam4.U_DATA_PRICE1 = InObj.parameters[k].price1;
			myParam4.U_DATA_TEXT3 = InObj.parameters[k].text3;
			myParam4.U_ITEM = InObj.parameters[k].product_b1_articlenumber;
			myParam4.U_PROJECT = InObj.parameters[k].psp;
			myParam4.U_CONTRACT_ITEM_UID = InObj.parameters[k].contract_item_uid;
			UDOObj.EON_CONTRACT_PARAMCollection.push(myParam4);
		}
	}

	// PO Numbers
	var POCollection = [];
	if (InObj.po_numbers) {
		if (SBOOBJ.EON_CONTRACT_POCollection) {
			// update existing PO numbers, but discard old entries that are not included in new request
			POCollection = SBOOBJ.EON_CONTRACT_POCollection.filter(function(sboPO) {
				return InObj.po_numbers.some(function(inPO) {
					return inPO.id === sboPO.U_ID;
				});
			}).map(function(po) {
				var matchedPO = InObj.po_numbers.find(function(inPO) {
					return inPO.id === po.U_ID;
				});
				return {
					DocEntry: po.DocEntry,
					LineId: po.LineId,
					U_ID: po.U_ID,
					U_PO_NUMBER: matchedPO.po_number,
					U_VALID_FROM: matchedPO.valid_from,
					U_VALID_TO: matchedPO.valid_to
				};
			});
		}
		// add new PO Numbers
		var newPOLines = InObj.po_numbers.filter(function(inPO) {
			return isCreateMode || !SBOOBJ.EON_CONTRACT_POCollection.some(function(po) {
				return inPO.id === po.U_ID;
			});
		}).map(function(newPO) {
			return {
				U_ID: newPO.id,
				U_PO_NUMBER: newPO.po_number,
				U_VALID_FROM: newPO.valid_from,
				U_VALID_TO: newPO.valid_to
			};
		});
		POCollection = POCollection.concat(newPOLines);
	}
	UDOObj.EON_CONTRACT_POCollection = POCollection;

	if (attachmentsObj.attachments2Lines && attachmentsObj.attachments2Lines.length > 0) {
		var ATTCollection = [];
		ATTCollection = attachmentsObj.attachments2Lines.map(function(newATT) {
			return {
				U_ATC_ENTY: newATT.AbsoluteEntry,
				U_VALID_FROM: InObj.begin_date,
				U_VALID_TO: '2099-12-31',
				U_ATC_LINE: newATT.LineNum,
				// U_TRGTPATH: entweder aus OADM lesen oder Attachments2 Objekt komplett laden
				// U_FILENAME: entweder aus fileObj durchschleifen oder Attachments2 Objekt komplett laden
				U_FILEEXT: newATT.U_EON_MIME_TYPE, // sollte identisch sein
				U_EON_EDOC_REL: newATT.U_EON_EDOC_REL,
				U_EON_ORIG_FILE: newATT.U_EON_ORIG_FILE,
				U_EON_MIME_TYPE: newATT.U_EON_MIME_TYPE,
				U_EON_DOC_GUID: newATT.U_EON_DOC_GUID,
				U_EON_TRANS_KEY: newATT.U_EON_TRANS_KEY
			};
		});
		UDOObj.EON_CONTRACT_ATTCollection = ATTCollection;
	}

	return UDOObj;
}

function mockbuildBPObj() {
	var bpObj = {};
	bpObj.Series = series_b2b;
	bpObj.CompanyPrivate = "I"; // JbpObj.type.toString();
	bpObj.CardName = 'Burgmueller';
	//bpObj.CardType = 'C';
	//bpObj.GroupCode = 100; // JbpObj.group.toString();
	//bpObj.Phone1 = JbpObj.phone.toString();
	//bpObj.Cellular = JbpObj.mobile_phone.toString();
	//bpObj.EmailAddress = JbpObj.email.toString();
	bpObj.U_EON_ID_CRM = 'BP777888999';
	bpObj.U_EON_ASSU = 'N';
	return bpObj;
}

function upsContract(searchparam, InObj, billingMode) {

	var output = {};
	var myDebug = {
		billingMode: billingMode,
		searchparam: searchparam
	};
	var SESSIONID = "";
	var NODEID = "";

	// SL credentials
	var loginInfo = {};
	loginInfo.UserName = UserName;
	loginInfo.Password = UserPass;
	loginInfo.CompanyDB = UserComp;

	var slError;

	//getProductId(InObj.product);
	if (outStatus !== "error") {
		// 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;
			}
		}
		if (ParDebug) {
			//output.loginInfo = loginInfo;
		}
		//	output.loginResponse = JSON.parse(response.body.asString());
		var loginResponse;
		try {
			loginResponse = JSON.parse(response.body.asString());
		} catch (e) {
			loginResponse = response.body.asString();
		}
		output.loginResponse = loginResponse;
		if (response.status !== 200) {
			outMsg = {};
			outMsg.method = "login";
			outMsg.status = "error";
			//outMsg.message = output.loginResponse;
			slError = 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(loginResponse);
			}
			outMsg.response = loginResponse;
			outMessages.push(outMsg);
			outStatus = "error";
		}

		if (outStatus !== "error") {

			outMsg = {};
			if (ParRecallMode) {
				outMsg.id = globalDebug.mynextval;
			}
			outMsg.method = "check " + ContractUDOID + " Exists";
			//Add BP
			//response = slib.createBusinessPartner(BPObject, SESSIONID,NODEID);
			//var responseGet = slib.getBusinessPartner(searchparam, SESSIONID, NODEID);
			var responseGet = slib.getDocUDO(ContractUDOID, searchparam, SESSIONID, NODEID);
			//var logoutresponse = slib.SLLogout(JSON.stringify(loginInfo), null, null);
			//Parse response body

			// Removes metadata
			//delete body["odata.metadata"];

			var DocEntry = 0;
			var bodyGetFull = {};

			var bodyGet;
			try {
				bodyGet = JSON.parse(responseGet.body.asString());
			} catch (e) {
				bodyGet = response.responseGet.asString();
			}
			myDebug.statusGet = responseGet.status;
			myDebug.responseGet = bodyGet;

			outMsg.statuscode = responseGet.status;
			if (responseGet.status >= 200 && responseGet.status < 300) {
				outMsg.status = "success";
				if (bodyGet.value[0]) {
					DocEntry = bodyGet.value[0].DocEntry;
					bodyGetFull = bodyGet.value[0];
					outMsg.message = "exists";
				} else {
					outMsg.message = "not exists";
				}
			} else if (responseGet.status === 404) {
				outMsg.status = "success";
				outMsg.message = "not exists";
			} else {
				outMsg.status = "error";
				//outMsg.message = bodyGet;
				slError = bodyGet.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(bodyGet);
				}
				outMsg.response = bodyGet;
				outStatus = "error";
			}
			outMessages.push(outMsg);

			/*		if (DocEntry > 0) {
			outMsg = {};
			outMsg.method = "get";
			outMsg.status = "success";
			outMsg.message = bodyGetFull;		
			outMessages.push(outMsg);
		}*/

			/*	var Code = "";
		var bodyGetFull = {};
		if (bodyGet.value[0]) {
			//Code = bodyGet.value[0].CardCode;

			outMsg = {};
			outMsg.method = "get";

			var responseGetFull = slib.getBusinessPartnerFull(CardCode, SESSIONID, NODEID);
			myDebug.statusGetFull = responseGetFull.status;
		//	bodyGetFull = JSON.parse(responseGetFull.body.asString());
			myDebug.responseGetFull = bodyGetFull;
			outMsg.statuscode = responseGetFull.status;
			if (responseGetFull.status >= 200 && responseGetFull.status < 300) {
				outMsg.status = "success";
			} else {
				outMsg.status = "error";
				outMsg.message = bodyGetFull;
				outStatus = "error";
			}
			outMessages.push(outMsg);
		}*/
			var attachmentsObj = {};
			if (outStatus !== "error" && InObj.attachments && InObj.attachments.length > 0) {
				attachmentsObj = prepareAttachments(InObj.attachments, SESSIONID, NODEID);
				myDebug.attachmentsObj = attachmentsObj;
			}
			if (outStatus !== "error") {

				var consContractObject = buildContractObj(InObj, bodyGetFull, billingMode, attachmentsObj);
				myDebug.ContractObj = consContractObject;

				if (DocEntry > 0) {
					output.method = "update";
					var responseUpdate = slib.updateDocUDO(ContractUDOID, DocEntry, JSON.stringify(consContractObject), SESSIONID, NODEID);
					outMsg = {};
					if (ParRecallMode) {
						outMsg.id = globalDebug.mynextval;
					}
					outMsg.method = "update";
					myDebug.statusUpdate = responseUpdate.status;
					outMsg.statuscode = responseUpdate.status;
					if (responseUpdate.status >= 200 && responseUpdate.status < 300) {
						myDebug.Updatemessage = "Update sucessfull";
						outMsg.status = "success";
					} else {
						myDebug.message = "Update error occured";
						outMsg.status = "error";
						if (responseUpdate.body) {
							myDebug.responseUpdate = JSON.parse(responseUpdate.body.asString());
							//outMsg.message = myDebug.responseUpdate;
							slError = myDebug.responseUpdate.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.responseUpdate);
							}
							outMsg.response = myDebug.responseUpdate;
						}
						outStatus = "error";
					}
					outMessages.push(outMsg);

				} else {

					outMsg = {};
					if (ParRecallMode) {
						outMsg.id = globalDebug.mynextval;
					}
					outMsg.method = "create";
					var responseCreate = slib.createDocUDO(ContractUDOID, JSON.stringify(consContractObject), SESSIONID, NODEID);
					myDebug.statusCreate = responseCreate.status;
					outMsg.statuscode = responseCreate.status;
					myDebug.responseCreate = JSON.parse(responseCreate.body.asString());
					if (responseCreate.status >= 200 && responseCreate.status < 300) {
						myDebug.Createmessage = "Create sucessfull";
						outMsg.status = "success";
						var createUDOObj = JSON.parse(responseCreate.body.asString());
						DocEntry = createUDOObj.DocEntry;
					} else {
						myDebug.Createmessage = "Create error occured";
						output.errorCreatestatus = myDebug.Createmessage;
						outMsg.status = "error";
						//outMsg.message = myDebug.responseCreate;
						outStatus = "error";
						slError = myDebug.responseCreate.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.responseCreate);
						}
						outMsg.response = myDebug.responseCreate;
					}
					outMessages.push(outMsg);
				}

			}
			output.DocEntry = DocEntry;

			slib.SLLogout(SESSIONID, NODEID);
		}
	}
	output.debug = myDebug;
	//$.trace.debug("DEBUG body.Get: " + JSON.stringify(myDebug));
	return output;
	//setOutputMessage();
	//return 0;

}

function getnextval() {
	var conn = $.db.getConnection();
	var pstmt;
	var query = "select \"NICEPORTAL\".\"SEQ_CONTRACT_ID\".NEXTVAL from dummy";
	pstmt = conn.prepareStatement(query);
	var rs = pstmt.executeQuery();
	if (!rs.next()) {
		outMsg = {};
		outMsg.method = "getnexval from sequence error";
		outMsg.status = "error";
		//	outMsg.message = e.message;
		outMessages.push(outMsg);
	} else {
		return rs.getString(1);
	}
	rs.close();
	pstmt.close();
	conn.close();
}

function setBPCardCode(tableid, bpcardcode) {
	var conn = $.db.getConnection();
	var pstmt;
	var query = "update \"NICEPORTAL\".\"UO_CRM_DATA_CONTRACTS\" set B1_CARDCODE = '" + bpcardcode +
		"', UPDATE_TS = CURRENT_TIMESTAMP where ID =" + tableid;
	pstmt = conn.prepareStatement(query);
	//	pstmt.setString(1,bpcardcode);
	//	pstmt.setString(2,tableid);
	var rs = pstmt.executeUpdate();
	globalDebug.UpdateQuery = query;

	pstmt.close();
	conn.commit();
	conn.close();

}

function setRESPONSE(tableid, respstatus, respmessage) {
	var conn = $.db.getConnection();
	var pstmt;
	var query =
		"update \"NICEPORTAL\".\"UO_CRM_DATA_CONTRACTS\" set RESPONSE_STATUS = ? , RESPONSE_MESSAGE =?, UPDATE_TS = CURRENT_TIMESTAMP where ID =" +
		tableid;
	pstmt = conn.prepareStatement(query);
	pstmt.setString(1, respstatus.toString());
	pstmt.setString(2, respmessage.toString());

	var rs = pstmt.executeUpdate();
	globalDebug.UpdateQuery = query;

	pstmt.close();
	conn.commit();
	conn.close();

}

function PutBPDataInTable(recalljsonRaw, id) {
	var mynextval;

	var pdcontent = ""
	if (ParRecallMode) {
		pdcontent = recalljsonRaw;
		mynextval = id;
		globalDebug.mynextval = mynextval;
	} else {
		mynextval = getnextval();
		globalDebug.mynextval = mynextval;
		pdcontent = $.request.body.asString();
	}

	//  pdcontent = pdcontent.replace(/\"([^(\")"]+)\":/g,"$1:"); 
	var JbpObj = JSON.parse(pdcontent);
	var conn = $.db.getConnection();
	var pstmt;
	var query;
	var vals = {};
	var data = [];
	//   var query2;
	//   var output = {results: [] };

	try {

		if (!ParRecallMode) {
			//     query = "INSERT INTO \"NICEPORTAL\".\"UO_CRM_DATA_BP\" (ID,ID_MSD,TYPE,GROUP,SALUTATION,TITLE,NAME,SURNAME,PHONE,MOBILE_PHONE,EMAIL,ADDRESS_JSON,IBAN,BIC,SEPA_MANDATE_ID,SIGNATURE_DATE,ACCOUNT_OWNER,STREET,STREET_NO,ZIP_CODE,CITY,JSONRAW,CREATE_TS,UPDATE_TS) values (\"NICEPORTAL\".\"SEQ_CRMBP_ID\".NEXTVAL,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)";
			query =
				"INSERT INTO \"NICEPORTAL\".\"UO_CRM_DATA_CONTRACTS\" (ID,ID_MSD,ID_CRM,JSONRAW,B1_CODE,CREATE_TS,UPDATE_TS) values (" + mynextval +
				",?,?,?,?,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)";
			//		"INSERT INTO \"NICEPORTAL\".\"UO_CRM_DATA_BP\" (ID,ID_MSD,ID_CRM,TYPE,BPGROUP,SALUTATION,TITLE,NAME,SURNAME,PHONE,MOBILE_PHONE,EMAIL,ADDRESS_JSON,IBAN,BIC,SEPA_MANDATE_ID,SIGNATURE_DATE,ACCOUNT_OWNER,STREET,STREET_NO,ZIP_CODE,CITY,JSONRAW,CREATE_TS,UPDATE_TS) values (\"NICEPORTAL\".\"SEQ_CRMBP_ID\".NEXTVAL,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)";

			pstmt = conn.prepareStatement(query);
			pstmt.setString(1, JbpObj.contract_id.toString());
			pstmt.setString(2, JbpObj.bp_crm.toString());
			pstmt.setString(3, JSON.stringify(JbpObj));
			pstmt.setString(4, getCardcodebyCRMID(JbpObj.bp_crm));
			var rs = pstmt.executeUpdate();
			pstmt.close();
			conn.commit();
			conn.close();

			outMsg = {};
			outMsg.method = "insertDB";
			outMsg.status = "success";
			outMessages.push(outMsg);
		}
		validatePayload(JbpObj);
		// validate project and psp elements
		var projectList = [];
		if (JbpObj.project) {
			projectList.push(JbpObj.project);
		}
		if (JbpObj.parameters) {
			JbpObj.parameters.forEach(function(param) {
				if (param.psp) {
					projectList.push(param.psp);
				}
			});
		}
		var uniqueProjectList = projectList.filter(onlyUnique);
		validateProjects(uniqueProjectList);
		// validate item codes
		if (JbpObj.parameters) {
			var itemCodes = [];
			JbpObj.parameters.forEach(function(param) {
				if (param.product_b1_articlenumber) {
					itemCodes.push(param.product_b1_articlenumber);
				}
			});
			var uniqueItemCodes = itemCodes.filter(onlyUnique);
			validateItemCodes(uniqueItemCodes);
		}
		var cardCode = getCardcodebyCRMID(JbpObj.bp_crm);
		if (cardCode) {
			var productList = getProductId(JbpObj.product);
			var retUpsertContract = [];
			if (productList) {
				productList.forEach(function(p) {
					if (p.BillingMode === 'AO') {
						outMsg = {};
						if (ParRecallMode) {
							outMsg.id = globalDebug.mynextval;
						}
						outMsg.method = "copyBP";
						outMsg.paramCardCode = cardCode;
						var bpoutput = copybplib.copyBP(ParDebug, cardCode);
						outMsg.result = bpoutput;
						outMessages.push(outMsg);

						var searchparam2 = {};
						searchparam2.key = "U_CUSTOMER eq '" + cardCode + "-L' and " + "U_EXT_ID_CRM";
						searchparam2.value = JbpObj.contract_id.toString();
						retUpsertContract.push(upsContract(searchparam2, JbpObj, p.BillingMode));
					} else {
						var searchparam1 = {};
						searchparam1.key = "U_CUSTOMER eq '" + cardCode + "' and " + "U_EXT_ID_CRM";
						searchparam1.value = JbpObj.contract_id.toString();
						retUpsertContract.push(upsContract(searchparam1, JbpObj, p.BillingMode));
					}
				});
			}
		}
		//var retUpsertBP = upsBP(searchparam, JSON.stringify(bpObj),JbpObj);
		//var retUpsertBP = upsertBP(searchparam, JSON.stringify(bpObj));
		//var retUpsertBP = addBP(JSON.stringify(bpObj));      

	} catch (e) {
		$.trace.error(e.message);
		outMsg = {};
		if (ParRecallMode) {
			outMsg.id = globalDebug.mynextval;
		}
		outMsg.method = "exceptionHandler";
		outMsg.status = "error";
		outMsg.message = e.message;
		if (debugLevel > 0) {
			outMsg.exceptionInfo = {};
			outMsg.exceptionInfo.name = e.name;
			outMsg.exceptionInfo.message = e.message;
			outMsg.exceptionInfo.fileName = e.fileName;
			outMsg.exceptionInfo.lineNumber = e.lineNumber;
			outMsg.exceptionInfo.columnNumber = e.columnNumber;
			outMsg.exceptionInfo.stack = e.stack;
			//outMsg.exceptionInfo.source = e.toSource();
		}
		outMessages.push(outMsg);

		vals.status = 'error';
		//vals.description = e.message;
		vals.messages = outMessages;

		if (ParDebug) {
			vals.debug = {};
			vals.debug.statement = query;
		}
		if (debugLevel > 0) {
			vals.debugInfos = globalDebug;
		}

		//	vals.group = JbpObj.group.toString();
		//	vals.bpobjekt = JSON.stringify(JbpObj);
		//	vals.SLBPObj = JSON.stringify(bpObj);
		//	vals.ResponsUPSER = JSON.stringify(retUpsertBP);
		//	vals.CardCode = retUpsertBP.CardCode;
		//vals.mode = JSON.stringify(retUpsertBP.mode);
		data.push(vals);
		var errbody = JSON.stringify(data);

		$.response.contentType = 'application/json';
		$.response.setBody(errbody);
		//$.response.status = $.net.http.INTERNAL_SERVER_ERROR;
		// auch bei Fehler HTTP Status 200, da der Response im CRM sonst evtl. nicht sauber verarbeitet wird
		$.response.status = $.net.http.OK;
		setRESPONSE(mynextval, outStatus, errbody);
		return;
	}
	vals.status = outStatus;
	//vals.description = 'Data accepted and proceeded';
	//	vals.id_crm = JbpObj.id_crm.toString();
	//vals.id_msd = JbpObj.id_msd.toString();
	//vals.id_b1 = retUpsertBP.CardCode;
	//vals.CardCode = retUpsertBP.CardCode;
	vals.messages = outMessages;
	//setBPCardCode(mynextval,retUpsertBP.CardCode);
	//vals.RespnUPSERJ = JSON.stringify(retUpsertBP);
	if (ParDebug) {
		if (debugLevel > 0) {
			vals.debugInfos = globalDebug;
		}
		vals.debug = {
			productList: productList,
			upsert: retUpsertContract
		};
	}
	//vals.mode = JSON.stringify(retUpsertBP.mode);
	data.push(vals);
	var okbody = JSON.stringify(data);
	//var body = JSON.stringify(test);
	setRESPONSE(mynextval, outStatus, okbody);
	$.response.contentType = 'application/json';
	$.response.setBody(okbody);
	$.response.status = $.net.http.OK;
}

function testmain() {

	var searchparam = {};
	searchparam.key = "U_EON_ID_CRM";
	searchparam.value = 'BP777888999x';

	var bpObj = mockbuildBPObj();

	var retUpsertBP = upsBP(searchparam, JSON.stringify(bpObj));

	// setOutputMessage();
}

function recallJSONRAW(idList) {

	var vals = {};
	var data = [];
	var conn = $.db.getConnection();
	var pstmt;
	var query = "SELECT \"JSONRAW\",\"ID\" FROM \"NICEPORTAL\".\"UO_CRM_DATA_CONTRACTS\" WHERE ID IN ( " + idList + ")";
	pstmt = conn.prepareStatement(query);
	//pstmt.setString(1, prodID);
	var rs = pstmt.executeQuery();
	if (!rs.next()) {
		outMsg = {};
		outMsg.method = "recallJSONRAW";
		outMsg.message = "Empty ID List Parameter: " + idList;
		if (debugLevel > 0) {
			outMsg.message += " / Query: " + query;
		}
		outMsg.status = "error";
		outStatus = "error";
		//	outMsg.message = e.message;
		outMessages.push(outMsg);

		vals.status = 'error';
		//vals.description = e.message;
		vals.messages = outMessages;

		if (ParDebug) {
			vals.debug = {};
			vals.debug.statement = query;
		}
		if (debugLevel > 0) {
			vals.debugInfos = globalDebug;
		}
		data.push(vals);
		var errbody = JSON.stringify(data);
		$.response.contentType = 'application/json';
		$.response.setBody(errbody);
		//$.response.status = $.net.http.INTERNAL_SERVER_ERROR;
		// auch bei Fehler HTTP Status 200, da der Response im CRM sonst evtl. nicht sauber verarbeitet wird
		$.response.status = $.net.http.OK;
	} else {
		do {
			PutBPDataInTable(rs.getNString(1), rs.getInteger(2));
		} while (rs.next());
	}

	//	}
	rs.close();
	pstmt.close();
	conn.close();
}

//testmain();

// begin org

ParDebug = $.request.parameters.get('debug') && $.request.parameters.get('debug').toLowerCase() === "true";
ParRecallIdList = $.request.parameters.get('recallidlist');
if (ParRecallIdList) {
	ParRecallMode = true;
	recallJSONRAW(ParRecallIdList);
} else {
	var content = $.request.body.asString();
	PutBPDataInTable();
}
//ende org

//SlLogin();

/*var req = 0;
req = $.request.parameters.get('crmid');

if (!req){ 
	output.error = "Invalid BP Request - " + req;                                                           
	$.response.status = $.net.http.OK;
	$.response.contentType = "application/json"; 
    $.response.setBody(JSON.stringify(output));
}else{
  
    //addBP(JSON.stringify(bpObj));
    //updBP(JSON.stringify(bpObj));
    //GetOrders(req);        
}
*/