def eba_2_7(instance, error_log): """EBA 2.7 - No unused or duplicated xbrli:context nodes""" aspects_map = {} for context in instance.contexts: # Check for unused contexts if not instance.facts.filter(context): detail_error = xbrl.Error.create( 'Unused xbrli:context nodes SHOULD NOT be present in the instance. [FRIS04]', severity=xml.ErrorSeverity.INFO) main_error = xbrl.Error.create( '[EBA.2.7] No unused or duplicated {context} nodes.', context=context.element, children=[detail_error], severity=xml.ErrorSeverity.WARNING) error_log.report(main_error) # Check for duplicated contexts duplicates = aspects_map.setdefault(xbrl.ConstraintSet(context), []) if duplicates: detail_error1 = xbrl.Error.create( 'An instance document SHOULD NOT contain duplicated context, unless required for technical reasons, e.g. to support XBRL streaming.', severity=xml.ErrorSeverity.INFO) detail_error2 = xbrl.Error.create( 'Context {context} is a duplicate of context {context2}', context=context, context2=duplicates[0], severity=xml.ErrorSeverity.OTHER) main_error = xbrl.Error.create( '[EBA.2.7] No unused or duplicated {context} nodes.', context=context.element, children=[detail_error1, detail_error2], severity=xml.ErrorSeverity.WARNING) error_log.report(main_error) else: duplicates.append(context)
def dqc_0011(instance,error_log,suppress_errors,namespaces): """DQC_0011 Dimensional Equivalents """ for rule_id, lineItemPrefix, lineItemName, dimItemPrefix, dimItemName, axisPrefix, axisName, memberPrefix, memberName, weight in dqc_0011_facts: lineConcept = instance.dts.resolve_concept(xml.QName(lineItemName, namespaces.get(lineItemPrefix))) dimConcept = instance.dts.resolve_concept(xml.QName(dimItemName,namespaces.get(dimItemPrefix))) axisConcept = instance.dts.resolve_concept(xml.QName(axisName,namespaces.get(axisPrefix))) memberConcept = instance.dts.resolve_concept(xml.QName(memberName,namespaces.get(memberPrefix))) # select all facts with name lineItemName and no value for explicit dimension axisName lineItemConstraintSet = xbrl.ConstraintSet() lineItemConstraintSet.add(xbrl.ConceptAspectValue(lineConcept)) lineItemConstraintSet.add(xbrl.ExplicitDimensionAspectValue(axisConcept)) lineFacts = instance.facts.filter(lineItemConstraintSet) for lineFact in lineFacts: if not isinstance(lineFact, xbrl.Item) or lineFact.xsi_nil: continue # select all facts with name dimItemName and explicit dimension axisName=memberName and all other aspect values equal to their respective value of lineFact dimItemConstraintSet = lineFact.aspect_values dimItemConstraintSet.add(xbrl.ConceptAspectValue(dimConcept)) dimItemConstraintSet.add(xbrl.ExplicitDimensionAspectValue(axisConcept, memberConcept)) dimFacts = instance.facts.filter(dimItemConstraintSet) lineValue = lineFact.effective_numeric_value for dimFact in dimFacts: if not isinstance(dimFact, xbrl.Item) or dimFact.xsi_nil: continue dimValue = dimFact.effective_numeric_value if dimValue * weight != lineValue: report_error(error_log,suppress_errors,rule_id,fact1=lineFact,fact2=dimFact,weight=weight)
def dqc_0001(instance,error_log,suppress_errors,namespaces): """DQC_0001 Axis with Inappropriate Members""" handled = set() for role in instance.dts.presentation_link_roles(): for dim, rels in _get_dimension_values(instance.dts.presentation_network(role)).items(): rule = dqc_0001_axis_members.get(dim.target_namespace,{}).get(dim.name) if rule: for rel in rels: member = rel.target if dim.default_member == member: continue ext = is_extension(member.target_namespace) if ext: valid = rule['extensions'] if isinstance(rule['extensions'], bool) else member.name in rule['extensions'] elif rule['disallowed']: valid = member.name not in rule['disallowed'] else: valid = member.name in rule['allowed'] if not valid and (dim,member) not in handled: # Mimick Arelle's behaviour of only reporting the first occurrence of each type of error handled.add((dim,member)) rule_id = 'DQC.US.0001.'+rule['id'].split('.')[-1] cs = xbrl.ConstraintSet() cs[dim] = member facts = instance.facts.filter(cs) for fact in facts: report_error(error_log,suppress_errors,rule_id,rel.arc,'ext' if ext else 'std',Rule={'axis':dim,'member':member},fact1=fact) if len(facts) == 0: report_error(error_log,suppress_errors,rule_id,rel.arc,'nofact',Rule={'axis':dim,'member':member},group=xbrl.Error.Param(instance.dts.role_definition(role),tooltip=role))
def _dqc_0004(instance,error_log,suppress_errors,rule_id,concept1,concept2): for fact1 in instance.facts.filter(concept1,allow_nil=False): # All comparisons between fact values occur between facts of equivalent dimensions. A rule will produce a message for each occurrence of the compared facts in equivalent dimensions. cs = xbrl.ConstraintSet(fact1) cs[xbrl.Aspect.CONCEPT] = concept2 for fact2 in instance.facts.filter(cs,allow_nil=False,allow_additional_dimensions=False): if not decimal_comparison(fact1,fact2,equal_within_tolerance): report_error(error_log,suppress_errors,rule_id,fact1=fact1,fact2=fact2)
def dqc_0005_48(instance,error_log,suppress_errors,namespaces,reporting_period_ends): """DQC_0005.48 Subsequent events""" dim_SubsequentEventTypeAxis = instance.dts.resolve_concept(xml.QName('SubsequentEventTypeAxis',namespaces.get('us-gaap'))) if dim_SubsequentEventTypeAxis: cs = xbrl.ConstraintSet() cs[dim_SubsequentEventTypeAxis] = xbrl.ExplicitDimensionAspectValue(dim_SubsequentEventTypeAxis,None) facts = instance.facts - instance.facts.filter(cs) _dqc_0005(instance,error_log,suppress_errors,'DQC.US.0005.48',namespaces,facts,reporting_period_ends,operator.gt,{'us-gaap:SubsequentEventTypeAxis':dim_SubsequentEventTypeAxis})
def dqc_0005_49(instance,error_log,suppress_errors,namespaces,reporting_period_ends): """DQC_0005.49 Subsequent events""" dim_StatementScenarioAxis = instance.dts.resolve_concept(xml.QName('StatementScenarioAxis',namespaces.get('us-gaap'))) if dim_StatementScenarioAxis: member_ScenarioForecastMember = instance.dts.resolve_concept(xml.QName('ScenarioForecastMember',namespaces.get('us-gaap'))) cs = xbrl.ConstraintSet() cs[dim_StatementScenarioAxis] = member_ScenarioForecastMember facts = instance.facts.filter(cs) _dqc_0005(instance,error_log,suppress_errors,'DQC.US.0005.49',namespaces,facts,reporting_period_ends,operator.gt,{'us-gaap:StatementScenarioAxis':dim_StatementScenarioAxis,'us-gaap:ScenarioForecastMember':member_ScenarioForecastMember})
def dqc_0009(instance,error_log,suppress_errors,namespaces): """DQC_0009 Element A must be less than or equal to Element B""" for rule_id, prefix1, name1, prefix2, name2 in dqc_0009_facts: concept1 = instance.dts.resolve_concept(xml.QName(name1,namespaces.get(prefix1))) concept2 = instance.dts.resolve_concept(xml.QName(name2,namespaces.get(prefix2))) if concept1 and concept2: for fact1 in instance.facts.filter(concept1,allow_nil=False): # All comparisons between fact values occur between facts of equivalent dimensions. A rule will produce a message for each occurrence of the compared facts in equivalent dimensions. cs = xbrl.ConstraintSet(fact1) cs[xbrl.Aspect.CONCEPT] = concept2 for fact2 in instance.facts.filter(cs,allow_nil=False,allow_additional_dimensions=False): if not decimal_comparison(fact1,fact2,less_or_equal): report_error(error_log,suppress_errors,rule_id,fact1=fact1,fact2=fact2)
def _dqc_0013_precondition_check(instance,namespaces,context): cs = xbrl.ConstraintSet(context) for name, summation in dqc_0013_preconditions.items(): cs[xbrl.Aspect.CONCEPT] = instance.dts.resolve_concept(xml.QName(name,namespaces['us-gaap'])) precondition_facts = instance.facts.filter(cs) if precondition_facts: val = 0 for name in summation: cs[xbrl.Aspect.CONCEPT] = instance.dts.resolve_concept(xml.QName(name,namespaces['us-gaap'])) for fact in instance.facts.filter(cs): val += fact.numeric_value if val > 0: return precondition_facts[0] return None
def eba_3_1(instance, error_log): """EBA 3.1 - Choice of Currency for Monetary facts""" eba_dim_CCA = instance.dts.resolve_concept( xml.QName('CCA', 'http://www.eba.europa.eu/xbrl/crr/dict/dim')) eba_dim_CUS = instance.dts.resolve_concept( xml.QName('CUS', 'http://www.eba.europa.eu/xbrl/crr/dict/dim')) eba_CA_x1 = instance.dts.resolve_concept( xml.QName('x1', 'http://www.eba.europa.eu/xbrl/crr/dict/dom/CA')) constraints = xbrl.ConstraintSet() constraints[eba_dim_CCA] = eba_CA_x1 denomination_facts = instance.facts.filter(constraints) for fact in denomination_facts: if fact.concept.is_monetary(): aspect_values = fact.aspect_values currency = aspect_values.get(eba_dim_CUS, None) if currency and currency.value.name != aspect_values[ xbrl.Aspect.UNIT].iso4217_currency: detail_error = xbrl.Error.create( 'For facts falling under point (b), whose context also includes the dimension “Currency with significant liabilities” (CUS), the currency of the fact (i.e. unit) MUST be consistent with the value given for this dimension.', severity=xml.ErrorSeverity.INFO) main_error = xbrl.Error.create( '[EBA.3.1] Choice of Currency for Monetary fact {fact}.', fact=fact, children=[detail_error]) error_log.report(main_error) monetary_units = [] for unit in instance.units: if unit.aspect_value.is_monetary(): monetary_units.append(unit) # Optimization: Only do the single currency check if more than one monetary unit is present if len(monetary_units) > 1: single_unit = None for fact in instance.child_items - denomination_facts: if fact.concept.is_monetary(): if single_unit is None: single_unit = fact.unit elif fact.unit != single_unit: detail_error = xbrl.Error.create( 'An instance MUST express all monetary facts which do not fall under point (b) using a single currency.', severity=xml.ErrorSeverity.INFO) main_error = xbrl.Error.create( '[EBA.3.1] Choice of Currency for Monetary fact {fact}.', fact=fact, children=[detail_error]) error_log.report(main_error)
def eba_2_21(instance, error_log): """EBA 2.21 - Duplicates of xbrli:xbrl/xbrli:unit""" aspects_map = {} for unit in instance.units: duplicates = aspects_map.setdefault(xbrl.ConstraintSet(unit), []) if duplicates: detail_error1 = xbrl.Error.create( 'An XBRL instance SHOULD NOT, in general, contain duplicated units, unless required for technical reasons, e.g. to support XBRL streaming.', severity=xml.ErrorSeverity.INFO) detail_error2 = xbrl.Error.create( 'Unit {unit} is a duplicate of unit {unit2}', unit=unit, unit2=duplicates[0], severity=xml.ErrorSeverity.OTHER) main_error = xbrl.Error.create( '[EBA.2.21] Duplicates of xbrli:xbrl/xbrli:unit.', location=unit.element, children=[detail_error1, detail_error2], severity=xml.ErrorSeverity.WARNING) error_log.report(main_error) else: duplicates.append(unit)