arelle-release 2.37.20__py3-none-any.whl → 2.37.22__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 +8 -0
- arelle/RuntimeOptions.py +0 -6
- arelle/XbrlConst.py +4 -0
- arelle/_version.py +2 -2
- arelle/api/Session.py +92 -60
- arelle/plugin/validate/NL/DisclosureSystems.py +14 -0
- arelle/plugin/validate/NL/LinkbaseType.py +77 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +236 -27
- arelle/plugin/validate/NL/ValidationPluginExtension.py +11 -1
- arelle/plugin/validate/NL/__init__.py +0 -4
- arelle/plugin/validate/NL/resources/config.xml +6 -1
- arelle/plugin/validate/NL/rules/nl_kvk.py +596 -118
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/METADATA +1 -1
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/RECORD +23 -21
- tests/integration_tests/validation/conformance_suite_configs.py +2 -0
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +16 -31
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024_gaap_other.py +244 -0
- tests/integration_tests/validation/discover_tests.py +2 -2
- tests/unit_tests/arelle/test_runtimeoptions.py +0 -13
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.20.dist-info → arelle_release-2.37.22.dist-info}/top_level.txt +0 -0
|
@@ -7,26 +7,28 @@ 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
|
|
17
|
-
from arelle.
|
|
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
|
-
from arelle.ModelValue import QName
|
|
21
|
+
from arelle.ModelValue import QName, qname
|
|
21
22
|
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
|
|
|
31
|
+
DEFAULT_MEMBER_ROLE_URI = 'https://www.nltaxonomie.nl/kvk/role/axis-defaults'
|
|
30
32
|
XBRLI_IDENTIFIER_PATTERN = re.compile(r"^(?!00)\d{8}$")
|
|
31
33
|
XBRLI_IDENTIFIER_SCHEMA = 'http://www.kvk.nl/kvk-id'
|
|
32
34
|
MAX_REPORT_PACKAGE_SIZE_MBS = 100
|
|
@@ -57,11 +59,51 @@ ALLOWABLE_LANGUAGES = frozenset((
|
|
|
57
59
|
'fr'
|
|
58
60
|
))
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES = frozenset((
|
|
61
63
|
'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-nlgaap-ext.xsd',
|
|
62
64
|
'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-ifrs-ext.xsd',
|
|
63
65
|
))
|
|
64
66
|
|
|
67
|
+
EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES = frozenset((
|
|
68
|
+
'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-other-gaap.xsd',
|
|
69
|
+
))
|
|
70
|
+
|
|
71
|
+
TAXONOMY_URLS_BY_YEAR = {
|
|
72
|
+
'2024': {
|
|
73
|
+
'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-nlgaap-ext.xsd',
|
|
74
|
+
'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-ifrs-ext.xsd',
|
|
75
|
+
'https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-other-gaap.xsd',
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
STANDARD_TAXONOMY_URLS = frozenset((
|
|
80
|
+
'http://www.nltaxonomie.nl/ifrs/20',
|
|
81
|
+
'https://www.nltaxonomie.nl/ifrs/20',
|
|
82
|
+
'http://www.nltaxonomie.nl/',
|
|
83
|
+
'https://www.nltaxonomie.nl/',
|
|
84
|
+
'http://www.xbrl.org/taxonomy/int/lei/',
|
|
85
|
+
'https://www.xbrl.org/taxonomy/int/lei/',
|
|
86
|
+
'http://www.xbrl.org/20',
|
|
87
|
+
'https://www.xbrl.org/20',
|
|
88
|
+
'http://www.xbrl.org/lrr/',
|
|
89
|
+
'https://www.xbrl.org/lrr/',
|
|
90
|
+
'http://xbrl.org/20',
|
|
91
|
+
'https://xbrl.org/20',
|
|
92
|
+
'http://xbrl.ifrs.org/',
|
|
93
|
+
'https://xbrl.ifrs.org/',
|
|
94
|
+
'http://www.xbrl.org/dtr/',
|
|
95
|
+
'https://www.xbrl.org/dtr/',
|
|
96
|
+
'http://xbrl.org/2020/extensible-enumerations-2.0',
|
|
97
|
+
'https://xbrl.org/2020/extensible-enumerations-2.0',
|
|
98
|
+
'http://www.w3.org/1999/xlink',
|
|
99
|
+
'https://www.w3.org/1999/xlink'
|
|
100
|
+
))
|
|
101
|
+
|
|
102
|
+
QN_DOMAIN_ITEM_TYPES = frozenset((
|
|
103
|
+
qname("{http://www.xbrl.org/dtr/type/2022-03-31}nonnum:domainItemType"),
|
|
104
|
+
))
|
|
105
|
+
|
|
106
|
+
|
|
65
107
|
@dataclass(frozen=True)
|
|
66
108
|
class ContextData:
|
|
67
109
|
contextsWithImproperContent: list[ModelContext | None]
|
|
@@ -69,6 +111,48 @@ class ContextData:
|
|
|
69
111
|
contextsWithPeriodTimeZone: list[ModelContext | None]
|
|
70
112
|
contextsWithSegments: list[ModelContext | None]
|
|
71
113
|
|
|
114
|
+
|
|
115
|
+
@dataclass(frozen=True)
|
|
116
|
+
class ExtensionData:
|
|
117
|
+
extensionConcepts: list[ModelConcept]
|
|
118
|
+
extensionDocuments: dict[ModelDocument, ExtensionDocumentData]
|
|
119
|
+
extensionImportedUrls: frozenset[str]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass(frozen=True)
|
|
123
|
+
class ExtensionDocumentData:
|
|
124
|
+
basename: str
|
|
125
|
+
hrefXlinkRole: str | None
|
|
126
|
+
linkbases: list[LinkbaseData]
|
|
127
|
+
|
|
128
|
+
def iterArcsByType(
|
|
129
|
+
self,
|
|
130
|
+
linkbaseType: LinkbaseType,
|
|
131
|
+
includeArcroles: set[str] | None = None,
|
|
132
|
+
excludeArcroles: set[str] | None = None,
|
|
133
|
+
) -> Iterable[_Element]:
|
|
134
|
+
"""
|
|
135
|
+
Returns a list of LinkbaseData objects for the specified LinkbaseType.
|
|
136
|
+
"""
|
|
137
|
+
for linkbase in self.iterLinkbasesByType(linkbaseType):
|
|
138
|
+
for arc in linkbase.arcs:
|
|
139
|
+
if includeArcroles is not None:
|
|
140
|
+
if arc.get(XbrlConst.qnXlinkArcRole.clarkNotation) not in includeArcroles:
|
|
141
|
+
continue
|
|
142
|
+
if excludeArcroles is not None:
|
|
143
|
+
if arc.get(XbrlConst.qnXlinkArcRole.clarkNotation) in excludeArcroles:
|
|
144
|
+
continue
|
|
145
|
+
yield arc
|
|
146
|
+
|
|
147
|
+
def iterLinkbasesByType(self, linkbaseType: LinkbaseType) -> Iterable[LinkbaseData]:
|
|
148
|
+
"""
|
|
149
|
+
Returns a list of LinkbaseData objects for the specified LinkbaseType.
|
|
150
|
+
"""
|
|
151
|
+
for linkbase in self.linkbases:
|
|
152
|
+
if linkbase.linkbaseType == linkbaseType:
|
|
153
|
+
yield linkbase
|
|
154
|
+
|
|
155
|
+
|
|
72
156
|
@dataclass(frozen=True)
|
|
73
157
|
class HiddenElementsData:
|
|
74
158
|
cssHiddenFacts: set[ModelInlineFact]
|
|
@@ -76,6 +160,7 @@ class HiddenElementsData:
|
|
|
76
160
|
hiddenFactsOutsideHiddenSection: set[ModelInlineFact]
|
|
77
161
|
requiredToDisplayFacts: set[ModelInlineFact]
|
|
78
162
|
|
|
163
|
+
|
|
79
164
|
@dataclass(frozen=True)
|
|
80
165
|
class InlineHTMLData:
|
|
81
166
|
baseElements: set[Any]
|
|
@@ -85,6 +170,19 @@ class InlineHTMLData:
|
|
|
85
170
|
factLangFootnotes: dict[ModelInlineFootnote, set[str]]
|
|
86
171
|
fractionElements: set[Any]
|
|
87
172
|
|
|
173
|
+
|
|
174
|
+
@dataclass(frozen=True)
|
|
175
|
+
class LinkbaseData:
|
|
176
|
+
arcs: list[_Element]
|
|
177
|
+
basename: str
|
|
178
|
+
element: _Element
|
|
179
|
+
linkbaseType: LinkbaseType | None
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def hasArcs(self) -> bool:
|
|
183
|
+
return len(self.arcs) > 0
|
|
184
|
+
|
|
185
|
+
|
|
88
186
|
@dataclass
|
|
89
187
|
class PluginValidationDataExtension(PluginData):
|
|
90
188
|
chamberOfCommerceRegistrationNumberQn: QName
|
|
@@ -93,6 +191,7 @@ class PluginValidationDataExtension(PluginData):
|
|
|
93
191
|
documentResubmissionUnsurmountableInaccuraciesQn: QName
|
|
94
192
|
entrypointRoot: str
|
|
95
193
|
entrypoints: set[str]
|
|
194
|
+
financialReportingPeriodQn: QName
|
|
96
195
|
financialReportingPeriodCurrentStartDateQn: QName
|
|
97
196
|
financialReportingPeriodCurrentEndDateQn: QName
|
|
98
197
|
financialReportingPeriodPreviousStartDateQn: QName
|
|
@@ -105,6 +204,12 @@ class PluginValidationDataExtension(PluginData):
|
|
|
105
204
|
def __hash__(self) -> int:
|
|
106
205
|
return id(self)
|
|
107
206
|
|
|
207
|
+
def addDomMbrs(self, modelXbrl: ModelXbrl, sourceDomMbr: ModelConcept, ELR: str, membersSet: set[ModelConcept]) -> None:
|
|
208
|
+
if isinstance(sourceDomMbr, ModelConcept) and sourceDomMbr not in membersSet:
|
|
209
|
+
membersSet.add(sourceDomMbr)
|
|
210
|
+
for domMbrRel in modelXbrl.relationshipSet(XbrlConst.domainMember, ELR).fromModelObject(sourceDomMbr):
|
|
211
|
+
self.addDomMbrs(modelXbrl, domMbrRel.toModelObject, domMbrRel.consecutiveLinkrole, membersSet)
|
|
212
|
+
|
|
108
213
|
@lru_cache(1)
|
|
109
214
|
def contextsByDocument(self, modelXbrl: ModelXbrl) -> dict[str, list[ModelContext]]:
|
|
110
215
|
contextsByDocument = defaultdict(list)
|
|
@@ -144,21 +249,6 @@ class PluginValidationDataExtension(PluginData):
|
|
|
144
249
|
contextsWithSegments=contextsWithSegments,
|
|
145
250
|
)
|
|
146
251
|
|
|
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
252
|
@lru_cache(1)
|
|
163
253
|
def checkHiddenElements(self, modelXbrl: ModelXbrl) -> HiddenElementsData:
|
|
164
254
|
cssHiddenFacts = set()
|
|
@@ -169,7 +259,7 @@ class PluginValidationDataExtension(PluginData):
|
|
|
169
259
|
presentedHiddenEltIds = defaultdict(list)
|
|
170
260
|
requiredToDisplayFacts = set()
|
|
171
261
|
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
|
|
172
|
-
ixNStag = getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11)
|
|
262
|
+
ixNStag = str(getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11))
|
|
173
263
|
for ixHiddenElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag + "hidden"):
|
|
174
264
|
for tag in (ixNStag + "nonNumeric", ixNStag+"nonFraction"):
|
|
175
265
|
for ixElt in ixHiddenElt.iterdescendants(tag=tag):
|
|
@@ -218,7 +308,7 @@ class PluginValidationDataExtension(PluginData):
|
|
|
218
308
|
tupleElements = set()
|
|
219
309
|
orphanedFootnotes = set()
|
|
220
310
|
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
|
|
221
|
-
ixNStag = getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11)
|
|
311
|
+
ixNStag = str(getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11))
|
|
222
312
|
ixTupleTag = ixNStag + "tuple"
|
|
223
313
|
ixFractionTag = ixNStag + "fraction"
|
|
224
314
|
for elts in modelXbrl.ixdsEltById.values(): # type: ignore[attr-defined]
|
|
@@ -291,6 +381,49 @@ class PluginValidationDataExtension(PluginData):
|
|
|
291
381
|
def getContextsWithSegments(self, modelXbrl: ModelXbrl) -> list[ModelContext | None]:
|
|
292
382
|
return self.checkContexts(modelXbrl).contextsWithSegments
|
|
293
383
|
|
|
384
|
+
@lru_cache(1)
|
|
385
|
+
def getDocumentsInDts(self, modelXbrl: ModelXbrl) -> dict[ModelDocument, str | None]:
|
|
386
|
+
modelDocuments: dict[ModelDocument, str | None] = {}
|
|
387
|
+
if modelXbrl.modelDocument is None:
|
|
388
|
+
return modelDocuments
|
|
389
|
+
|
|
390
|
+
def _getDocumentsInDts(modelDocument: ModelDocument) -> None:
|
|
391
|
+
for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items():
|
|
392
|
+
if referencedDocument in modelDocuments:
|
|
393
|
+
continue
|
|
394
|
+
if referencedDocument.inDTS:
|
|
395
|
+
modelDocuments[referencedDocument] = modelDocumentReference.referringXlinkRole
|
|
396
|
+
_getDocumentsInDts(referencedDocument)
|
|
397
|
+
|
|
398
|
+
modelDocuments[modelXbrl.modelDocument] = None
|
|
399
|
+
_getDocumentsInDts(modelXbrl.modelDocument)
|
|
400
|
+
return modelDocuments
|
|
401
|
+
|
|
402
|
+
def getDomainMembers(self, modelXbrl: ModelXbrl) -> set[ModelConcept]:
|
|
403
|
+
domainMembers = set() # concepts which are dimension domain members
|
|
404
|
+
hcPrimaryItems: set[ModelConcept] = set()
|
|
405
|
+
hcMembers: set[Any] = set()
|
|
406
|
+
for hasHypercubeArcrole in (XbrlConst.all, XbrlConst.notAll):
|
|
407
|
+
hasHypercubeRelationships = modelXbrl.relationshipSet(hasHypercubeArcrole).fromModelObjects()
|
|
408
|
+
for hasHcRels in hasHypercubeRelationships.values():
|
|
409
|
+
for hasHcRel in hasHcRels:
|
|
410
|
+
sourceConcept: ModelConcept = hasHcRel.fromModelObject
|
|
411
|
+
hcPrimaryItems.add(sourceConcept)
|
|
412
|
+
# find associated primary items to source concept
|
|
413
|
+
for domMbrRel in modelXbrl.relationshipSet(XbrlConst.domainMember).fromModelObject(sourceConcept):
|
|
414
|
+
if domMbrRel.consecutiveLinkrole == hasHcRel.linkrole: # only those related to this hc
|
|
415
|
+
self.addDomMbrs(modelXbrl, domMbrRel.toModelObject, domMbrRel.consecutiveLinkrole, hcPrimaryItems)
|
|
416
|
+
hc = hasHcRel.toModelObject
|
|
417
|
+
for hcDimRel in modelXbrl.relationshipSet(XbrlConst.hypercubeDimension, hasHcRel.consecutiveLinkrole).fromModelObject(hc):
|
|
418
|
+
dim = hcDimRel.toModelObject
|
|
419
|
+
if isinstance(dim, ModelConcept):
|
|
420
|
+
for dimDomRel in modelXbrl.relationshipSet(XbrlConst.dimensionDomain, hcDimRel.consecutiveLinkrole).fromModelObject(dim):
|
|
421
|
+
dom = dimDomRel.toModelObject
|
|
422
|
+
if isinstance(dom, ModelConcept):
|
|
423
|
+
self.addDomMbrs(modelXbrl, dom, dimDomRel.consecutiveLinkrole, hcMembers)
|
|
424
|
+
domainMembers.update(hcMembers)
|
|
425
|
+
return domainMembers
|
|
426
|
+
|
|
294
427
|
def getEligibleForTransformHiddenFacts(self, modelXbrl: ModelXbrl) -> set[ModelInlineFact]:
|
|
295
428
|
return self.checkHiddenElements(modelXbrl).eligibleForTransformHiddenFacts
|
|
296
429
|
|
|
@@ -321,8 +454,19 @@ class PluginValidationDataExtension(PluginData):
|
|
|
321
454
|
)
|
|
322
455
|
|
|
323
456
|
@lru_cache(1)
|
|
324
|
-
def
|
|
325
|
-
|
|
457
|
+
def getExtensionFilenameFormatPattern(self) -> re.Pattern[str]:
|
|
458
|
+
return re.compile(
|
|
459
|
+
r"^(?<base>[^-]*)"
|
|
460
|
+
r"-(?<year>\d{4})-(?<month>0[1-9]|1[012])-(?<day>0?[1-9]|[12][0-9]|3[01])"
|
|
461
|
+
r"(?<suffix>[_pre|_cal|_lab|_def]*)"
|
|
462
|
+
r"(?<lang>-*[^-]*)"
|
|
463
|
+
r"\.(?<extension>xsd|xml)$",
|
|
464
|
+
flags=re.ASCII
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
@lru_cache(1)
|
|
468
|
+
def getFilenameParts(self, filename: str, filenamePattern: re.Pattern[str]) -> dict[str, Any] | None:
|
|
469
|
+
match = filenamePattern.match(filename)
|
|
326
470
|
if match:
|
|
327
471
|
return match.groupdict()
|
|
328
472
|
return None
|
|
@@ -331,6 +475,56 @@ class PluginValidationDataExtension(PluginData):
|
|
|
331
475
|
def getIxdsDocBasenames(self, modelXbrl: ModelXbrl) -> set[str]:
|
|
332
476
|
return set(Path(url).name for url in getattr(modelXbrl, "ixdsDocUrls", []))
|
|
333
477
|
|
|
478
|
+
def getExtensionConcepts(self, modelXbrl: ModelXbrl) -> list[ModelConcept]:
|
|
479
|
+
"""
|
|
480
|
+
Returns a list of extension concepts in the DTS.
|
|
481
|
+
"""
|
|
482
|
+
extensionConcepts = []
|
|
483
|
+
for concepts in modelXbrl.nameConcepts.values():
|
|
484
|
+
for concept in concepts:
|
|
485
|
+
if self.isExtensionUri(concept.qname.namespaceURI, modelXbrl):
|
|
486
|
+
extensionConcepts.append(concept)
|
|
487
|
+
return extensionConcepts
|
|
488
|
+
|
|
489
|
+
@lru_cache(1)
|
|
490
|
+
def getExtensionData(self, modelXbrl: ModelXbrl) -> ExtensionData:
|
|
491
|
+
extensionDocuments = {}
|
|
492
|
+
extensionImportedUrls = set()
|
|
493
|
+
documentsInDts = self.getDocumentsInDts(modelXbrl)
|
|
494
|
+
for modelDocument, hrefXlinkRole in documentsInDts.items():
|
|
495
|
+
if not self.isExtensionUri(modelDocument.uri, modelDocument.modelXbrl):
|
|
496
|
+
# Skip non-extension documents
|
|
497
|
+
continue
|
|
498
|
+
if modelDocument.type in (ModelDocumentType.LINKBASE, ModelDocumentType.SCHEMA):
|
|
499
|
+
extensionDocuments[modelDocument] = ExtensionDocumentData(
|
|
500
|
+
basename=modelDocument.basename,
|
|
501
|
+
hrefXlinkRole=hrefXlinkRole,
|
|
502
|
+
linkbases=self.getLinkbaseData(modelDocument),
|
|
503
|
+
)
|
|
504
|
+
if modelDocument.type == ModelDocumentType.SCHEMA:
|
|
505
|
+
for doc, docRef in modelDocument.referencesDocument.items():
|
|
506
|
+
if "import" in docRef.referenceTypes:
|
|
507
|
+
extensionImportedUrls.add(doc.uri)
|
|
508
|
+
return ExtensionData(
|
|
509
|
+
extensionConcepts=self.getExtensionConcepts(modelXbrl),
|
|
510
|
+
extensionDocuments=extensionDocuments,
|
|
511
|
+
extensionImportedUrls=frozenset(sorted(extensionImportedUrls)),
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
def getLinkbaseData(self, modelDocument: ModelDocument) -> list[LinkbaseData]:
|
|
515
|
+
linkbases = []
|
|
516
|
+
for linkbaseType in LinkbaseType:
|
|
517
|
+
for linkElt in modelDocument.xmlRootElement.iterdescendants(tag=linkbaseType.getLinkQn().clarkNotation):
|
|
518
|
+
arcQn = linkbaseType.getArcQn()
|
|
519
|
+
arcs = list(linkElt.iterdescendants(tag=arcQn.clarkNotation))
|
|
520
|
+
linkbases.append(LinkbaseData(
|
|
521
|
+
arcs=arcs,
|
|
522
|
+
basename=modelDocument.basename,
|
|
523
|
+
element=linkElt,
|
|
524
|
+
linkbaseType=linkbaseType,
|
|
525
|
+
))
|
|
526
|
+
return linkbases
|
|
527
|
+
|
|
334
528
|
def getNoMatchLangFootnotes(self, modelXbrl: ModelXbrl) -> set[ModelInlineFootnote]:
|
|
335
529
|
return self.checkInlineHTMLElements(modelXbrl).noMatchLangFootnotes
|
|
336
530
|
|
|
@@ -346,6 +540,14 @@ class PluginValidationDataExtension(PluginData):
|
|
|
346
540
|
def getTupleElements(self, modelXbrl: ModelXbrl) -> set[tuple[Any]]:
|
|
347
541
|
return self.checkInlineHTMLElements(modelXbrl).tupleElements
|
|
348
542
|
|
|
543
|
+
@lru_cache(1)
|
|
544
|
+
def getReportingPeriod(self, modelXbrl: ModelXbrl) -> str | None:
|
|
545
|
+
reportingPeriodFacts = modelXbrl.factsByQname.get(self.financialReportingPeriodQn, set())
|
|
546
|
+
for fact in reportingPeriodFacts:
|
|
547
|
+
if fact.xValid >= VALID:
|
|
548
|
+
return cast(str, fact.xValue)
|
|
549
|
+
return None
|
|
550
|
+
|
|
349
551
|
@lru_cache(1)
|
|
350
552
|
def getReportXmlLang(self, modelXbrl: ModelXbrl) -> str | None:
|
|
351
553
|
reportXmlLang = None
|
|
@@ -365,13 +567,20 @@ class PluginValidationDataExtension(PluginData):
|
|
|
365
567
|
def getTargetElements(self, modelXbrl: ModelXbrl) -> list[Any]:
|
|
366
568
|
targetElements = []
|
|
367
569
|
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
|
|
368
|
-
ixNStag = getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11)
|
|
570
|
+
ixNStag = str(getattr(ixdsHtmlRootElt.modelDocument, "ixNStag", ixbrl11))
|
|
369
571
|
ixTags = set(ixNStag + ln for ln in ("nonNumeric", "nonFraction", "references", "relationship"))
|
|
370
572
|
for elt, depth in etreeIterWithDepth(ixdsHtmlRootElt):
|
|
371
573
|
if elt.tag in ixTags and elt.get("target"):
|
|
372
574
|
targetElements.append(elt)
|
|
373
575
|
return targetElements
|
|
374
576
|
|
|
577
|
+
def isExtensionUri(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
578
|
+
if uri.startswith(modelXbrl.uriDir):
|
|
579
|
+
return True
|
|
580
|
+
if not any(uri.startswith(taxonomyUri) for taxonomyUri in STANDARD_TAXONOMY_URLS):
|
|
581
|
+
return True
|
|
582
|
+
return False
|
|
583
|
+
|
|
375
584
|
@lru_cache(1)
|
|
376
585
|
def isFilenameValidCharacters(self, filename: str) -> bool:
|
|
377
586
|
match = self.getFilenameAllowedCharactersPattern().match(filename)
|
|
@@ -10,7 +10,9 @@ from arelle.ModelXbrl import ModelXbrl
|
|
|
10
10
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
11
11
|
from arelle.typing import TypeGetText
|
|
12
12
|
from arelle.utils.validate.ValidationPlugin import ValidationPlugin
|
|
13
|
-
from .DisclosureSystems import DISCLOSURE_SYSTEM_NT16, DISCLOSURE_SYSTEM_NT17, DISCLOSURE_SYSTEM_NT18,
|
|
13
|
+
from .DisclosureSystems import (DISCLOSURE_SYSTEM_NT16, DISCLOSURE_SYSTEM_NT17, DISCLOSURE_SYSTEM_NT18,
|
|
14
|
+
DISCLOSURE_SYSTEM_NT19, DISCLOSURE_SYSTEM_NL_INLINE_2024,
|
|
15
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024_GAAP_OTHER)
|
|
14
16
|
from .PluginValidationDataExtension import PluginValidationDataExtension
|
|
15
17
|
|
|
16
18
|
_: TypeGetText
|
|
@@ -174,6 +176,13 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
174
176
|
entrypoints = {entrypointRoot + e for e in [
|
|
175
177
|
'kvk-annual-report-ifrs-ext.xsd',
|
|
176
178
|
'kvk-annual-report-nlgaap-ext.xsd',
|
|
179
|
+
]}
|
|
180
|
+
elif disclosureSystem == DISCLOSURE_SYSTEM_NL_INLINE_2024_GAAP_OTHER:
|
|
181
|
+
jenvNamespace = 'https://www.nltaxonomie.nl/bw2-titel9/2024-12-31/bw2-titel9-cor'
|
|
182
|
+
kvkINamespace = None
|
|
183
|
+
nlTypesNamespace = None
|
|
184
|
+
entrypointRoot = 'http://www.nltaxonomie.nl/kvk/2024-12-31/'
|
|
185
|
+
entrypoints = {entrypointRoot + e for e in [
|
|
177
186
|
'kvk-annual-report-other-gaap.xsd',
|
|
178
187
|
]}
|
|
179
188
|
else:
|
|
@@ -186,6 +195,7 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
186
195
|
documentResubmissionUnsurmountableInaccuraciesQn=qname(f'{{{kvkINamespace}}}DocumentResubmissionDueToUnsurmountableInaccuracies'),
|
|
187
196
|
entrypointRoot=entrypointRoot,
|
|
188
197
|
entrypoints=entrypoints,
|
|
198
|
+
financialReportingPeriodQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriod'),
|
|
189
199
|
financialReportingPeriodCurrentStartDateQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriodCurrentStartDate'),
|
|
190
200
|
financialReportingPeriodCurrentEndDateQn=qname(f'{{{jenvNamespace}}}FinancialReportingPeriodCurrentEndDate'),
|
|
191
201
|
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
|
}
|
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
<!-- see arelle/config/disclosuresystems.xml for full comments -->
|
|
6
6
|
<DisclosureSystem
|
|
7
7
|
names="NL-INLINE-2024|nl-inline-2024|kvk-inline-2024-preview"
|
|
8
|
-
description="Checks for NL INLINE 2024"
|
|
8
|
+
description="Checks for NL INLINE 2024. For use with NL GAAP and NL IFRS"
|
|
9
|
+
validationType="NL"
|
|
10
|
+
/>
|
|
11
|
+
<DisclosureSystem
|
|
12
|
+
names="NL-INLINE-2024-GAAP-OTHER|nl-inline-2024-gaap-other|kvk-inline-2024-gaap-other-preview"
|
|
13
|
+
description="Checks for NL INLINE 2024. For use with other GAAP."
|
|
9
14
|
validationType="NL"
|
|
10
15
|
/>
|
|
11
16
|
<DisclosureSystem
|