    importPackage(Packages.de.elo.ix.client);

    //@include lib_Class.js
    //@include lib_sol.common.IxUtils.js
    //@include lib_sol.common.SordUtils.js
    //@include lib_sol.common.ix.DynKwlUtils.js
    //@include lib_sol.common.ix.FunctionBase.js

    var logger = sol.create("sol.Logger", { scope: "sol.contract.ix.functions.CreatePartner" });

    /**
     * Creates a new company and contact if indicated by the contract metadata.
     *
     * New elements will only be created, if the map field 'CONTRACT_CREATE_PARTNER' has the value '1' (checkbox activated).
     * A new company will be created, if there was no 'PARTNER_NO' found in the contract metadata.
     * A new contact will be created, if there was no 'CONTACT_REFERENCE' found in the contract metadata.
     *
     * @eloix
     *
     * @requires sol.common.Config
     * @requires sol.common.IxUtils
     * @requires sol.common.StringUtils
     * @requires sol.common.SordUtils
     * @requires sol.common.WfUtils
     * @requires sol.common.ix.DynKwlUtils
     * @requires sol.common.ix.FunctionBase
     *
     */
    sol.define("sol.contract.ix.functions.CreatePartner", {
      extend: "sol.common.ix.FunctionBase",

      requiredConfig: ["objId"],

      /**
       * @cfg {String} objId (required)
       */

      /**
       * @private
       * @property {String} CONTACTLIST_REF_FIELD
       * This defines the field, where the contact list reference will be written to.
       */
      CONTACTLIST_REF_FIELD: "CONTACTLIST_REFERENCE",

      /**
       * @private
       * @property {Object} MAPPINGS
       * This defines the mapping from the contract to the contact fields for the element creation.
       */
      MAPPINGS: {
        company: [
          { from: { type: "GRP", key: "PARTNER_NAME" }, to: { type: "GRP", key: "COMPANY_NAME" } },
          { from: { type: "MAP", key: "ADDRESS_STREET" }, to: { type: "GRP", key: "ADDRESS_STREET" } },
          { from: { type: "MAP", key: "ADDRESS_ADDITION" }, to: { type: "GRP", key: "ADDRESS_ADDITION" } },
          { from: { type: "MAP", key: "ADDRESS_ZIP_CODE" }, to: { type: "GRP", key: "ADDRESS_ZIP_CODE" } },
          { from: { type: "MAP", key: "ADDRESS_CITY" }, to: { type: "GRP", key: "ADDRESS_CITY" } },
          { from: { type: "MAP", key: "ADDRESS_STATE" }, to: { type: "GRP", key: "ADDRESS_STATE" } },
          { from: { type: "MAP", key: "ADDRESS_COUNTRY" }, to: { type: "GRP", key: "ADDRESS_COUNTRY" } },
          { from: { type: "MAP", key: "CONTRACT_FINANCE_IBAN" }, to: { type: "GRP", key: "FINANCE_IBAN" } },
          { from: { type: "MAP", key: "CONTRACT_FINANCE_BIC" }, to: { type: "GRP", key: "FINANCE_BIC" } },
          { from: { type: "MAP", key: "CONTRACT_FINANCE_VAT_ID_NO" }, to: { type: "GRP", key: "FINANCE_VAT_ID_NO" } },
          { from: { type: "MAP", key: "CONTRACT_FINANCE_TAX_NO" }, to: { type: "GRP", key: "FINANCE_TAX_NO" } },
          { from: { type: "MAP", key: "CONTRACT_FINANCE_REGISTER_NUMBER" }, to: { type: "GRP", key: "FINANCE_REGISTER_NUMBER" } }
        ],
        contact: [
          { from: { type: "GRP", key: "PARTNER_NO" }, to: { type: "GRP", key: "COMPANY_REFERENCE" } },
          { from: { type: "GRP", key: "CONTACT_FIRSTNAME" }, to: { type: "GRP", key: "CONTACT_FIRSTNAME" } },
          { from: { type: "GRP", key: "CONTACT_LASTNAME" }, to: { type: "GRP", key: "CONTACT_LASTNAME" } },
          { from: { type: "MAP", key: "PARTNER_PHONE" }, to: { type: "GRP", key: "CONTACT_TELEPHONE" } },
          { from: { type: "MAP", key: "PARTNER_EMAIL" }, to: { type: "GRP", key: "CONTACT_EMAIL" } },
          { from: { type: "MAP", key: "ADDRESS_STREET" }, to: { type: "GRP", key: "ADDRESS_STREET" } },
          { from: { type: "MAP", key: "ADDRESS_ADDITION" }, to: { type: "GRP", key: "ADDRESS_ADDITION" } },
          { from: { type: "MAP", key: "ADDRESS_ZIP_CODE" }, to: { type: "GRP", key: "ADDRESS_ZIP_CODE" } },
          { from: { type: "MAP", key: "ADDRESS_CITY" }, to: { type: "GRP", key: "ADDRESS_CITY" } },
          { from: { type: "MAP", key: "ADDRESS_STATE" }, to: { type: "GRP", key: "ADDRESS_STATE" } },
          { from: { type: "MAP", key: "ADDRESS_COUNTRY" }, to: { type: "GRP", key: "ADDRESS_COUNTRY" } },
          { from: { type: "GRP", key: "FINANCE_IBAN" }, to: { type: "MAP", key: "CONTRACT_FINANCE_IBAN" } },
          { from: { type: "GRP", key: "FINANCE_BIC" }, to: { type: "MAP", key: "CONTRACT_FINANCE_BIC" } },
          { from: { type: "GRP", key: "FINANCE_VAT_ID_NO" }, to: { type: "MAP", key: "CONTRACT_FINANCE_VAT_ID_NO" } },
          { from: { type: "GRP", key: "FINANCE_TAX_NO" }, to: { type: "MAP", key: "CONTRACT_FINANCE_TAX_NO" } },
          { from: { type: "GRP", key: "FINANCE_REGISTER_NUMBER" }, to: { type: "MAP", key: "CONTRACT_FINANCE_REGISTER_NUMBER" } }
        ]
      },

      initialize: function (config) {
        var me = this;
        me.contractConfig = sol.create("sol.common.Config", { compose: "/contract/Configuration/contract.config" }).config;
        me.$super("sol.common.ix.FunctionBase", "initialize", [config]);

        me.contactlistReference = me.contractConfig.contactIntegration.defaultContactlistReference;
      },

      /**
       * Creates the contract partner if requested.
       */
      process: function () {
        var me = this,
            sord, clRefCfg, clRefValue, companyResult, contactResult;

        if (me.checkAutomaticCreation()) {

          sord = ixConnect.ix().checkoutSord(me.objId, SordC.mbAllIndex, LockC.NO);

          // if a contact list reference is set, it overrides the default contact list initialized from config
          clRefCfg = { type: "MAP", key: me.contractConfig.mapFields.contactlistRef };
          if (!me.isEmpty(sord, clRefCfg)) {
            clRefValue = sol.common.SordUtils.getValue(sord, clRefCfg);
            if (clRefValue) {
              me.contactlistReference = String(clRefValue);
            }
          }

          if (me.checkCreateCompany(sord)) {
            companyResult = sol.common.IxUtils.execute("RF_sol_contact_service_CreateCompany", {
              companyType: "Default", // TODO -> from config (node or json?)
              data: me.buildData(sord, me.MAPPINGS.company)
            });
            me.logger.info("created new company", companyResult);

            me.updatePartnerNo(sord, companyResult);
          }

          if (me.checkCreateContact(sord)) {
            contactResult = sol.common.IxUtils.execute("RF_sol_contact_service_CreateContact", {
              contactType: "Default", // TODO -> from config (node or json?)
              data: me.buildData(sord, me.MAPPINGS.contact)
            });
            me.logger.info("created new contact", contactResult);

            me.updateContactRef(sord, contactResult);
          }

          ixConnect.ix().checkinSord(sord, SordC.mbAllIndex, LockC.NO);

        }
      },

      /**
       * @private
       * Checks, if automatic cretion is activated
       * @return {Boolean}
       */
      checkAutomaticCreation: function () {
        var me = this,
            passed = false,
            fieldName, mapitems;

        fieldName = me.contractConfig.mapFields.createPartnerContact;
        mapitems = ixConnect.ix().checkoutMap(MapDomainC.DOMAIN_SORD, me.objId, [fieldName], LockC.NO).items;

        if (mapitems && (mapitems.length === 1)) {
          passed = (String(mapitems[0].value) === "1"); // checkbox checked
        }

        return passed;
      },

      /**
       * @private
       * Checks, if a new company should be created.
       *
       * If no company reference is defined (i.e. was not selected from the list) and a name was provided a new one will be created.
       * @param {de.elo.ix.client.Sord} sord
       * @return {Boolean}
       */
      checkCreateCompany: function (sord) {
        var me = this;
        return me.isEmpty(sord, { type: "GRP", key: me.contractConfig.fields.partnerNo })
          && !me.isEmpty(sord, { type: "GRP", key: me.contractConfig.fields.partnerName });
      },

      /**
       * @private
       * Checks, if a new contact should be created.
       *
       * If no contact reference is defined (i.e. was not selected from the list) as well as a first and lastname are provided a new one will be created.
       * @param {de.elo.ix.client.Sord} sord
       * @return {Boolean}
       */
      checkCreateContact: function (sord) {
        var me = this;
        return me.isEmpty(sord, { type: "MAP", key: me.contractConfig.mapFields.contactRef })
          && !me.isEmpty(sord, { type: "GRP", key: me.contractConfig.fields.contactFirstName })
          && !me.isEmpty(sord, { type: "GRP", key: me.contractConfig.fields.contactLastName });
      },

      /**
       * @private
       * Checks, if a field contains a value.
       * @param {de.elo.ix.client.Sord} sord
       * @param {Object} fieldCfg
       * @return {Boolean}
       */
      isEmpty: function (sord, fieldCfg) {
        var passed = true,
            value;

        if (sord && fieldCfg) {
          value = sol.common.SordUtils.getValue(sord, fieldCfg);
          passed = sol.common.StringUtils.isBlank(value);
        }

        return passed;
      },

      /**
       * @private
       * Creates the data for the RF_sol_contact_service_CreateCompany function.
       * @param {de.elo.ix.client.Sord} sord
       * @param {Object[]} mappings
       * @return {Object[]}
       */
      buildData: function (sord, mappings) {
        var me = this,
            data = [];

        data.push({ type: "GRP", key: me.CONTACTLIST_REF_FIELD, value: me.contactlistReference });

        mappings.forEach(function (mapping) {
          var value;
          value = sol.common.SordUtils.getValue(sord, mapping.from);
          if (value) {
            mapping.to.value = String(value);
            data.push(mapping.to);
          }
        });

        return data;
      },

      /**
       * @private
       * Updates the sord with the partner reference value
       * @param {de.elo.ix.client.Sord} sord
       * @param {Object} createResult
       */
      updatePartnerNo: function (sord, createResult) {
        var me = this;
        if (createResult.reference) {
          sol.common.SordUtils.setObjKeyValue(sord, me.contractConfig.fields.partnerNo, createResult.reference);
        }
      },

      /**
       * @private
       * Updates the sord with the partner reference value
       * @param {de.elo.ix.client.Sord} sord
       * @param {Object} createResult
       */
      updateContactRef: function (sord, createResult) {
        var me = this,
            mapitem;
        if (createResult.reference) {
          mapitem = new KeyValue(me.contractConfig.mapFields.contactRef, createResult.reference);
          ixConnect.ix().checkinMap(MapDomainC.DOMAIN_SORD, sord.id, sord.id, [mapitem], LockC.NO);
        }
      }

    });

    /**
     * @member sol.contract.ix.functions.CreatePartner
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onEnterNode
     */
    function onEnterNode(clInfo, userId, wFDiagram, nodeId) {
      logger.enter("onEnterNode_CreatePartner", { flowId: wFDiagram.id, nodeId: nodeId });
      var params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId),
          module;

      params.objId = wFDiagram.objId;
      module = sol.create("sol.contract.ix.functions.CreatePartner", params);

      module.process();

      logger.exit("onEnterNode_CreatePartner");
    }

    /**
     * @member sol.contract.ix.functions.CreatePartner
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onExitNode
     */
    function onExitNode(clInfo, userId, wFDiagram, nodeId) {
      logger.enter("onExitNode_CreatePartner", { flowId: wFDiagram.id, nodeId: nodeId });
      var params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId),
          module;

      params.objId = wFDiagram.objId;
      module = sol.create("sol.contract.ix.functions.CreatePartner", params);

      module.process();

      logger.exit("onExitNode_CreatePartner");
    }