diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
index 37c805cefb818b0f2c3349c7f74120aa956f6294..3226194b7c748c60ff7b268cef748a56d8abdf4e 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -10,6 +10,10 @@
       <key>Event End</key>
       <value>Veranstaltungs Ende</value>
     </entry>
+    <entry>
+      <key>Attributes of attribute group \"%0\" have to be used at least %1.</key>
+      <value>Eigenschaften der Eigenschaftsgruppe \"%0\" müssen mindestens %1 verwendet werden.</value>
+    </entry>
     <entry>
       <key>Redirect needs a full Url with http/https</key>
       <value>Für die Weiterleitung wird eine vollständige Url mit http/https benötigt</value>
@@ -67,6 +71,10 @@
       <value>Objekt nicht gefunden
 </value>
     </entry>
+    <entry>
+      <key>Attributes of attribute group \"%0\" can't be used more than %1.</key>
+      <value>Eigenschaften der Eigenschaftsgruppe \"%0\" dürfen maximal %1 verwendet werden.</value>
+    </entry>
     <entry>
       <key>Change responsible</key>
       <value>Verantwortlichen wechseln</value>
diff --git a/process/Attribute_lib/process.js b/process/Attribute_lib/process.js
index 965a947abf7ef27ac5867379970efcdb7eb8e93e..20c80caef6169b6cf056b192bec61ae87320761e 100644
--- a/process/Attribute_lib/process.js
+++ b/process/Attribute_lib/process.js
@@ -78,6 +78,40 @@ AttributeUtil.getPossibleAttributes = function (pObjectType, pIncludeGroups, pFi
 
     if (pAttributeCount)
     {
+        // get parents of already linked attributes
+        var parentAttributes = AttributeUtil.getAllParents(Object.keys(pAttributeCount));
+        
+        // get max usage from attribute parents
+        var parentAttributesMaxCount = [];
+
+        if (parentAttributes.length > 0)
+        {   
+            // retrieve all max counts of the parent attributes
+            parentAttributesMaxCount = newSelect("AB_ATTRIBUTEID, MAX_COUNT")
+                                            .from("AB_ATTRIBUTEUSAGE")
+                                            .join("AB_ATTRIBUTE", "AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+                                            .where("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", parentAttributes, SqlBuilder.IN())
+                                            .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType)
+                                            .table();
+        }
+        
+        // count how many children of each parent are already linked
+        var parentChildUsageCount = {};
+        parentAttributes.forEach(function(attr) { parentChildUsageCount[attr] = (parentChildUsageCount[attr] || 0) + 1; });
+        
+        // if actual usage >= max usage exclude parent and its children
+        for each (let parentAttr in parentAttributesMaxCount)
+        {
+            if (parentAttr[1] && parentChildUsageCount[parentAttr[0]] >= parentAttr[1])
+            {
+                // exclude this parent and its children
+                attrSelect.and(newWhere()
+                                .and("AB_ATTRIBUTE.AB_ATTRIBUTEID", parentAttr[0], SqlBuilder.NOT_EQUAL())
+                                .and("AB_ATTRIBUTE.AB_ATTRIBUTEID", AttributeUtil.getAllChildren(parentAttr[0]), SqlBuilder.NOT_IN())
+                );
+            }
+        }
+        
         for (let attributeId in pAttributeCount)
         {
             attrSelect.and(newWhere()
@@ -336,6 +370,37 @@ AttributeUtil.getAllChildren = function (pAttributeIds)
     return childIds;
 }
 
+/**
+ * Returns the ids of all superordinate attributes of an attribute.
+ * 
+ * @param {String|Array} pAttributeIds              <p>
+ *                                                  The id(s) of the attribute(s).<br>
+ * @return {String[]}                               <p>
+ *                                                  Array with the ids of every superordinate attribute.<br>
+ */
+AttributeUtil.getAllParents = function (pAttributeIds)
+{
+    var parentIds = [];
+    if (typeof(pAttributeIds) == "string")
+        pAttributeIds = [pAttributeIds];
+        
+    while (pAttributeIds.length > 0)
+    {
+        pAttributeIds = newSelect("ATTRIBUTE_PARENT_ID")
+                            .from("AB_ATTRIBUTE")
+                            .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", pAttributeIds, SqlBuilder.IN())
+                            .arrayColumn();
+
+        if (pAttributeIds.length > 0)
+            parentIds = parentIds.concat(pAttributeIds);
+    }
+    
+    // remove empty array elements
+    parentIds = parentIds.filter(function (id) { return id != null && id != '' });
+    
+    return parentIds;
+}
+
 /**
  * Checks if an attribute has attribute relations.
  * 
@@ -718,7 +783,58 @@ AttributeRelationUtils.validateAttributeCount = function (pRowId, pObjectType, p
         //retrieve all min/max counts of the possible attributes
         minMaxCounts = minMaxCountsSelect.table();
     }
+    
+    // attribute ids of current attribute changes (client) and attributerelations (database)
+    var currentAttributes = [];
+    for (let attribute in countObj) if (countObj[attribute] > 0) currentAttributes.push(attribute);
+
+    // get all parent attributes of current attributes
+    var currentParentAttributes = AttributeUtil.getAllParents(currentAttributes);
+    
+    // get all possible parent attributes
+    var possibleParentAttributes = newSelect("distinct ATTRIBUTE_PARENT_ID")
+                                    .from("AB_ATTRIBUTE")
+                                    .where("AB_ATTRIBUTE.AB_ATTRIBUTEID", possibleAttributes, SqlBuilder.IN())
+                                    .arrayColumn();
+    // remove empty elements
+    possibleParentAttributes = possibleParentAttributes.filter(function (el) { return el != null && el != ""; });
+    
+    // count current usages of parent attributes
+    var countParentObj = {};
+    currentParentAttributes.forEach(function(parentAttribute) { countParentObj[parentAttribute] = (countParentObj[parentAttribute] || 0) + 1; });
+    
+    // add missing possible parent attributes with usage of 0 to countParentObj
+    var addAttr;
+    for each (let possibleParent in possibleParentAttributes)
+    {
+        addAttr = true; 
         
+        for (let countParent in countParentObj)
+        {
+            if (possibleParent == countParent)
+            { 
+                addAttr = false; 
+                break; 
+            }
+        }
+        
+        if (addAttr) countParentObj[possibleParent] = 0;
+    }
+    
+    var minMaxParentCounts = [];
+
+    if (possibleParentAttributes.length > 0)
+    {   
+        var minMaxParentCountsSelect = newSelect("AB_ATTRIBUTEID, ATTRIBUTE_NAME, MIN_COUNT, MAX_COUNT")
+                                        .from("AB_ATTRIBUTEUSAGE")
+                                        .join("AB_ATTRIBUTE", "AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID")
+                                        .where("AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID", possibleParentAttributes, SqlBuilder.IN())
+                                        .and("AB_ATTRIBUTEUSAGE.OBJECT_TYPE", pObjectType);
+
+        // retrieve all min/max counts of parent attributes
+        minMaxParentCounts = minMaxParentCountsSelect.table();
+    }
+
     var validationMessage = [];
     minMaxCounts.forEach(function ([attributeId, name, minCount, maxCount])
     {
@@ -730,6 +846,16 @@ AttributeRelationUtils.validateAttributeCount = function (pRowId, pObjectType, p
             validationMessage.push(translate.withArguments("Attribute \"%0\" can't be used more than %1.", [name, _getTranslatedCount(maxCount)]));
     }, countObj);
     
+    minMaxParentCounts.forEach(function ([attributeId, name, minCount, maxCount])
+    {
+        let count = this[attributeId] || 0;
+        //compares the actual usage with the min and max count and generates a message if the usage is too low or too high
+        if (count < minCount)
+            validationMessage.push(translate.withArguments("Attributes of attribute group \"%0\" have to be used at least %1.", [name, _getTranslatedCount(minCount)]));
+        if (maxCount && count > maxCount)
+            validationMessage.push(translate.withArguments("Attributes of attribute group \"%0\" can't be used more than %1.", [name, _getTranslatedCount(maxCount)]));
+    }, countParentObj);
+    
     return validationMessage.join("\n");
     
     //returns the correct count expression by choosing either singular (1 time) or plural (2 times)
@@ -2062,4 +2188,4 @@ AttributeRelation.prototype.deleteAttribute = function (pOmitValidation)
     newWhere("AB_ATTRIBUTERELATION.AB_ATTRIBUTERELATIONID", this.attributeRelationId)
         .deleteData();
     return true;
-}
\ No newline at end of file
+}