def checkFormulaRules(val, formula, nameVariables): from arelle.ModelFormulaObject import (Aspect) if not (formula.hasRule(Aspect.CONCEPT) or formula.source(Aspect.CONCEPT)): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "concept"): val.modelXbrl.error( _("Formula {0} concept rule does not have a nearest source and does not have a child element" ).format(formula.xlinkLabel), "err", "xbrlfe:incompleteConceptRule") else: val.modelXbrl.error( _("Formula {0} omits a rule for the concept aspect").format( formula.xlinkLabel), "err", "xbrlfe:missingConceptRule") if (not (formula.hasRule(Aspect.SCHEME) or formula.source(Aspect.SCHEME)) or not (formula.hasRule(Aspect.VALUE) or formula.source(Aspect.VALUE))): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "entityIdentifier"): val.modelXbrl.error( _("Formula {0} entity identifier rule does not have a nearest source and does not have either a @scheme or a @value attribute" ).format(formula.xlinkLabel), "err", "xbrlfe:incompleteEntityIdentifierRule") else: val.modelXbrl.error( _("Formula {0} omits a rule for the entity identifier aspect" ).format(formula.xlinkLabel), "err", "xbrlfe:missingEntityIdentifierRule") if not (formula.hasRule(Aspect.PERIOD_TYPE) or formula.source(Aspect.PERIOD_TYPE)): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "period"): val.modelXbrl.error( _("Formula {0} period rule does not have a nearest source and does not have a child element" ).format(formula.xlinkLabel), "err", "xbrlfe:incompletePeriodRule") else: val.modelXbrl.error( _("Formula {0} omits a rule for the period aspect").format( formula.xlinkLabel), "err", "xbrlfe:missingPeriodRule") # for unit need to see if the qname is statically determinable to determine if numeric concept = val.modelXbrl.qnameConcepts.get( formula.evaluateRule(None, Aspect.CONCEPT)) if not concept: # is there a source with a static QName filter sourceFactVar = nameVariables.get(formula.source(Aspect.CONCEPT)) if isinstance(sourceFactVar, ModelFactVariable): for varFilterRels in (formula.groupFilterRelationships, sourceFactVar.filterRelationships): for varFilterRel in varFilterRels: filter = varFilterRel.toModelObject if isinstance( filter, ModelConceptName ): # relationship not constrained to real filters for conceptQname in filter.conceptQnames: concept = val.modelXbrl.qnameConcepts.get( conceptQname) if concept and concept.isNumeric: break if concept: # from concept aspect rule or from source factVariable concept Qname filter if concept.isNumeric: if not (formula.hasRule(Aspect.MULTIPLY_BY) or formula.hasRule( Aspect.DIVIDE_BY) or formula.source(Aspect.UNIT)): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "unit"): val.modelXbrl.error( _("Formula {0} unit rule does not have a source and does not have a child element" ).format(formula.xlinkLabel), "err", "xbrlfe:missingSAVForUnitRule") else: val.modelXbrl.error( _("Formula {0} omits a rule for the unit aspect" ).format(formula.xlinkLabel), "err", "xbrlfe:missingUnitRule") elif (formula.hasRule(Aspect.MULTIPLY_BY) or formula.hasRule(Aspect.DIVIDE_BY) or formula.source(Aspect.UNIT, acceptFormulaSource=False)): val.modelXbrl.error( _("Formula {0} has a rule for the unit aspect of a non-numeric concept {1}" ).format(formula.xlinkLabel, str(concept.qname)), "err", "xbrlfe:conflictingAspectRules") aspectPeriodType = formula.evaluateRule(None, Aspect.PERIOD_TYPE) if ((concept.periodType == "duration" and aspectPeriodType == "instant") or (concept.periodType == "instant" and aspectPeriodType in ("duration", "forever"))): val.modelXbrl.error( _("Formula {0} has a rule for the {2} period aspect of a {3} concept {1}" ).format(formula.xlinkLabel, str(concept.qname), aspectPeriodType, concept.periodType), "err", "xbrlfe:conflictingAspectRules") # check dimension elements for eltName, dim, badUsageErr, missingSavErr in ( ("explicitDimension", "explicit", "xbrlfe:badUsageOfExplicitDimensionRule", "xbrlfe:missingSAVForExplicitDimensionRule"), ("typedDimension", "typed", "xbrlfe:badUsageOfTypedDimensionRule", "xbrlfe:missingSAVForTypedDimensionRule")): for dimElt in XmlUtil.descendants(formula.element, XbrlConst.formula, eltName): dimQname = qname(dimElt, dimElt.getAttribute("dimension")) dimConcept = val.modelXbrl.qnameConcepts.get(dimQname) if dimQname and ( not dimConcept or (not dimConcept.isExplicitDimension if dim == "explicit" else not dimConcept.isTypedDimension)): val.modelXbrl.error( _("Formula {0} dimension attribute {1} on the {2} dimension rule contains a QName that does not identify an {2} dimension." ).format(formula.xlinkLabel, dimQname, dim), "err", badUsageErr) elif not XmlUtil.hasChild(dimElt, XbrlConst.formula, "*") and not formula.source( Aspect.DIMENSIONS, dimElt): val.modelXbrl.error( _("Formula {0} {1} dimension rule does not have any child elements and does not have a SAV for the {2} dimension that is identified by its dimension attribute." ).format(formula.xlinkLabel, dim, dimQname), "err", missingSavErr) # check aspect model expectations if formula.aspectModel == "non-dimensional": unexpectedElts = XmlUtil.descendants( formula.element, XbrlConst.formula, ("explicitDimension", "typedDimension")) if unexpectedElts: val.modelXbrl.error( _("Formula {0} aspect model, {1}, includes an rule for aspect not defined in this aspect model: {2}" ).format( formula.xlinkLabel, formula.aspectModel, ", ".join([elt.localName for elt in unexpectedElts])), "err", "xbrlfe:unrecognisedAspectRule") # check source qnames for sourceElt in ([formula.element] + XmlUtil.descendants( formula.element, XbrlConst.formula, "*", "source", "*")): if sourceElt.hasAttribute("source"): qnSource = qname(sourceElt, sourceElt.getAttribute("source"), noPrefixIsNoNamespace=True) if qnSource == XbrlConst.qnFormulaUncovered: if formula.implicitFiltering != "true": val.modelXbrl.error( _("Formula {0}, not implicit filtering element has formulaUncovered source: {1}" ).format(formula.xlinkLabel, sourceElt.localName), "err", "xbrlfe:illegalUseOfUncoveredQName") elif qnSource not in nameVariables: val.modelXbrl.error( _("Variable set {0}, source {1} is not in the variable set" ).format(formula.xlinkLabel, qnSource), "err", "xbrlfe:nonexistentSourceVariable") else: factVariable = nameVariables.get(qnSource) if not isinstance(factVariable, ModelFactVariable): val.modelXbrl.error( _("Variable set {0}, source {1} not a factVariable but is a {2}" ).format(formula.xlinkLabel, qnSource, factVariable.localName), "err", "xbrlfe:nonexistentSourceVariable") elif factVariable.fallbackValue is not None: val.modelXbrl.error( _("Formula {0}: source {1} is a fact variable that has a fallback value" ).format(formula.xlinkLabel, str(qnSource)), "err", "xbrlfe:bindEmptySourceVariable") elif sourceElt.localName == "formula" and factVariable.bindAsSequence == "true": val.modelXbrl.error( _("Formula {0}: formula source {1} is a fact variable that binds as a sequence" ).format(formula.xlinkLabel, str(qnSource)), "err", "xbrlfe:defaultAspectValueConflicts")
def checkFormulaRules(val, formula, nameVariables): from arelle.ModelFormulaObject import (Aspect) if not (formula.hasRule(Aspect.CONCEPT) or formula.source(Aspect.CONCEPT)): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "concept"): val.modelXbrl.error(_("Formula {0} concept rule does not have a nearest source and does not have a child element").format(formula.xlinkLabel), "err", "xbrlfe:incompleteConceptRule") else: val.modelXbrl.error(_("Formula {0} omits a rule for the concept aspect").format(formula.xlinkLabel), "err", "xbrlfe:missingConceptRule") if (not (formula.hasRule(Aspect.SCHEME) or formula.source(Aspect.SCHEME)) or not (formula.hasRule(Aspect.VALUE) or formula.source(Aspect.VALUE))): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "entityIdentifier"): val.modelXbrl.error(_("Formula {0} entity identifier rule does not have a nearest source and does not have either a @scheme or a @value attribute").format(formula.xlinkLabel), "err", "xbrlfe:incompleteEntityIdentifierRule") else: val.modelXbrl.error(_("Formula {0} omits a rule for the entity identifier aspect").format(formula.xlinkLabel), "err", "xbrlfe:missingEntityIdentifierRule") if not (formula.hasRule(Aspect.PERIOD_TYPE) or formula.source(Aspect.PERIOD_TYPE)): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "period"): val.modelXbrl.error(_("Formula {0} period rule does not have a nearest source and does not have a child element").format(formula.xlinkLabel), "err", "xbrlfe:incompletePeriodRule") else: val.modelXbrl.error(_("Formula {0} omits a rule for the period aspect").format(formula.xlinkLabel), "err", "xbrlfe:missingPeriodRule") # for unit need to see if the qname is statically determinable to determine if numeric concept = val.modelXbrl.qnameConcepts.get(formula.evaluateRule(None, Aspect.CONCEPT)) if not concept: # is there a source with a static QName filter sourceFactVar = nameVariables.get(formula.source(Aspect.CONCEPT)) if isinstance(sourceFactVar, ModelFactVariable): for varFilterRels in (formula.groupFilterRelationships, sourceFactVar.filterRelationships): for varFilterRel in varFilterRels: filter = varFilterRel.toModelObject if isinstance(filter,ModelConceptName): # relationship not constrained to real filters for conceptQname in filter.conceptQnames: concept = val.modelXbrl.qnameConcepts.get(conceptQname) if concept and concept.isNumeric: break if concept: # from concept aspect rule or from source factVariable concept Qname filter if concept.isNumeric: if not (formula.hasRule(Aspect.MULTIPLY_BY) or formula.hasRule(Aspect.DIVIDE_BY) or formula.source(Aspect.UNIT)): if XmlUtil.hasDescendant(formula.element, XbrlConst.formula, "unit"): val.modelXbrl.error(_("Formula {0} unit rule does not have a source and does not have a child element").format(formula.xlinkLabel), "err", "xbrlfe:missingSAVForUnitRule") else: val.modelXbrl.error(_("Formula {0} omits a rule for the unit aspect").format(formula.xlinkLabel), "err", "xbrlfe:missingUnitRule") elif (formula.hasRule(Aspect.MULTIPLY_BY) or formula.hasRule(Aspect.DIVIDE_BY) or formula.source(Aspect.UNIT, acceptFormulaSource=False)): val.modelXbrl.error(_("Formula {0} has a rule for the unit aspect of a non-numeric concept {1}").format(formula.xlinkLabel, str(concept.qname)), "err", "xbrlfe:conflictingAspectRules") aspectPeriodType = formula.evaluateRule(None, Aspect.PERIOD_TYPE) if ((concept.periodType == "duration" and aspectPeriodType == "instant") or (concept.periodType == "instant" and aspectPeriodType in ("duration","forever"))): val.modelXbrl.error(_("Formula {0} has a rule for the {2} period aspect of a {3} concept {1}").format(formula.xlinkLabel, str(concept.qname), aspectPeriodType, concept.periodType), "err", "xbrlfe:conflictingAspectRules") # check dimension elements for eltName, dim, badUsageErr, missingSavErr in (("explicitDimension", "explicit", "xbrlfe:badUsageOfExplicitDimensionRule", "xbrlfe:missingSAVForExplicitDimensionRule"), ("typedDimension", "typed", "xbrlfe:badUsageOfTypedDimensionRule", "xbrlfe:missingSAVForTypedDimensionRule")): for dimElt in XmlUtil.descendants(formula.element, XbrlConst.formula, eltName): dimQname = qname(dimElt, dimElt.getAttribute("dimension")) dimConcept = val.modelXbrl.qnameConcepts.get(dimQname) if dimQname and (not dimConcept or (not dimConcept.isExplicitDimension if dim == "explicit" else not dimConcept.isTypedDimension)): val.modelXbrl.error(_("Formula {0} dimension attribute {1} on the {2} dimension rule contains a QName that does not identify an {2} dimension.").format(formula.xlinkLabel, dimQname, dim), "err", badUsageErr) elif not XmlUtil.hasChild(dimElt, XbrlConst.formula, "*") and not formula.source(Aspect.DIMENSIONS, dimElt): val.modelXbrl.error(_("Formula {0} {1} dimension rule does not have any child elements and does not have a SAV for the {2} dimension that is identified by its dimension attribute.").format(formula.xlinkLabel, dim, dimQname), "err", missingSavErr) # check aspect model expectations if formula.aspectModel == "non-dimensional": unexpectedElts = XmlUtil.descendants(formula.element, XbrlConst.formula, ("explicitDimension", "typedDimension")) if unexpectedElts: val.modelXbrl.error(_("Formula {0} aspect model, {1}, includes an rule for aspect not defined in this aspect model: {2}").format( formula.xlinkLabel, formula.aspectModel, ", ".join([elt.localName for elt in unexpectedElts])), "err", "xbrlfe:unrecognisedAspectRule") # check source qnames for sourceElt in ([formula.element] + XmlUtil.descendants(formula.element, XbrlConst.formula, "*", "source","*")): if sourceElt.hasAttribute("source"): qnSource = qname(sourceElt, sourceElt.getAttribute("source"), noPrefixIsNoNamespace=True) if qnSource == XbrlConst.qnFormulaUncovered: if formula.implicitFiltering != "true": val.modelXbrl.error(_("Formula {0}, not implicit filtering element has formulaUncovered source: {1}").format( formula.xlinkLabel, sourceElt.localName), "err", "xbrlfe:illegalUseOfUncoveredQName") elif qnSource not in nameVariables: val.modelXbrl.error(_("Variable set {0}, source {1} is not in the variable set").format( formula.xlinkLabel, qnSource), "err", "xbrlfe:nonexistentSourceVariable") else: factVariable = nameVariables.get(qnSource) if not isinstance(factVariable, ModelFactVariable): val.modelXbrl.error(_("Variable set {0}, source {1} not a factVariable but is a {2}").format( formula.xlinkLabel, qnSource, factVariable.localName), "err", "xbrlfe:nonexistentSourceVariable") elif factVariable.fallbackValue is not None: val.modelXbrl.error(_("Formula {0}: source {1} is a fact variable that has a fallback value").format(formula.xlinkLabel, str(qnSource)), "err", "xbrlfe:bindEmptySourceVariable") elif sourceElt.localName == "formula" and factVariable.bindAsSequence == "true": val.modelXbrl.error(_("Formula {0}: formula source {1} is a fact variable that binds as a sequence").format(formula.xlinkLabel, str(qnSource)), "err", "xbrlfe:defaultAspectValueConflicts")