def scale_values(facts): """ Accepts a list of facts and returns a list of values from those facts re-scaled by the least precise fact's precision Makes no guarantees about data type, known types are integers, floats, and Decimals Precision attribute is not allowed in EFM 6.5.17, so this assumes decimals-use only. :param facts: list of facts to scale :type facts: list [:class:'~arelle.ModelInstanceObject.ModelFact'] :return: list of values rescaled by least precise fact's precision :rtype: list [:class:'~decimals.Decimal'] """ decimals = set([fact.decimals for fact in facts]) if len(decimals) == 1: # if all decimals match, then just return xValue # (their already scaled value) return [fact.xValue for fact in facts] # Redo the set getting only numbers decimals = { int(fact.decimals) for fact in facts if fact.decimals != 'INF' and fact.decimals is not None } min_decimals_str = str(min(decimals)) return [ ValidateXbrlCalcs.roundFact( DecimalOverriddenFact(fact, min_decimals_str), inferDecimals=True ) for fact in facts ]
def scale_values(facts): """ Accepts a list of facts and returns a list of values from those facts re-scaled by the least precise fact's precision Makes no guarantees about data type, known types are integers, floats, and Decimals Precision attribute is not allowed in EFM 6.5.17, so this assumes decimals-use only. :param facts: list of facts to scale :type facts: list [:class:'~arelle.ModelInstanceObject.ModelFact'] :return: list of values rescaled by least precise fact's precision :rtype: list [:class:'~decimals.Decimal'] """ decimals = set([fact.decimals for fact in facts]) if len(decimals) == 1: # if all decimals match, then just return xValue # (their already scaled value) return [fact.xValue for fact in facts] # Redo the set getting only numbers decimals = { int(fact.decimals) for fact in facts if fact.decimals != 'INF' and fact.decimals is not None } min_decimals_str = str(min(decimals)) return [ ValidateXbrlCalcs.roundFact(DecimalOverriddenFact( fact, min_decimals_str), inferDecimals=True) for fact in facts ]
def validate(self, modelXbrl, parameters=None): self.parameters = parameters self.precisionPattern = re.compile("^([0-9]+|INF)$") self.decimalsPattern = re.compile("^(-?[0-9]+|INF)$") self.isoCurrencyPattern = re.compile(r"^[A-Z]{3}$") self.modelXbrl = modelXbrl self.validateDisclosureSystem = modelXbrl.modelManager.validateDisclosureSystem self.disclosureSystem = modelXbrl.modelManager.disclosureSystem self.validateEFM = self.validateDisclosureSystem and self.disclosureSystem.EFM self.validateGFM = self.validateDisclosureSystem and self.disclosureSystem.GFM self.validateEFMorGFM = self.validateDisclosureSystem and self.disclosureSystem.EFMorGFM self.validateHMRC = self.validateDisclosureSystem and self.disclosureSystem.HMRC self.validateSBRNL = self.validateDisclosureSystem and self.disclosureSystem.SBRNL self.validateXmlLang = self.validateDisclosureSystem and self.disclosureSystem.xmlLangPattern self.validateCalcLB = modelXbrl.modelManager.validateCalcLB self.validateInferDecimals = modelXbrl.modelManager.validateInferDecimals # xlink validation modelXbrl.profileStat(None) modelXbrl.modelManager.showStatus(_("validating links")) modelLinks = set() self.remoteResourceLocElements = set() self.genericArcArcroles = set() for baseSetExtLinks in modelXbrl.baseSets.values(): for baseSetExtLink in baseSetExtLinks: modelLinks.add(baseSetExtLink) # ext links are unique (no dups) for modelLink in modelLinks: fromToArcs = {} locLabels = {} resourceLabels = {} resourceArcTos = [] for arcElt in modelLink.iterchildren(): if isinstance(arcElt,ModelObject): xlinkType = arcElt.get("{http://www.w3.org/1999/xlink}type") # locator must have an href if xlinkType == "locator": if arcElt.get("{http://www.w3.org/1999/xlink}href") is None: modelXbrl.error("xlink:locatorHref", _("Xlink locator %(xlinkLabel)s missing href in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=arcElt.get("{http://www.w3.org/1999/xlink}label")) locLabels[arcElt.get("{http://www.w3.org/1999/xlink}label")] = arcElt elif xlinkType == "resource": resourceLabels[arcElt.get("{http://www.w3.org/1999/xlink}label")] = arcElt # can be no duplicated arcs between same from and to elif xlinkType == "arc": fromLabel = arcElt.get("{http://www.w3.org/1999/xlink}from") toLabel = arcElt.get("{http://www.w3.org/1999/xlink}to") fromTo = (fromLabel,toLabel) if fromTo in fromToArcs: modelXbrl.error("xlink:dupArcs", _("Duplicate xlink arcs in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel) else: fromToArcs[fromTo] = arcElt if arcElt.namespaceURI == XbrlConst.link: if arcElt.localName in arcNamesTo21Resource: #("labelArc","referenceArc"): resourceArcTos.append((toLabel, arcElt.get("use"), arcElt)) elif self.isGenericArc(arcElt): arcrole = arcElt.get("{http://www.w3.org/1999/xlink}arcrole") self.genericArcArcroles.add(arcrole) if arcrole in (XbrlConst.elementLabel, XbrlConst.elementReference): resourceArcTos.append((toLabel, arcrole, arcElt)) # values of type (not needed for validating parsers) if xlinkType not in xlinkTypeValues: # ("", "simple", "extended", "locator", "arc", "resource", "title", "none"): modelXbrl.error("xlink:type", _("Xlink type %(xlinkType)s invalid in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkType=xlinkType) # values of actuate (not needed for validating parsers) xlinkActuate = arcElt.get("{http://www.w3.org/1999/xlink}actuate") if xlinkActuate not in xlinkActuateValues: # ("", "onLoad", "onRequest", "other", "none"): modelXbrl.error("xlink:actuate", _("Actuate %(xlinkActuate)s invalid in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkActuate=xlinkActuate) # values of show (not needed for validating parsers) xlinkShow = arcElt.get("{http://www.w3.org/1999/xlink}show") if xlinkShow not in xlinkShowValues: # ("", "new", "replace", "embed", "other", "none"): modelXbrl.error("xlink:show", _("Show %(xlinkShow)s invalid in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkShow=xlinkShow) # check from, to of arcs have a resource or loc for fromTo, arcElt in fromToArcs.items(): fromLabel, toLabel = fromTo for name, value, sect in (("from", fromLabel, "3.5.3.9.2"),("to",toLabel, "3.5.3.9.3")): if value not in locLabels and value not in resourceLabels: modelXbrl.error("xbrl.{0}:arcResource".format(sect), _("Arc in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s attribute '%(attribute)s' has no matching loc or resource label"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel, attribute=name) if arcElt.localName == "footnoteArc" and arcElt.namespaceURI == XbrlConst.link and \ arcElt.get("{http://www.w3.org/1999/xlink}arcrole") == XbrlConst.factFootnote: if fromLabel not in locLabels: modelXbrl.error("xbrl.4.11.1.3.1:factFootnoteArcFrom", _("Footnote arc in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s \"from\" is not a loc"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel) if toLabel not in resourceLabels or qname(resourceLabels[toLabel]) != XbrlConst.qnLinkFootnote: modelXbrl.error("xbrl.4.11.1.3.1:factFootnoteArcTo", _("Footnote arc in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s \"to\" is not a footnote resource"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel) # check unprohibited label arcs to remote locs for resourceArcTo in resourceArcTos: resourceArcToLabel, resourceArcUse, arcElt = resourceArcTo if resourceArcToLabel in locLabels: toLabel = locLabels[resourceArcToLabel] if resourceArcUse == "prohibited": self.remoteResourceLocElements.add(toLabel) else: modelXbrl.error("xbrl.5.2.2.3:labelArcRemoteResource", _("Unprohibited labelArc in extended link %(linkrole)s has illegal remote resource loc labeled %(xlinkLabel)s href %(xlinkHref)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=resourceArcToLabel, xlinkHref=toLabel.get("{http://www.w3.org/1999/xlink}href")) elif resourceArcToLabel in resourceLabels: toResource = resourceLabels[resourceArcToLabel] if resourceArcUse == XbrlConst.elementLabel: if not self.isGenericLabel(toResource): modelXbrl.error("xbrlle.2.1.1:genericLabelTarget", _("Generic label arc in extended link %(linkrole)s to %(xlinkLabel)s must target a generic label"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=resourceArcToLabel) elif resourceArcUse == XbrlConst.elementReference: if not self.isGenericReference(toResource): modelXbrl.error("xbrlre.2.1.1:genericReferenceTarget", _("Generic reference arc in extended link %(linkrole)s to %(xlinkLabel)s must target a generic reference"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=resourceArcToLabel) resourceArcTos = None # dereference arcs modelXbrl.profileStat(_("validateLinks")) modelXbrl.dimensionDefaultConcepts = {} modelXbrl.qnameDimensionDefaults = {} modelXbrl.qnameDimensionContextElement = {} # check base set cycles, dimensions modelXbrl.modelManager.showStatus(_("validating relationship sets")) for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if arcrole.startswith("XBRL-") or ELR is None or \ linkqname is None or arcqname is None: continue elif arcrole in XbrlConst.standardArcroleCyclesAllowed: # TODO: table should be in this module, where it is used cyclesAllowed, specSect = XbrlConst.standardArcroleCyclesAllowed[arcrole] elif arcrole in self.modelXbrl.arcroleTypes and len(self.modelXbrl.arcroleTypes[arcrole]) > 0: cyclesAllowed = self.modelXbrl.arcroleTypes[arcrole][0].cyclesAllowed if arcrole in self.genericArcArcroles: specSect = "xbrlgene:violatedCyclesConstraint" else: specSect = "xbrl.5.1.4.3:cycles" else: cyclesAllowed = "any" specSect = None if cyclesAllowed != "any" or arcrole in (XbrlConst.summationItem,) \ or arcrole in self.genericArcArcroles \ or arcrole.startswith(XbrlConst.formulaStartsWith): relsSet = modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname) if cyclesAllowed != "any" and \ (XbrlConst.isStandardExtLinkQname(linkqname) and XbrlConst.isStandardArcQname(arcqname)) \ or arcrole in self.genericArcArcroles: noUndirected = cyclesAllowed == "none" fromRelationships = relsSet.fromModelObjects() for relFrom, rels in fromRelationships.items(): cycleFound = self.fwdCycle(relsSet, rels, noUndirected, {relFrom}) if cycleFound is not None: path = str(relFrom.qname) + " " + " - ".join( "{0}:{1} {2}".format(rel.modelDocument.basename, rel.sourceline, rel.toModelObject.qname) for rel in reversed(cycleFound[1:])) modelXbrl.error(specSect, _("Relationships have a %(cycle)s cycle in arcrole %(arcrole)s \nlink role %(linkrole)s \nlink %(linkname)s, \narc %(arcname)s, \npath %(path)s"), modelObject=cycleFound[1:], cycle=cycleFound[0], path=path, arcrole=arcrole, linkrole=ELR, linkname=linkqname, arcname=arcqname), break # check calculation arcs for weight issues (note calc arc is an "any" cycles) if arcrole == XbrlConst.summationItem: for modelRel in relsSet.modelRelationships: weight = modelRel.weight fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept is not None and toConcept is not None: if weight == 0: modelXbrl.error("xbrl.5.2.5.2.1:zeroWeight", _("Calculation relationship has zero weight from %(source)s to %(target)s in link role %(linkrole)s"), modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR), fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if (fromBalance == toBalance and weight < 0) or \ (fromBalance != toBalance and weight > 0): modelXbrl.error("xbrl.5.1.1.2:balanceCalcWeightIllegal" + ("Negative" if weight < 0 else "Positive"), _("Calculation relationship has illegal weight %(weight)s from %(source)s, %(sourceBalance)s, to %(target)s, %(targetBalance)s, in link role %(linkrole)s (per 5.1.1.2 Table 6)"), modelObject=modelRel, weight=weight, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR, sourceBalance=fromBalance, targetBalance=toBalance) if not fromConcept.isNumeric or not toConcept.isNumeric: modelXbrl.error("xbrl.5.2.5.2:nonNumericCalc", _("Calculation relationship has illegal concept from %(source)s%(sourceNumericDecorator)s to %(target)s%(targetNumericDecorator)s in link role %(linkrole)s"), modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR, sourceNumericDecorator="" if fromConcept.isNumeric else _(" (non-numeric)"), targetNumericDecorator="" if toConcept.isNumeric else _(" (non-numeric)")) # check presentation relationships for preferredLabel issues elif arcrole == XbrlConst.parentChild: for modelRel in relsSet.modelRelationships: preferredLabel = modelRel.preferredLabel toConcept = modelRel.toModelObject if preferredLabel is not None and toConcept is not None and \ toConcept.label(preferredLabel=preferredLabel,fallbackToQname=False) is None: modelXbrl.error("xbrl.5.2.4.2.1:preferredLabelMissing", _("Presentation relationship from %(source)s to %(target)s in link role %(linkrole)s missing preferredLabel %(preferredLabel)s"), modelObject=modelRel, source=modelRel.fromModelObject.qname, target=toConcept.qname, linkrole=ELR, preferredLabel=preferredLabel) # check essence-alias relationships elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept is not None and toConcept is not None: if fromConcept.type != toConcept.type or fromConcept.periodType != toConcept.periodType: modelXbrl.error("xbrl.5.2.6.2.2:essenceAliasTypes", _("Essence-alias relationship from %(source)s to %(target)s in link role %(linkrole)s has different types or periodTypes"), modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR) fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if fromBalance and toBalance and fromBalance != toBalance: modelXbrl.error("xbrl.5.2.6.2.2:essenceAliasBalance", _("Essence-alias relationship from %(source)s to %(target)s in link role %(linkrole)s has different balances")).format( modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR) elif modelXbrl.hasXDT and arcrole.startswith(XbrlConst.dimStartsWith): ValidateXbrlDimensions.checkBaseSet(self, arcrole, ELR, relsSet) elif (modelXbrl.hasFormulae or modelXbrl.hasTableRendering) and arcrole.startswith(XbrlConst.formulaStartsWith): ValidateFormula.checkBaseSet(self, arcrole, ELR, relsSet) modelXbrl.isDimensionsValidated = True modelXbrl.profileStat(_("validateRelationships")) # instance checks modelXbrl.modelManager.showStatus(_("validating instance")) self.footnoteRefs = set() if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE or \ modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRL: self.factsWithDeprecatedIxNamespace = [] self.checkFacts(modelXbrl.facts) if self.factsWithDeprecatedIxNamespace: self.modelXbrl.info("arelle:info", _("%(count)s facts have deprecated transformation namespace %(namespace)s"), modelObject=self.factsWithDeprecatedIxNamespace, count=len(self.factsWithDeprecatedIxNamespace), namespace=FunctionIxt.deprecatedNamespaceURI) del self.factsWithDeprecatedIxNamespace #instance checks for cntx in modelXbrl.contexts.values(): if cntx.isStartEndPeriod: try: if cntx.endDatetime <= cntx.startDatetime: self.modelXbrl.error("xbrl.4.7.2:periodStartBeforeEnd", _("Context %(contextID)s must have startDate less than endDate"), modelObject=cntx, contextID=cntx.id) except (TypeError, ValueError) as err: self.modelXbrl.error("xbrl.4.7.2:contextDateError", _("Context %(contextID) startDate or endDate: %(error)s"), modelObject=cntx, contextID=cntx.id, error=err) elif cntx.isInstantPeriod: try: cntx.instantDatetime #parse field except ValueError as err: self.modelXbrl.error("xbrl.4.7.2:contextDateError", _("Context %(contextID)s instant date: %(error)s"), modelObject=cntx, contextID=cntx.id, error=err) self.segmentScenario(cntx.segment, cntx.id, "segment", "4.7.3.2") self.segmentScenario(cntx.scenario, cntx.id, "scenario", "4.7.4") for unit in modelXbrl.units.values(): mulDivMeasures = unit.measures if mulDivMeasures: for measures in mulDivMeasures: for measure in measures: if measure.namespaceURI == XbrlConst.xbrli and not \ measure in (XbrlConst.qnXbrliPure, XbrlConst.qnXbrliShares): self.modelXbrl.error("xbrl.4.8.2:measureElement", _("Unit %(unitID)s illegal measure: %(measure)s"), modelObject=unit, unitID=unit.id, measure=measure) for numeratorMeasure in mulDivMeasures[0]: if numeratorMeasure in mulDivMeasures[1]: self.modelXbrl.error("xbrl.4.8.4:measureBothNumDenom", _("Unit %(unitID)s numerator measure: %(measure)s also appears as denominator measure"), modelObject=unit, unitID=unit.id, measure=numeratorMeasure) modelXbrl.profileStat(_("validateInstance")) if modelXbrl.hasXDT: modelXbrl.modelManager.showStatus(_("validating dimensions")) ''' uncomment if using otherFacts in checkFact dimCheckableFacts = set(f for f in modelXbrl.factsInInstance if f.concept.isItem and f.context is not None) while (dimCheckableFacts): # check one and all of its compatible family members f = dimCheckableFacts.pop() ValidateXbrlDimensions.checkFact(self, f, dimCheckableFacts) del dimCheckableFacts ''' self.checkFactDimensions(modelXbrl.facts) # check fact dimensions in document order for cntx in modelXbrl.contexts.values(): ValidateXbrlDimensions.checkContext(self,cntx) modelXbrl.profileStat(_("validateDimensions")) # dimensional validity #concepts checks modelXbrl.modelManager.showStatus(_("validating concepts")) for concept in modelXbrl.qnameConcepts.values(): conceptType = concept.type if XbrlConst.isStandardNamespace(concept.qname.namespaceURI) or \ not concept.modelDocument.inDTS: continue if concept.isTuple: # must be global if not concept.getparent().localName == "schema": self.modelXbrl.error("xbrl.4.9:tupleGloballyDeclared", _("Tuple %(concept)s must be declared globally"), modelObject=concept, concept=concept.qname) if concept.periodType: self.modelXbrl.error("xbrl.4.9:tuplePeriodType", _("Tuple %(concept)s must not have periodType"), modelObject=concept, concept=concept.qname) if concept.balance: self.modelXbrl.error("xbrl.4.9:tupleBalance", _("Tuple %(concept)s must not have balance"), modelObject=concept, concept=concept.qname) if conceptType is not None: # check attribute declarations for attribute in conceptType.attributes.values(): if attribute.qname.namespaceURI in (XbrlConst.xbrli, XbrlConst.link, XbrlConst.xlink, XbrlConst.xl): self.modelXbrl.error("xbrl.4.9:tupleAttribute", _("Tuple %(concept)s must not have attribute in this namespace %(attribute)s"), modelObject=concept, concept=concept.qname, attribute=attribute.qname) # check for mixed="true" or simple content if XmlUtil.descendantAttr(conceptType, XbrlConst.xsd, ("complexType", "complexContent"), "mixed") == "true": self.modelXbrl.error("xbrl.4.9:tupleMixedContent", _("Tuple %(concept)s must not have mixed content"), modelObject=concept, concept=concept.qname) if XmlUtil.descendant(conceptType, XbrlConst.xsd, "simpleContent"): self.modelXbrl.error("xbrl.4.9:tupleSimpleContent", _("Tuple %(concept)s must not have simple content"), modelObject=concept, concept=concept.qname) # child elements must be item or tuple for elementQname in conceptType.elements: childConcept = self.modelXbrl.qnameConcepts.get(elementQname) if childConcept is None: self.modelXbrl.error("xbrl.4.9:tupleElementUndefined", _("Tuple %(concept)s element %(tupleElement)s not defined"), modelObject=concept, concept=str(concept.qname), tupleElement=elementQname) elif not (childConcept.isItem or childConcept.isTuple or # isItem/isTuple do not include item or tuple itself childConcept.qname == XbrlConst.qnXbrliItem or # subs group includes item as member childConcept.qname == XbrlConst.qnXbrliTuple): self.modelXbrl.error("xbrl.4.9:tupleElementItemOrTuple", _("Tuple %(concept)s must not have element %(tupleElement)s not an item or tuple"), modelObject=concept, concept=concept.qname, tupleElement=elementQname) elif concept.isItem: if concept.periodType not in periodTypeValues: #("instant","duration"): self.modelXbrl.error("xbrl.5.1.1.1:itemPeriodType", _("Item %(concept)s must have a valid periodType"), modelObject=concept, concept=concept.qname) if concept.isMonetary: if concept.balance not in balanceValues: #(None, "credit","debit"): self.modelXbrl.error("xbrl.5.1.1.2:itemBalance", _("Item %(concept)s must have a valid balance %(balance)s"), modelObject=concept, concept=concept.qname, balance=concept.balance) else: if concept.balance: self.modelXbrl.error("xbrl.5.1.1.2:itemBalance", _("Item %(concept)s may not have a balance"), modelObject=concept, concept=concept.qname) if concept.baseXbrliType not in baseXbrliTypes: self.modelXbrl.error("xbrl.5.1.1.3:itemType", _("Item %(concept)s type %(itemType)s invalid"), modelObject=concept, concept=concept.qname, itemType=concept.baseXbrliType) if modelXbrl.hasXDT: if concept.isHypercubeItem and not concept.abstract == "true": self.modelXbrl.error("xbrldte:HypercubeElementIsNotAbstractError", _("Hypercube item %(concept)s must be abstract"), modelObject=concept, concept=concept.qname) elif concept.isDimensionItem and not concept.abstract == "true": self.modelXbrl.error("xbrldte:DimensionElementIsNotAbstractError", _("Dimension item %(concept)s must be abstract"), modelObject=concept, concept=concept.qname) if modelXbrl.hasXDT: ValidateXbrlDimensions.checkConcept(self, concept) modelXbrl.profileStat(_("validateConcepts")) modelXbrl.modelManager.showStatus(_("validating DTS")) self.DTSreferenceResourceIDs = {} ValidateXbrlDTS.checkDTS(self, modelXbrl.modelDocument, []) del self.DTSreferenceResourceIDs global validateUniqueParticleAttribution if validateUniqueParticleAttribution is None: from arelle.XmlValidateParticles import validateUniqueParticleAttribution for modelType in modelXbrl.qnameTypes.values(): validateUniqueParticleAttribution(modelXbrl, modelType.particlesList, modelType) modelXbrl.profileStat(_("validateDTS")) if self.validateCalcLB: modelXbrl.modelManager.showStatus(_("Validating instance calculations")) ValidateXbrlCalcs.validate(modelXbrl, inferDecimals=self.validateInferDecimals) modelXbrl.profileStat(_("validateCalculations")) if (modelXbrl.modelManager.validateUtr or (self.parameters and self.parameters.get(qname("forceUtrValidation",noPrefixIsNoNamespace=True),(None,"false"))[1] == "true") or #(self.validateEFM and #any((concept.namespaceURI in self.disclosureSystem.standardTaxonomiesDict) # for concept in self.modelXbrl.nameConcepts.get("UTR",())))): (self.validateEFM and any(modelDoc.definesUTR for modelDoc in self.modelXbrl.urlDocs.values()))): ValidateUtr.validate(modelXbrl) modelXbrl.profileStat(_("validateUTR")) if modelXbrl.hasFormulae or modelXbrl.modelRenderingTables: ValidateFormula.validate(self, statusMsg=_("compiling formulae and rendering tables") if (modelXbrl.hasFormulae and modelXbrl.modelRenderingTables) else (_("compiling formulae") if modelXbrl.hasFormulae else _("compiling rendering tables"))) modelXbrl.modelManager.showStatus(_("ready"), 2000)
def validate(self, modelXbrl, parameters=None): self.parameters = parameters self.NCnamePattern = re.compile( "^[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]" r"[_\-\." "\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*$" ) self.precisionPattern = re.compile("^([0-9]+|INF)$") self.decimalsPattern = re.compile("^(-?[0-9]+|INF)$") self.isoCurrencyPattern = re.compile(r"^[A-Z]{3}$") self.modelXbrl = modelXbrl self.validateDisclosureSystem = modelXbrl.modelManager.validateDisclosureSystem self.disclosureSystem = modelXbrl.modelManager.disclosureSystem self.validateEFM = self.validateDisclosureSystem and self.disclosureSystem.EFM self.validateGFM = self.validateDisclosureSystem and self.disclosureSystem.GFM self.validateEFMorGFM = self.validateDisclosureSystem and self.disclosureSystem.EFMorGFM self.validateHMRC = self.validateDisclosureSystem and self.disclosureSystem.HMRC self.validateSBRNL = self.validateDisclosureSystem and self.disclosureSystem.SBRNL self.validateXmlLang = self.validateDisclosureSystem and self.disclosureSystem.xmlLangPattern self.validateCalcLB = modelXbrl.modelManager.validateCalcLB self.validateInferDecimals = modelXbrl.modelManager.validateInferDecimals # xlink validation modelXbrl.modelManager.showStatus(_("validating links")) modelLinks = set() self.remoteResourceLocElements = set() self.genericArcArcroles = set() for baseSetExtLinks in modelXbrl.baseSets.values(): for baseSetExtLink in baseSetExtLinks: modelLinks.add( baseSetExtLink) # ext links are unique (no dups) for modelLink in modelLinks: fromToArcs = {} locLabels = {} resourceLabels = {} resourceArcTos = [] for arcElt in modelLink.element.childNodes: if arcElt.nodeType == 1: xlinkType = arcElt.getAttributeNS(XbrlConst.xlink, "type") # locator must have an href if xlinkType == "locator": if not arcElt.hasAttributeNS(XbrlConst.xlink, "href"): modelXbrl.error( _("Linkbase {0} extended link {1} locator {2} missing href" ).format( modelLink.modelDocument.basename, modelLink.role, arcElt.hasAttributeNS( XbrlConst.xlink, "label")), "err", "xlink:locatorHref") locLabels[arcElt.getAttributeNS( XbrlConst.xlink, "label")] = arcElt elif xlinkType == "resource": resourceLabels[arcElt.getAttributeNS( XbrlConst.xlink, "label")] = arcElt # can be no duplicated arcs between same from and to elif xlinkType == "arc": fromLabel = arcElt.getAttributeNS( XbrlConst.xlink, "from") toLabel = arcElt.getAttributeNS(XbrlConst.xlink, "to") fromTo = (fromLabel, toLabel) if fromTo in fromToArcs: modelXbrl.error( _("Linkbase {0} extended link {1} duplicate arcs from {2} to {3}" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel), "err", "xlink:dupArcs") else: fromToArcs[fromTo] = arcElt if arcElt.namespaceURI == XbrlConst.link: if arcElt.localName in arcNamesTo21Resource: #("labelArc","referenceArc"): resourceArcTos.append( (toLabel, arcElt.getAttribute("use"))) elif self.isGenericArc(arcElt): arcrole = arcElt.getAttributeNS( XbrlConst.xlink, "arcrole") self.genericArcArcroles.add(arcrole) if arcrole in (XbrlConst.elementLabel, XbrlConst.elementReference): resourceArcTos.append((toLabel, arcrole)) # values of type (not needed for validating parsers) if xlinkType not in xlinkTypeValues: # ("", "simple", "extended", "locator", "arc", "resource", "title", "none"): modelXbrl.error( _("Linkbase {0} extended link {1} type {2} invalid" ).format(modelLink.modelDocument.basename, modelLink.role, xlinkType), "err", "xlink:type") # values of actuate (not needed for validating parsers) xlinkActuate = arcElt.getAttributeNS( XbrlConst.xlink, "actuate") if xlinkActuate not in xlinkActuateValues: # ("", "onLoad", "onRequest", "other", "none"): modelXbrl.error( _("Linkbase {0} extended link {1} actuate {2} invalid" ).format(modelLink.modelDocument.basename, modelLink.role, xlinkActuate), "err", "xlink:actuate") # values of show (not needed for validating parsers) xlinkShow = arcElt.getAttributeNS(XbrlConst.xlink, "show") if xlinkShow not in xlinkShowValues: # ("", "new", "replace", "embed", "other", "none"): modelXbrl.error( _("Linkbase {0} extended link {1} show {2} invalid" ).format(modelLink.modelDocument.basename, modelLink.role, xlinkShow), "err", "xlink:show") # values of label, from, to (not needed for validating parsers) for name in xlinkLabelAttributes: # ("label", "from", "to"): value = arcElt.getAttributeNS(XbrlConst.xlink, name) if value != "" and not self.NCnamePattern.match(value): modelXbrl.error( _("Linkbase {0} extended link {1} element {2} {3} '{4}' not an NCname" ).format(modelLink.modelDocument.basename, modelLink.role, arcElt.tagName, name, value), "err", "xlink:{0}".format(name)) # check from, to of arcs have a resource or loc for fromTo, arcElt in fromToArcs.items(): fromLabel, toLabel in fromTo for name, value, sect in (("from", fromLabel, "3.5.3.9.2"), ("to", toLabel, "3.5.3.9.3")): if value not in locLabels and value not in resourceLabels: modelXbrl.error( _("Arc in linkbase {0} extended link {1} from {2} to {3} attribute \"{4}\" has no matching loc or resource label" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel, name), "err", "xbrl.{0}:arcResource".format(sect)) if arcElt.localName == "footnoteArc" and arcElt.namespaceURI == XbrlConst.link and \ arcElt.getAttributeNS(XbrlConst.xlink,"arcrole") == XbrlConst.factFootnote: if fromLabel not in locLabels: modelXbrl.error( _("FootnoteArc in {0} extended link {1} from {2} to {3} \"from\" is not a loc" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel), "err", "xbrl.4.11.1.3.1:factFootnoteArcFrom") if toLabel not in resourceLabels or qname( resourceLabels[toLabel] ) != XbrlConst.qnLinkFootnote: modelXbrl.error( _("FootnoteArc in {0} extended link {1} from {2} to {3} \"to\" is not a footnote resource" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel), "err", "xbrl.4.11.1.3.1:factFootnoteArcTo") # check unprohibited label arcs to remote locs for resourceArcTo in resourceArcTos: resourceArcToLabel, resourceArcUse = resourceArcTo if resourceArcToLabel in locLabels: toLabel = locLabels[resourceArcToLabel] if resourceArcUse == "prohibited": self.remoteResourceLocElements.add(toLabel) else: modelXbrl.error( _("Unprohibited labelArc in linkbase {0} extended link {1} has illegal remote resource loc labeled {2} href {3}" ).format( modelLink.modelDocument.basename, modelLink.role, resourceArcToLabel, toLabel.getAttributeNS( XbrlConst.xlink, "href")), "err", "xbrl.5.2.2.3:labelArcRemoteResource") elif resourceArcToLabel in resourceLabels: toResource = resourceLabels[resourceArcToLabel] if resourceArcUse == XbrlConst.elementLabel: if not self.isGenericLabel(toResource): modelXbrl.error( _("Generic label arc in linkbase {0} extended link {1} to {2} must target a generic label" ).format(modelLink.modelDocument.basename, modelLink.role, resourceArcToLabel), "err", "xbrlle.2.1.1:genericLabelTarget") elif resourceArcUse == XbrlConst.elementReference: if not self.isGenericReference(toResource): modelXbrl.error( _("Generic reference arc in linkbase {0} extended link {1} to {2} must target a generic reference" ).format(modelLink.modelDocument.basename, modelLink.role, resourceArcToLabel), "err", "xbrlre.2.1.1:genericReferenceTarget") self.dimensionDefaults = {} modelXbrl.qnameDimensionDefaults = {} modelXbrl.qnameDimensionContextElement = {} # check base set cycles, dimensions modelXbrl.modelManager.showStatus(_("validating relationship sets")) for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if arcrole.startswith("XBRL-") or ELR is None or \ linkqname is None or arcqname is None: continue elif arcrole in XbrlConst.standardArcroleCyclesAllowed: cyclesAllowed, specSect = XbrlConst.standardArcroleCyclesAllowed[ arcrole] elif arcrole in self.modelXbrl.arcroleTypes and len( self.modelXbrl.arcroleTypes[arcrole]) > 0: cyclesAllowed = self.modelXbrl.arcroleTypes[arcrole][ 0].cyclesAllowed if arcrole in self.genericArcArcroles: specSect = "xbrlgene:violatedCyclesConstraint" else: specSect = "xbrl.5.1.4.3:cycles" else: cyclesAllowed = "any" specSect = None if cyclesAllowed != "any" or arcrole in (XbrlConst.summationItem,) \ or arcrole in self.genericArcArcroles \ or arcrole.startswith(XbrlConst.formulaStartsWith): relsSet = modelXbrl.relationshipSet(arcrole, ELR, linkqname, arcqname) if cyclesAllowed != "any" and \ (XbrlConst.isStandardExtLinkQname(linkqname) and XbrlConst.isStandardArcQname(arcqname)) \ or arcrole in self.genericArcArcroles: noUndirected = cyclesAllowed == "none" fromRelationships = relsSet.fromModelObjects() for relFrom, rels in fromRelationships.items(): cycleFound = self.fwdCycle(relsSet, rels, noUndirected, {relFrom}) if cycleFound: modelXbrl.error( _("Relationships have a {0} cycle in arcrole {1} link role {2} link {3}, arc {4} starting from {5}" ).format(cycleFound, arcrole, ELR, linkqname, arcqname, relFrom.qname), "err", "{0}".format(specSect)) break # check calculation arcs for weight issues (note calc arc is an "any" cycles) if arcrole == XbrlConst.summationItem: for modelRel in relsSet.modelRelationships: weight = modelRel.weight fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept and toConcept: if weight == 0: modelXbrl.error( _("Calculation relationship has zero weight from {0} to {1} in link role {2}" ).format(fromConcept.qname, toConcept.qname, ELR), "err", "xbrl.5.2.5.2.1:zeroWeight") fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if (fromBalance == toBalance and weight < 0) or \ (fromBalance != toBalance and weight > 0): modelXbrl.error( _("Calculation relationship has illegal weight {0} from {1}, {2}, to {3}, {4} in link role {5} (per 5.1.1.2 Table 6)" ).format(weight, fromConcept.qname, fromBalance, toConcept.qname, toBalance, ELR), "err", "xbrl.5.1.1.2:balanceCalcWeight") if not fromConcept.isNumeric or not toConcept.isNumeric: modelXbrl.error( _("Calculation relationship has illegal concept from {0}{1} to {2}{3} in link role {4}" ).format( fromConcept.qname, "" if fromConcept.isNumeric else " (non-numeric)", toConcept.qname, "" if fromConcept.isNumeric else " (non-numeric)", ELR), "err", "xbrl.5.2.5.2:nonNumericCalc") # check presentation relationships for preferredLabel issues elif arcrole == XbrlConst.parentChild: for modelRel in relsSet.modelRelationships: preferredLabel = modelRel.preferredLabel toConcept = modelRel.toModelObject if preferredLabel and toConcept and \ toConcept.label(preferredLabel=preferredLabel,fallbackToQname=False) is None: modelXbrl.error( _("Presentation relationship from {0} to {1} in link role {2} missing preferredLabel {3}" ).format(modelRel.fromModelObject.qname, toConcept.qname, ELR, preferredLabel), "err", "xbrl.5.2.4.2.1:preferredLabelMissing") # check essence-alias relationships elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept and toConcept: if fromConcept.type != toConcept.type or fromConcept.periodType != toConcept.periodType: modelXbrl.error( _("Essence-alias relationship from {0} to {1} in link role {2} has different types or periodTypes" ).format(fromConcept.qname, toConcept.qname, ELR), "err", "xbrl.5.2.6.2.2:essenceAliasTypes") fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if fromBalance and toBalance and fromBalance != toBalance: modelXbrl.error( _("Essence-alias relationship from {0} to {1} in link role {2} has different balances" ).format(fromConcept.qname, toConcept.qname, ELR), "err", "xbrl.5.2.6.2.2:essenceAliasBalance") elif modelXbrl.hasXDT and arcrole.startswith( XbrlConst.dimStartsWith): ValidateXbrlDimensions.checkBaseSet(self, arcrole, ELR, relsSet) elif modelXbrl.hasFormulae and arcrole.startswith( XbrlConst.formulaStartsWith): ValidateFormula.checkBaseSet(self, arcrole, ELR, relsSet) # instance checks modelXbrl.modelManager.showStatus(_("validating instance")) self.footnoteRefs = set() if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE or \ modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRL: for f in modelXbrl.facts: concept = f.concept if concept: if concept.isNumeric: unit = f.unit if f.unitID is None or unit is None: self.modelXbrl.error( _("Fact {0} context {1} is numeric and must have a unit" ).format(modelXbrl.modelDocument.basename, f.qname, f.contextID), "err", "xbrl.4.6.2:numericUnit") else: if concept.isMonetary: measures = unit.measures if not measures or len(measures[0]) != 1 or len(measures[1]) != 0 or \ measures[0][0].namespaceURI != XbrlConst.iso4217 or \ not self.isoCurrencyPattern.match(measures[0][0].localName): self.modelXbrl.error( _("Fact {0} context {1} must have a monetary unit {2}" ).format( modelXbrl.modelDocument.basename, f.qname, f.contextID, f.unitID), "err", "xbrl.4.8.2:monetaryFactUnit") elif concept.isShares: measures = unit.measures if not measures or len(measures[0]) != 1 or len(measures[1]) != 0 or \ measures[0][0] != XbrlConst.qnXbrliShares: self.modelXbrl.error( _("Fact {0} context {1} must have a xbrli:shares unit {2}" ).format(f.qname, f.contextID, f.unitID), "err", "xbrl.4.8.2:sharesFactUnit") precision = f.precision hasPrecision = precision is not None if hasPrecision and precision != "INF" and not precision.isdigit( ): self.modelXbrl.error( _("Fact {0} context {1} precision {2} is invalid" ).format(f.qname, f.contextID, precision), "err", "xbrl.4.6.4:precision") decimals = f.decimals hasDecimals = decimals is not None if hasPrecision and not self.precisionPattern.match( precision): self.modelXbrl.error( _("Fact {0} context {1} precision {2} is invalid" ).format(f.qname, f.contextID, precision), "err", "xbrl.4.6.4:precision") if hasPrecision and hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} can not have both precision and decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:bothPrecisionAndDecimals") if hasDecimals and not self.decimalsPattern.match( decimals): self.modelXbrl.error( _("Fact {0} context {1} decimals {2} is invalid" ).format(f.qname, f.contextID, decimals), "err", "xbrl.4.6.5:decimals") if concept.isItem: context = f.context if context is None: self.modelXbrl.error( _("Item {0} must have a context").format( f.qname), "err", "xbrl.4.6.1:itemContextRef") else: periodType = concept.periodType if (periodType == "instant" and not context.isInstantPeriod) or \ (periodType == "duration" and not (context.isStartEndPeriod or context.isForeverPeriod)): self.modelXbrl.error( _("Fact {0} context {1} has period type {2} conflict with context" ).format(f.qname, f.contextID, periodType), "err", "xbrl.4.7.2:contextPeriodType") if modelXbrl.hasXDT: ValidateXbrlDimensions.checkFact(self, f) # check precision and decimals if f.xsiNil == "true": if hasPrecision or hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} can not be nil and have either precision or decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:nilPrecisionDecimals") elif concept.isFraction: if hasPrecision or hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} is a fraction concept and cannot have either precision or decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:fractionPrecisionDecimals") numerator, denominator = f.fractionValue if not (numerator == "INF" or numerator.isnumeric()): self.modelXbrl.error( _("Fact {0} context {1} is a fraction with invalid numerator {2}" ).format(f.qname, f.contextID, numerator), "err", "xbrl.5.1.1:fractionPrecisionDecimals") if not denominator.isnumeric() or int( denominator) == 0: self.modelXbrl.error( _("Fact {0} context {1} is a fraction with invalid denominator {2}" ).format(f.qname, f.contextID, denominator), "err", "xbrl.5.1.1:fractionPrecisionDecimals") else: if modelXbrl.modelDocument.type != ModelDocument.Type.INLINEXBRL: for child in f.element.childNodes: if child.nodeType == 1: self.modelXbrl.error( _("Fact {0} context {1} may not have child elements {2}" ).format(f.qname, f.contextID, child.tagName), "err", "xbrl.5.1.1:itemMixedContent") break if concept.isNumeric and not hasPrecision and not hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} is a numeric concept and must have either precision or decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:missingPrecisionDecimals") elif concept.isTuple: if f.contextID: self.modelXbrl.error( _("Tuple {0} must not have a context").format( f.qname), "err", "xbrl.4.6.1:tupleContextRef") if hasPrecision or hasDecimals: self.modelXbrl.error( _("Fact {0} is a tuple and cannot have either precision or decimals" ).format(f.qname), "err", "xbrl.4.6.3:tuplePrecisionDecimals") # custom attributes may be allowed by anyAttribute but not by 2.1 for attrQname, attrValue in XbrlUtil.attributes( self.modelXbrl, concept, f.element): if attrQname.namespaceURI in (XbrlConst.xbrli, XbrlConst.link, XbrlConst.xlink, XbrlConst.xl): self.modelXbrl.error( _("Fact {0} is a tuple and must not have attribute in this namespace {1}" ).format(f.qname, attrQname), "err", "xbrl.4.9:tupleAttribute") else: self.modelXbrl.error( _("Fact {0} must be an item or tuple").format( f.qname), "err", "xbrl.4.6:notItemOrTuple") from arelle.ModelObject import ModelInlineFact if isinstance(f, ModelInlineFact): self.footnoteRefs.update(f.footnoteRefs) #instance checks for cntx in modelXbrl.contexts.values(): if cntx.isStartEndPeriod: try: if cntx.endDatetime <= cntx.startDatetime: self.modelXbrl.error( _("Context {0} must have startDate less than endDate" ).format(cntx.id), "err", "xbrl.4.7.2:periodStartBeforeEnd") except ValueError as err: self.modelXbrl.error( _("Context {0} startDate or endDate: {1}").format( cntx.id, err), "err", "xbrl.4.7.2:contextDateError") elif cntx.isInstantPeriod: try: cntx.instantDatetime #parse field except ValueError as err: self.modelXbrl.error( _("Context {0} instant date: {1}").format( cntx.id, err), "err", "xbrl.4.7.2:contextDateError") self.segmentScenario(cntx.segment, cntx.id, "segment", "4.7.3.2") self.segmentScenario(cntx.scenario, cntx.id, "scenario", "4.7.4") if modelXbrl.hasXDT: ValidateXbrlDimensions.checkContext(self, cntx) for unit in modelXbrl.units.values(): mulDivMeasures = unit.measures if mulDivMeasures: for measures in mulDivMeasures: for measure in measures: if measure.namespaceURI == XbrlConst.xbrli and not \ measure in (XbrlConst.qnXbrliPure, XbrlConst.qnXbrliShares): self.modelXbrl.error( _("Unit {0} illegal measure: {1}").format( unit.id, measure), "err", "xbrl.4.8.2:measureElement") for numeratorMeasure in mulDivMeasures[0]: if numeratorMeasure in mulDivMeasures[1]: self.modelXbrl.error( _("Unit {0} numerator measure: {1} also appears as denominator measure" ).format(unit.id, numeratorMeasure), "err", "xbrl.4.8.4:measureBothNumDenom") #concepts checks modelXbrl.modelManager.showStatus(_("validating concepts")) for concept in modelXbrl.qnameConcepts.values(): conceptType = concept.type if XbrlConst.isStandardNamespace(concept.namespaceURI) or \ not concept.modelDocument.inDTS: continue if concept.isTuple: # must be global if not concept.element.parentNode.localName == "schema": self.modelXbrl.error( _("Tuple {0} must be declared globally").format( concept.qname), "err", "xbrl.4.9:tupleGloballyDeclared") if concept.periodType: self.modelXbrl.error( _("Tuple {0} must not have periodType").format( concept.qname), "err", "xbrl.4.9:tuplePeriodType") if concept.balance: self.modelXbrl.error( _("Tuple {0} must not have balance").format( concept.qname), "err", "xbrl.4.9:tupleBalance") # check attribute declarations for attributeQname in conceptType.attributes: if attributeQname.namespaceURI in (XbrlConst.xbrli, XbrlConst.link, XbrlConst.xlink, XbrlConst.xl): self.modelXbrl.error( _("Tuple {0} must not have attribute in this namespace {1}" ).format(concept.qname, attributeQname), "err", "xbrl.4.9:tupleAttribute") # check for mixed="true" or simple content if XmlUtil.descendantAttr(conceptType.element, XbrlConst.xsd, ("complexType", "complexContent"), "mixed") == "true": self.modelXbrl.error( _("Tuple {0} must not have mixed content").format( concept.qname), "err", "xbrl.4.9:tupleMixedContent") if XmlUtil.descendant(conceptType.element, XbrlConst.xsd, "simpleContent"): self.modelXbrl.error( _("Tuple {0} must not have simple content").format( concept.qname), "err", "xbrl.4.9:tupleSimpleContent") # child elements must be item or tuple for elementQname in conceptType.elements: childConcept = self.modelXbrl.qnameConcepts.get( elementQname) if childConcept is None: self.modelXbrl.error( _("Tuple {0} element {1} not defined").format( concept.qname, elementQname), "err", "xbrl.4.9:tupleElementUndefined") elif not ( childConcept.isItem or childConcept.isTuple or # isItem/isTuple do not include item or tuple itself childConcept.qname == XbrlConst.qnXbrliItem or # subs group includes item as member childConcept.qname == XbrlConst.qnXbrliTuple): self.modelXbrl.error( _("Tuple {0} must not have element {1} not an item or tuple" ).format(concept.qname, elementQname), "err", "xbrl.4.9:tupleElementItemOrTuple") elif concept.isItem: if concept.periodType not in periodTypeValues: #("instant","duration"): self.modelXbrl.error( _("Item {0} must have a valid periodType").format( concept.qname), "err", "xbrl.5.1.1.1:itemPeriodType") if concept.isMonetary: if concept.balance not in balanceValues: #(None, "credit","debit"): self.modelXbrl.error( _("Item {0} must have a valid balance {1}").format( concept.qname, concept.balance), "err", "xbrl.5.1.1.2:itemBalance") else: if concept.balance: self.modelXbrl.error( _("Item {0} may not have a balance").format( concept.qname), "err", "xbrl.5.1.1.2:itemBalance") if concept.baseXbrliType not in baseXbrliTypes: self.modelXbrl.error( _("Item {0} type {1} invalid").format( concept.qname, concept.baseXbrliType), "err", "xbrl.5.1.1.3:itemType") if modelXbrl.hasXDT: if concept.isHypercubeItem and not concept.abstract == "true": self.modelXbrl.error( _("Hypercube item {0} must be abstract").format( concept.qname), "err", "xbrldte:HypercubeElementIsNotAbstractError") elif concept.isDimensionItem and not concept.abstract == "true": self.modelXbrl.error( _("Dimension item {0} must be abstract").format( concept.qname), "err", "xbrldte:DimensionElementIsNotAbstractError") if modelXbrl.hasXDT: ValidateXbrlDimensions.checkConcept(self, concept) modelXbrl.modelManager.showStatus(_("validating DTS")) self.DTSreferenceResourceIDs = {} ValidateXbrlDTS.checkDTS(self, modelXbrl.modelDocument, []) del self.DTSreferenceResourceIDs if self.validateCalcLB: modelXbrl.modelManager.showStatus( _("Validating instance calculations")) ValidateXbrlCalcs.validate( modelXbrl, inferPrecision=(not self.validateInferDecimals)) if modelXbrl.modelManager.validateUtr: ValidateUtr.validate(modelXbrl) if modelXbrl.hasFormulae: ValidateFormula.validate(self) modelXbrl.modelManager.showStatus(_("ready"), 2000)