arelle-release 2.37.38__py3-none-any.whl → 2.37.40__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arelle-release might be problematic. Click here for more details.

@@ -1266,7 +1266,7 @@ class ModelContext(ModelObject):
1266
1266
 
1267
1267
  def isEntityIdentifierEqualTo(self, cntx2):
1268
1268
  """(bool) -- True if entityIdentifier values are equal (scheme and text value)"""
1269
- return self.entityIdentifierHash == cntx2.entityIdentifierHash
1269
+ return self.entityIdentifier == cntx2.entityIdentifier
1270
1270
 
1271
1271
  def isEqualTo(self, cntx2, dimensionalAspectModel=None) -> bool:
1272
1272
  if dimensionalAspectModel is None: dimensionalAspectModel = self.modelXbrl.hasXDT
@@ -12,6 +12,8 @@ from arelle import Locale, XbrlConst, XbrlUtil
12
12
  from arelle.ModelObject import ObjectPropertyViewWrapper
13
13
  from arelle.PythonUtil import flattenSequence, strTruncate
14
14
  from arelle.XmlValidateConst import UNVALIDATED, VALID
15
+ from arelle.utils.Contexts import partitionModelXbrlContexts
16
+ from arelle.utils.Units import partitionModelXbrlUnits
15
17
 
16
18
  if TYPE_CHECKING:
17
19
  from _decimal import Decimal
@@ -147,27 +149,15 @@ class ValidateXbrlCalcs:
147
149
 
148
150
  # identify equal contexts
149
151
  modelXbrl.profileActivity()
150
- uniqueContextHashes = {}
151
- for context in modelXbrl.contexts.values():
152
- h = context.contextDimAwareHash
153
- if h in uniqueContextHashes:
154
- if context.isEqualTo(uniqueContextHashes[h]):
155
- self.mapContext[context] = uniqueContextHashes[h]
156
- else:
157
- uniqueContextHashes[h] = context
158
- del uniqueContextHashes
152
+ for exemplar_context, *contexts in partitionModelXbrlContexts(modelXbrl).values():
153
+ for context in contexts:
154
+ self.mapContext[context] = exemplar_context
159
155
  modelXbrl.profileActivity("... identify equal contexts", minTimeToShow=1.0)
160
156
 
161
157
  # identify equal units
162
- uniqueUnitHashes = {}
163
- for unit in modelXbrl.units.values():
164
- h = unit.hash
165
- if h in uniqueUnitHashes:
166
- if unit.isEqualTo(uniqueUnitHashes[h]):
167
- self.mapUnit[unit] = uniqueUnitHashes[h]
168
- else:
169
- uniqueUnitHashes[h] = unit
170
- del uniqueUnitHashes
158
+ for exemplar_unit, *units in partitionModelXbrlUnits(modelXbrl).values():
159
+ for unit in units:
160
+ self.mapUnit[unit] = exemplar_unit
171
161
  modelXbrl.profileActivity("... identify equal units", minTimeToShow=1.0)
172
162
 
173
163
  # identify concepts participating in essence-alias relationships
arelle/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.37.38'
21
- __version_tuple__ = version_tuple = (2, 37, 38)
20
+ __version__ = version = '2.37.40'
21
+ __version_tuple__ = version_tuple = (2, 37, 40)
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  import datetime
7
7
  import itertools
8
+ from collections import defaultdict
8
9
  from collections.abc import Callable, Iterable
9
10
  from typing import Optional, cast
10
11
 
@@ -14,6 +15,7 @@ from arelle.ModelValue import QName
14
15
  from arelle.ModelXbrl import ModelXbrl
15
16
  from arelle.typing import TypeGetText
16
17
  from arelle.UrlUtil import scheme
18
+ from arelle.utils.Contexts import ContextHashKey
17
19
  from arelle.utils.validate.Validation import Validation
18
20
  from arelle.ValidateFilingText import parseImageDataURL
19
21
  from arelle.ValidateXbrl import ValidateXbrl
@@ -266,19 +268,18 @@ def getFactsGroupedByContextId(modelXbrl: ModelXbrl, *conceptQns: QName) -> dict
266
268
  return dict(sorted(groupedFacts.items()))
267
269
 
268
270
 
269
- def groupFactsByContextHash(facts: set[ModelFact] ) -> dict[str, list[ModelFact]]:
271
+ def groupFactsByContextHash(facts: set[ModelFact]) -> dict[ContextHashKey, list[ModelFact]]:
270
272
  """
271
- Groups facts by their contextDimAwareHash.
272
- :return: A dictionary of contextDimAwareHashes to list of facts.
273
+ Groups facts by their context hash key.
274
+ :return: A dictionary of context hash keys to list of facts.
273
275
  """
274
- groupedFacts: dict[str, list[ModelFact]] = {}
276
+ groupedFacts: defaultdict[ContextHashKey, list[ModelFact]] = defaultdict(list)
275
277
  for fact in facts:
276
278
  if fact.xValid >= VALID:
277
- contextHash = fact.context.contextDimAwareHash
278
- if contextHash not in groupedFacts:
279
- groupedFacts[contextHash] = []
279
+ contextHash = ContextHashKey(fact.context, dimensionalAspectModel=True)
280
280
  groupedFacts[contextHash].append(fact)
281
- return dict(sorted(groupedFacts.items()))
281
+ groupedFacts.default_factory = None
282
+ return groupedFacts
282
283
 
283
284
 
284
285
  def lookup_namespaced_facts(modelXbrl: ModelXbrl, namespaceURI: str) -> set[ModelFact]:
@@ -602,9 +602,9 @@ def rule_fr57(
602
602
  DBA.FR57.Equality(Error):
603
603
  Assets (fsa:Assets) must equal Liabilities (fsa:LiabilitiesAndEquity)
604
604
  """
605
- currenStartDateFacts = getFactsWithDimension(val,pluginData.reportingPeriodStartDateQn, pluginData.consolidatedSoloDimensionQn, [pluginData.consolidatedMemberQn, pluginData.soloMemberQn])
605
+ currentStartDateFacts = getFactsWithDimension(val, pluginData.reportingPeriodStartDateQn, pluginData.consolidatedSoloDimensionQn, [pluginData.consolidatedMemberQn, pluginData.soloMemberQn])
606
606
  currentEndDateFacts = getFactsWithDimension(val, pluginData.reportingPeriodEndDateQn, pluginData.consolidatedSoloDimensionQn, [pluginData.consolidatedMemberQn, pluginData.soloMemberQn])
607
- currentGroupedFacts = groupFactsByContextHash(currenStartDateFacts.union(currentEndDateFacts))
607
+ currentGroupedFacts = groupFactsByContextHash(currentStartDateFacts.union(currentEndDateFacts))
608
608
  precedingEndDateFacts = getFactsWithDimension(val, pluginData.precedingReportingPeriodEndDateQn, pluginData.consolidatedSoloDimensionQn, [pluginData.consolidatedMemberQn, pluginData.soloMemberQn])
609
609
  precedingStartDateFacts = getFactsWithDimension(val, pluginData.precedingReportingPeriodStartDateQn, pluginData.consolidatedSoloDimensionQn, [pluginData.consolidatedMemberQn, pluginData.soloMemberQn])
610
610
  precedingGroupedFacts = groupFactsByContextHash(precedingEndDateFacts.union(precedingStartDateFacts))
@@ -635,7 +635,7 @@ def rule_fr57(
635
635
  equityFacts = getFactsWithoutDimension(val, pluginData.equityQn)
636
636
  profitLossFacts = getFactsWithoutDimension(val, pluginData.profitLossQn)
637
637
  liabilitiesAndEquityFacts = getFactsWithoutDimension(val, pluginData.liabilitiesAndEquityQn)
638
- for context, facts in currentGroupedFacts.items():
638
+ for facts in currentGroupedFacts.values():
639
639
  if len(facts) == 2:
640
640
  for fact in facts:
641
641
  if fact.qname == pluginData.reportingPeriodStartDateQn:
@@ -42,8 +42,10 @@ def rule_tr01(
42
42
  gsd_facts = lookup_namespaced_facts(val.modelXbrl, NAMESPACE_GSD)
43
43
  facts_in_error = []
44
44
  for fact in gsd_facts:
45
- if (fact.context.entityIdentifier != cvr_fact.context.entityIdentifier or
46
- fact.context.periodHash != cvr_fact.context.periodHash):
45
+ if not (
46
+ fact.context.isEntityIdentifierEqualTo(cvr_fact.context) and
47
+ fact.context.isPeriodEqualTo(cvr_fact.context)
48
+ ):
47
49
  facts_in_error.append(fact)
48
50
  if len(facts_in_error) > 0:
49
51
  yield Validation.error(
@@ -17,6 +17,7 @@ from arelle.ValidateUtr import ValidateUtr
17
17
  from arelle.Version import authorLabel, copyrightLabel
18
18
  from arelle.XbrlConst import qnEnumerationItemTypes
19
19
  from arelle.ModelInstanceObject import ModelFact
20
+ from arelle.utils.Contexts import getDuplicateContextGroups
20
21
  import regex as re
21
22
  from lxml import etree
22
23
  from collections import defaultdict
@@ -484,17 +485,13 @@ def validateFacts(val, factsToCheck):
484
485
 
485
486
  del unitHashes
486
487
 
487
- cntxHashes = {}
488
+ if not getattr(modelXbrl, "isStreamingMode", False):
489
+ for contexts in getDuplicateContextGroups(modelXbrl):
490
+ modelXbrl.log("WARNING" if val.isEIOPAfullVersion else "ERROR",
491
+ "EIOPA.S.2.7.b",
492
+ _("Duplicate contexts MUST NOT be reported, contexts %(cntx1)s and %(cntx2)s are equivalent.'"),
493
+ modelObject=contexts, cntx1=contexts[0].id, cntx2=contexts[1].id)
488
494
  for cntx in modelXbrl.contexts.values():
489
- h = cntx.contextDimAwareHash
490
- if h in cntxHashes and cntx.isEqualTo(cntxHashes[h]):
491
- if not getattr(modelXbrl, "isStreamingMode", False):
492
- modelXbrl.log("WARNING" if val.isEIOPAfullVersion else "ERROR",
493
- "EIOPA.S.2.7.b",
494
- _("Duplicate contexts MUST NOT be reported, contexts %(cntx1)s and %(cntx2)s are equivalent.'"),
495
- modelObject=(cntx, cntxHashes[h]), cntx1=cntx.id, cntx2=cntxHashes[h].id)
496
- else:
497
- cntxHashes[h] = cntx
498
495
  for _dim in cntx.qnameDims.values():
499
496
  _dimQn = _dim.dimensionQname
500
497
  prefixUsed(val, _dimQn.namespaceURI, _dimQn.prefix)
@@ -3,6 +3,7 @@ See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
+ from datetime import timedelta
6
7
  from typing import Any, cast, Iterable
7
8
 
8
9
  import regex
@@ -16,7 +17,9 @@ from arelle.ValidateXbrlCalcs import insignificantDigits
16
17
  from arelle.XbrlConst import qnXbrlScenario, qnXbrldiExplicitMember, xhtmlBaseIdentifier, xmlBaseIdentifier
17
18
  from arelle.XmlValidate import VALID
18
19
  from arelle.typing import TypeGetText
20
+ from arelle.utils.Contexts import getDuplicateContextGroups
19
21
  from arelle.utils.PluginHooks import ValidationHook
22
+ from arelle.utils.Units import getDuplicateUnitGroups
20
23
  from arelle.utils.validate.Decorator import validation
21
24
  from arelle.utils.validate.Validation import Validation
22
25
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
@@ -213,6 +216,100 @@ def rule_gfm_1_2_5(
213
216
  )
214
217
 
215
218
 
219
+ @validation(
220
+ hook=ValidationHook.XBRL_FINALLY,
221
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
222
+ )
223
+ def rule_gfm_1_2_7(
224
+ pluginData: PluginValidationDataExtension,
225
+ val: ValidateXbrl,
226
+ *args: Any,
227
+ **kwargs: Any,
228
+ ) -> Iterable[Validation]:
229
+ """
230
+ EDINET.EC5700W: [GFM 1.2.7] An instance must not contain duplicate xbrli:context elements.
231
+ """
232
+ for contexts in getDuplicateContextGroups(val.modelXbrl):
233
+ yield Validation.warning(
234
+ codes='EDINET.EC5700W.GFM.1.2.7',
235
+ msg=_('Duplicate context. Remove the duplicate.'),
236
+ modelObject = contexts
237
+ )
238
+
239
+
240
+ @validation(
241
+ hook=ValidationHook.XBRL_FINALLY,
242
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
243
+ )
244
+ def rule_gfm_1_2_8(
245
+ pluginData: PluginValidationDataExtension,
246
+ val: ValidateXbrl,
247
+ *args: Any,
248
+ **kwargs: Any,
249
+ ) -> Iterable[Validation]:
250
+ """
251
+ EDINET.EC5700W: [GFM 1.2.8] Every xbrli:context element must appear in at least one
252
+ contextRef attribute in the same instance.
253
+ """
254
+ unused_contexts = list(set(val.modelXbrl.contexts.values()) - set(val.modelXbrl.contextsInUse))
255
+ unused_contexts.sort(key=lambda x: x.id)
256
+ for context in unused_contexts:
257
+ yield Validation.warning(
258
+ codes='EDINET.EC5700W.GFM.1.2.8',
259
+ msg=_('If you are not using a context, delete it if it is not needed.'),
260
+ modelObject = context
261
+ )
262
+
263
+
264
+ @validation(
265
+ hook=ValidationHook.XBRL_FINALLY,
266
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
267
+ )
268
+ def rule_gfm_1_2_9(
269
+ pluginData: PluginValidationDataExtension,
270
+ val: ValidateXbrl,
271
+ *args: Any,
272
+ **kwargs: Any,
273
+ ) -> Iterable[Validation]:
274
+ """
275
+ EDINET.EC5700W: [GFM 1.2.9] The same date must not appear as the content of both an xbrli:startDate and
276
+ an xbrli:endDate in an instance.
277
+ """
278
+ invalidDurationContexts = []
279
+ for contexts in val.modelXbrl.contextsByDocument().values():
280
+ for context in contexts:
281
+ if not context.isInstantPeriod:
282
+ if context.endDatetime and context.startDatetime and context.startDatetime == context.endDatetime - timedelta(days=1):
283
+ invalidDurationContexts.append(context)
284
+ if len(invalidDurationContexts) > 0:
285
+ for context in invalidDurationContexts:
286
+ yield Validation.warning(
287
+ codes='EDINET.EC5700W.GFM.1.2.9',
288
+ msg=_("Set the context's startDate and endDate elements to different dates."),
289
+ modelObject=context
290
+ )
291
+
292
+
293
+ @validation(
294
+ hook=ValidationHook.XBRL_FINALLY,
295
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
296
+ )
297
+ def rule_gfm_1_2_10(
298
+ pluginData: PluginValidationDataExtension,
299
+ val: ValidateXbrl,
300
+ *args: Any,
301
+ **kwargs: Any,
302
+ ) -> Iterable[Validation]:
303
+ """
304
+ EDINET.EC5700W: [GFM 1.2.10] Element xbrli:xbrl must not have duplicate child xbrli:unit elements.
305
+ """
306
+ for duplicateUnits in getDuplicateUnitGroups(val.modelXbrl):
307
+ yield Validation.warning(
308
+ codes='EDINET.EC5700W.GFM.1.2.10',
309
+ msg=_('The unit element contains duplicate content. Please remove the duplicates.'),
310
+ modelObject = duplicateUnits
311
+ )
312
+
216
313
 
217
314
  @validation(
218
315
  hook=ValidationHook.XBRL_FINALLY,
@@ -24,6 +24,8 @@ from arelle.ModelRelationshipSet import ModelRelationshipSet
24
24
  from arelle.ModelValue import QName, qname
25
25
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
26
26
  from arelle.PythonUtil import isLegacyAbs, strTruncate
27
+ from arelle.utils.Contexts import partitionModelXbrlContexts
28
+ from arelle.utils.Units import partitionModelXbrlUnits
27
29
  from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
28
30
  from arelle.UrlUtil import decodeBase64DataImage, isHttpUrl, scheme
29
31
  from arelle.ValidateFilingText import parseImageDataURL
@@ -564,27 +566,17 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
564
566
  # identify unique contexts and units
565
567
  mapContext = {}
566
568
  mapUnit = {}
567
- uniqueContextHashes: dict[Any, Any] = {}
568
- for context in modelXbrl.contexts.values():
569
- h = context.contextDimAwareHash
570
- if h in uniqueContextHashes:
571
- if context.isEqualTo(uniqueContextHashes[h]):
572
- mapContext[context] = uniqueContextHashes[h]
573
- else:
574
- uniqueContextHashes[h] = context
575
- del uniqueContextHashes
576
- uniqueUnitHashes: dict[Any, Any] = {}
569
+ for exemplar_context, *contexts in partitionModelXbrlContexts(modelXbrl).values():
570
+ for context in contexts:
571
+ mapContext[context] = exemplar_context
577
572
  utrValidator = ValidateUtr(modelXbrl)
578
573
  utrUnitIds = set(u.unitId
579
574
  for unitItemType in utrValidator.utrItemTypeEntries.values()
580
575
  for u in unitItemType.values())
576
+ for exemplar_unit, *units in partitionModelXbrlUnits(modelXbrl).values():
577
+ for unit in units:
578
+ mapUnit[unit] = exemplar_unit
581
579
  for unit in modelXbrl.units.values():
582
- h = unit.hash
583
- if h in uniqueUnitHashes:
584
- if unit.isEqualTo(uniqueUnitHashes[h]):
585
- mapUnit[unit] = uniqueUnitHashes[h]
586
- else:
587
- uniqueUnitHashes[h] = unit
588
580
  # check if any custom measure is in UTR
589
581
  for measureTerm in unit.measures:
590
582
  for measure in measureTerm:
@@ -594,7 +586,6 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
594
586
  modelXbrl.warning("ESEF.RTS.III.1.G1-7-1.customUnitInUtr",
595
587
  _("Custom measure SHOULD NOT duplicate a UnitID of UTR: %(measure)s"),
596
588
  modelObject=unit, measure=measure)
597
- del uniqueUnitHashes
598
589
 
599
590
  reportedMandatory: set[QName] = set()
600
591
  precisionFacts = set()
@@ -30,6 +30,8 @@ from arelle.utils.validate.ESEFImage import ImageValidationParameters, checkSVGC
30
30
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
31
31
  from arelle.PythonUtil import isLegacyAbs, normalizeSpace
32
32
  from arelle.PythonUtil import strTruncate
33
+ from arelle.utils.Contexts import partitionModelXbrlContexts
34
+ from arelle.utils.Units import partitionModelXbrlUnits
33
35
  from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
34
36
  from arelle.UrlUtil import isHttpUrl
35
37
  from arelle.ValidateUtr import ValidateUtr
@@ -629,27 +631,17 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
629
631
  # identify unique contexts and units
630
632
  mapContext = {}
631
633
  mapUnit = {}
632
- uniqueContextHashes: dict[Any, Any] = {}
633
- for context in modelXbrl.contexts.values():
634
- h = context.contextDimAwareHash
635
- if h in uniqueContextHashes:
636
- if context.isEqualTo(uniqueContextHashes[h]):
637
- mapContext[context] = uniqueContextHashes[h]
638
- else:
639
- uniqueContextHashes[h] = context
640
- del uniqueContextHashes
641
- uniqueUnitHashes: dict[Any, Any] = {}
634
+ for exemplar_context, *contexts in partitionModelXbrlContexts(modelXbrl).values():
635
+ for context in contexts:
636
+ mapContext[context] = exemplar_context
642
637
  utrValidator = ValidateUtr(modelXbrl)
643
638
  utrUnitIds = set(u.unitId
644
639
  for unitItemType in utrValidator.utrItemTypeEntries.values()
645
640
  for u in unitItemType.values())
641
+ for exemplar_unit, *units in partitionModelXbrlUnits(modelXbrl).values():
642
+ for unit in units:
643
+ mapUnit[unit] = exemplar_unit
646
644
  for unit in modelXbrl.units.values():
647
- h = unit.hash
648
- if h in uniqueUnitHashes:
649
- if unit.isEqualTo(uniqueUnitHashes[h]):
650
- mapUnit[unit] = uniqueUnitHashes[h]
651
- else:
652
- uniqueUnitHashes[h] = unit
653
645
  # check if any custom measure is in UTR
654
646
  for measureTerm in unit.measures:
655
647
  for measure in measureTerm:
@@ -659,7 +651,6 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
659
651
  modelXbrl.warning("ESEF.RTS.III.1.G1-7-1.customUnitInUtr",
660
652
  _("Custom measure SHOULD NOT duplicate a UnitID of UTR: %(measure)s"),
661
653
  modelObject=unit, measure=measure)
662
- del uniqueUnitHashes
663
654
 
664
655
  reportedMandatory: set[QName] = set()
665
656
  precisionFacts = set()
@@ -33,6 +33,7 @@ from arelle.PrototypeDtsObject import LinkPrototype, LocPrototype, ArcPrototype
33
33
  from arelle.Version import authorLabel, copyrightLabel
34
34
  from arelle.XbrlConst import xbrli, xhtml
35
35
  from arelle.XmlValidateConst import VALID
36
+ from arelle.utils.Contexts import getDuplicateContextGroups
36
37
 
37
38
  def dislosureSystemTypes(disclosureSystem, *args, **kwargs):
38
39
  # return ((disclosure system name, variable name), ...)
@@ -121,7 +122,6 @@ def validateXbrlFinally(val, *args, **kwargs):
121
122
 
122
123
  #6.5.4 scenario
123
124
  segContexts = set()
124
- uniqueContextHashes = {}
125
125
  contextIDs = set()
126
126
  precisionFacts = set()
127
127
  formType = None
@@ -133,16 +133,12 @@ def validateXbrlFinally(val, *args, **kwargs):
133
133
  for c in modelXbrl.contexts.values():
134
134
  if XmlUtil.hasChild(c, xbrli, "segment"):
135
135
  segContexts.add(c)
136
- h = c.contextDimAwareHash
137
- if h in uniqueContextHashes:
138
- if c.isEqualTo(uniqueContextHashes[h]):
139
- modelXbrl.error("FERC.6.05.07",
140
- _("The instance document contained more than one context equivalent to %(context)s (%(context2)s). "
141
- "Please remove duplicate contexts from the instance."),
142
- modelObject=(c, uniqueContextHashes[h]), context=c.id, context2=uniqueContextHashes[h].id)
143
- else:
144
- uniqueContextHashes[h] = c
145
136
  contextIDs.add(c.id)
137
+ for contexts in getDuplicateContextGroups(modelXbrl):
138
+ modelXbrl.error("FERC.6.05.07",
139
+ _("The instance document contained more than one context equivalent to %(context)s (%(context2)s). "
140
+ "Please remove duplicate contexts from the instance."),
141
+ modelObject=contexts, context=contexts[0].id, context2=contexts[1].id)
146
142
 
147
143
  if segContexts:
148
144
  modelXbrl.error("FERC.6.05.04",
@@ -414,25 +414,13 @@ class PluginValidationDataExtension(PluginData):
414
414
  calcRelSet = modelXbrl.relationshipSet(XbrlConst.summationItems)
415
415
  dimensionalData = self.getDimensionalData(modelXbrl)
416
416
  primaryItems = dimensionalData.primaryItems
417
- domainMembers = dimensionalData.domainMembers
418
417
  extensionData = self.getExtensionData(modelXbrl)
419
418
  for concept in extensionData.extensionConcepts:
420
- extLineItem = False
421
419
  if concept.isPrimaryItem and \
422
420
  not concept.isAbstract and \
423
421
  concept in primaryItems and \
424
422
  not widerNarrowerRelSet.contains(concept) and \
425
423
  not calcRelSet.fromModelObject(concept):
426
- extLineItem = True
427
- elif concept.isAbstract and \
428
- concept not in domainMembers and \
429
- concept.type is not None and \
430
- not concept.type.isDomainItemType and \
431
- not concept.isHypercubeItem and \
432
- not concept.isDimensionItem and \
433
- not widerNarrowerRelSet.contains(concept):
434
- extLineItem = True
435
- if extLineItem:
436
424
  if not generalSpecialRelSet.contains(concept):
437
425
  extLineItemsNotAnchored.add(concept)
438
426
  else:
@@ -18,6 +18,7 @@ from arelle.ModelObject import ModelObject, ModelComment
18
18
  from arelle.ModelValue import QName, qname
19
19
  from arelle.ValidateXbrl import ValidateXbrl
20
20
  from arelle.typing import TypeGetText
21
+ from arelle.utils.Contexts import partitionModelXbrlContexts
21
22
  from arelle.utils.PluginHooks import ValidationHook
22
23
  from arelle.utils.validate.Decorator import validation
23
24
  from arelle.utils.validate.Validation import Validation
@@ -579,10 +580,7 @@ def rule_fr_nl_3_04(
579
580
  """
580
581
  FR-NL-3.04: An XBRL instance document MUST NOT contain duplicate 'xbrli:context' elements
581
582
  """
582
- duplicates = defaultdict(list)
583
- for context in val.modelXbrl.contexts.values():
584
- duplicates[context.contextDimAwareHash].append(context)
585
- for duplicate_contexts in duplicates.values():
583
+ for duplicate_contexts in partitionModelXbrlContexts(val.modelXbrl).values():
586
584
  if len(duplicate_contexts) > 1:
587
585
  yield Validation.error(
588
586
  codes='NL.FR-NL-3.04',
@@ -1711,11 +1711,11 @@ def rule_nl_kvk_4_4_6_1(
1711
1711
  unreportedLbLocs.add(rel.fromLocator)
1712
1712
  if len(unreportedLbLocs) > 0:
1713
1713
  yield Validation.warning(
1714
- # Subtitle is capitalized inconsistently here because is tmatches the conformance suite. This may change in the future.
1714
+ # Subtitle is capitalized inconsistently here because it matches the conformance suite. This may change in the future.
1715
1715
  codes='NL.NL-KVK.4.4.6.1.UsableConceptsNotAppliedByTaggedFacts',
1716
1716
  modelObject=unreportedLbLocs,
1717
- msg=_('Axis is missing a default member or the default member does not match the taxonomy defaults. '
1718
- 'Update to set default member based on taxonomy defaults.')
1717
+ msg=_('Concept was found but not reported on any facts. '
1718
+ 'Remove any unused concepts or ensure concept is applied to applicable facts.'),
1719
1719
  )
1720
1720
 
1721
1721
 
@@ -10,6 +10,7 @@ from .PluginValidationDataExtension import PluginValidationDataExtension
10
10
 
11
11
  _: TypeGetText
12
12
 
13
+ EQUITY = 'Equity'
13
14
  IE_PROFIT_LOSS = 'ProfitLossBeforeTax'
14
15
  IE_PROFIT_LOSS_ORDINARY = 'ProfitLossOnOrdinaryActivitiesBeforeTax'
15
16
  PRINCIPAL_CURRENCY = 'PrincipalCurrencyUsedInBusinessReport'
@@ -19,14 +19,15 @@ from arelle.ModelInstanceObject import ModelInlineFact, ModelUnit
19
19
  from arelle.ModelValue import qname
20
20
  from arelle.ModelXbrl import ModelXbrl
21
21
  from arelle.PythonUtil import strTruncate
22
+ from arelle.utils.Contexts import partitionModelXbrlContexts
23
+ from arelle.utils.Units import partitionModelXbrlUnits
22
24
  from arelle.utils.PluginHooks import ValidationHook
23
25
  from arelle.utils.validate.Decorator import validation
24
26
  from arelle.utils.validate.Validation import Validation
25
27
  from arelle.ValidateXbrlCalcs import inferredDecimals, rangeValue
26
28
  from arelle.XbrlConst import qnXbrliMonetaryItemType, qnXbrliXbrl, xhtml
27
29
  from arelle.XmlValidateConst import VALID
28
- from . import errorOnNegativeFact
29
- from ..ValidationPluginExtension import IE_PROFIT_LOSS_ORDINARY, IE_PROFIT_LOSS, PRINCIPAL_CURRENCY, TURNOVER_REVENUE
30
+ from ..ValidationPluginExtension import EQUITY, PRINCIPAL_CURRENCY, TURNOVER_REVENUE
30
31
  from ..PluginValidationDataExtension import MANDATORY_ELEMENTS, SCHEMA_PATTERNS, TR_NAMESPACES, PluginValidationDataExtension
31
32
 
32
33
 
@@ -167,7 +168,6 @@ def rule_main(
167
168
  schemeEntityIds = set()
168
169
  mapContext = {} # identify unique contexts and units
169
170
  mapUnit = {}
170
- uniqueContextHashes: dict[str, str] = {}
171
171
  hasCRO = False
172
172
  unsupportedSchemeContexts = []
173
173
  mismatchIdentifierContexts = []
@@ -180,13 +180,10 @@ def rule_main(
180
180
  mismatchIdentifierContexts.append(context)
181
181
  if scheme == "http://www.cro.ie/":
182
182
  hasCRO = True
183
- h = context.contextDimAwareHash
184
- if h in uniqueContextHashes:
185
- if context.isEqualTo(uniqueContextHashes[h]):
186
- mapContext[context] = uniqueContextHashes[h]
187
- else:
188
- uniqueContextHashes[h] = context
189
- del uniqueContextHashes
183
+ for exemplar_context, *contexts in partitionModelXbrlContexts(modelXbrl).values():
184
+ for context in contexts:
185
+ mapContext[context] = exemplar_context
186
+
190
187
  if len(schemeEntityIds) > 1:
191
188
  modelXbrl.error("ROS:differentContextEntityIdentifiers",
192
189
  _("Context entity identifier not all the same: %(schemeEntityIds)s."),
@@ -202,15 +199,9 @@ def rule_main(
202
199
  modelObject=mismatchIdentifierContexts,
203
200
  identifiers=", ".join(sorted(set(c.entityIdentifier[1] for c in mismatchIdentifierContexts))))
204
201
 
205
- uniqueUnitHashes: dict[str, ModelUnit] = {}
206
- for unit in modelXbrl.units.values():
207
- h = unit.hash
208
- if h in uniqueUnitHashes:
209
- if unit.isEqualTo(uniqueUnitHashes[h]):
210
- mapUnit[unit] = uniqueUnitHashes[h]
211
- else:
212
- uniqueUnitHashes[h] = unit
213
- del uniqueUnitHashes
202
+ for exemplar_unit, *units in partitionModelXbrlUnits(modelXbrl).values():
203
+ for unit in units:
204
+ mapUnit[unit] = exemplar_unit
214
205
 
215
206
  if hasCRO and "ie-common" in nsMap:
216
207
  mandatory.add(qname("ie-common:CompaniesRegistrationOfficeNumber", nsMap)) # type-ignore[arg-type]
@@ -293,6 +284,42 @@ def rule_main(
293
284
  modelXbrl.modelManager.showStatus(None)
294
285
 
295
286
 
287
+ @validation(
288
+ hook=ValidationHook.XBRL_FINALLY,
289
+ )
290
+ def rule_ros19(
291
+ pluginData: PluginValidationDataExtension,
292
+ val: ValidateXbrl,
293
+ *args: Any,
294
+ **kwargs: Any,
295
+ ) -> Iterable[Validation]:
296
+ """
297
+ ROS: Rule 19: DPLTurnoverRevenue should be tested to be less than or equal to 10x the absolute value of Equity
298
+ """
299
+ equity_facts = val.modelXbrl.factsByLocalName.get(EQUITY, set())
300
+ largest_equity_fact = None
301
+ for e_fact in equity_facts:
302
+ if e_fact.xValid >= VALID:
303
+ if largest_equity_fact is None or abs(int(e_fact.value)) > abs(int(largest_equity_fact.value)):
304
+ largest_equity_fact = e_fact
305
+
306
+ turnover_facts = val.modelXbrl.factsByLocalName.get(TURNOVER_REVENUE, set())
307
+ largest_turnover_fact = None
308
+ for t_fact in turnover_facts:
309
+ if t_fact.xValid >= VALID:
310
+ if largest_turnover_fact is None or int(t_fact.value) > int(largest_turnover_fact.value):
311
+ largest_turnover_fact = t_fact
312
+
313
+ if (largest_equity_fact is not None and
314
+ largest_turnover_fact is not None and
315
+ int(largest_turnover_fact.value) > (10 * abs(int(largest_equity_fact.value)))):
316
+ yield Validation.error(
317
+ "ROS.19",
318
+ _("Turnover / Revenue (DPLTurnoverRevenue) may exceed the maximum expected value. Please review the submission and, if correct, test your submission with Revenue Online's iXBRL test facility."),
319
+ modelObject=[largest_equity_fact, largest_turnover_fact],
320
+ )
321
+
322
+
296
323
  @validation(
297
324
  hook=ValidationHook.XBRL_FINALLY,
298
325
  )
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from arelle.utils.Equivalence import partitionIntoEquivalenceClasses
6
+
7
+ if TYPE_CHECKING:
8
+ from collections.abc import Iterable
9
+ from arelle.ModelXbrl import ModelXbrl
10
+ from arelle.ModelInstanceObject import ModelContext
11
+
12
+ class ContextHashKey:
13
+ __slots__ = ('context', 'dimensionalAspectModel', 'hash')
14
+ context: ModelContext
15
+ dimensionalAspectModel: bool
16
+ hash: int
17
+
18
+ def __init__(self, context: ModelContext, dimensionalAspectModel: bool) -> None:
19
+ self.context = context
20
+ self.dimensionalAspectModel = dimensionalAspectModel
21
+ self.hash = self.context.contextDimAwareHash if dimensionalAspectModel else self.context.contextNonDimAwareHash
22
+
23
+ def __eq__(self, o: Any) -> bool:
24
+ if isinstance(o, ContextHashKey):
25
+ return self.dimensionalAspectModel == o.dimensionalAspectModel and self.context.isEqualTo(o.context, self.dimensionalAspectModel)
26
+ return NotImplemented
27
+
28
+ def __hash__(self) -> int:
29
+ return self.hash
30
+
31
+ def partitionContexts(contexts: Iterable[ModelContext], dimensionalAspectModel: bool) -> dict[ContextHashKey, tuple[ModelContext, ...]]:
32
+ return partitionIntoEquivalenceClasses(contexts, lambda c: ContextHashKey(c, dimensionalAspectModel))
33
+
34
+ def partitionModelXbrlContexts(modelXbrl: ModelXbrl) -> dict[ContextHashKey, tuple[ModelContext, ...]]:
35
+ return partitionContexts(modelXbrl.contexts.values(), dimensionalAspectModel=modelXbrl.hasXDT)
36
+
37
+ def getDuplicateContextGroups(modelXbrl: ModelXbrl) -> list[tuple[ModelContext, ...]]:
38
+ return [partition for partition in partitionModelXbrlContexts(modelXbrl).values() if len(partition) > 1]
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import defaultdict
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Any,
7
+ Callable,
8
+ TypeVar,
9
+ )
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Iterable
13
+ from arelle.ModelXbrl import ModelXbrl
14
+
15
+ T = TypeVar('T')
16
+ K = TypeVar('K')
17
+
18
+ def partitionIntoEquivalenceClasses(items: Iterable[T], key: Callable[[T], K]) -> dict[K, tuple[T, ...]]:
19
+ d = defaultdict(list)
20
+ for item in items:
21
+ d[key(item)].append(item)
22
+ return {k: tuple(v) for k, v in d.items()}
arelle/utils/Units.py ADDED
@@ -0,0 +1,36 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from arelle.utils.Equivalence import partitionIntoEquivalenceClasses
6
+
7
+ if TYPE_CHECKING:
8
+ from collections.abc import Iterable
9
+ from arelle.ModelXbrl import ModelXbrl
10
+ from arelle.ModelInstanceObject import ModelUnit
11
+
12
+ class UnitHashKey:
13
+ __slots__ = ('unit', 'hash')
14
+ unit: ModelUnit
15
+ hash: int
16
+
17
+ def __init__(self, unit: ModelUnit) -> None:
18
+ self.unit = unit
19
+ self.hash = self.unit.hash
20
+
21
+ def __eq__(self, o: Any) -> bool:
22
+ if isinstance(o, UnitHashKey):
23
+ return self.unit.isEqualTo(o.unit)
24
+ return NotImplemented
25
+
26
+ def __hash__(self) -> int:
27
+ return self.hash
28
+
29
+ def partitionUnits(units: Iterable[ModelUnit]) -> dict[UnitHashKey, tuple[ModelUnit, ...]]:
30
+ return partitionIntoEquivalenceClasses(units, UnitHashKey)
31
+
32
+ def partitionModelXbrlUnits(modelXbrl: ModelXbrl) -> dict[UnitHashKey, tuple[ModelUnit, ...]]:
33
+ return partitionUnits(modelXbrl.units.values())
34
+
35
+ def getDuplicateUnitGroups(modelXbrl: ModelXbrl) -> list[tuple[ModelUnit, ...]]:
36
+ return [partition for partition in partitionModelXbrlUnits(modelXbrl).values() if len(partition) > 1]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.38
3
+ Version: 2.37.40
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -37,7 +37,7 @@ arelle/Locale.py,sha256=07IDxv8nIfvhyRRllFdEAKRI3yo1D2v781cTrjb_RoQ,33193
37
37
  arelle/ModelDocument.py,sha256=Sq6umEdn-aNHjxIpEsXTT7A4V25nGY0JiylSnhr9zSI,130749
38
38
  arelle/ModelDtsObject.py,sha256=nvHQs4BDmPxY6mqLiBuqIGRJXyA1EqOEGB2f3S6A6w4,88656
39
39
  arelle/ModelFormulaObject.py,sha256=-eb0lBYciEeAvvGduIs3AHGNGrxge9_0g1cVT6UUgvc,122560
40
- arelle/ModelInstanceObject.py,sha256=gRRPEZR-x6IL-0z0-asKd2nbgReZFKHKmk7QTxKXI64,74254
40
+ arelle/ModelInstanceObject.py,sha256=fUiDb6aS2ls0GkrVxxkPyZqEr-RI4qsGQJjKkAK2tUk,74246
41
41
  arelle/ModelManager.py,sha256=QUNcD2LC_YyyGFU8bFTSuzIGI1qpOK55KBlQ697Ep1I,11075
42
42
  arelle/ModelObject.py,sha256=Rttkhv-PtfneZyDYsG5FDh98BzT97ameTmwNdqFaOv0,18657
43
43
  arelle/ModelObjectFactory.py,sha256=XuNF4Re3p00tODCdyspfar_DNCXfARqCaLEkntgAZ0g,8750
@@ -72,7 +72,7 @@ arelle/ValidateInfoset.py,sha256=Rz_XBi5Ha43KpxXYhjLolURcWVx5qmqyjLxw48Yt9Dg,203
72
72
  arelle/ValidateUtr.py,sha256=oxOPrOa1XEzBay4miXvx6eRLTnVFYUIJC9ueWUk4EkI,13633
73
73
  arelle/ValidateVersReport.py,sha256=RMe7GlcyZV0HoVFHL0qOGrKm4et-6yPq5dmikkhnvoU,43196
74
74
  arelle/ValidateXbrl.py,sha256=kBiY_q9QmORwC8VxGpRq9mfufknt08nEAeSgNh1ov-M,78005
75
- arelle/ValidateXbrlCalcs.py,sha256=vx1LYbu2l6wY88O9vyaThK5gOG59R9ggHX3FapbN3XA,44308
75
+ arelle/ValidateXbrlCalcs.py,sha256=6OMjIef6ixJGkxH-hgIRbMRJ46e52KEo-8vDyM97krc,44028
76
76
  arelle/ValidateXbrlDTS.py,sha256=yxvpTpImEbrQuLJ2aJf38FjA-OEznpWWdsDK0GtLXIU,104003
77
77
  arelle/ValidateXbrlDimensions.py,sha256=Qv7_CI65SiUcpgsnEc86XuMjKz81-170wE5dmZqnvHc,38373
78
78
  arelle/Version.py,sha256=RjbPSSiH57VzZFhhUmEmvLsL8uSb5VwEIj2zq-krhsY,934
@@ -123,7 +123,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
123
123
  arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
124
124
  arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
125
125
  arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
- arelle/_version.py,sha256=-XXBu4KjzmMOb27ISFZtxgU868vA14VkfmOyupc5LJw,515
126
+ arelle/_version.py,sha256=rxJ_q4vyjbOfPG_eNhLacH0vqtMeCXGlM6EfD3QLIcE,515
127
127
  arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
128
128
  arelle/api/Session.py,sha256=27HVuK3Bz1_21l4_RLn1IQg6v0MNsUEYrHajymyWwxI,7429
129
129
  arelle/archive/CustomLogger.py,sha256=v_JXOCQLDZcfaFWzxC9FRcEf9tQi4rCI4Sx7jCuAVQI,1231
@@ -384,13 +384,13 @@ arelle/plugin/validate/DBA/PluginValidationDataExtension.py,sha256=R0lNf-3-lKHln
384
384
  arelle/plugin/validate/DBA/ValidationPluginExtension.py,sha256=JozGD4LqGn8zbc_-BhZOPMJl0PSboloS6oG8UwwTz6I,48638
385
385
  arelle/plugin/validate/DBA/__init__.py,sha256=KhmlUkqgsRtEXpu5DZBXFzv43nUTvi-0sdDNRfw5Up4,1564
386
386
  arelle/plugin/validate/DBA/resources/config.xml,sha256=KHfo7SrjzmjHbfwIJBmESvOOjdIv4Av26BCcZxfn3Pg,875
387
- arelle/plugin/validate/DBA/rules/__init__.py,sha256=t6xULMkNT7naI-sKmiD3ohX2Q41TTKqFPtKI01BKiyA,10349
388
- arelle/plugin/validate/DBA/rules/fr.py,sha256=lWsJ3J68ecrzwaE5uh_9ymFispXiuK_GmGZbFoODLgo,71324
387
+ arelle/plugin/validate/DBA/rules/__init__.py,sha256=eBm6FAb_WnBBYfgLSwsO30gVjpWaHxLqRHRJAIBj7oo,10418
388
+ arelle/plugin/validate/DBA/rules/fr.py,sha256=IAegA_Fc-Zkjj8qlzFuo_52gNVHW-lAddBFCQw8W8P4,71318
389
389
  arelle/plugin/validate/DBA/rules/tc.py,sha256=CXPOGHpab9Y-iV84wDXrsE-rPe_d6Uhw4HjEyTBEzq4,1572
390
390
  arelle/plugin/validate/DBA/rules/th.py,sha256=mDrjescz6106jBGjdH6bipqx48BnxcjHSkNL1qQf0QE,6227
391
391
  arelle/plugin/validate/DBA/rules/tm.py,sha256=ui9oKBqlAForwkQ9kk9KBiUogTJE5pv1RbIejKASprY,11797
392
- arelle/plugin/validate/DBA/rules/tr.py,sha256=zdi3kQ82whmweVWRLbMvcNpM8sqtUliPsGfd81rgZws,14671
393
- arelle/plugin/validate/EBA/__init__.py,sha256=1kW-04W32sStSAL8wvW1ZpXnjlFv6KLbfE4aifYUB2A,46000
392
+ arelle/plugin/validate/DBA/rules/tr.py,sha256=4TootFjl0HXsKZk1XNBCyj-vnjRs4lg35hfiz_b_4wU,14684
393
+ arelle/plugin/validate/EBA/__init__.py,sha256=x3zXNcdSDJ3kHfL7kMs0Ve0Vs9oWbzNFVf1TK4Avmy8,45924
394
394
  arelle/plugin/validate/EBA/config.xml,sha256=37wMVUAObK-XEqakqD8zPNog20emYt4a_yfL1AKubF8,2022
395
395
  arelle/plugin/validate/EDINET/Constants.py,sha256=QG69rWdpIrpQzZQkRcDWk2i3rlBVohr4VtSdW-IS5_w,734
396
396
  arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
@@ -404,7 +404,7 @@ arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5
404
404
  arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
405
405
  arelle/plugin/validate/EDINET/rules/edinet.py,sha256=PKbylqzhbi2ItMCW-eliaQysCzYg_rAkWzwRJmX1RtQ,4180
406
406
  arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
407
- arelle/plugin/validate/EDINET/rules/gfm.py,sha256=SaVUW1cvrwRSjoq_W97HGCk-3ccE7MKfLut3K8yuUQ8,16657
407
+ arelle/plugin/validate/EDINET/rules/gfm.py,sha256=Zn803dztEEVAFqOdOnbkWTPt3h6e7DXAulc68U14ylc,20062
408
408
  arelle/plugin/validate/EDINET/rules/upload.py,sha256=HZ-9Gk6WtIichTGcSsSGIrMXNgsgJYQYwfUKeLs1XWU,20369
409
409
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
410
410
  arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
@@ -412,19 +412,19 @@ arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlx
412
412
  arelle/plugin/validate/ESEF/__init__.py,sha256=LL7uYOcGPHgjwTlcfW2oWMqWiqrZ5yABzcKkJZFrZis,20391
413
413
  arelle/plugin/validate/ESEF/ESEF_2021/DTS.py,sha256=6Za7BANwwc_egxLCgbgWzwUGOXZv9IF1I7JCkDNt2Tw,26277
414
414
  arelle/plugin/validate/ESEF/ESEF_2021/Image.py,sha256=4bnhuy5viBU0viPjb4FhcRRjVVKlNdnKLFdSGg3sZvs,4871
415
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py,sha256=Oh_Qy2Shug3wN1-uwct0BCnuNe36RoCQvLEJxdmE1HY,63941
415
+ arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py,sha256=R3cVpiq3x0YyH5cT1L_CfAR_8onn0nHVuruFSFsszCk,63675
416
416
  arelle/plugin/validate/ESEF/ESEF_2021/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
417
417
  arelle/plugin/validate/ESEF/ESEF_Current/DTS.py,sha256=epp-PBh1NJzQqgxUE6C468HmoDc2w3j54rMwfiOAry4,29334
418
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py,sha256=XMFlRc9X-dwMkyaEMWxmNWnTRBlFjzpA8JsyMWknzvs,75251
418
+ arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py,sha256=J9hJcLlE6FA84Ppgthiu4ju0KHJ-JdmVub2qKAKm3as,74985
419
419
  arelle/plugin/validate/ESEF/ESEF_Current/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
420
420
  arelle/plugin/validate/ESEF/resources/authority-validations.json,sha256=bR0ZMpiQL9LqiyzrjhAGMDLEHBHimsDuDu9SQY5xofA,15503
421
421
  arelle/plugin/validate/ESEF/resources/config.xml,sha256=t3STU_-QYM7Ay8YwZRPapnohiWVWhjfr4L2Rjx9xN9U,3902
422
- arelle/plugin/validate/FERC/__init__.py,sha256=V4fXcFKBsjFFPs9_1NhvDjWpEQCoQM0tRQMS0I1Ua7U,11462
422
+ arelle/plugin/validate/FERC/__init__.py,sha256=rC1OYNBWnoXowEohiR9yagHtAi_NPAlmaahRSQhSdS4,11325
423
423
  arelle/plugin/validate/FERC/config.xml,sha256=bn9b8eCqJA1J62rYq1Nz85wJrMGAahVmmnIUQZyerjo,1919
424
424
  arelle/plugin/validate/FERC/resources/ferc-utr.xml,sha256=OCRj9IUpdXATCBXKbB71apYx9kxcNtZW-Hq4s-avsRY,2663
425
425
  arelle/plugin/validate/NL/DisclosureSystems.py,sha256=urRmYJ8RnGPlTgSVKW7zGN4_4CtL3OVKlcI3LwTpBz4,561
426
426
  arelle/plugin/validate/NL/LinkbaseType.py,sha256=BwRQl4XZFFCopufC2FEMLhYENNTk2JUWVQvnIUsaqtI,3108
427
- arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=HB6jIGhsQFE3GKI8FpD96g3cP6IWj3ij4dE_xmSkmMA,34235
427
+ arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=4yO0UlShSFk_iWxLbCZ9SHB1wuJQMcKcDGmVkHcltIY,33672
428
428
  arelle/plugin/validate/NL/ValidationPluginExtension.py,sha256=h6CjGJJkE8YqrzPiA8uNaCzn_P6HspH-6ja89tLCXxY,17978
429
429
  arelle/plugin/validate/NL/__init__.py,sha256=W-SHohiAWM7Yi77gAbt-D3vvZNAB5s1j16mHCTFta6w,3158
430
430
  arelle/plugin/validate/NL/resources/config.xml,sha256=qBE6zywFSmemBSWonuTII5iuOCUlNb1nvkpMbsZb5PM,1853
@@ -432,16 +432,16 @@ arelle/plugin/validate/NL/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
432
432
  arelle/plugin/validate/NL/rules/br_kvk.py,sha256=0SwKieWzTDm3YMsXPS6zTdgbk7_Z9CzqRkRmCRz1OiQ,15789
433
433
  arelle/plugin/validate/NL/rules/fg_nl.py,sha256=OJF2EYx4KDTdNggHiw5Trq5S5g7WGpbb7YvO6_IrrGU,10704
434
434
  arelle/plugin/validate/NL/rules/fr_kvk.py,sha256=kYqXt45S6eM32Yg9ii7pUhOMfJaHurgYqQ73FyQALs8,8171
435
- arelle/plugin/validate/NL/rules/fr_nl.py,sha256=bKY3vLInnZFNwdGXp-sUTvjgRi3dK3n2GiJ0-GlAsdY,31503
436
- arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=l_pxjaj9kyFKl6uYYDHQghuzKxAUy_P8Lko7lbz-wJc,90189
435
+ arelle/plugin/validate/NL/rules/fr_nl.py,sha256=vwzk6_P6jhszpeboTM09uOHtm-Lijzz7Iqu7NsYKyaE,31444
436
+ arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=KE2bOX1eQWxW1cVzMGR-HLgJ36zUq77D9DpA9-GvwNk,90165
437
437
  arelle/plugin/validate/ROS/DisclosureSystems.py,sha256=rJ81mwQDYTi6JecFZ_zhqjjz3VNQRgjHNSh0wcQWAQE,18
438
438
  arelle/plugin/validate/ROS/PluginValidationDataExtension.py,sha256=IV7ILhNvgKwQXqbpSA6HRNt9kEnejCyMADI3wyyIgk0,4036
439
- arelle/plugin/validate/ROS/ValidationPluginExtension.py,sha256=FBhEp8t396vGdvCbMEimfcxmGiGnhXMen-yVLWnkFaI,758
439
+ arelle/plugin/validate/ROS/ValidationPluginExtension.py,sha256=HrLoJWHMFjou3j7ro6Ajrcab1JOgi505ZFPEM5a3QOY,776
440
440
  arelle/plugin/validate/ROS/__init__.py,sha256=KuWg1MHVzA2S6eaHFptvP3Vu_5gQWf3OUYC97clB7zI,2075
441
441
  arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6SOWnrfm8,654
442
442
  arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
443
443
  arelle/plugin/validate/ROS/rules/__init__.py,sha256=wW7BUAIb7sRkOxC1Amc_ZKrz03FM-Qh1TyZe6wxYaAU,1567
444
- arelle/plugin/validate/ROS/rules/ros.py,sha256=Vv7qKMmhBEuPu7y_g0sjiDLnm5VXXz6EVdzonJbCUtg,18436
444
+ arelle/plugin/validate/ROS/rules/ros.py,sha256=pkEDeMeDibxGO597VWQrp4sz7JAxttWbKkn-ncKHvus,19635
445
445
  arelle/plugin/validate/UK/ValidateUK.py,sha256=0UhSwsY1lrY-EAEBJJR9QY38YXGBZ6PEgmuC5gQfBlI,57813
446
446
  arelle/plugin/validate/UK/__init__.py,sha256=KE6s_B-EvrHDCtWQz2N_wQwyx_ZbWhYNV2GfQnluxMw,30655
447
447
  arelle/plugin/validate/UK/config.xml,sha256=mUFhWDfBzGTn7v0ZSmf4HaweQTMJh_4ZcJmD9mzCHrA,1547
@@ -743,9 +743,12 @@ arelle/resources/libs/Tktable2.11/win-x86_64/tkTable.tcl,sha256=JrSZRngZFHtw8Svp
743
743
  arelle/scripts-macOS/startWebServer.command,sha256=KXLSwAwchDZBlL-k9PYXdf39RNBtte4vV076_kIz2Ow,91
744
744
  arelle/scripts-unix/startWebServer.sh,sha256=_0puRzaGkdMZoFn3R7hDti9a3ryN6kTZAXwLweeZU1s,42
745
745
  arelle/scripts-windows/startWebServer.bat,sha256=qmnF1yrjNo__bi4QodONWlN0qHShVLTKptJQYyZtgcY,122
746
+ arelle/utils/Contexts.py,sha256=j9uSBAXGkunlJGC9SscCbb1cj3oU_J3b_yjdhj2B4a4,1714
746
747
  arelle/utils/EntryPointDetection.py,sha256=4RzercL0xE4PJrwoeUYq3S-E7PMSD-IspyS9bwK2RYM,4722
748
+ arelle/utils/Equivalence.py,sha256=Ac6sENvh-WHJlBJneV_-6n_MF2C1filHETkUEiucLJg,525
747
749
  arelle/utils/PluginData.py,sha256=GUnuZaApm1J4Xm9ZA1U2M1aask-AaNGviLtc0fgXbFg,265
748
750
  arelle/utils/PluginHooks.py,sha256=CeVxti23VjERQl4xWFucDVTW63TCG2PUdnxpjd3x_Ms,31170
751
+ arelle/utils/Units.py,sha256=c9bwnu9Xnm00gC9Q6qQ1ogsEeTEXGRH7rahlEbrEWnQ,1201
749
752
  arelle/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
750
753
  arelle/utils/validate/Decorator.py,sha256=8LGmA171HZgKrALtsMKyHqMNM-XCdwJOv6KpZz4pC2c,3161
751
754
  arelle/utils/validate/DetectScriptsInXhtml.py,sha256=RFBh_Z24OjR69s71qQzSzbxdU-WCTWuvYlONN-BgpZ0,2098
@@ -756,9 +759,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
756
759
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
757
760
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
758
761
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
759
- arelle_release-2.37.38.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
760
- arelle_release-2.37.38.dist-info/METADATA,sha256=FHHaBwTHXeDmmnXHgmKzkl3Cs-Y8btdXUJCrodSOXSk,9137
761
- arelle_release-2.37.38.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
762
- arelle_release-2.37.38.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
763
- arelle_release-2.37.38.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
764
- arelle_release-2.37.38.dist-info/RECORD,,
762
+ arelle_release-2.37.40.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
763
+ arelle_release-2.37.40.dist-info/METADATA,sha256=APvbQz9C9vSm13meJ_BiDNePwH_bHMXmYHcrFeemYbo,9137
764
+ arelle_release-2.37.40.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
765
+ arelle_release-2.37.40.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
766
+ arelle_release-2.37.40.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
767
+ arelle_release-2.37.40.dist-info/RECORD,,