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

    //@include lib_Class.js
    //@include lib_moment.js
    //@include lib_decimal-light.js
    //@include lib_sol.common.SordUtils.js
    //@include lib_sol.common.Map.js
    //@include lib_sol.common.DateUtils.js
    //@include lib_sol.common.ix.FunctionBase.js
    //@include lib_sol.contract.DurationUtils.js

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

    /**
     * Extends the contract period and recalculates the payment plan
     *
     * @eloix
     *
     * @requires sol.common.SordUtils
     * @requires sol.common.Map
     * @requires sol.common.WfUtils
     * @requires sol.common.ix.RfUtils
     * @requires sol.common.ix.FunctionBase
     *
     */
    sol.define("sol.contract.ix.functions.ExtendDuration", {
      extend: "sol.common.ix.FunctionBase",

      requiredConfig: ["objId"],

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

      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.unitSuffix = me.unitSuffix || "_UNIT";
      },

      /**
       * Extends the contract duration
       */
      process: function () {
        var me = this,
            newData = [],
            tplSord, updates, mapData;

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

        tplSord = sol.common.SordUtils.getTemplateSord(me.sord).sord;

        updates = {
          objKeys: {},
          mapKeys: {}
        };

        sol.contract.DurationUtils.calcContractEnd(tplSord, updates);

        sol.contract.DurationUtils.calcTerminationDateFixed(tplSord, updates);
        sol.contract.DurationUtils.calcReminderDate(tplSord, updates);

        me.fillUpdates(updates.objKeys, "GRP", newData);
        me.fillUpdates(updates.mapKeys, "MAP", newData);

        mapData = sol.common.SordUtils.updateSord(me.sord, newData);

        if (mapData) {
          ixConnect.ix().checkinMap(MapDomainC.DOMAIN_SORD, me.sord.id, me.sord.id, mapData, LockC.NO);
        }

        me.sordMap = sol.create("sol.common.SordMap", { objId: me.sord.id });
        me.sordMap.read();
        me.calcPaymentPlan();
        ixConnect.ix().checkinSord(me.sord, SordC.mbAllIndex, LockC.NO);
        me.sordMap.write();
      },

      /**
       * @private
       * @param {Object} valuesObj
       * @param {String} type
       * @param {Array} newData
       */
      fillUpdates: function (valuesObj, type, newData) {
        var key, value;

        for (key in valuesObj) {
          value = valuesObj[key];
          newData.push({ type: type, key: key, value: value });
        }
      },

      /**
       * Sets a value
       * @private
       * @param {Object} fieldDef Field definition
       * @param {String} fieldDef.type Field type
       * @param {String} fieldDef.name Field name
       * @param {String} value Value
       */
      setValue: function (fieldDef, value) {
        var me = this;
        if (!fieldDef || !fieldDef.type || !fieldDef.key) {
          throw "Field definition is incomplete";
        }
        fieldDef.value = value;
        me.newData.push(fieldDef);
      },

      /**
       * @private
       * Calculates and sets a date
       * @param {String} srcDateField Source date field
       * @param {String} durationField Duration field
       * @param {String} dstDateField Destination date field
       * @param {Object} params Parameters
       * @param {Object} params.endOfField
       * @param {Object} params.minTermStartField
       * @param {Object} params.minTermNumberField
       * @param {Object} params.minTermNumberUnitField
       * @param {Boolean} params.substractOneDay
       * @param {Boolean} params.minToday
       * @return {String}
       */
      setCalculatedDate: function (srcDateField, durationField, dstDateField, params) {
        var me = this,
            srcIsoDate, durationNumber, durationUnit, dstIsoDate,
            minTermStartIsoDate, minTermNumber, minTermUnit, minTermIsoDate, todayIso;

        params = params || {};

        todayIso = sol.common.SordUtils.nowIsoForConnection(null, { startOfDay: true });

        switch (String(srcDateField.type)) {
          case "$NOW":
            srcIsoDate = todayIso;
            break;
          case "CONST":
            srcIsoDate = srcDateField.value || "";
            break;
          default:
            srcIsoDate = sol.common.SordUtils.getValue(me.sord, srcDateField);
            break;
        }

        if (!srcIsoDate) {
          return;
        }

        durationNumber = sol.common.SordUtils.getValue(me.sord, durationField);
        durationUnit = sol.common.SordUtils.getLocalizedKwlKey(me.sord, { type: durationField.type, key: durationField.key + me.unitSuffix });

        if (params.endOfField) {
          params.endOfUnit = sol.common.SordUtils.getLocalizedKwlKey(me.sord, params.endOfField) || "d";
        }
        if (params.substractOneDay) {
          srcIsoDate = sol.contract.DurationUtils.calculateDate(srcIsoDate, -1, "d");
        }
        dstIsoDate = sol.contract.DurationUtils.calculateDate(srcIsoDate, durationNumber, durationUnit, params);

        if (params.minTermNumberField) {
          minTermStartIsoDate = sol.common.SordUtils.getValue(me.sord, params.minTermStartField);
          minTermNumber = sol.common.SordUtils.getObjKeyValueAsNumber(me.sord, params.minTermNumberField) || 0;
          minTermUnit = sol.common.SordUtils.getLocalizedKwlKey(me.sord, params.minTermNumberUnitField);
          minTermIsoDate = sol.contract.DurationUtils.calculateDate(minTermStartIsoDate, minTermNumber, minTermUnit);

          if (minTermIsoDate > dstIsoDate) {
            dstIsoDate = minTermIsoDate;
          }

          if (params.minToday && (dstIsoDate < todayIso)) {
            dstIsoDate = todayIso;
          }
        }

        me.setValue(dstDateField, dstIsoDate);
        return dstIsoDate;
      },

      /**
       * @private
       * Re-calulates the payment plan
       */
      calcPaymentPlan: function () {
        var me = this,
            total, currencyCode, baseCurrencyCode, amountLocalCurr, exchangeRate;

        total = new Decimal(0);

        me.sordMap.forEachRow(me.contractConfig.mapFields.contractCashflowRecurringUnit, function (i) {
          var singleAmount,  startDateIso, endDateIso, unit, rowTotal;

          rowTotal = new Decimal(0);

          singleAmount = me.sordMap.getNumValue(me.contractConfig.mapFields.contractCashflowSingle + i);
          if (singleAmount) {
            startDateIso = me.sordMap.getValue(me.contractConfig.mapFields.contractCashflowDate + i) || sol.common.SordUtils.getObjKeyValue(me.sord, me.contractConfig.fields.contractStart);
            endDateIso = me.sordMap.getValue(me.contractConfig.mapFields.contractCashflowTo + i) || sol.common.SordUtils.getObjKeyValue(me.sord, me.contractConfig.fields.contractEnd) || sol.common.SordUtils.getObjKeyValue(me.sord, me.contractConfig.fields.nextPossibleTermination);
            unit = me.sordMap.getValue(me.contractConfig.mapFields.contractCashflowRecurringUnit + i);
            rowTotal = sol.contract.DurationUtils.calcRowTotal(startDateIso, endDateIso, singleAmount, unit);
            total = total.plus(rowTotal);
          }
          me.sordMap.setNumValue(me.contractConfig.mapFields.contractCashflowAmount + i, rowTotal.toNumber());
        }, me);

        sol.common.SordUtils.setObjKeyValueAsNumber(me.sord, me.contractConfig.fields.cashflowSum, total.toNumber());

        baseCurrencyCode = sol.contract.DurationUtils.getBaseCurrency();
        currencyCode = sol.common.SordUtils.getObjKeyValue(me.sord, me.contractConfig.fields.currencyCode);

        if (!currencyCode || (currencyCode == baseCurrencyCode)) {
          sol.common.SordUtils.setObjKeyValue(me.sord, me.contractConfig.fields.currencyCode, baseCurrencyCode);
          me.sordMap.setValue(me.contractConfig.mapFields.exchangeRate, "1");
        }
        exchangeRate = me.sordMap.getNumValue(me.contractConfig.mapFields.exchangeRate);
        amountLocalCurr = sol.contract.DurationUtils.calcLocalCurrencyAmount(total, exchangeRate);
        sol.common.SordUtils.setObjKeyValueAsNumber(me.sord, me.contractConfig.fields.cashflowSumLocalCurr, amountLocalCurr);
      }
    });

    /**
     * @member sol.contract.ix.functions.ExtendDuration
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onEnterNode
     */
    function onEnterNode(clInfo, userId, wFDiagram, nodeId) {
      logger.enter("onEnterNode_ExtendDuration", { 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.ExtendDuration", params);

      module.process();

      logger.exit("onEnterNode_ExtendDuration");
    }

    /**
     * @member sol.contract.ix.functions.ExtendDuration
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onExitNode
     */
    function onExitNode(clInfo, userId, wFDiagram, nodeId) {
      logger.enter("onExitNode_ExtendDuration", { 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.ExtendDuration", params);

      module.process();

      logger.exit("onExitNode_ExtendDuration");
    }

    /**
     * @member sol.common.ix.functions.ChangeRights
     * @method RF_sol_contract_function_ExtendDuration
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#RF_FunctionName
     */
    function RF_sol_contract_function_ExtendDuration(iXSEContext, args) {
      logger.enter("RF_sol_contract_function_ExtendDuration", args);
      var params = sol.common.ix.RfUtils.parseAndCheckParams(iXSEContext, arguments.callee.name, args, "objId"),
          module = sol.create("sol.contract.ix.functions.ExtendDuration", params);

      module.process();

      logger.exit("RF_sol_contract_function_ExtendDuration");
    }