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 +}