arelle-release 2.37.20__py3-none-any.whl → 2.37.21__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.

arelle/CntlrCmdLine.py CHANGED
@@ -537,6 +537,14 @@ def parseArgs(args):
537
537
  runtimeOptions = RuntimeOptions(pluginOptions=pluginOptions, **baseOptions)
538
538
  except RuntimeOptionsException as e:
539
539
  parser.error(f"{e}, please try\n python CntlrCmdLine.py --help")
540
+ if (
541
+ runtimeOptions.entrypointFile is None and
542
+ not runtimeOptions.proxy and
543
+ not runtimeOptions.plugins and
544
+ not pluginOptions and
545
+ not runtimeOptions.webserver
546
+ ):
547
+ parser.error("No entrypoint specified, please try\n python CntlrCmdLine.py --help")
540
548
  return runtimeOptions, arellePluginModules
541
549
 
542
550
 
arelle/RuntimeOptions.py CHANGED
@@ -183,12 +183,6 @@ class RuntimeOptions:
183
183
  raise RuntimeOptionsException('Provided plugin options already exist as base options {}'.format(existingBaseOptions))
184
184
  for optionName, optionValue in pluginOptions.items():
185
185
  setattr(self, optionName, optionValue)
186
- if (self.entrypointFile is None and
187
- not self.proxy and
188
- not self.plugins and
189
- not pluginOptions and
190
- not self.webserver):
191
- raise RuntimeOptionsException('Incorrect arguments')
192
186
  if self.webserver and not hasWebServer():
193
187
  raise RuntimeOptionsException("Webserver option requires webserver module")
194
188
  if self.webserver and any((
arelle/XbrlConst.py CHANGED
@@ -101,6 +101,7 @@ elementLabel = "http://xbrl.org/arcrole/2008/element-label"
101
101
  genLabel = "http://xbrl.org/2008/label"
102
102
  qnGenLabel = qname("{http://xbrl.org/2008/label}label")
103
103
  xbrldt = "http://xbrl.org/2005/xbrldt"
104
+ qnXbrldtClosed = qname("{http://xbrl.org/2005/xbrldt}xbrldt:closed")
104
105
  qnXbrldtHypercubeItem = qname("{http://xbrl.org/2005/xbrldt}xbrldt:hypercubeItem")
105
106
  qnXbrldtDimensionItem = qname("{http://xbrl.org/2005/xbrldt}xbrldt:dimensionItem")
106
107
  qnXbrldtContextElement = qname("{http://xbrl.org/2005/xbrldt}xbrldt:contextElement")
@@ -108,6 +109,7 @@ xbrldi = "http://xbrl.org/2006/xbrldi"
108
109
  qnXbrldiExplicitMember = qname("{http://xbrl.org/2006/xbrldi}xbrldi:explicitMember")
109
110
  qnXbrldiTypedMember = qname("{http://xbrl.org/2006/xbrldi}xbrldi:typedMember")
110
111
  xlink = "http://www.w3.org/1999/xlink"
112
+ qnXlinkArcRole = qname("{http://www.w3.org/1999/xlink}xlink:arcrole")
111
113
  xl = "http://www.xbrl.org/2003/XLink"
112
114
  qnXlExtended = qname("{http://www.xbrl.org/2003/XLink}xl:extended")
113
115
  qnXlLocator = qname("{http://www.xbrl.org/2003/XLink}xl:locator")
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.20'
21
- __version_tuple__ = version_tuple = (2, 37, 20)
20
+ __version__ = version = '2.37.21'
21
+ __version_tuple__ = version_tuple = (2, 37, 21)
arelle/api/Session.py CHANGED
@@ -1,10 +1,7 @@
1
1
  """
2
2
  See COPYRIGHT.md for copyright information.
3
3
 
4
- The Arelle Python Beta API (located in `arelle.api` module) is an in-progress API module.
5
- A roadmap for this API is in development.
6
-
7
- Users of this API should expect changes in future releases.
4
+ The `arelle.api` module is the supported method for integrating Arelle into other Python applications.
8
5
  """
9
6
  from __future__ import annotations
10
7
 
@@ -12,7 +9,7 @@ import logging
12
9
  from types import TracebackType
13
10
  from typing import Any, BinaryIO
14
11
 
15
- from arelle import PluginManager, PackageManager
12
+ from arelle import PackageManager, PluginManager
16
13
  from arelle.CntlrCmdLine import CntlrCmdLine, createCntlrAndPreloadPlugins
17
14
  from arelle.ModelXbrl import ModelXbrl
18
15
  from arelle.RuntimeOptions import RuntimeOptions
@@ -81,6 +78,7 @@ class Session:
81
78
  responseZipStream: BinaryIO | None = None,
82
79
  logHandler: logging.Handler | None = None,
83
80
  logFilters: list[logging.Filter] | None = None,
81
+ sourceZipStreamFileName: str | None = None,
84
82
  ) -> bool:
85
83
  """
86
84
  Perform a run using the given options.
@@ -88,8 +86,11 @@ class Session:
88
86
  :param sourceZipStream: Optional stream to read source data from.
89
87
  :param responseZipStream: Options stream to write response data to.
90
88
  :param logHandler: Optional log handler to use for logging.
89
+ :param sourceZipStreamFileName: Optional file name to use for the passed zip stream.
91
90
  :return: True if the run was successful, False otherwise.
92
91
  """
92
+ if sourceZipStreamFileName is not None and sourceZipStream is None:
93
+ raise ValueError("sourceZipStreamFileName may only be provided if sourceZipStream is not None.")
93
94
  PackageManager.reset()
94
95
  PluginManager.reset()
95
96
  if self._cntlr is None:
@@ -143,4 +144,5 @@ class Session:
143
144
  options,
144
145
  sourceZipStream=sourceZipStream,
145
146
  responseZipStream=responseZipStream,
147
+ sourceZipStreamFileName=sourceZipStreamFileName,
146
148
  )
@@ -0,0 +1,77 @@
1
+ '''
2
+ See COPYRIGHT.md for copyright information.
3
+ '''
4
+ from __future__ import annotations
5
+
6
+ from enum import Enum
7
+
8
+ from arelle import XbrlConst
9
+ from arelle.ModelValue import QName
10
+
11
+
12
+ class LinkbaseType(Enum):
13
+ CALCULATION = "calculation"
14
+ DEFINITION = "definition"
15
+ LABEL = "label"
16
+ PRESENTATION = "presentation"
17
+ REFERENCE = "reference"
18
+
19
+ @staticmethod
20
+ def fromRefUri(refUri: str | None) -> LinkbaseType | None:
21
+ """
22
+ Returns the LinkbaseType corresponding to the given ref URI.
23
+ If the URI does not match any known linkbase reference type, returns None.
24
+ """
25
+ if refUri is None:
26
+ return None
27
+ return LINKBASE_TYPE_BY_REF_URI.get(refUri, None)
28
+
29
+ def getArcQn(self) -> QName:
30
+ """
31
+ Returns the qname of the arc associated with this LinkbaseType.
32
+ """
33
+ return LINKBASE_ARC_QN[self]
34
+
35
+ def getLinkQn(self) -> QName:
36
+ """
37
+ Returns the qname of the link associated with this LinkbaseType.
38
+ """
39
+ return LINKBASE_LINK_QN[self]
40
+
41
+ def getLowerName(self) -> str:
42
+ """
43
+ Returns the lower-case name of this LinkbaseType.
44
+ """
45
+ return self.value.lower()
46
+
47
+ def getRefUri(self) -> str:
48
+ """
49
+ Returns the ref URI associated with this LinkbaseType.
50
+ """
51
+ return LINKBASE_REF_URIS[self]
52
+
53
+
54
+ LINKBASE_ARC_QN = {
55
+ LinkbaseType.CALCULATION: XbrlConst.qnLinkCalculationArc,
56
+ LinkbaseType.DEFINITION: XbrlConst.qnLinkDefinitionArc,
57
+ LinkbaseType.LABEL: XbrlConst.qnLinkLabelArc,
58
+ LinkbaseType.PRESENTATION: XbrlConst.qnLinkPresentationArc,
59
+ LinkbaseType.REFERENCE: XbrlConst.qnLinkReferenceArc,
60
+ }
61
+
62
+ LINKBASE_LINK_QN = {
63
+ LinkbaseType.CALCULATION: XbrlConst.qnLinkCalculationLink,
64
+ LinkbaseType.DEFINITION: XbrlConst.qnLinkDefinitionLink,
65
+ LinkbaseType.LABEL: XbrlConst.qnLinkLabelLink,
66
+ LinkbaseType.PRESENTATION: XbrlConst.qnLinkPresentationLink,
67
+ LinkbaseType.REFERENCE: XbrlConst.qnLinkReferenceLink,
68
+ }
69
+
70
+ LINKBASE_REF_URIS = {
71
+ LinkbaseType.CALCULATION: "http://www.xbrl.org/2003/role/calculationLinkbaseRef",
72
+ LinkbaseType.DEFINITION: "http://www.xbrl.org/2003/role/definitionLinkbaseRef",
73
+ LinkbaseType.LABEL: "http://www.xbrl.org/2003/role/labelLinkbaseRef",
74
+ LinkbaseType.PRESENTATION: "http://www.xbrl.org/2003/role/presentationLinkbaseRef",
75
+ LinkbaseType.REFERENCE: "http://www.xbrl.org/2003/role/referenceLinkbaseRef",
76
+ }
77
+ LINKBASE_TYPE_BY_REF_URI = {v: k for k, v in LINKBASE_REF_URIS.items()}
@@ -7,14 +7,15 @@ from collections import defaultdict
7
7
  from dataclasses import dataclass
8
8
  from functools import lru_cache
9
9
  from pathlib import Path
10
- from typing import Any, TYPE_CHECKING, cast
10
+ from typing import Any, TYPE_CHECKING, cast, Iterable
11
11
 
12
12
  import regex as re
13
- from lxml.etree import _Comment, _ElementTree, _Entity, _ProcessingInstruction
13
+ from lxml.etree import _Comment, _ElementTree, _Entity, _ProcessingInstruction, _Element
14
14
 
15
+ from arelle import XbrlConst
15
16
  from arelle.FunctionIxt import ixtNamespaces
16
- from arelle import ModelDocument as ModelDocumentFile
17
- from arelle.ModelDocument import ModelDocument
17
+ from arelle.ModelDocument import ModelDocument, Type as ModelDocumentType
18
+ from arelle.ModelDtsObject import ModelConcept
18
19
  from arelle.ModelInstanceObject import ModelContext, ModelFact, ModelInlineFootnote, ModelUnit, ModelInlineFact
19
20
  from arelle.ModelObject import ModelObject
20
21
  from arelle.ModelValue import QName
@@ -22,10 +23,10 @@ from arelle.ModelXbrl import ModelXbrl
22
23
  from arelle.typing import assert_type
23
24
  from arelle.utils.PluginData import PluginData
24
25
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
25
- from arelle.ValidateXbrl import ValidateXbrl
26
26
  from arelle.XbrlConst import ixbrl11, xhtmlBaseIdentifier, xmlBaseIdentifier
27
27
  from arelle.XmlValidate import lexicalPatterns
28
28
  from arelle.XmlValidateConst import VALID
29
+ from .LinkbaseType import LinkbaseType
29
30
 
30
31
  XBRLI_IDENTIFIER_PATTERN = re.compile(r"^(?!00)\d{8}$")
31
32
  XBRLI_IDENTIFIER_SCHEMA = 'http://www.kvk.nl/kvk-id'
@@ -57,11 +58,43 @@ ALLOWABLE_LANGUAGES = frozenset((
57
58
  'fr'
58
59
  ))
59
60
 
60
- EFFECTIVE_TAXONOMY_URLS = frozenset((
61
+ EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES = frozenset((
61
62
  'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-nlgaap-ext.xsd',
62
63
  'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-ifrs-ext.xsd',
63
64
  ))
64
65
 
66
+ TAXONOMY_URLS_BY_YEAR = {
67
+ '2024': {
68
+ 'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-nlgaap-ext.xsd',
69
+ 'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-ifrs-ext.xsd',
70
+ 'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-other-gaap.xsd',
71
+ }
72
+ }
73
+
74
+ STANDARD_TAXONOMY_URLS = frozenset((
75
+ 'http://www.nltaxonomie.nl/ifrs/20',
76
+ 'https://www.nltaxonomie.nl/ifrs/20',
77
+ 'http://www.nltaxonomie.nl/',
78
+ 'https://www.nltaxonomie.nl/',
79
+ 'http://www.xbrl.org/taxonomy/int/lei/',
80
+ 'https://www.xbrl.org/taxonomy/int/lei/',
81
+ 'http://www.xbrl.org/20',
82
+ 'https://www.xbrl.org/20',
83
+ 'http://www.xbrl.org/lrr/',
84
+ 'https://www.xbrl.org/lrr/',
85
+ 'http://xbrl.org/20',
86
+ 'https://xbrl.org/20',
87
+ 'http://xbrl.ifrs.org/',
88
+ 'https://xbrl.ifrs.org/',
89
+ 'http://www.xbrl.org/dtr/',
90
+ 'https://www.xbrl.org/dtr/',
91
+ 'http://xbrl.org/2020/extensible-enumerations-2.0',
92
+ 'https://xbrl.org/2020/extensible-enumerations-2.0',
93
+ 'http://www.w3.org/1999/xlink',
94
+ 'https://www.w3.org/1999/xlink'
95
+ ))
96
+
97
+
65
98
  @dataclass(frozen=True)
66
99
  class ContextData:
67
100
  contextsWithImproperContent: list[ModelContext | None]
@@ -69,6 +102,48 @@ class ContextData:
69
102
  contextsWithPeriodTimeZone: list[ModelContext | None]
70
103
  contextsWithSegments: list[ModelContext | None]
71
104
 
105
+
106
+ @dataclass(frozen=True)
107
+ class ExtensionData:
108
+ extensionConcepts: list[ModelConcept]
109
+ extensionDocuments: dict[ModelDocument, ExtensionDocumentData]
110
+ extensionImportedUrls: frozenset[str]
111
+
112
+
113
+ @dataclass(frozen=True)
114
+ class ExtensionDocumentData:
115
+ basename: str
116
+ hrefXlinkRole: str | None
117
+ linkbases: list[LinkbaseData]
118
+
119
+ def iterArcsByType(
120
+ self,
121
+ linkbaseType: LinkbaseType,
122
+ includeArcroles: set[str] | None = None,
123
+ excludeArcroles: set[str] | None = None,
124
+ ) -> Iterable[_Element]:
125
+ """
126
+ Returns a list of LinkbaseData objects for the specified LinkbaseType.
127
+ """
128
+ for linkbase in self.iterLinkbasesByType(linkbaseType):
129
+ for arc in linkbase.arcs:
130
+ if includeArcroles is not None:
131
+ if arc.get(XbrlConst.qnXlinkArcRole.clarkNotation) not in includeArcroles:
132
+ continue
133
+ if excludeArcroles is not None:
134
+ if arc.get(XbrlConst.qnXlinkArcRole.clarkNotation) in excludeArcroles:
135
+ continue
136
+ yield arc
137
+
138
+ def iterLinkbasesByType(self, linkbaseType: LinkbaseType) -> Iterable[LinkbaseData]:
139
+ """
140
+ Returns a list of LinkbaseData objects for the specified LinkbaseType.
141
+ """
142
+ for linkbase in self.linkbases:
143
+ if linkbase.linkbaseType == linkbaseType:
144
+ yield linkbase
145
+
146
+
72
147
  @dataclass(frozen=True)
73
148
  class HiddenElementsData:
74
149
  cssHiddenFacts: set[ModelInlineFact]
@@ -76,6 +151,7 @@ class HiddenElementsData:
76
151
  hiddenFactsOutsideHiddenSection: set[ModelInlineFact]
77
152
  requiredToDisplayFacts: set[ModelInlineFact]
78
153
 
154
+
79
155
  @dataclass(frozen=True)
80
156
  class InlineHTMLData:
81
157
  baseElements: set[Any]
@@ -85,6 +161,19 @@ class InlineHTMLData:
85
161
  factLangFootnotes: dict[ModelInlineFootnote, set[str]]
86
162
  fractionElements: set[Any]
87
163
 
164
+
165
+ @dataclass(frozen=True)
166
+ class LinkbaseData:
167
+ arcs: list[_Element]
168
+ basename: str
169
+ element: _Element
170
+ linkbaseType: LinkbaseType | None
171
+
172
+ @property
173
+ def hasArcs(self) -> bool:
174
+ return len(self.arcs) > 0
175
+
176
+
88
177
  @dataclass
89
178
  class PluginValidationDataExtension(PluginData):
90
179
  chamberOfCommerceRegistrationNumberQn: QName
@@ -93,6 +182,7 @@ class PluginValidationDataExtension(PluginData):
93
182
  documentResubmissionUnsurmountableInaccuraciesQn: QName
94
183
  entrypointRoot: str
95
184
  entrypoints: set[str]
185
+ financialReportingPeriodQn: QName
96
186
  financialReportingPeriodCurrentStartDateQn: QName
97
187
  financialReportingPeriodCurrentEndDateQn: QName
98
188
  financialReportingPeriodPreviousStartDateQn: QName
@@ -144,21 +234,6 @@ class PluginValidationDataExtension(PluginData):
144
234
  contextsWithSegments=contextsWithSegments,
145
235
  )
146
236
 
147
- def checkFilingDTS(
148
- self,
149
- val: ValidateXbrl,
150
- modelDocument: ModelDocument,
151
- visited: list[ModelDocument]
152
- ) -> None:
153
- visited.append(modelDocument)
154
- for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items():
155
- if referencedDocument not in visited and referencedDocument.inDTS:
156
- self.checkFilingDTS(val, referencedDocument, visited)
157
- if modelDocument.type == ModelDocumentFile.Type.SCHEMA:
158
- for doc, docRef in modelDocument.referencesDocument.items():
159
- if "import" in docRef.referenceTypes:
160
- val.extensionImportedUrls.add(doc.uri)
161
-
162
237
  @lru_cache(1)
163
238
  def checkHiddenElements(self, modelXbrl: ModelXbrl) -> HiddenElementsData:
164
239
  cssHiddenFacts = set()
@@ -169,7 +244,7 @@ class PluginValidationDataExtension(PluginData):
169
244
  presentedHiddenEltIds = defaultdict(list)
170
245
  requiredToDisplayFacts = set()
171
246
  for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
172
- ixNStag = getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11)
247
+ ixNStag = str(getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11))
173
248
  for ixHiddenElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag + "hidden"):
174
249
  for tag in (ixNStag + "nonNumeric", ixNStag+"nonFraction"):
175
250
  for ixElt in ixHiddenElt.iterdescendants(tag=tag):
@@ -218,7 +293,7 @@ class PluginValidationDataExtension(PluginData):
218
293
  tupleElements = set()
219
294
  orphanedFootnotes = set()
220
295
  for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
221
- ixNStag = getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11)
296
+ ixNStag = str(getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11))
222
297
  ixTupleTag = ixNStag + "tuple"
223
298
  ixFractionTag = ixNStag + "fraction"
224
299
  for elts in modelXbrl.ixdsEltById.values(): # type: ignore[attr-defined]
@@ -291,6 +366,24 @@ class PluginValidationDataExtension(PluginData):
291
366
  def getContextsWithSegments(self, modelXbrl: ModelXbrl) -> list[ModelContext | None]:
292
367
  return self.checkContexts(modelXbrl).contextsWithSegments
293
368
 
369
+ @lru_cache(1)
370
+ def getDocumentsInDts(self, modelXbrl: ModelXbrl) -> dict[ModelDocument, str | None]:
371
+ modelDocuments: dict[ModelDocument, str | None] = {}
372
+ if modelXbrl.modelDocument is None:
373
+ return modelDocuments
374
+
375
+ def _getDocumentsInDts(modelDocument: ModelDocument) -> None:
376
+ for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items():
377
+ if referencedDocument in modelDocuments:
378
+ continue
379
+ if referencedDocument.inDTS:
380
+ modelDocuments[referencedDocument] = modelDocumentReference.referringXlinkRole
381
+ _getDocumentsInDts(referencedDocument)
382
+
383
+ modelDocuments[modelXbrl.modelDocument] = None
384
+ _getDocumentsInDts(modelXbrl.modelDocument)
385
+ return modelDocuments
386
+
294
387
  def getEligibleForTransformHiddenFacts(self, modelXbrl: ModelXbrl) -> set[ModelInlineFact]:
295
388
  return self.checkHiddenElements(modelXbrl).eligibleForTransformHiddenFacts
296
389
 
@@ -321,8 +414,19 @@ class PluginValidationDataExtension(PluginData):
321
414
  )
322
415
 
323
416
  @lru_cache(1)
324
- def getFilenameParts(self, filename: str) -> dict[str, Any] | None:
325
- match = self.getFilenameFormatPattern().match(filename)
417
+ def getExtensionFilenameFormatPattern(self) -> re.Pattern[str]:
418
+ return re.compile(
419
+ r"^(?<base>[^-]*)"
420
+ r"-(?<year>\d{4})-(?<month>0[1-9]|1[012])-(?<day>0?[1-9]|[12][0-9]|3[01])"
421
+ r"(?<suffix>[_pre|_cal|_lab|_def]*)"
422
+ r"(?<lang>-*[^-]*)"
423
+ r"\.(?<extension>xsd|xml)$",
424
+ flags=re.ASCII
425
+ )
426
+
427
+ @lru_cache(1)
428
+ def getFilenameParts(self, filename: str, filenamePattern: re.Pattern[str]) -> dict[str, Any] | None:
429
+ match = filenamePattern.match(filename)
326
430
  if match:
327
431
  return match.groupdict()
328
432
  return None
@@ -331,6 +435,56 @@ class PluginValidationDataExtension(PluginData):
331
435
  def getIxdsDocBasenames(self, modelXbrl: ModelXbrl) -> set[str]:
332
436
  return set(Path(url).name for url in getattr(modelXbrl, "ixdsDocUrls", []))
333
437
 
438
+ def getExtensionConcepts(self, modelXbrl: ModelXbrl) -> list[ModelConcept]:
439
+ """
440
+ Returns a list of extension concepts in the DTS.
441
+ """
442
+ extensionConcepts = []
443
+ for concepts in modelXbrl.nameConcepts.values():
444
+ for concept in concepts:
445
+ if self.isExtensionUri(concept.qname.namespaceURI, modelXbrl):
446
+ extensionConcepts.append(concept)
447
+ return extensionConcepts
448
+
449
+ @lru_cache(1)
450
+ def getExtensionData(self, modelXbrl: ModelXbrl) -> ExtensionData:
451
+ extensionDocuments = {}
452
+ extensionImportedUrls = set()
453
+ documentsInDts = self.getDocumentsInDts(modelXbrl)
454
+ for modelDocument, hrefXlinkRole in documentsInDts.items():
455
+ if not self.isExtensionUri(modelDocument.uri, modelDocument.modelXbrl):
456
+ # Skip non-extension documents
457
+ continue
458
+ if modelDocument.type in (ModelDocumentType.LINKBASE, ModelDocumentType.SCHEMA):
459
+ extensionDocuments[modelDocument] = ExtensionDocumentData(
460
+ basename=modelDocument.basename,
461
+ hrefXlinkRole=hrefXlinkRole,
462
+ linkbases=self.getLinkbaseData(modelDocument),
463
+ )
464
+ if modelDocument.type == ModelDocumentType.SCHEMA:
465
+ for doc, docRef in modelDocument.referencesDocument.items():
466
+ if "import" in docRef.referenceTypes:
467
+ extensionImportedUrls.add(doc.uri)
468
+ return ExtensionData(
469
+ extensionConcepts=self.getExtensionConcepts(modelXbrl),
470
+ extensionDocuments=extensionDocuments,
471
+ extensionImportedUrls=frozenset(sorted(extensionImportedUrls)),
472
+ )
473
+
474
+ def getLinkbaseData(self, modelDocument: ModelDocument) -> list[LinkbaseData]:
475
+ linkbases = []
476
+ for linkbaseType in LinkbaseType:
477
+ for linkElt in modelDocument.xmlRootElement.iterdescendants(tag=linkbaseType.getLinkQn().clarkNotation):
478
+ arcQn = linkbaseType.getArcQn()
479
+ arcs = list(linkElt.iterdescendants(tag=arcQn.clarkNotation))
480
+ linkbases.append(LinkbaseData(
481
+ arcs=arcs,
482
+ basename=modelDocument.basename,
483
+ element=linkElt,
484
+ linkbaseType=linkbaseType,
485
+ ))
486
+ return linkbases
487
+
334
488
  def getNoMatchLangFootnotes(self, modelXbrl: ModelXbrl) -> set[ModelInlineFootnote]:
335
489
  return self.checkInlineHTMLElements(modelXbrl).noMatchLangFootnotes
336
490
 
@@ -346,6 +500,14 @@ class PluginValidationDataExtension(PluginData):
346
500
  def getTupleElements(self, modelXbrl: ModelXbrl) -> set[tuple[Any]]:
347
501
  return self.checkInlineHTMLElements(modelXbrl).tupleElements
348
502
 
503
+ @lru_cache(1)
504
+ def getReportingPeriod(self, modelXbrl: ModelXbrl) -> str | None:
505
+ reportingPeriodFacts = modelXbrl.factsByQname.get(self.financialReportingPeriodQn, set())
506
+ for fact in reportingPeriodFacts:
507
+ if fact.xValid >= VALID:
508
+ return cast(str, fact.xValue)
509
+ return None
510
+
349
511
  @lru_cache(1)
350
512
  def getReportXmlLang(self, modelXbrl: ModelXbrl) -> str | None:
351
513
  reportXmlLang = None
@@ -365,13 +527,20 @@ class PluginValidationDataExtension(PluginData):
365
527
  def getTargetElements(self, modelXbrl: ModelXbrl) -> list[Any]:
366
528
  targetElements = []
367
529
  for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
368
- ixNStag = getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11)
530
+ ixNStag = str(getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11))
369
531
  ixTags = set(ixNStag + ln for ln in ("nonNumeric", "nonFraction", "references", "relationship"))
370
532
  for elt, depth in etreeIterWithDepth(ixdsHtmlRootElt):
371
533
  if elt.tag in ixTags and elt.get("target"):
372
534
  targetElements.append(elt)
373
535
  return targetElements
374
536
 
537
+ def isExtensionUri(self, uri: str, modelXbrl: ModelXbrl) -> bool:
538
+ if uri.startswith(modelXbrl.uriDir):
539
+ return True
540
+ if not any(uri.startswith(taxonomyUri) for taxonomyUri in STANDARD_TAXONOMY_URLS):
541
+ return True
542
+ return False
543
+
375
544
  @lru_cache(1)
376
545
  def isFilenameValidCharacters(self, filename: str) -> bool:
377
546
  match = self.getFilenameAllowedCharactersPattern().match(filename)
@@ -186,6 +186,7 @@ class ValidationPluginExtension(ValidationPlugin):
186
186
  documentResubmissionUnsurmountableInaccuraciesQn=qname(f'{{{kvkINamespace}}}DocumentResubmissionDueToUnsurmountableInaccuracies'),
187
187
  entrypointRoot=entrypointRoot,
188
188
  entrypoints=entrypoints,
189
+ financialReportingPeriodQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriod'),
189
190
  financialReportingPeriodCurrentStartDateQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriodCurrentStartDate'),
190
191
  financialReportingPeriodCurrentEndDateQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriodCurrentEndDate'),
191
192
  financialReportingPeriodPreviousStartDateQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriodPreviousStartDate'),
@@ -46,9 +46,6 @@ def modelXbrlLoadComplete(*args: Any, **kwargs: Any) -> ModelDocument | LoadingE
46
46
  def validateFinally(*args: Any, **kwargs: Any) -> None:
47
47
  return validationPlugin.validateFinally(*args, **kwargs)
48
48
 
49
- def validateXbrlStart(val: ValidateXbrl, parameters: dict[Any, Any], *args: Any, **kwargs: Any) -> None:
50
- val.extensionImportedUrls = set()
51
-
52
49
  def validateXbrlFinally(*args: Any, **kwargs: Any) -> None:
53
50
  return validationPlugin.validateXbrlFinally(*args, **kwargs)
54
51
 
@@ -64,7 +61,6 @@ __pluginInfo__ = {
64
61
  "DisclosureSystem.Types": disclosureSystemTypes,
65
62
  "DisclosureSystem.ConfigURL": disclosureSystemConfigURL,
66
63
  "ModelXbrl.LoadComplete": modelXbrlLoadComplete,
67
- "Validate.XBRL.Start": validateXbrlStart,
68
64
  "Validate.XBRL.Finally": validateXbrlFinally,
69
65
  "ValidateFormula.Finished": validateFinally,
70
66
  }
@@ -12,7 +12,7 @@ from arelle.XmlValidateConst import VALID
12
12
  from collections.abc import Iterable
13
13
  from typing import Any, cast, TYPE_CHECKING
14
14
 
15
- from arelle import XmlUtil
15
+ from arelle import XmlUtil, XbrlConst
16
16
  from arelle.ValidateXbrl import ValidateXbrl
17
17
  from arelle.typing import TypeGetText
18
18
  from arelle.utils.PluginHooks import ValidationHook
@@ -21,10 +21,11 @@ from arelle.utils.validate.Validation import Validation
21
21
  from arelle.ValidateDuplicateFacts import getHashEquivalentFactGroups, getAspectEqualFacts
22
22
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
23
23
  from ..DisclosureSystems import DISCLOSURE_SYSTEM_NL_INLINE_2024
24
+ from ..LinkbaseType import LinkbaseType
24
25
  from ..PluginValidationDataExtension import (PluginValidationDataExtension, ALLOWABLE_LANGUAGES,
25
- DISALLOWED_IXT_NAMESPACES, EFFECTIVE_TAXONOMY_URLS,
26
- MAX_REPORT_PACKAGE_SIZE_MBS, XBRLI_IDENTIFIER_PATTERN,
27
- XBRLI_IDENTIFIER_SCHEMA)
26
+ DISALLOWED_IXT_NAMESPACES, EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES,
27
+ MAX_REPORT_PACKAGE_SIZE_MBS, TAXONOMY_URLS_BY_YEAR,
28
+ XBRLI_IDENTIFIER_PATTERN, XBRLI_IDENTIFIER_SCHEMA)
28
29
 
29
30
  if TYPE_CHECKING:
30
31
  from arelle.ModelXbrl import ModelXbrl
@@ -532,7 +533,7 @@ def rule_nl_kvk_3_4_1_3 (
532
533
  if len(facts) > 0:
533
534
  yield Validation.error(
534
535
  codes='NL.NL-KVK.3.4.1.3.transformableElementIncludedInHiddenSection',
535
- msg=_('The ix:hidden section should not include elements that are eligible for transformation'
536
+ msg=_('The ix:hidden section should not include elements that are eligible for transformation '
536
537
  'according to the latest recommended Transformation Rules Registry.'),
537
538
  modelObject=facts
538
539
  )
@@ -770,7 +771,7 @@ def rule_nl_kvk_3_6_3_1(
770
771
  """
771
772
  invalidBasenames = []
772
773
  for basename in pluginData.getIxdsDocBasenames(val.modelXbrl):
773
- filenameParts = pluginData.getFilenameParts(basename)
774
+ filenameParts = pluginData.getFilenameParts(basename, pluginData.getFilenameFormatPattern())
774
775
  if not filenameParts:
775
776
  continue # Filename is not formatted correctly enough to determine {base}
776
777
  if len(filenameParts.get('base', '')) > 20:
@@ -803,7 +804,7 @@ def rule_nl_kvk_3_6_3_2(
803
804
  """
804
805
  invalidBasenames = []
805
806
  for basename in pluginData.getIxdsDocBasenames(val.modelXbrl):
806
- filenameParts = pluginData.getFilenameParts(basename)
807
+ filenameParts = pluginData.getFilenameParts(basename, pluginData.getFilenameFormatPattern())
807
808
  if not filenameParts:
808
809
  invalidBasenames.append(basename)
809
810
  if len(invalidBasenames) > 0:
@@ -907,6 +908,96 @@ def rule_nl_kvk_3_7_1_2(
907
908
  )
908
909
 
909
910
 
911
+ @validation(
912
+ hook=ValidationHook.XBRL_FINALLY,
913
+ disclosureSystems=[
914
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
915
+ ],
916
+ )
917
+ def rule_nl_kvk_4_1_1_1(
918
+ pluginData: PluginValidationDataExtension,
919
+ val: ValidateXbrl,
920
+ *args: Any,
921
+ **kwargs: Any,
922
+ ) -> Iterable[Validation]:
923
+ """
924
+ NL-KVK.4.1.1.1: Extension taxonomies MUST consist of at least a schema file and presentation,
925
+ calculation and definition linkbases.
926
+ A label linkbase is also required if extension elements are present.
927
+ """
928
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
929
+ linkbaseIsMissing = {
930
+ LinkbaseType.CALCULATION: True,
931
+ LinkbaseType.DEFINITION: True,
932
+ LinkbaseType.LABEL: len(extensionData.extensionConcepts) > 0,
933
+ LinkbaseType.PRESENTATION: True,
934
+ }
935
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
936
+ hasArcs = False
937
+ linkbaseType = LinkbaseType.fromRefUri(extensionDocumentData.hrefXlinkRole)
938
+ for linkbaseData in extensionDocumentData.linkbases:
939
+ if linkbaseType is not None:
940
+ if linkbaseType == linkbaseData.linkbaseType:
941
+ if linkbaseData.hasArcs:
942
+ hasArcs = True
943
+ break
944
+ elif linkbaseData.hasArcs:
945
+ linkbaseType = linkbaseData.linkbaseType
946
+ hasArcs = True
947
+ break
948
+ if linkbaseType is None:
949
+ continue
950
+ if hasArcs and linkbaseIsMissing.get(linkbaseType, False):
951
+ linkbaseIsMissing[linkbaseType] = False
952
+ missingFiles = set(linkbaseType.getLowerName() for linkbaseType, isMissing in linkbaseIsMissing.items() if isMissing)
953
+ if len(missingFiles) > 0:
954
+ yield Validation.error(
955
+ codes='NL.NL-KVK.4.1.1.1.extensionTaxonomyWrongFilesStructure',
956
+ msg=_('The extension taxonomy is missing one or more required components: %(missingFiles)s '
957
+ 'Review to ensure that the schema file, presentation, calculation, '
958
+ 'and definition linkbases are included and not empty. '
959
+ 'A label linkbase is also required if extension elements are present.'),
960
+ modelObject=val.modelXbrl, missingFiles=", ".join(missingFiles)
961
+ )
962
+
963
+
964
+ @validation(
965
+ hook=ValidationHook.XBRL_FINALLY,
966
+ disclosureSystems=[
967
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
968
+ ],
969
+ )
970
+ def rule_nl_kvk_4_1_1_2(
971
+ pluginData: PluginValidationDataExtension,
972
+ val: ValidateXbrl,
973
+ *args: Any,
974
+ **kwargs: Any,
975
+ ) -> Iterable[Validation]:
976
+ """
977
+ NL-KVK.4.1.1.2: Each linkbase type MUST be provided in a separate linkbase file.
978
+ """
979
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
980
+ errors = []
981
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
982
+ linkbasesFound = set(
983
+ linkbase.linkbaseType.getLowerName()
984
+ for linkbase in extensionDocumentData.linkbases
985
+ if linkbase.linkbaseType is not None
986
+ )
987
+ if len(linkbasesFound) > 1:
988
+ errors.append((modelDocument, linkbasesFound))
989
+ for modelDocument, linkbasesFound in errors:
990
+ yield Validation.error(
991
+ codes='NL.NL-KVK.4.1.1.2.linkbasesNotSeparateFiles',
992
+ msg=_('Linkbase types are not stored in separate files. '
993
+ 'Review linkbase files and ensure they are provided as individual files. '
994
+ 'Found: %(linkbasesFound)s. in %(basename)s.'),
995
+ modelObject=modelDocument.xmlRootElement,
996
+ basename=modelDocument.basename,
997
+ linkbasesFound=", ".join(sorted(linkbasesFound))
998
+ )
999
+
1000
+
910
1001
  @validation(
911
1002
  hook=ValidationHook.XBRL_FINALLY,
912
1003
  disclosureSystems=[
@@ -921,17 +1012,314 @@ def rule_nl_kvk_4_1_2_1(
921
1012
  ) -> Iterable[Validation]:
922
1013
  """
923
1014
  NL-KVK.4.1.2.1: Validate that the imported taxonomy matches the KVK-specified entry point.
924
- - https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-nlgaap-ext.xsd,
925
- - https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-ifrs-ext.xsd.
1015
+ - https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-nlgaap-ext.xsd
1016
+ - https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-ifrs-ext.xsd
926
1017
  """
927
- if val.modelXbrl.modelDocument is not None:
928
- pluginData.checkFilingDTS(val, val.modelXbrl.modelDocument, [])
929
- if not any(e in val.extensionImportedUrls for e in EFFECTIVE_TAXONOMY_URLS):
930
- yield Validation.error(
931
- codes='NL.NL-KVK.4.1.2.1.requiredEntryPointNotImported',
932
- msg=_('The extension taxonomy must import the entry point of the taxonomy files prepared by KVK.'),
933
- modelObject=val.modelXbrl.modelDocument
934
- )
1018
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1019
+ matches = extensionData.extensionImportedUrls & EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES
1020
+ if not matches:
1021
+ yield Validation.error(
1022
+ codes='NL.NL-KVK.4.1.2.1.requiredEntryPointNotImported',
1023
+ msg=_('The extension taxonomy must import the entry point of the taxonomy files prepared by KVK.'),
1024
+ modelObject=val.modelXbrl.modelDocument
1025
+ )
1026
+
1027
+
1028
+ @validation(
1029
+ hook=ValidationHook.XBRL_FINALLY,
1030
+ disclosureSystems=[
1031
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1032
+ ],
1033
+ )
1034
+ def rule_nl_kvk_4_1_2_2(
1035
+ pluginData: PluginValidationDataExtension,
1036
+ val: ValidateXbrl,
1037
+ *args: Any,
1038
+ **kwargs: Any,
1039
+ ) -> Iterable[Validation]:
1040
+ """
1041
+ NL-KVK.4.1.2.2: The legal entity’s extension taxonomy MUST import the applicable version of
1042
+ the taxonomy files prepared by KVK.
1043
+ """
1044
+ reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
1045
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1046
+ matches = extensionData.extensionImportedUrls & TAXONOMY_URLS_BY_YEAR.get(reportingPeriod or '', set())
1047
+ if not reportingPeriod or not matches:
1048
+ yield Validation.error(
1049
+ codes='NL.NL-KVK.4.1.2.2.incorrectKvkTaxonomyVersionUsed',
1050
+ msg=_('The extension taxonomy MUST import the applicable version of the taxonomy files prepared by KVK '
1051
+ 'for the reported financial reporting period of %(reportingPeriod)s.'),
1052
+ modelObject=val.modelXbrl.modelDocument
1053
+ )
1054
+
1055
+
1056
+ @validation(
1057
+ hook=ValidationHook.XBRL_FINALLY,
1058
+ disclosureSystems=[
1059
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1060
+ ],
1061
+ )
1062
+ def rule_nl_kvk_4_1_5_1(
1063
+ pluginData: PluginValidationDataExtension,
1064
+ val: ValidateXbrl,
1065
+ *args: Any,
1066
+ **kwargs: Any,
1067
+ ) -> Iterable[Validation]:
1068
+ """
1069
+ NL-KVK.4.1.5.1: The `{base}` component of the extension document filename SHOULD not exceed twenty characters.
1070
+ """
1071
+ invalidBasenames = []
1072
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1073
+ for extensionDocument in extensionData.extensionDocuments.values():
1074
+ basename = extensionDocument.basename
1075
+ filenameParts = pluginData.getFilenameParts(basename, pluginData.getExtensionFilenameFormatPattern())
1076
+ if not filenameParts:
1077
+ continue # Filename is not formatted correctly enough to determine {base}
1078
+ if len(filenameParts.get('base', '')) > 20:
1079
+ invalidBasenames.append(basename)
1080
+ if len(invalidBasenames) > 0:
1081
+ yield Validation.warning(
1082
+ codes='NL.NL-KVK.4.1.5.1.baseComponentInNameOfTaxonomyFileExceedsTwentyCharacters',
1083
+ invalidBasenames=', '.join(invalidBasenames),
1084
+ msg=_('The {base} component of the extension document filename is greater than twenty characters. '
1085
+ 'The {base} component can either be the KVK number or the legal entity\'s name. '
1086
+ 'If the legal entity\'s name has been utilized, review to shorten the name to twenty characters or less. '
1087
+ 'Invalid filenames: %(invalidBasenames)s'))
1088
+
1089
+
1090
+ @validation(
1091
+ hook=ValidationHook.XBRL_FINALLY,
1092
+ disclosureSystems=[
1093
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1094
+ ],
1095
+ )
1096
+ def rule_nl_kvk_4_1_5_2(
1097
+ pluginData: PluginValidationDataExtension,
1098
+ val: ValidateXbrl,
1099
+ *args: Any,
1100
+ **kwargs: Any,
1101
+ ) -> Iterable[Validation]:
1102
+ """
1103
+ NL-KVK.4.1.5.2: Extension document filename SHOULD match the {base}-{date}_{suffix}-{lang}.{extension} pattern.
1104
+ """
1105
+ invalidBasenames = []
1106
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1107
+ for extensionDocument in extensionData.extensionDocuments.values():
1108
+ basename = extensionDocument.basename
1109
+ filenameParts = pluginData.getFilenameParts(basename, pluginData.getExtensionFilenameFormatPattern())
1110
+ if not filenameParts:
1111
+ invalidBasenames.append(basename)
1112
+ if len(invalidBasenames) > 0:
1113
+ yield Validation.warning(
1114
+ codes='NL.NL-KVK.4.1.5.2.extensionTaxonomyDocumentNameDoesNotFollowNamingConvention',
1115
+ invalidBasenames=', '.join(invalidBasenames),
1116
+ msg=_('The extension document filename does not match the naming convention outlined by the KVK. '
1117
+ 'It is recommended to be in the {base}-{date}_{suffix}-{lang}.{extension} format. '
1118
+ '{extension} must be one of the following: html, htm, xhtml. '
1119
+ 'Review formatting and update as appropriate. '
1120
+ 'Invalid filenames: %(invalidBasenames)s'))
1121
+
1122
+
1123
+ @validation(
1124
+ hook=ValidationHook.XBRL_FINALLY,
1125
+ disclosureSystems=[
1126
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1127
+ ],
1128
+ )
1129
+ def rule_nl_kvk_4_2_0_1(
1130
+ pluginData: PluginValidationDataExtension,
1131
+ val: ValidateXbrl,
1132
+ *args: Any,
1133
+ **kwargs: Any,
1134
+ ) -> Iterable[Validation]:
1135
+ """
1136
+ NL-KVK.4.2.0.1: Tuples MUST NOT be defined in extension taxonomy.
1137
+ """
1138
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1139
+ tupleConcepts = [
1140
+ concept for concept in extensionData.extensionConcepts if concept.isTuple
1141
+ ]
1142
+ if len(tupleConcepts) > 0:
1143
+ yield Validation.error(
1144
+ codes='NL.NL-KVK.4.2.0.1.tupleElementUsed',
1145
+ modelObject=tupleConcepts,
1146
+ msg=_('The extension taxonomy must not define tuple concepts.'))
1147
+
1148
+
1149
+ @validation(
1150
+ hook=ValidationHook.XBRL_FINALLY,
1151
+ disclosureSystems=[
1152
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1153
+ ],
1154
+ )
1155
+ def rule_nl_kvk_4_2_0_2(
1156
+ pluginData: PluginValidationDataExtension,
1157
+ val: ValidateXbrl,
1158
+ *args: Any,
1159
+ **kwargs: Any,
1160
+ ) -> Iterable[Validation]:
1161
+ """
1162
+ NL-KVK.4.2.0.2: Items with xbrli:fractionItemType data type MUST NOT be defined in extension taxonomy
1163
+ """
1164
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1165
+ fractionConcepts = [
1166
+ concept for concept in extensionData.extensionConcepts if concept.isFraction
1167
+ ]
1168
+ if len(fractionConcepts) > 0:
1169
+ yield Validation.error(
1170
+ codes='NL.NL-KVK.4.2.0.2.fractionElementUsed',
1171
+ modelObject=fractionConcepts,
1172
+ msg=_('The extension taxonomy must not define fraction concepts.'))
1173
+
1174
+
1175
+ @validation(
1176
+ hook=ValidationHook.XBRL_FINALLY,
1177
+ disclosureSystems=[
1178
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1179
+ ],
1180
+ )
1181
+ def rule_nl_kvk_4_2_1_1(
1182
+ pluginData: PluginValidationDataExtension,
1183
+ val: ValidateXbrl,
1184
+ *args: Any,
1185
+ **kwargs: Any,
1186
+ ) -> Iterable[Validation]:
1187
+ """
1188
+ NL-KVK.4.2.1.1: Extension taxonomy MUST set xbrli:scenario as context element on definition arcs with
1189
+ http://xbrl.org/int/dim/arcrole/all and http://xbrl.org/int/dim/arcrole/notAll arcroles.
1190
+ """
1191
+ errors = []
1192
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1193
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1194
+ for arc in extensionDocumentData.iterArcsByType(LinkbaseType.DEFINITION, includeArcroles={XbrlConst.all, XbrlConst.notAll}):
1195
+ if arc.get(XbrlConst.qnXbrldtContextElement.clarkNotation) != "scenario":
1196
+ errors.append(arc)
1197
+ if len(errors) > 0:
1198
+ yield Validation.error(
1199
+ codes='NL.NL-KVK.4.2.1.1.scenarioNotUsedInExtensionTaxonomy',
1200
+ modelObject=errors,
1201
+ msg=_('The definition linkbase is missing xbrli:scenario in extension taxonomy. '
1202
+ 'Review definition linkbase and update as appropriate.'),
1203
+ )
1204
+
1205
+
1206
+ @validation(
1207
+ hook=ValidationHook.XBRL_FINALLY,
1208
+ disclosureSystems=[
1209
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1210
+ ],
1211
+ )
1212
+ def rule_nl_kvk_4_4_1_1(
1213
+ pluginData: PluginValidationDataExtension,
1214
+ val: ValidateXbrl,
1215
+ *args: Any,
1216
+ **kwargs: Any,
1217
+ ) -> Iterable[Validation]:
1218
+ """
1219
+ NL-KVK.4.4.1.1: Arithmetical relationships defined in the calculation linkbase of an extension taxonomy
1220
+ MUST use the https://xbrl.org/2023/arcrole/summation-item arcrole as defined in Calculation 1.1 specification.
1221
+ """
1222
+ errors = []
1223
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1224
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1225
+ for arc in extensionDocumentData.iterArcsByType(LinkbaseType.CALCULATION, excludeArcroles={XbrlConst.summationItem11}):
1226
+ errors.append(arc)
1227
+ if len(errors) > 0:
1228
+ yield Validation.error(
1229
+ codes='NL.NL-KVK.4.4.1.1.incorrectSummationItemArcroleUsed',
1230
+ modelObject=errors,
1231
+ msg=_('Calculation relationships should follow the requirements of the Calculation 1.1 specification. '
1232
+ 'Update to ensure use of summation-item arcrole in the calculation linkbase.'),
1233
+ )
1234
+
1235
+
1236
+ @validation(
1237
+ hook=ValidationHook.XBRL_FINALLY,
1238
+ disclosureSystems=[
1239
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1240
+ ],
1241
+ )
1242
+ def rule_nl_kvk_4_4_2_1(
1243
+ pluginData: PluginValidationDataExtension,
1244
+ val: ValidateXbrl,
1245
+ *args: Any,
1246
+ **kwargs: Any,
1247
+ ) -> Iterable[Validation]:
1248
+ """
1249
+ NL-KVK.4.4.2.1: Extension taxonomies MUST NOT define definition arcs
1250
+ with http://xbrl.org/int/dim/arcrole/notAll arcrole.
1251
+ """
1252
+ errors = []
1253
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1254
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1255
+ for arc in extensionDocumentData.iterArcsByType(LinkbaseType.DEFINITION, includeArcroles={XbrlConst.notAll}):
1256
+ errors.append(arc)
1257
+ if len(errors) > 0:
1258
+ yield Validation.error(
1259
+ codes='NL.NL-KVK.4.4.2.1.notAllArcroleUsedInDefinitionLinkbase',
1260
+ modelObject=errors,
1261
+ msg=_('Incorrect hypercube settings are found. Ensure that positive hypercubes are in use.'),
1262
+ )
1263
+
1264
+
1265
+ @validation(
1266
+ hook=ValidationHook.XBRL_FINALLY,
1267
+ disclosureSystems=[
1268
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1269
+ ],
1270
+ )
1271
+ def rule_nl_kvk_4_4_2_2(
1272
+ pluginData: PluginValidationDataExtension,
1273
+ val: ValidateXbrl,
1274
+ *args: Any,
1275
+ **kwargs: Any,
1276
+ ) -> Iterable[Validation]:
1277
+ """
1278
+ NL-KVK.4.4.2.2: Hypercubes appearing as target of definition arc with
1279
+ http://xbrl.org/int/dim/arcrole/all arcrole MUST have xbrldt:closed attribute set to “true”.
1280
+ """
1281
+ errors = []
1282
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1283
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1284
+ for arc in extensionDocumentData.iterArcsByType(LinkbaseType.DEFINITION, includeArcroles={XbrlConst.all}):
1285
+ if arc.get(XbrlConst.qnXbrldtClosed.clarkNotation, "false") != "true":
1286
+ errors.append(arc)
1287
+ if len(errors) > 0:
1288
+ yield Validation.error(
1289
+ codes='NL.NL-KVK.4.4.2.2.openPositiveHypercubeInDefinitionLinkbase',
1290
+ modelObject=errors,
1291
+ msg=_('Incorrect hypercube settings are found. Ensure that positive hypercubes are closed.'),
1292
+ )
1293
+
1294
+
1295
+ @validation(
1296
+ hook=ValidationHook.XBRL_FINALLY,
1297
+ disclosureSystems=[
1298
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
1299
+ ],
1300
+ )
1301
+ def rule_nl_kvk_4_4_2_3(
1302
+ pluginData: PluginValidationDataExtension,
1303
+ val: ValidateXbrl,
1304
+ *args: Any,
1305
+ **kwargs: Any,
1306
+ ) -> Iterable[Validation]:
1307
+ """
1308
+ NL-KVK.4.4.2.3: Hypercubes appearing as target of definition arc with
1309
+ http://xbrl.org/int/dim/arcrole/notAll arcrole MUST have xbrldt:closed attribute set to “false”.
1310
+ """
1311
+ errors = []
1312
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1313
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1314
+ for arc in extensionDocumentData.iterArcsByType(LinkbaseType.DEFINITION, includeArcroles={XbrlConst.notAll}):
1315
+ if arc.get(XbrlConst.qnXbrldtClosed.clarkNotation, "true") != "false":
1316
+ errors.append(arc)
1317
+ if len(errors) > 0:
1318
+ yield Validation.error(
1319
+ codes='NL.NL-KVK.4.4.2.3.closedNegativeHypercubeInDefinitionLinkbase',
1320
+ modelObject=errors,
1321
+ msg=_('Incorrect hypercube settings are found. Ensure that negative hypercubes are not closed.'),
1322
+ )
935
1323
 
936
1324
 
937
1325
  @validation(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.20
3
+ Version: 2.37.21
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License: Apache-2.0
@@ -1,7 +1,7 @@
1
1
  arelle/Aspect.py,sha256=Pn9I91D1os1RTVj6htuxTfRzVMhmVDtrbKvV_zy9xMI,5470
2
2
  arelle/BetaFeatures.py,sha256=T_tPac-FiozHyYLCemt0RoHJ1JahUE71L-0tHmIRKpE,858
3
3
  arelle/Cntlr.py,sha256=sf5Xe19t5E0wKzhdlXl1p5r6gMtCmPhYAi8RVY0jz7Y,30449
4
- arelle/CntlrCmdLine.py,sha256=ZFfrfWirxSoGOZerRlSxFUhr9h4pnN7HGpVopm1T1U8,89949
4
+ arelle/CntlrCmdLine.py,sha256=uCpv_MaDGwMX1A3EZdN5h678aDaI0MTgRCQwOEKlimY,90250
5
5
  arelle/CntlrComServer.py,sha256=h1KPf31uMbErpxTZn_iklDqUMGFgQnjZkFkFjd8gtLQ,1888
6
6
  arelle/CntlrProfiler.py,sha256=2VQJudiUhxryVypxjODx2ccP1-n60icTiWs5lSEokhQ,972
7
7
  arelle/CntlrQuickBooks.py,sha256=BMqd5nkNQOZyNFPefkTeWUUDCYNS6BQavaG8k1Lepu4,31543
@@ -56,7 +56,7 @@ arelle/PluginUtils.py,sha256=0vFQ29wVVpU0cTY3YOBL6FhNQhhCTwShBH4qTJGLnvc,2426
56
56
  arelle/PrototypeDtsObject.py,sha256=0lf60VcXR_isx57hBPrc7vEMkFpYkVuK4JVBSmopzkQ,7989
57
57
  arelle/PrototypeInstanceObject.py,sha256=CXUoDllhDqpMvSQjqIYi1Ywp-J8fLhQRV9wVD2YXgVo,13204
58
58
  arelle/PythonUtil.py,sha256=pkh3-XixCOiVHhGlrt8l0va-iWCtkyO5ZMjEvNAadRA,12524
59
- arelle/RuntimeOptions.py,sha256=89pSw3zFhjHzn75RaHAl4-iPXL8awTZny-frozn9EHQ,8718
59
+ arelle/RuntimeOptions.py,sha256=XjTxA2tcdc_P2v7xZGTx_-imjOqtcl48ch3S-Q-q-7Y,8462
60
60
  arelle/SocketUtils.py,sha256=1wa2sA_QhM8F1klHFq90V1AgO1-hY9pJm0554FiF7Lc,677
61
61
  arelle/SystemInfo.py,sha256=6330pNedRkAPlEKl-ZdaZHiGuucEGZMI-Jrdy7B1rrU,2454
62
62
  arelle/TableStructure.py,sha256=PABOHJiTa56cHyF9qRLD7TohmCHyDTrGEltW8toP_rk,29409
@@ -114,7 +114,7 @@ arelle/ViewWinVersReport.py,sha256=aYfsOgynVZpMzl6f2EzQCBLzdihYGycwb5SiTghkgMQ,9
114
114
  arelle/ViewWinXml.py,sha256=4ZGKtjaoCwU9etKYm9ZAS7jSmUxba1rqNEdv0OIyjTY,1250
115
115
  arelle/WatchRss.py,sha256=5Ih4igH2MM4hpOuAXy9eO0QAyZ7jZR3S5bPzo2sdFpw,14097
116
116
  arelle/WebCache.py,sha256=B62IxIHLX4hcDr_0MJGfmzUXau2ONqiMk6vLVLxAIhA,45057
117
- arelle/XbrlConst.py,sha256=njOrR7sJaLO4V2EXrlCp8Usk-rU-r4j_8B00FicwFU4,56819
117
+ arelle/XbrlConst.py,sha256=7Pk386ZP5-zeQspyajgl-y34hq9LxpaYHTKrHq5Km-8,56958
118
118
  arelle/XbrlUtil.py,sha256=s2Vmrh-sZI5TeuqsziKignOc3ao-uUgnCNoelP4dDj0,9212
119
119
  arelle/XhtmlValidate.py,sha256=0gtm7N-kXK0RB5o3c1AQXjfFuRp1w2fKZZAeyruNANw,5727
120
120
  arelle/XmlUtil.py,sha256=1VToOOylF8kbEorEdZLThmq35j9bmuF_DS2q9NthnHU,58774
@@ -123,9 +123,9 @@ 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=6XCSb3wXvbuwzUZ4u7I9uuIYrvk_rC4_utLxrLm9hX8,515
126
+ arelle/_version.py,sha256=7J7GR3gzAoXDRoz2kLGCfdxRcD180tJLB4oVmPwBN4k,515
127
127
  arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
128
- arelle/api/Session.py,sha256=O8zpg7MJys9uxwwHf8OsSlZxpPdq7A3ONyY39Q4A3Kc,6218
128
+ arelle/api/Session.py,sha256=DE45eTuhPkIPsLj0IFqDc9gMq2XoUf-aMBgW19MeGbE,6523
129
129
  arelle/archive/CustomLogger.py,sha256=v_JXOCQLDZcfaFWzxC9FRcEf9tQi4rCI4Sx7jCuAVQI,1231
130
130
  arelle/archive/LoadEFMvalidate.py,sha256=HR1ZJmOvWGUlWEsWd0tGCa2TTtZSNzeL6tgN1TFfrl0,986
131
131
  arelle/archive/LoadSavePreLbCsv.py,sha256=mekr1R6OE5d3xdUCZIVfSeolyet0HO8R6wsHnW4eyaA,767
@@ -410,16 +410,17 @@ arelle/plugin/validate/FERC/__init__.py,sha256=V4fXcFKBsjFFPs9_1NhvDjWpEQCoQM0tR
410
410
  arelle/plugin/validate/FERC/config.xml,sha256=bn9b8eCqJA1J62rYq1Nz85wJrMGAahVmmnIUQZyerjo,1919
411
411
  arelle/plugin/validate/FERC/resources/ferc-utr.xml,sha256=OCRj9IUpdXATCBXKbB71apYx9kxcNtZW-Hq4s-avsRY,2663
412
412
  arelle/plugin/validate/NL/DisclosureSystems.py,sha256=kTjpxkgwn58wHCbaLRBInirOy-2cpK9MLWEFJ_193y4,180
413
- arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=3fAYo-rilJHN8I0XVdM3AxGIMF0j8StZ52DNCK_fQdk,17620
414
- arelle/plugin/validate/NL/ValidationPluginExtension.py,sha256=2qvvOqBkgk2LwERTHDuxtrRupYz3yRyhH71XQLbl9F4,15507
415
- arelle/plugin/validate/NL/__init__.py,sha256=KPqcRHHYjaTQOLzN7vh3GtUhsVAB-aQGtrp_TUsV9LE,3064
413
+ arelle/plugin/validate/NL/LinkbaseType.py,sha256=csXEqLaU43tN58RUG3oeD3nUYcdHl1OWSKaxpOhbTXk,2515
414
+ arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=bvyz-GKCaHs-Y0ifIxIh5FqN73Eqd3OPW4O8HWtup5E,24065
415
+ arelle/plugin/validate/NL/ValidationPluginExtension.py,sha256=0Ze1RFTlnHeAeDnMG-dAVT7WKrgNQ2iflMm87ZnVwLQ,15601
416
+ arelle/plugin/validate/NL/__init__.py,sha256=23cF5ih2wu0RO_S0B52nVB7LrdlmnYcctOUezF0kKQ8,2874
416
417
  arelle/plugin/validate/NL/resources/config.xml,sha256=i_ns2wHmQYjhkRItevRR8tzfkl31ASfbWlc5t6pDB-w,1117
417
418
  arelle/plugin/validate/NL/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
418
419
  arelle/plugin/validate/NL/rules/br_kvk.py,sha256=0SwKieWzTDm3YMsXPS6zTdgbk7_Z9CzqRkRmCRz1OiQ,15789
419
420
  arelle/plugin/validate/NL/rules/fg_nl.py,sha256=4Puq5wAjtK_iNd4wisH_R0Z_EKJ7MT2OCai5g4t1MPE,10714
420
421
  arelle/plugin/validate/NL/rules/fr_kvk.py,sha256=-_BLeWGoZ_f56p5VO4X40S45Ny3Ej-WK6Srei1KVSxU,8170
421
422
  arelle/plugin/validate/NL/rules/fr_nl.py,sha256=-M1WtXp06khhtkfOVPCa-b8UbC281gk4YfDhvtAVlnI,31424
422
- arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=PZoVkuf0aCHtCL30Ye4pavgyyX2GF3ZRpkaezcHwBfw,34690
423
+ arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=0jiagDyV1x7Qh6evVLz9eeJLbyAD9ZUI9Wk_yX0mVNU,50380
423
424
  arelle/plugin/validate/ROS/DisclosureSystems.py,sha256=rJ81mwQDYTi6JecFZ_zhqjjz3VNQRgjHNSh0wcQWAQE,18
424
425
  arelle/plugin/validate/ROS/PluginValidationDataExtension.py,sha256=IV7ILhNvgKwQXqbpSA6HRNt9kEnejCyMADI3wyyIgk0,4036
425
426
  arelle/plugin/validate/ROS/ValidationPluginExtension.py,sha256=FBhEp8t396vGdvCbMEimfcxmGiGnhXMen-yVLWnkFaI,758
@@ -741,7 +742,7 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
741
742
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
742
743
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
743
744
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
744
- arelle_release-2.37.20.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
745
+ arelle_release-2.37.21.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
745
746
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
746
747
  tests/integration_tests/download_cache.py,sha256=jVMIVICsZjcVc9DCPPu3fCjF9_cWSS3tqSynhFs3oAM,4097
747
748
  tests/integration_tests/integration_test_util.py,sha256=H7mncbv0T9ZeVyrtk9Hohe3k6jgcYykHkt-LGE-Q9aQ,10270
@@ -796,7 +797,7 @@ tests/integration_tests/validation/conformance_suite_configurations/kvk_nt16.py,
796
797
  tests/integration_tests/validation/conformance_suite_configurations/kvk_nt17.py,sha256=lmEZonthFm0YKFmp1dwXtdJ2T7txUeSpL4mbAo8fl4Y,1292
797
798
  tests/integration_tests/validation/conformance_suite_configurations/kvk_nt18.py,sha256=EG2RQVkvFENhzUF3fl3QvDnH7ZPYS1n1Fo8bhfmSczM,1205
798
799
  tests/integration_tests/validation/conformance_suite_configurations/kvk_nt19.py,sha256=FAzf9RhRmn_8yowpplJho2zEspX9FxJiVq8SjZT3Dsc,1199
799
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py,sha256=9cUaoPCo68OshIH8lK4TPdR9dg4Gl0swTHhAkML_Bks,10918
800
+ tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py,sha256=K24GT014o_d3-cvuAkkkzGeo_JDxBh_pWuyYWXo9xLI,10470
800
801
  tests/integration_tests/validation/conformance_suite_configurations/nl_nt16.py,sha256=O_LFVBZPkjxmbrU7_C7VTLtrdoCUx2bYXOXw6_MlRtQ,846
801
802
  tests/integration_tests/validation/conformance_suite_configurations/nl_nt17.py,sha256=aTN3Ez6lPsZsuypHZP84DneOtYxUZSjUiGypHy6ofHQ,846
802
803
  tests/integration_tests/validation/conformance_suite_configurations/nl_nt18.py,sha256=sqHLjrHc95dTu0guTgKkphaKM1zNfKGnN4GKkZDLzeU,845
@@ -1579,7 +1580,7 @@ tests/unit_tests/arelle/test_ordered_set.py,sha256=GkJgvdWIME5g5f3MD0tWJmAI6-aej
1579
1580
  tests/unit_tests/arelle/test_packagemanager.py,sha256=Gs4J0cmB35czCIXj7xXhDlPLG9r68D5GGfZH8PKjMfw,2566
1580
1581
  tests/unit_tests/arelle/test_pluginmanager.py,sha256=_Gi03PP-6FZ7mWqe2ysS_N_suOQamRHYhAuWolfKGug,5920
1581
1582
  tests/unit_tests/arelle/test_qname.py,sha256=0aKh6jYWmY4Xg3wOS839Tdqa1SHwHuha6akv5T6qddY,4892
1582
- tests/unit_tests/arelle/test_runtimeoptions.py,sha256=Udv6Rk-VIdEMr8X4kvi-hraTvNMKm05LcgzP9ugX8HM,1705
1583
+ tests/unit_tests/arelle/test_runtimeoptions.py,sha256=OB4ds28ODYjzgm9wlojd_fIRf7iRGOmDaPgxm4kiCwM,1315
1583
1584
  tests/unit_tests/arelle/test_system_info.py,sha256=G9VtKX9WCaas2D2s-Yw-4kcq6_zcY-LkjOveGvQNvZI,655
1584
1585
  tests/unit_tests/arelle/test_updater.py,sha256=8DDHTpog8l9T8fHhHtrE9coPWTyFCgf-BCDm9nTtr6E,16163
1585
1586
  tests/unit_tests/arelle/test_urlutil.py,sha256=3WTHxic3XiiOGZQxkHm9m97kFbLHOc27oXypU8fFt1w,914
@@ -1594,8 +1595,8 @@ tests/unit_tests/arelle/oim/test_load.py,sha256=NxiUauQwJVfWAHbbpsMHGSU2d3Br8Pki
1594
1595
  tests/unit_tests/arelle/plugin/test_plugin_imports.py,sha256=bdhIs9frAnFsdGU113yBk09_jis-z43dwUItMFYuSYM,1064
1595
1596
  tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py,sha256=XHABmejQt7RlZ0udh7v42f2Xb2STGk_fSaIaJ9i2xo0,878
1596
1597
  tests/unit_tests/arelle/utils/validate/test_decorator.py,sha256=ZS8FqIY1g-2FCbjF4UYm609dwViax6qBMRJSi0vfuhY,2482
1597
- arelle_release-2.37.20.dist-info/METADATA,sha256=K7acQC5mcoN1XxT008aOzdJB1bDThOfqEnCVSQ4fAx0,9134
1598
- arelle_release-2.37.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1599
- arelle_release-2.37.20.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
1600
- arelle_release-2.37.20.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
1601
- arelle_release-2.37.20.dist-info/RECORD,,
1598
+ arelle_release-2.37.21.dist-info/METADATA,sha256=XKmpRZx95BpPCfr3HGOZd2y9DH8Rypx2up7QJSp_bgY,9134
1599
+ arelle_release-2.37.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1600
+ arelle_release-2.37.21.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
1601
+ arelle_release-2.37.21.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
1602
+ arelle_release-2.37.21.dist-info/RECORD,,
@@ -19,10 +19,15 @@ config = ConformanceSuiteConfig(
19
19
  *NL_PACKAGES['NL-INLINE-2024'],
20
20
  ],
21
21
  expected_additional_testcase_errors={f"conformance-suite-2024-sbr-domein-handelsregister/tests/{s}": val for s, val in {
22
+ 'G3-1-3_1/index.xml:TC2_invalid': {
23
+ 'scenarioNotUsedInExtensionTaxonomy': 1, # Also fails 4.2.1.1
24
+ },
22
25
  'G3-5-3_1/index.xml:TC2_invalid': {
23
26
  'arelle:ixdsTargetNotDefined': 1,
27
+ 'extensionTaxonomyWrongFilesStructure': 1,
24
28
  # This test is looking at the usage of the target attribute and does not import the correct taxonomy urls
25
29
  'requiredEntryPointNotImported': 1,
30
+ 'incorrectKvkTaxonomyVersionUsed': 1,
26
31
  },
27
32
  'G3-6-3_3/index.xml:TC2_invalid': {
28
33
  # Testcase expects only 3.6.3.3, but has a filename that has invalid characters (3.6.3.3)
@@ -37,15 +42,27 @@ config = ConformanceSuiteConfig(
37
42
  'undefinedLanguageForTextFact': 1,
38
43
  'taggedTextFactOnlyInLanguagesOtherThanLanguageOfAReport': 5,
39
44
  },
45
+ 'G4-1-2_2/index.xml:TC2_invalid': {
46
+ 'incorrectSummationItemArcroleUsed': 1, # Also fails 4.4.1.1
47
+ # Test imports https://www.nltaxonomie.nl/kvk/2024-03-31/kvk-annual-report-nlgaap-ext.xsd which is the draft taxonomy and not the final
48
+ 'requiredEntryPointNotImported': 1,
49
+ },
50
+ 'G4-4-2_1/index.xml:TC2_invalid': {
51
+ 'closedNegativeHypercubeInDefinitionLinkbase': 1, # Also fails 4.4.2.3
52
+ },
40
53
  'G5-1-3_1/index.xml:TC1_valid': {
54
+ 'extensionTaxonomyWrongFilesStructure': 1,
41
55
  # This test is looking at the import of the Other GAAP entry point and thus does not import
42
56
  # the standard GAAP or IFRS
43
57
  'requiredEntryPointNotImported': 1,
58
+ 'incorrectKvkTaxonomyVersionUsed': 1,
44
59
  },
45
60
  'G5-1-3_2/index.xml:TC1_valid': {
61
+ 'extensionTaxonomyWrongFilesStructure': 1,
46
62
  # This test is looking at the import of the Other GAAP entry point and thus does not import
47
63
  # the standard GAAP or IFRS
48
64
  'requiredEntryPointNotImported': 1,
65
+ 'incorrectKvkTaxonomyVersionUsed': 1,
49
66
  },
50
67
  'RTS_Annex_II_Par_1_RTS_Annex_IV_par_7/index.xml:TC2_valid': {
51
68
  'undefinedLanguageForTextFact': 1,
@@ -73,6 +90,8 @@ config = ConformanceSuiteConfig(
73
90
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-4-1_1/index.xml:TC2_invalid', # Produces: [err:XPTY0004] Variable set Het entity identifier scheme dat bij dit feit hoort MOET het standaard KVK identifier scheme zijn
74
91
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-4-1_2/index.xml:TC2_invalid', # Expects fractionElementUsed”. Note the double quote at the end.
75
92
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-4-2_1/index.xml:TC2_invalid', # Produces 'EFM.6.03.11' and 'NL.NL-KVK.3.4.2.1.htmlOrXmlBaseUsed'
93
+ 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-0_2/index.xml:TC2_invalid', # Expects fractionElementUsed”. Note the double quote at the end.
94
+ 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-1_1/index.xml:TC2_invalid', # Expects IncorrectSummationItemArcroleUsed. Note the capital first character.
76
95
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_2_G3-1-1_1/index.xml:TC2_invalid', # Expects NonIdenticalIdentifier instead of nonIdenticalIdentifier (note the cap N)
77
96
 
78
97
 
@@ -85,27 +104,12 @@ config = ConformanceSuiteConfig(
85
104
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-5-1_5/index.xml:TC2_invalid',
86
105
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-5-1_5/index.xml:TC3_invalid',
87
106
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-6-2_1/index.xml:TC2_invalid',
88
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_1/index.xml:TC3_invalid',
89
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_1/index.xml:TC4_invalid',
90
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_1/index.xml:TC5_invalid',
91
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_1/index.xml:TC6_invalid',
92
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_1/index.xml:TC7_invalid',
93
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_2/index.xml:TC2_invalid',
94
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-1_2/index.xml:TC3_invalid',
95
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-2_2/index.xml:TC2_invalid',
96
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-0_1/index.xml:TC2_invalid',
97
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-0_2/index.xml:TC2_invalid',
98
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-1_1/index.xml:TC2_invalid', # Expects scenarioNotUsedInExtensionTaxonomy and segmentUsed errors. scenarioNotUsedInExtensionTaxonomy not yet implemented.
99
107
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-2_2/index.xml:TC2_invalid',
100
108
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-3_1/index.xml:TC2_invalid',
101
109
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-3-1_1/index.xml:TC2_invalid',
102
110
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-3-1_1/index.xml:TC3_invalid',
103
111
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-3-1_1/index.xml:TC4_invalid',
104
112
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-3-2_1/index.xml:TC2_invalid',
105
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-1_1/index.xml:TC2_invalid',
106
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-2_1/index.xml:TC2_invalid',
107
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-2_2/index.xml:TC2_invalid',
108
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-2_3/index.xml:TC2_invalid',
109
113
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-2_4/index.xml:TC2_invalid',
110
114
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-3_1/index.xml:TC2_invalid',
111
115
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-4-3_1/index.xml:TC3_invalid',
@@ -130,7 +134,6 @@ config = ConformanceSuiteConfig(
130
134
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_4_3/index.xml:TC5_invalid',
131
135
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_5/index.xml:TC2_invalid',
132
136
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_5/index.xml:TC3_invalid',
133
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_6/index.xml:TC3_invalid',
134
137
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_6/index.xml:TC4_invalid',
135
138
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_8_G4-4-5/index.xml:TC2_invalid',
136
139
  'conformance-suite-2024-sbr-domein-handelsregister/tests/RTS_Annex_IV_Par_8_G4-4-5/index.xml:TC3_invalid',
@@ -32,19 +32,6 @@ def test_incorrect_arguments_with_webserver():
32
32
  )
33
33
 
34
34
 
35
- @patch('arelle.RuntimeOptions.hasWebServer')
36
- def test_incorrect_arguments(mockwebserver):
37
- with pytest.raises(RuntimeOptionsException, match="Incorrect arguments"):
38
- mockwebserver.return_value = False
39
- RuntimeOptions(
40
- entrypointFile=None,
41
- proxy=None,
42
- plugins=None,
43
- pluginOptions=None,
44
- webserver=None,
45
- )
46
-
47
-
48
35
  def test_set_runtime_options():
49
36
  runtimeOptions = RuntimeOptions(
50
37
  abortOnMajorError=True,